form.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  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. v-if="form.sysMusicScore.isOpenMetronome ===1"
  159. label="伴奏(不含节拍器)"
  160. prop="sysMusicScore.url"
  161. >
  162. <singe-file-upload
  163. tips="仅支持上传 mp3/aac 格式音频文件"
  164. accept=".mp3, .aac"
  165. v-model="form.sysMusicScore.url"
  166. />
  167. </el-form-item>
  168. <el-form-item
  169. v-else
  170. label="伴奏(含节拍器)"
  171. prop="sysMusicScore.metronomeUrl"
  172. >
  173. <singe-file-upload
  174. tips="仅支持上传 mp3/aac 格式音频文件"
  175. accept=".mp3, .aac"
  176. v-model="form.sysMusicScore.metronomeUrl"
  177. />
  178. </el-form-item>
  179. <el-form-item
  180. label="MIDI"
  181. prop="sysMusicScore.midiUrl"
  182. >
  183. <singe-file-upload
  184. tips="仅支持上传 mid 格式音频文件"
  185. accept=".mid"
  186. v-model="form.sysMusicScore.midiUrl"
  187. />
  188. </el-form-item>
  189. <div
  190. class="files"
  191. v-for="(song, index) in form.sysMusicScoreAccompaniments"
  192. :key="index"
  193. >
  194. <el-row>
  195. <el-col :span="12">
  196. <el-form-item
  197. :prop="`sysMusicScoreAccompaniments.${index}.subjectId`"
  198. label="声部"
  199. >
  200. <!-- :rules="[{required: true, message: '请选择声部'}]" -->
  201. <el-select
  202. style="width: 100% !important"
  203. v-model="song.subjectId"
  204. clearable
  205. placeholder="请选择声部"
  206. >
  207. <el-option
  208. v-for="item in selects.subjects"
  209. :value="item.id"
  210. :label="item.name"
  211. :key="item.id"
  212. :disabled="hasSubjectId(item.id)"
  213. ></el-option>
  214. </el-select>
  215. </el-form-item>
  216. </el-col>
  217. <el-col :span="12">
  218. <el-form-item
  219. :prop="`sysMusicScoreAccompaniments.${index}.speed`"
  220. label="速度"
  221. :rules="[{ required: true, message: '请输入速度' }]"
  222. >
  223. <el-input
  224. type="number"
  225. placeholder="请输入速度"
  226. v-model="song.speed"
  227. />
  228. </el-form-item>
  229. </el-col>
  230. <el-col :span="12">
  231. <el-form-item
  232. :prop="`sysMusicScoreAccompaniments.${index}.isShowFingering`"
  233. label="指法展示"
  234. :rules="[{ required: true, message: '请选择是否展示指法' }]"
  235. >
  236. <el-select
  237. style="width: 100% !important"
  238. v-model="song.isShowFingering"
  239. placeholder="请选择是否展示指法"
  240. >
  241. <el-option :value="true" label="是"></el-option>
  242. <el-option :value="false" label="否"></el-option>
  243. </el-select>
  244. </el-form-item>
  245. </el-col>
  246. <el-col :span="12">
  247. <el-form-item
  248. :prop="`sysMusicScoreAccompaniments.${index}.memo`"
  249. label="描述"
  250. >
  251. <el-input placeholder="请输入描述" v-model="song.memo" />
  252. </el-form-item>
  253. </el-col>
  254. <el-col :span="12" v-if="form.sysMusicScore.isOpenMetronome ===1">
  255. <el-form-item
  256. label="原音(不含节拍器)"
  257. :prop="`sysMusicScoreAccompaniments.${index}.mp3Url`"
  258. :rules="[
  259. {
  260. required: form.sysMusicScore.isOpenMetronome ? form.sysMusicScore.playMode === 'MP3' : false,
  261. message: '请上传原音(不含节拍器)',
  262. },
  263. ]"
  264. >
  265. <singe-file-upload
  266. tips="仅支持上传 mp3/aac 格式音频文件"
  267. accept=".mp3, .aac"
  268. v-model="song.mp3Url"
  269. />
  270. </el-form-item>
  271. </el-col>
  272. <el-col :span="12" v-else>
  273. <el-form-item
  274. label="原音(含节拍器)"
  275. :prop="`sysMusicScoreAccompaniments.${index}.metronomeMp3Url`"
  276. :rules="[
  277. {
  278. required: form.sysMusicScore.isOpenMetronome ? false : form.sysMusicScore.playMode === 'MP3',
  279. message: '原音(含节拍器)',
  280. },
  281. ]"
  282. >
  283. <singe-file-upload
  284. tips="仅支持上传 mp3/aac 格式音频文件"
  285. accept=".mp3, .aac"
  286. v-model="song.metronomeMp3Url"
  287. />
  288. </el-form-item>
  289. </el-col>
  290. </el-row>
  291. <el-row>
  292. <el-col :span="12">
  293. <el-form-item
  294. label="MusicXML"
  295. :prop="`sysMusicScoreAccompaniments.${index}.xmlUrl`"
  296. :rules="[{ required: true, message: '请选择MusicXML文件' }]"
  297. >
  298. <singe-file-upload
  299. tips="仅支持上传 xml 格式文件"
  300. accept=".xml"
  301. v-model="song.xmlUrl"
  302. @inputFile="inputFile"
  303. />
  304. </el-form-item>
  305. </el-col>
  306. <el-col :span="12" v-if="partListNames.length > 1">
  307. <el-form-item
  308. label="所属轨道"
  309. :prop="`sysMusicScoreAccompaniments.${index}.track`"
  310. :rules="[{ required: true, message: '请选择所属轨道' }]"
  311. >
  312. <el-select
  313. style="width: 100% !important"
  314. v-model="song.track"
  315. clearable
  316. placeholder="请选择轨道"
  317. >
  318. <el-option
  319. v-for="item in partListNames"
  320. :value="item"
  321. :label="item"
  322. :key="item"
  323. :disabled="hasPartName(item)"
  324. ></el-option>
  325. </el-select>
  326. </el-form-item>
  327. </el-col>
  328. </el-row>
  329. <el-button
  330. class="file-remove"
  331. type="text"
  332. @click="removeSys(index)"
  333. :disabled="form.sysMusicScoreAccompaniments.length == 1"
  334. >删除</el-button
  335. >
  336. </div>
  337. <el-button
  338. @click="createSys"
  339. type="info"
  340. style="width: 100%; margin-bottom: 20px"
  341. plain
  342. >添加原音</el-button
  343. >
  344. <div class="btns">
  345. <el-button type="primary" @click="submit">提交</el-button>
  346. <el-button @click="$listeners.close">取消</el-button>
  347. </div>
  348. </el-form>
  349. </div>
  350. </template>
  351. <script>
  352. import axios from 'axios'
  353. import { Add, Update, queryPageSysExam, queryTree } from "../api";
  354. import { getAllmemberRank } from "@/views/resetTeaming/api";
  355. export default {
  356. props: ["detail", "type"],
  357. data() {
  358. return {
  359. xmlFirstSpeed: '',
  360. partListNames: [],
  361. tree: [],
  362. memberRankList: [], // 会员列表
  363. form: {
  364. rankIdType: 0, // 收费会员类型 默认免费
  365. sysMusicScore: {
  366. isOpenMetronome: 0, // 是否开启节拍器 默认关闭
  367. name: "",
  368. rankIds: "", // 收费会员编号
  369. url: "",
  370. metronomeUrl: "",
  371. midiUrl: "",
  372. order: "",
  373. musicScoreCategoriesId: [],
  374. // 兼容之前数据,默认选择云教练
  375. clientType: "SMART_PRACTICE",
  376. renderFrom: "",
  377. playMode: "MP3",
  378. enableEvaluation: 1
  379. },
  380. sysMusicScoreAccompaniments: [
  381. {
  382. subjectId: "",
  383. speed: "",
  384. mp3Url: "",
  385. xmlUrl: "",
  386. isShowFingering: true,
  387. mome: "",
  388. track: ""
  389. },
  390. ],
  391. delExamSongAccompanimentIds: [],
  392. },
  393. treeProps: {
  394. value: "id",
  395. label: "name",
  396. children: "sysMusicScoreCategoriesList",
  397. },
  398. };
  399. },
  400. async mounted() {
  401. this.$store.dispatch("setSubjects");
  402. await this.FetchTree();
  403. await this.memberRank();
  404. if (this.detail) {
  405. this.$set(this.form, "sysMusicScore", {
  406. isOpenMetronome:Number(this.detail.isOpenMetronome),
  407. name: this.detail.name,
  408. url: this.detail.url,
  409. midiUrl: this.detail.midiUrl,
  410. rankIds: this.detail.rankIds,
  411. order: this.detail.order,
  412. clientType: this.detail.clientType,
  413. enableEvaluation: +this.detail.enableEvaluation,
  414. metronomeUrl: this.detail.metronomeUrl,
  415. renderFrom: this.detail.renderFrom,
  416. playMode: this.detail.playMode,
  417. musicScoreCategoriesId: this.detail.categoriesId
  418. ? this.formatParentId(this.detail.categoriesId, this.tree)
  419. : [],
  420. });
  421. if (this.detail.rankIds) {
  422. this.form.rankIdType = 1;
  423. } else {
  424. this.form.rankIdType = 0;
  425. }
  426. const xmlres = await axios(this.detail.xmlUrl)
  427. this.partListNames = this.getPartListNames(xmlres.data)
  428. this.FeatchDetailList();
  429. } else {
  430. // 新增条件下默认设置为收费
  431. this.rankChange(1);
  432. this.form.rankIdType = 1;
  433. }
  434. },
  435. methods: {
  436. getPartListNames(xml) {
  437. if (!xml) return []
  438. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  439. const partList = xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
  440. const partListNames = Array.from(partList).map(item => item.getElementsByTagName('part-name')?.[0].textContent || '')
  441. this.xmlFirstSpeed = xmlParse.getElementsByTagName('per-minute')?.[0]?.textContent || ''
  442. return partListNames
  443. },
  444. inputFile(file) {
  445. const xmlRead = new FileReader()
  446. xmlRead.onload = res => {
  447. this.partListNames = this.getPartListNames(res.target.result)
  448. for (let j = 0; j < this.form.sysMusicScoreAccompaniments.length; j++) {
  449. this.form.sysMusicScoreAccompaniments[j].track = this.partListNames[j]
  450. if (!this.form.sysMusicScoreAccompaniments[j].speed) {
  451. this.form.sysMusicScoreAccompaniments[j].speed = this.xmlFirstSpeed
  452. }
  453. this.$set(this.form, 'sysMusicScoreAccompaniments', this.form.sysMusicScoreAccompaniments)
  454. }
  455. for (let index = this.form.sysMusicScoreAccompaniments.length; index < this.partListNames.length; index++) {
  456. const part = this.partListNames[index]
  457. const sysData = {
  458. ...this.form.sysMusicScoreAccompaniments[0],
  459. metronomeMp3Url: "",
  460. mp3Url: "",
  461. track: part,
  462. }
  463. if (!sysData.speed) {
  464. sysData.speed = this.xmlFirstSpeed
  465. }
  466. this.createSys(sysData)
  467. }
  468. }
  469. xmlRead.readAsText(file.raw)
  470. },
  471. rankChange(value) {
  472. if (value) {
  473. let tempIds = [];
  474. this.memberRankList.forEach((item) => {
  475. tempIds.push(item.id);
  476. });
  477. this.form.sysMusicScore.rankIds = tempIds.join(",");
  478. } else {
  479. // 会员购买重置
  480. this.form.sysMusicScore.rankIds = "";
  481. }
  482. },
  483. async memberRank() {
  484. try {
  485. const res = await getAllmemberRank({ isDefault: 0 });
  486. this.memberRankList = res.data || [];
  487. } catch (e) {
  488. console.log(e);
  489. }
  490. },
  491. formatParentId(id, list, ids = []) {
  492. for (const item of list) {
  493. if (item.sysMusicScoreCategoriesList) {
  494. const cIds = this.formatParentId(
  495. id,
  496. item.sysMusicScoreCategoriesList,
  497. [...ids, item.id]
  498. );
  499. if (cIds.includes(id)) {
  500. return cIds;
  501. }
  502. }
  503. if (item.id === id) {
  504. return [...ids, id];
  505. }
  506. }
  507. return ids;
  508. },
  509. async FetchTree() {
  510. try {
  511. const res = await queryTree();
  512. this.tree = res.data;
  513. this.formatTree(this.tree)
  514. } catch (error) {}
  515. },
  516. formatTree(data){
  517. for(let i of data) {
  518. if(i.sysMusicScoreCategoriesList && i.sysMusicScoreCategoriesList.length > 0) {
  519. this.formatTree(i.sysMusicScoreCategoriesList, i)
  520. } else {
  521. i.sysMusicScoreCategoriesList = null
  522. }
  523. }
  524. },
  525. async FeatchDetailList() {
  526. try {
  527. const res = await queryPageSysExam({
  528. sysMusicScoreId: this.detail.id,
  529. });
  530. const result = res.data || [];
  531. result.forEach((item) => {
  532. if (!item.subjectId) {
  533. item.subjectId = null;
  534. }
  535. });
  536. this.$set(this.form, "sysMusicScoreAccompaniments", result);
  537. } catch (error) {}
  538. },
  539. createSys(initData) {
  540. this.form.sysMusicScoreAccompaniments.push(Object.assign({
  541. subjectId: "",
  542. speed: "",
  543. mp3Url: "",
  544. xmlUrl: "",
  545. track: "",
  546. }, (initData || {})));
  547. },
  548. async removeSys(index) {
  549. try {
  550. await this.$confirm("是否确认删除此原音?", "提示", {
  551. type: "warning",
  552. });
  553. if (this.form.sysMusicScoreAccompaniments[index]) {
  554. this.form.delExamSongAccompanimentIds.push(
  555. this.form.sysMusicScoreAccompaniments[index].id
  556. );
  557. }
  558. this.form.sysMusicScoreAccompaniments.splice(index, 1);
  559. } catch (error) {}
  560. },
  561. hasPartName(name) {
  562. const names = [];
  563. for (const item of this.form.sysMusicScoreAccompaniments) {
  564. names.push(item.track);
  565. }
  566. return names.includes(name);
  567. },
  568. hasSubjectId(id) {
  569. const ids = [];
  570. for (const item of this.form.sysMusicScoreAccompaniments) {
  571. ids.push(item.subjectId);
  572. }
  573. return ids.includes(id);
  574. },
  575. async submit() {
  576. this.$refs.form.validate(async (valid) => {
  577. if (valid) {
  578. if (!this.detail) {
  579. await Add({
  580. ...this.form,
  581. sysMusicScore: {
  582. ...this.form.sysMusicScore,
  583. type: "COMMON",
  584. showFlag: 0,
  585. musicScoreCategoriesId: (
  586. this.form.sysMusicScore.musicScoreCategoriesId || []
  587. ).pop(),
  588. },
  589. });
  590. this.$message.success("提交成功");
  591. } else {
  592. await Update({
  593. ...this.form,
  594. sysMusicScore: {
  595. ...this.form.sysMusicScore,
  596. type: "COMMON",
  597. id: this.detail.id,
  598. showFlag: this.detail.showFlag,
  599. musicScoreCategoriesId: (
  600. this.form.sysMusicScore.musicScoreCategoriesId || []
  601. ).pop(),
  602. },
  603. });
  604. this.$message.success("修改成功");
  605. }
  606. this.$listeners.close();
  607. this.$listeners.submited();
  608. }
  609. });
  610. },
  611. },
  612. };
  613. </script>
  614. <style lang="less" scoped>
  615. .btns {
  616. text-align: right;
  617. }
  618. .files {
  619. background-color: #f8f8f8;
  620. padding: 20px 0;
  621. padding-right: 20px;
  622. margin-bottom: 20px;
  623. border-radius: 5px;
  624. position: relative;
  625. .file-remove {
  626. position: absolute;
  627. right: 20px;
  628. bottom: 10px;
  629. }
  630. }
  631. </style>