index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. <template>
  2. <div class="m-container">
  3. <h2>
  4. <div class="squrt"></div>
  5. 教学伴奏
  6. </h2>
  7. <div class="m-core">
  8. <el-button
  9. @click="open('COMMON')"
  10. type="primary"
  11. v-permission="'sysMusicScore/add'"
  12. >添加公用伴奏</el-button
  13. >
  14. <!-- <el-button @click="open('PERSON')" type="primary" v-permission="'sysMusicScore/add'">添加个人伴奏</el-button> -->
  15. <saveform
  16. ref="searchForm"
  17. :model.sync="searchForm"
  18. inline
  19. style="margin-top: 20px"
  20. >
  21. <el-form-item prop="search">
  22. <el-input
  23. v-model="searchForm.search"
  24. @submit="submit"
  25. @reset="reset"
  26. @keyup.enter.native="
  27. (e) => {
  28. e.target.blur();
  29. $refs.searchForm.save();
  30. submit()
  31. }
  32. "
  33. clearable
  34. placeholder="伴奏编号/伴奏名称"
  35. />
  36. </el-form-item>
  37. <!-- <el-form-item prop="organId">
  38. <el-select
  39. class="multiple"
  40. v-model.trim="searchForm.organId"
  41. filterable
  42. multiple
  43. collapse-tags
  44. clearable
  45. placeholder="请选择分部"
  46. >
  47. <el-option
  48. v-for="(item, index) in selects.branchs"
  49. :key="index"
  50. :label="item.name"
  51. :value="item.id"
  52. ></el-option>
  53. </el-select>
  54. </el-form-item> -->
  55. <!-- 勇哥说没事 -->
  56. <!-- <el-form-item prop="type">
  57. <el-select
  58. v-model="searchForm.type"
  59. clearable
  60. placeholder="请选择类型"
  61. >
  62. <el-option
  63. v-for="(item, key) in songUseType"
  64. :key="key"
  65. :label="item"
  66. :value="key"
  67. ></el-option>
  68. </el-select>
  69. </el-form-item> -->
  70. <!-- <el-form-item prop="clientType">
  71. <el-select
  72. v-model="searchForm.clientType"
  73. clearable
  74. filterable
  75. placeholder="请选择客户端类型"
  76. >
  77. <el-option value="NETWORK_ROOM" label="网络教室"></el-option>
  78. <el-option value="SMART_PRACTICE" label="云教练"></el-option>
  79. </el-select>
  80. </el-form-item> -->
  81. <el-form-item prop="categoriesId">
  82. <el-cascader
  83. ref="myCascader"
  84. popper-class="myCascader"
  85. @change="changeCategor"
  86. :show-all-levels="true"
  87. clearable
  88. v-model="searchForm.categoriesId"
  89. style="width: 100%"
  90. :options="tree"
  91. placeholder="请选择分类"
  92. :props="treeProps"
  93. ></el-cascader>
  94. </el-form-item>
  95. <el-form-item prop="subjectId">
  96. <el-select
  97. v-model="searchForm.subjectId"
  98. clearable
  99. placeholder="请选择声部"
  100. >
  101. <el-option
  102. v-for="item in selects.subjects"
  103. :value="item.id"
  104. :label="item.name"
  105. :key="item.id"
  106. ></el-option>
  107. </el-select>
  108. </el-form-item>
  109. <el-form-item prop="rankType">
  110. <el-select
  111. v-model="searchForm.rankType"
  112. clearable
  113. filterable
  114. placeholder="请选择是否收费"
  115. >
  116. <el-option :value="0" label="免费"></el-option>
  117. <el-option :value="1" label="收费"></el-option>
  118. </el-select>
  119. </el-form-item>
  120. <el-form-item prop="showFlag">
  121. <el-select
  122. v-model="searchForm.showFlag"
  123. clearable
  124. filterable
  125. placeholder="请选择伴奏状态"
  126. >
  127. <el-option :value="1" label="启用"></el-option>
  128. <el-option :value="0" label="停用"></el-option>
  129. </el-select>
  130. </el-form-item>
  131. <el-form-item>
  132. <el-button @click="submit" type="primary">搜索</el-button>
  133. <el-button @click="reset" type="danger">重置</el-button>
  134. <el-button
  135. @click="onExport"
  136. type="primary"
  137. v-permission="'export/exportMusicScoreList'"
  138. >导出</el-button
  139. >
  140. </el-form-item>
  141. </saveform>
  142. <el-table
  143. style="width: 100%"
  144. :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
  145. :data="tableList"
  146. >
  147. <el-table-column align="center" prop="id" label="编号" width="80px">
  148. <template slot-scope="scope">
  149. <div>
  150. <copy-text>{{ scope.row.id }}</copy-text>
  151. </div>
  152. </template>
  153. </el-table-column>
  154. <el-table-column align="center" prop="name" label="名称" width="180px">
  155. <template slot-scope="scope">
  156. <div>
  157. <copy-text>{{ scope.row.name }}</copy-text>
  158. </div>
  159. </template>
  160. </el-table-column>
  161. <!-- <el-table-column
  162. align="center"
  163. prop="organName"
  164. label="所属分部"
  165. width="180px"
  166. >
  167. <template slot-scope="scope">
  168. <div>
  169. <Tooltip :content="scope.row.organName" />
  170. </div>
  171. </template>
  172. </el-table-column> -->
  173. <el-table-column
  174. align="center"
  175. prop="subjectName"
  176. label="所属声部"
  177. width="180px"
  178. >
  179. <template slot-scope="scope">
  180. <div>
  181. <Tooltip :content="scope.row.subjectName" />
  182. </div>
  183. </template>
  184. </el-table-column>
  185. <el-table-column align="center" prop="type" label="类型">
  186. <template slot-scope="scope">
  187. {{ scope.row.type | songUseTypeFormat }}
  188. </template>
  189. </el-table-column>
  190. <!-- <el-table-column
  191. align="center"
  192. prop="speed"
  193. label="速度"
  194. /> -->
  195. <el-table-column
  196. align="center"
  197. prop="categoriesName"
  198. label="分类"
  199. width="180px"
  200. />
  201. <el-table-column align="center" label="是否收费" width="180px">
  202. <template slot-scope="scope">
  203. {{ scope.row.rankIds ? "收费" : "免费" }}
  204. </template>
  205. </el-table-column>
  206. <el-table-column align="center" label="节拍器" width="180px">
  207. <template slot-scope="scope">
  208. {{ scope.row.isOpenMetronome ? "播放" : "不播放" }}
  209. </template>
  210. </el-table-column>
  211. <el-table-column
  212. align="center"
  213. prop="subjectNames"
  214. label="是否上传伴奏"
  215. width="120px"
  216. >
  217. <template slot-scope="scope">
  218. <div>
  219. <p>
  220. 含节拍器:{{ scope.row.accompanimentMetronomeUrl ? "是" : "否" }}
  221. </p>
  222. <p>不含节拍器:{{ scope.row.accompanimentUrl ? "是" : "否" }}</p>
  223. </div>
  224. </template>
  225. </el-table-column>
  226. <el-table-column align="center" label="伴奏状态" width="180px">
  227. <template slot-scope="scope">
  228. {{ scope.row.showFlag ? "启用" : "停用" }}
  229. </template>
  230. </el-table-column>
  231. <!-- <el-table-column
  232. align="center"
  233. prop="categoriesName"
  234. label="客户端类型"
  235. width="180px"
  236. >
  237. <template slot-scope="scope">
  238. <div>
  239. {{ scope.row.clientType | clientType }}
  240. </div>
  241. </template>
  242. </el-table-column> -->
  243. <!-- clientType -->
  244. <el-table-column
  245. align="center"
  246. prop="createUserName"
  247. label="上传人"
  248. width="180px"
  249. />
  250. <el-table-column
  251. align="center"
  252. prop="createTime"
  253. label="上传时间"
  254. width="180px"
  255. />
  256. <el-table-column
  257. align="center"
  258. width="180px"
  259. label="操作"
  260. fixed="right"
  261. >
  262. <template slot-scope="scope">
  263. <!-- <el-button
  264. type="text"
  265. @click="looker(scope.row)"
  266. :disabled="
  267. (scope.row.isOpenMetronome
  268. ? !scope.row.url
  269. : !scope.row.metronomeUrl) ||
  270. scope.row.clientType != 'SMART_PRACTICE'
  271. "
  272. >预览</el-button
  273. > -->
  274. <el-dropdown :disabled="scope.row.clientType != 'SMART_PRACTICE'">
  275. <span class="el-dropdown-link">
  276. 预览<i class="el-icon-arrow-down el-icon--right"></i>
  277. </span>
  278. <el-dropdown-menu slot="dropdown">
  279. <el-dropdown-item>
  280. <el-button type="text" @click="looker(scope.row, 1)" >系统自带节拍器</el-button></el-dropdown-item>
  281. </el-dropdown-item>
  282. <el-dropdown-item>
  283. <el-button type="text" @click="looker(scope.row, 0)" >原音自带节拍器</el-button></el-dropdown-item>
  284. </el-dropdown-menu>
  285. </el-dropdown>
  286. <el-button
  287. type="text"
  288. @click="player(scope.row)"
  289. :disabled="!scope.row.url"
  290. >播放</el-button
  291. >
  292. <el-button type="text" @click="changeStatus(scope.row)">{{
  293. scope.row.showFlag ? "停用" : "启用"
  294. }}</el-button>
  295. <el-button
  296. type="text"
  297. @click="transcod(scope.row)"
  298. :disabled="!!scope.row.showFlag"
  299. v-permission="'sysMusicScore/transcod'"
  300. >转码</el-button
  301. >
  302. <el-button
  303. type="text"
  304. @click="edit(scope.row)"
  305. :disabled="!!scope.row.showFlag"
  306. v-permission="'sysMusicScore/update'"
  307. >修改</el-button
  308. >
  309. <el-button
  310. type="text"
  311. @click="remove(scope.row)"
  312. v-permission="'sysMusicScore/del'"
  313. >删除</el-button
  314. >
  315. </template>
  316. </el-table-column>
  317. </el-table>
  318. <pagination
  319. sync
  320. :total.sync="rules.total"
  321. :page.sync="rules.page"
  322. :limit.sync="rules.limit"
  323. :page-sizes="rules.page_size"
  324. @pagination="FetchList"
  325. />
  326. </div>
  327. <el-dialog
  328. v-if="audioVisible"
  329. width="400px"
  330. :visible.sync="audioVisible"
  331. title="播放伴奏"
  332. >
  333. <audio style="display: block; margin: auto" controls :src="activeUrl" />
  334. </el-dialog>
  335. <el-dialog
  336. :title="title"
  337. :visible.sync="visible"
  338. width="740px"
  339. v-if="visible"
  340. >
  341. <submit-form
  342. :detail="detail"
  343. :type="type"
  344. @submited="FetchList"
  345. @close="visible = false"
  346. />
  347. </el-dialog>
  348. <el-dialog
  349. v-if="lookVisible"
  350. width="667px"
  351. class="lookForm"
  352. :visible.sync="lookVisible"
  353. title="预览"
  354. >
  355. <template slot="title">
  356. <span style="color: #fff;">预览</span>
  357. <el-select v-model="selectPart" placeholder="请选择分谱">
  358. <el-option
  359. v-for="(item, index) in parts"
  360. :key="item"
  361. :label="item"
  362. :value="index">
  363. </el-option>
  364. </el-select>
  365. </template>
  366. <iframe
  367. id="iframe"
  368. v-if="lookVisible"
  369. style="width: 667px; height: 386px"
  370. ref="iframe"
  371. :src="accompanyUrl + '?part-index=' + selectPart"
  372. />
  373. <div class="iframe_back"></div>
  374. <div class="iframe_help"></div>
  375. <div class="iframe_header_back"></div>
  376. </el-dialog>
  377. </div>
  378. </template>
  379. <script>
  380. import saveform from "@/components/save-form";
  381. import pagination from "@/components/Pagination/index";
  382. import { songUseType } from "@/constant";
  383. import { QueryPage, Del, Show, queryTree, Transcod } from "./api";
  384. import form from "./modals/form";
  385. import { vaildTeachingUrl } from "@/utils/validate";
  386. import { getToken } from "@/utils/auth";
  387. import deepClone from "@/helpers/deep-clone/";
  388. import Tooltip from "@/components/Tooltip/index";
  389. import { Export } from "@/utils/downLoadFile";
  390. import qs from "qs";
  391. import cleanDeep from "clean-deep";
  392. export default {
  393. name: "accompaniment",
  394. components: {
  395. saveform,
  396. pagination,
  397. "submit-form": form,
  398. Tooltip,
  399. },
  400. data() {
  401. return {
  402. parts: [],
  403. selectPart: 0,
  404. tabName: "first",
  405. type: "",
  406. activeUrl: "",
  407. songUseType,
  408. lookVisible: false,
  409. accompanyUrl: null, // 预览地址
  410. accompanyUrl2: null, // 预览地址
  411. audioVisible: false,
  412. tableList: [],
  413. searchForm: {
  414. search: "",
  415. // type: "",
  416. subjectId: "",
  417. categoriesId: [],
  418. clientType: "",
  419. rankType: null,
  420. showFlag: null,
  421. },
  422. rules: {
  423. // 分页规则
  424. limit: 10, // 限制显示条数
  425. page: 1, // 当前页
  426. total: 0, // 总条数
  427. page_size: [10, 20, 40, 50], // 选择限制显示条数
  428. },
  429. detail: null,
  430. visible: false,
  431. tree: [],
  432. treeProps: {
  433. value: "id",
  434. label: "name",
  435. children: "sysMusicScoreCategoriesList",
  436. checkStrictly: true,
  437. expandTrigger: "hover",
  438. },
  439. };
  440. },
  441. computed: {
  442. title() {
  443. const t1 = this.detail ? "修改" : "添加";
  444. let t2 = this.type === "COMMON" ? "公用" : "个人";
  445. if (this.detail) {
  446. t2 = this.detail.type === "COMMON" ? "公用" : "个人";
  447. }
  448. return t1 + t2 + "伴奏";
  449. },
  450. },
  451. async mounted() {
  452. const { query, params } = this.$route;
  453. await this.FetchTree();
  454. if (params.categoriesId) {
  455. // console.log(this.tree)
  456. // console.log(this.formatParentId(params.categoriesId, this.tree))
  457. this.searchForm.categoriesId = this.formatParentId(
  458. params.categoriesId,
  459. this.tree
  460. );
  461. }
  462. this.$store.dispatch("setSubjects");
  463. this.$store.dispatch("setBranchs");
  464. await this.FetchList();
  465. // 点击Cascader label选中
  466. },
  467. methods: {
  468. formatParentId(id, list, ids = []) {
  469. for (const item of list) {
  470. if (item.sysMusicScoreCategoriesList) {
  471. const cIds = this.formatParentId(
  472. id,
  473. item.sysMusicScoreCategoriesList,
  474. [...ids, item.id]
  475. );
  476. if (cIds.includes(id)) {
  477. return cIds;
  478. }
  479. }
  480. if (item.id === id) {
  481. return [...ids, id];
  482. }
  483. }
  484. return ids;
  485. },
  486. async FetchTree() {
  487. try {
  488. const res = await queryTree();
  489. this.tree = res.data;
  490. this.formatTree(this.tree);
  491. } catch (error) {}
  492. },
  493. formatTree(data) {
  494. for (let i of data) {
  495. if (
  496. i.sysMusicScoreCategoriesList &&
  497. i.sysMusicScoreCategoriesList.length > 0
  498. ) {
  499. this.formatTree(i.sysMusicScoreCategoriesList, i);
  500. } else {
  501. i.sysMusicScoreCategoriesList = null;
  502. }
  503. }
  504. },
  505. async FetchList() {
  506. try {
  507. let { categoriesId, organId, ...search } = deepClone(this.searchForm);
  508. const res = await QueryPage({
  509. ...search,
  510. categoriesId:
  511. categoriesId && categoriesId.length > 0 ? categoriesId.pop() : null,
  512. page: this.rules.page,
  513. rows: this.rules.limit,
  514. });
  515. this.tableList = res.data.rows;
  516. this.$set(this.rules, "total", res.data.total);
  517. } catch (error) {}
  518. },
  519. submit() {
  520. this.$set(this.rules, "page", 1);
  521. this.$refs.searchForm.validate((valid) => {
  522. if (valid) {
  523. this.FetchList();
  524. }
  525. });
  526. },
  527. reset() {
  528. this.$refs.searchForm.resetFields();
  529. this.FetchList();
  530. },
  531. getPartListNames(xml) {
  532. if (!xml) return [];
  533. const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
  534. const partList =
  535. xmlParse
  536. .getElementsByTagName("part-list")?.[0]
  537. ?.getElementsByTagName("score-part") || [];
  538. const partListNames = Array.from(partList).map(
  539. (item) => item.getElementsByTagName("part-name")?.[0].textContent || ""
  540. );
  541. this.xmlFirstSpeed =
  542. xmlParse.getElementsByTagName("per-minute")?.[0]?.textContent || "";
  543. return partListNames.filter(text => text.toLocaleUpperCase() !== 'COMMON');
  544. },
  545. async looker(row, num) {
  546. try {
  547. this.selectPart = 0;
  548. const res = await fetch(row.xmlUrl);
  549. const text = await res.text();
  550. this.parts = this.getPartListNames(text);
  551. } catch (error) {
  552. this.parts = [];
  553. this.selectPart = 0;
  554. }
  555. this.accompanyUrl =
  556. vaildTeachingUrl() +
  557. "/accompany?Authorization=" +
  558. getToken() +
  559. "&platform=web&isOpenMetronome=" +
  560. num +
  561. '&isMove=1' +
  562. "#/detail/" +
  563. row.id;
  564. this.lookVisible = true;
  565. // this.$nextTick(() => {
  566. // console.log(this.$refs.iframe)
  567. // let iframe = this.$refs.iframe
  568. // iframe.onload = function() {
  569. // console.log('完成', iframe)
  570. // iframe.contentWindow.document.querySelector('#tips-step-0').style.display = 'none'
  571. // }
  572. // })
  573. },
  574. player(row) {
  575. this.activeUrl = row.url;
  576. this.audioVisible = true;
  577. },
  578. async changeStatus(row) {
  579. try {
  580. let status = row.showFlag ? "停用" : "启用";
  581. await this.$confirm("是否确认" + status + "此伴奏?", "提示", {
  582. type: "warning",
  583. });
  584. await Show({
  585. sysMusicScoreId: row.id,
  586. showFlag: row.showFlag ? 0 : 1,
  587. });
  588. this.$message.success(status + "成功");
  589. this.FetchList();
  590. } catch (error) {}
  591. },
  592. edit(row) {
  593. this.detail = row;
  594. this.visible = true;
  595. },
  596. async transcod(row) {
  597. try {
  598. const res = await Transcod(row);
  599. console.log(res);
  600. } catch (error) {}
  601. },
  602. open(type) {
  603. this.type = type;
  604. this.detail = null;
  605. this.visible = true;
  606. },
  607. async remove(row) {
  608. try {
  609. await this.$confirm("是否确认删除此伴奏?", "提示", {
  610. type: "warning",
  611. });
  612. await Del(row.id);
  613. this.$message.success("删除成功");
  614. this.FetchList();
  615. } catch (error) {}
  616. },
  617. changeCategor(e) {
  618. this.$refs.myCascader.dropDownVisible = false;
  619. },
  620. onExport() {
  621. let { categoriesId, organId, ...search } = deepClone(this.searchForm);
  622. Export(
  623. this,
  624. {
  625. url: "/api-web/export/exportMusicScoreList",
  626. params: {
  627. ...search,
  628. categoriesId:
  629. categoriesId && categoriesId.length > 0
  630. ? categoriesId.pop()
  631. : null,
  632. },
  633. fileName: `教学伴奏导出.xls`,
  634. method: "get",
  635. },
  636. "确定导出教学伴奏"
  637. );
  638. },
  639. },
  640. };
  641. </script>
  642. <style lang="less">
  643. .myCascader {
  644. .el-radio {
  645. width: 100%;
  646. height: 100%;
  647. z-index: 10;
  648. position: absolute;
  649. top: 10px;
  650. right: 10px;
  651. }
  652. .el-radio__input {
  653. visibility: hidden;
  654. }
  655. .el-cascader-node__postfix {
  656. top: 10px;
  657. }
  658. }
  659. </style>
  660. <style lang="less" scoped>
  661. ::v-deep .el-dropdown {
  662. margin-right: 10px;
  663. color: #14928a;
  664. }
  665. .remark {
  666. display: inline;
  667. overflow: hidden;
  668. white-space: pre;
  669. }
  670. .lookForm {
  671. ::v-deep .el-dialog__body {
  672. padding: 0;
  673. }
  674. }
  675. .iframe_back {
  676. width: 195px;
  677. height: 45px;
  678. position: absolute;
  679. bottom: 50px;
  680. background: transparent;
  681. left: 50%;
  682. z-index: 99;
  683. margin-left: -100px;
  684. }
  685. .iframe_help {
  686. position: absolute;
  687. background: transparent;
  688. width: 50px;
  689. height: 120px;
  690. top: 50%;
  691. right: 0;
  692. margin-top: -35px;
  693. }
  694. .iframe_header_back {
  695. background: transparent;
  696. width: 225px;
  697. height: 50px;
  698. position: absolute;
  699. top: 65px;
  700. left: 20px;
  701. }
  702. </style>