form.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <template>
  2. <div>
  3. <el-form ref="form" :model="form" label-width="150px">
  4. <el-form-item
  5. prop="sysMusicScore.name"
  6. label="曲名"
  7. :rules="[{ required: true, message: '请输入曲名' }]"
  8. >
  9. <el-input placeholder="请输入曲名" v-model="form.sysMusicScore.name" />
  10. </el-form-item>
  11. <el-form-item
  12. prop="sysMusicScore.musicScoreCategoriesId"
  13. label="分类"
  14. :rules="[{ required: true, message: '请选择分类' }]"
  15. >
  16. <el-cascader
  17. v-model="form.sysMusicScore.musicScoreCategoriesId"
  18. style="width: 100%"
  19. :options="tree"
  20. placeholder="请选择分类"
  21. :props="treeProps"
  22. ></el-cascader>
  23. <!-- <el-select style="width: 100%!important;" v-model="form.sysMusicScore.musicScoreCategoriesId" placeholder="请选择声部">
  24. <el-option
  25. v-for="item in selects.subjects"
  26. :value="item.id"
  27. :label="item.name"
  28. :key="item.id"
  29. ></el-option>
  30. </el-select> -->
  31. </el-form-item>
  32. <!-- 是否收费 免费 收费 -->
  33. <el-form-item
  34. prop="rankIdType"
  35. label="是否收费"
  36. :rules="[{ required: true, message: '请选择是否收费' }]"
  37. >
  38. <el-select
  39. style="width: 100% !important"
  40. v-model="form.rankIdType"
  41. placeholder="请选择是否收费"
  42. @change="rankChange"
  43. >
  44. <!-- <el-option
  45. :value="item.id"
  46. :label="item.name"
  47. v-for="item in memberRankList"
  48. :key="item.id"
  49. ></el-option> -->
  50. <el-option :value="0" label="免费"></el-option>
  51. <el-option :value="1" label="收费"></el-option>
  52. </el-select>
  53. </el-form-item>
  54. <el-form-item
  55. prop="sysMusicScore.isOpenMetronome"
  56. label="节拍器"
  57. :rules="[{required: true, message: '请选择节拍器'}]"
  58. >
  59. <template slot="label">
  60. <span>
  61. 节拍器
  62. <el-tooltip placement="top" popper-class="mTooltip">
  63. <div slot="content">
  64. 是否播放系统自带节拍器
  65. </div>
  66. <i
  67. class="el-icon-question"
  68. style="font-size: 18px; color: #f56c6c"
  69. ></i>
  70. </el-tooltip>
  71. </span>
  72. </template>
  73. <el-select
  74. style="width: 100% !important"
  75. v-model="form.sysMusicScore.isOpenMetronome"
  76. placeholder="请选择节拍器"
  77. >
  78. <!-- <el-option
  79. :value="item.id"
  80. :label="item.name"
  81. v-for="item in memberRankList"
  82. :key="item.id"
  83. ></el-option> -->
  84. <el-option
  85. :value="0"
  86. label="不播放"
  87. ></el-option>
  88. <el-option
  89. :value="1"
  90. label="播放"
  91. ></el-option>
  92. </el-select>
  93. </el-form-item>
  94. <!-- <el-form-item
  95. prop="sysMusicScore.clientType"
  96. label="客户端类型"
  97. :rules="[{ required: true, message: '请选择客户端类型' }]"
  98. >
  99. <el-select
  100. style="width: 100% !important"
  101. v-model="form.sysMusicScore.clientType"
  102. placeholder="请选择客户端类型"
  103. >
  104. <el-option value="NETWORK_ROOM" label="网络教室"></el-option>
  105. <el-option value="SMART_PRACTICE" label="团练宝"></el-option>
  106. </el-select>
  107. </el-form-item> -->
  108. <!-- <el-form-item
  109. prop="sysMusicScore.renderFrom"
  110. label="渲染模式"
  111. :rules="[{ required: true, message: '请选择渲染模式' }]"
  112. >
  113. <el-select
  114. style="width: 100% !important"
  115. v-model="form.sysMusicScore.renderFrom"
  116. placeholder="请选择渲染模式"
  117. >
  118. <el-option value="H5" label="H5"></el-option>
  119. <el-option value="APP" label="原生"></el-option>
  120. </el-select>
  121. </el-form-item> -->
  122. <el-form-item
  123. prop="sysMusicScore.playMode"
  124. label="播放模式"
  125. :rules="[{ required: true, message: '请选择播放模式' }]"
  126. >
  127. <el-select
  128. style="width: 100% !important"
  129. v-model="form.sysMusicScore.playMode"
  130. placeholder="请选择播放模式"
  131. >
  132. <el-option value="MP3" label="MP3播放"></el-option>
  133. <el-option value="XML" label="XML播放"></el-option>
  134. </el-select>
  135. </el-form-item>
  136. <el-form-item
  137. prop="sysMusicScore.enableEvaluation"
  138. label="支持评测"
  139. :rules="[{ required: true, message: '请选择支持评测' }]"
  140. >
  141. <el-radio-group v-model="form.sysMusicScore.enableEvaluation">
  142. <el-radio :label="1">是</el-radio>
  143. <el-radio :label="0">否</el-radio>
  144. </el-radio-group>
  145. </el-form-item>
  146. <el-form-item
  147. prop="sysMusicScore.order"
  148. label="排序"
  149. :rules="[{ required: true, message: '请输入排序' }, {
  150. pattern: /^([1-9]\d*|[0]{1,1})$/,
  151. message: '请输入正确的排序',
  152. trigger: 'blur',
  153. },]"
  154. >
  155. <el-input placeholder="请输入排序" v-model="form.sysMusicScore.order" />
  156. </el-form-item>
  157. <el-form-item
  158. label="原音(不含节拍器)"
  159. prop="sysMusicScore.url"
  160. :rules="[
  161. {
  162. required: form.sysMusicScore.isOpenMetronome ? form.sysMusicScore.playMode === 'MP3' : false,
  163. message: '请上传原音',
  164. },
  165. ]"
  166. >
  167. <singe-file-upload
  168. tips="仅支持上传 mp3/aac 格式音频文件"
  169. accept=".mp3, .aac"
  170. v-model="form.sysMusicScore.url"
  171. />
  172. </el-form-item>
  173. <el-form-item
  174. label="原音(含节拍器)"
  175. prop="sysMusicScore.metronomeUrl"
  176. :rules="[
  177. {
  178. required: form.sysMusicScore.isOpenMetronome ? false : form.sysMusicScore.playMode === 'MP3',
  179. message: '原音(含节拍器)',
  180. },
  181. ]"
  182. >
  183. <singe-file-upload
  184. tips="仅支持上传 mp3/aac 格式音频文件"
  185. accept=".mp3, .aac"
  186. v-model="form.sysMusicScore.metronomeUrl"
  187. />
  188. </el-form-item>
  189. <el-form-item
  190. label="MID原音"
  191. prop="sysMusicScore.midiUrl"
  192. >
  193. <singe-file-upload
  194. tips="仅支持上传 mid 格式音频文件"
  195. accept=".mid"
  196. v-model="form.sysMusicScore.midiUrl"
  197. />
  198. </el-form-item>
  199. <div
  200. class="files"
  201. v-for="(song, index) in form.sysMusicScoreAccompaniments"
  202. :key="index"
  203. >
  204. <el-row>
  205. <el-col :span="12">
  206. <el-form-item
  207. :prop="`sysMusicScoreAccompaniments.${index}.subjectId`"
  208. label="声部"
  209. >
  210. <!-- :rules="[{required: true, message: '请选择声部'}]" -->
  211. <el-select
  212. style="width: 100% !important"
  213. v-model="song.subjectId"
  214. clearable
  215. placeholder="请选择声部"
  216. >
  217. <el-option
  218. v-for="item in selects.subjects"
  219. :value="item.id"
  220. :label="item.name"
  221. :key="item.id"
  222. :disabled="hasSubjectId(item.id)"
  223. ></el-option>
  224. </el-select>
  225. </el-form-item>
  226. </el-col>
  227. <el-col :span="12">
  228. <el-form-item
  229. :prop="`sysMusicScoreAccompaniments.${index}.speed`"
  230. label="速度"
  231. :rules="[{ required: true, message: '请输入速度' }]"
  232. >
  233. <el-input
  234. type="number"
  235. placeholder="请输入速度"
  236. v-model="song.speed"
  237. />
  238. </el-form-item>
  239. </el-col>
  240. <el-col :span="12">
  241. <el-form-item
  242. :prop="`sysMusicScoreAccompaniments.${index}.isShowFingering`"
  243. label="指法展示"
  244. :rules="[{ required: true, message: '请选择是否展示指法' }]"
  245. >
  246. <el-select
  247. style="width: 100% !important"
  248. v-model="song.isShowFingering"
  249. placeholder="请选择是否展示指法"
  250. >
  251. <el-option :value="true" label="是"></el-option>
  252. <el-option :value="false" label="否"></el-option>
  253. </el-select>
  254. </el-form-item>
  255. </el-col>
  256. <el-col :span="12">
  257. <el-form-item
  258. :prop="`sysMusicScoreAccompaniments.${index}.memo`"
  259. label="描述"
  260. >
  261. <el-input placeholder="请输入描述" v-model="song.memo" />
  262. </el-form-item>
  263. </el-col>
  264. <el-col :span="12">
  265. <el-form-item
  266. label="伴奏(不含节拍器)"
  267. :prop="`sysMusicScoreAccompaniments.${index}.mp3Url`"
  268. >
  269. <singe-file-upload
  270. tips="仅支持上传 mp3/aac 格式音频文件"
  271. accept=".mp3, .aac"
  272. v-model="song.mp3Url"
  273. />
  274. </el-form-item>
  275. </el-col>
  276. <el-col :span="12">
  277. <el-form-item
  278. label="伴奏(含节拍器)"
  279. :prop="`sysMusicScoreAccompaniments.${index}.metronomeMp3Url`"
  280. >
  281. <singe-file-upload
  282. tips="仅支持上传 mp3/aac 格式音频文件"
  283. accept=".mp3, .aac"
  284. v-model="song.metronomeMp3Url"
  285. />
  286. </el-form-item>
  287. </el-col>
  288. </el-row>
  289. <el-row>
  290. <el-col :span="12">
  291. <el-form-item
  292. label="MusicXML"
  293. :prop="`sysMusicScoreAccompaniments.${index}.xmlUrl`"
  294. :rules="[{ required: true, message: '请选择MusicXML文件' }]"
  295. >
  296. <singe-file-upload
  297. tips="仅支持上传 xml 格式文件"
  298. accept=".xml"
  299. v-model="song.xmlUrl"
  300. @inputFile="inputFile"
  301. />
  302. </el-form-item>
  303. </el-col>
  304. <el-col :span="12" v-if="partListNames.length > 1">
  305. <el-form-item
  306. label="所属轨道"
  307. :prop="`sysMusicScoreAccompaniments.${index}.track`"
  308. :rules="[{ required: true, message: '请选择所属轨道' }]"
  309. >
  310. <el-select
  311. style="width: 100% !important"
  312. v-model="song.track"
  313. clearable
  314. placeholder="请选择轨道"
  315. >
  316. <el-option
  317. v-for="item in partListNames"
  318. :value="item"
  319. :label="item"
  320. :key="item"
  321. :disabled="hasPartName(item)"
  322. ></el-option>
  323. </el-select>
  324. </el-form-item>
  325. </el-col>
  326. </el-row>
  327. <el-button
  328. class="file-remove"
  329. type="text"
  330. @click="removeSys(index)"
  331. :disabled="form.sysMusicScoreAccompaniments.length == 1"
  332. >删除</el-button
  333. >
  334. </div>
  335. <el-button
  336. @click="createSys"
  337. type="info"
  338. style="width: 100%; margin-bottom: 20px"
  339. plain
  340. >添加伴奏</el-button
  341. >
  342. <div class="btns">
  343. <el-button type="primary" @click="submit">提交</el-button>
  344. <el-button @click="$listeners.close">取消</el-button>
  345. </div>
  346. </el-form>
  347. </div>
  348. </template>
  349. <script>
  350. import { Add, Update, queryPageSysExam, queryTree } from "../api";
  351. import { getAllmemberRank } from "@/views/resetTeaming/api";
  352. export default {
  353. props: ["detail", "type"],
  354. data() {
  355. return {
  356. partListNames: [],
  357. tree: [],
  358. memberRankList: [], // 会员列表
  359. form: {
  360. rankIdType: 0, // 收费会员类型 默认免费
  361. sysMusicScore: {
  362. isOpenMetronome: 0, // 是否开启节拍器 默认关闭
  363. name: "",
  364. rankIds: "", // 收费会员编号
  365. url: "",
  366. metronomeUrl: "",
  367. midiUrl: "",
  368. order: "",
  369. musicScoreCategoriesId: [],
  370. // 兼容之前数据,默认选择团练宝
  371. clientType: "SMART_PRACTICE",
  372. renderFrom: "",
  373. playMode: "",
  374. enableEvaluation: 1
  375. },
  376. sysMusicScoreAccompaniments: [
  377. {
  378. subjectId: "",
  379. speed: "",
  380. mp3Url: "",
  381. xmlUrl: "",
  382. isShowFingering: null,
  383. mome: "",
  384. track: ""
  385. },
  386. ],
  387. delExamSongAccompanimentIds: [],
  388. },
  389. treeProps: {
  390. value: "id",
  391. label: "name",
  392. children: "sysMusicScoreCategoriesList",
  393. },
  394. };
  395. },
  396. async mounted() {
  397. this.$store.dispatch("setSubjects");
  398. await this.FetchTree();
  399. await this.memberRank();
  400. if (this.detail) {
  401. this.$set(this.form, "sysMusicScore", {
  402. isOpenMetronome:Number(this.detail.isOpenMetronome),
  403. name: this.detail.name,
  404. url: this.detail.url,
  405. midiUrl: this.detail.midiUrl,
  406. rankIds: this.detail.rankIds,
  407. order: this.detail.order,
  408. clientType: this.detail.clientType,
  409. enableEvaluation: +this.detail.enableEvaluation,
  410. metronomeUrl: this.detail.metronomeUrl,
  411. renderFrom: this.detail.renderFrom,
  412. playMode: this.detail.playMode,
  413. musicScoreCategoriesId: this.detail.categoriesId
  414. ? this.formatParentId(this.detail.categoriesId, this.tree)
  415. : [],
  416. });
  417. if (this.detail.rankIds) {
  418. this.form.rankIdType = 1;
  419. } else {
  420. this.form.rankIdType = 0;
  421. }
  422. this.FeatchDetailList();
  423. }
  424. },
  425. methods: {
  426. getPartListNames(xml) {
  427. if (!xml) return []
  428. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  429. const partList = xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
  430. const partListNames = Array.from(partList).map(item => item.getElementsByTagName('part-name')?.[0].textContent || '')
  431. return partListNames
  432. },
  433. inputFile(file) {
  434. const xmlRead = new FileReader()
  435. xmlRead.onload = res => {
  436. this.partListNames = this.getPartListNames(res.target.result)
  437. for (let j = 0; j < this.form.sysMusicScoreAccompaniments.length; j++) {
  438. this.form.sysMusicScoreAccompaniments[j].track = this.partListNames[j]
  439. this.$set(this.form, 'sysMusicScoreAccompaniments', this.form.sysMusicScoreAccompaniments)
  440. }
  441. for (let index = this.form.sysMusicScoreAccompaniments.length; index < this.partListNames.length; index++) {
  442. const part = this.partListNames[index]
  443. this.createSys({
  444. ...this.form.sysMusicScoreAccompaniments[0],
  445. metronomeMp3Url: "",
  446. mp3Url: "",
  447. track: part,
  448. })
  449. }
  450. }
  451. xmlRead.readAsText(file.raw)
  452. },
  453. rankChange(value) {
  454. if (value) {
  455. let tempIds = [];
  456. this.memberRankList.forEach((item) => {
  457. tempIds.push(item.id);
  458. });
  459. this.form.sysMusicScore.rankIds = tempIds.join(",");
  460. } else {
  461. // 会员购买重置
  462. this.form.sysMusicScore.rankIds = "";
  463. }
  464. },
  465. async memberRank() {
  466. try {
  467. const res = await getAllmemberRank({ isDefault: 0 });
  468. this.memberRankList = res.data || [];
  469. } catch (e) {
  470. console.log(e);
  471. }
  472. },
  473. formatParentId(id, list, ids = []) {
  474. for (const item of list) {
  475. if (item.sysMusicScoreCategoriesList) {
  476. const cIds = this.formatParentId(
  477. id,
  478. item.sysMusicScoreCategoriesList,
  479. [...ids, item.id]
  480. );
  481. if (cIds.includes(id)) {
  482. return cIds;
  483. }
  484. }
  485. if (item.id === id) {
  486. return [...ids, id];
  487. }
  488. }
  489. return ids;
  490. },
  491. async FetchTree() {
  492. try {
  493. const res = await queryTree();
  494. this.tree = res.data;
  495. this.formatTree(this.tree)
  496. } catch (error) {}
  497. },
  498. formatTree(data){
  499. for(let i of data) {
  500. if(i.sysMusicScoreCategoriesList && i.sysMusicScoreCategoriesList.length > 0) {
  501. this.formatTree(i.sysMusicScoreCategoriesList, i)
  502. } else {
  503. i.sysMusicScoreCategoriesList = null
  504. }
  505. }
  506. },
  507. async FeatchDetailList() {
  508. try {
  509. const res = await queryPageSysExam({
  510. sysMusicScoreId: this.detail.id,
  511. });
  512. const result = res.data || [];
  513. result.forEach((item) => {
  514. if (!item.subjectId) {
  515. item.subjectId = null;
  516. }
  517. });
  518. this.$set(this.form, "sysMusicScoreAccompaniments", result);
  519. } catch (error) {}
  520. },
  521. createSys(initData) {
  522. this.form.sysMusicScoreAccompaniments.push(Object.assign({
  523. subjectId: "",
  524. speed: "",
  525. mp3Url: "",
  526. xmlUrl: "",
  527. track: "",
  528. }, (initData || {})));
  529. },
  530. async removeSys(index) {
  531. try {
  532. await this.$confirm("是否确认删除此伴奏?", "提示", {
  533. type: "warning",
  534. });
  535. if (this.form.sysMusicScoreAccompaniments[index]) {
  536. this.form.delExamSongAccompanimentIds.push(
  537. this.form.sysMusicScoreAccompaniments[index].id
  538. );
  539. }
  540. this.form.sysMusicScoreAccompaniments.splice(index, 1);
  541. } catch (error) {}
  542. },
  543. hasPartName(name) {
  544. const names = [];
  545. for (const item of this.form.sysMusicScoreAccompaniments) {
  546. names.push(item.track);
  547. }
  548. return names.includes(name);
  549. },
  550. hasSubjectId(id) {
  551. const ids = [];
  552. for (const item of this.form.sysMusicScoreAccompaniments) {
  553. ids.push(item.subjectId);
  554. }
  555. return ids.includes(id);
  556. },
  557. async submit() {
  558. this.$refs.form.validate(async (valid) => {
  559. if (valid) {
  560. if (!this.detail) {
  561. await Add({
  562. ...this.form,
  563. sysMusicScore: {
  564. ...this.form.sysMusicScore,
  565. type: "COMMON",
  566. showFlag: 0,
  567. musicScoreCategoriesId: (
  568. this.form.sysMusicScore.musicScoreCategoriesId || []
  569. ).pop(),
  570. },
  571. });
  572. this.$message.success("提交成功");
  573. } else {
  574. await Update({
  575. ...this.form,
  576. sysMusicScore: {
  577. ...this.form.sysMusicScore,
  578. type: "COMMON",
  579. id: this.detail.id,
  580. showFlag: this.detail.showFlag,
  581. musicScoreCategoriesId: (
  582. this.form.sysMusicScore.musicScoreCategoriesId || []
  583. ).pop(),
  584. },
  585. });
  586. this.$message.success("修改成功");
  587. }
  588. this.$listeners.close();
  589. this.$listeners.submited();
  590. }
  591. });
  592. },
  593. },
  594. };
  595. </script>
  596. <style lang="less" scoped>
  597. .btns {
  598. text-align: right;
  599. }
  600. .files {
  601. background-color: #f8f8f8;
  602. padding: 20px 0;
  603. padding-right: 20px;
  604. margin-bottom: 20px;
  605. border-radius: 5px;
  606. position: relative;
  607. .file-remove {
  608. position: absolute;
  609. right: 20px;
  610. bottom: 10px;
  611. }
  612. }
  613. </style>