music-operation.tsx 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490
  1. import type { SelectOption } from 'naive-ui'
  2. import {
  3. NAlert,
  4. NButton,
  5. NCascader,
  6. NCheckbox,
  7. NCheckboxGroup,
  8. NForm,
  9. NFormItemGi,
  10. NGi,
  11. NGrid,
  12. NInput,
  13. NInputNumber,
  14. NModal,
  15. NRadio,
  16. NRadioGroup,
  17. NSelect,
  18. NSpace,
  19. NSpin,
  20. useDialog,
  21. useMessage
  22. } from 'naive-ui'
  23. import { defineComponent, onMounted, onUnmounted, PropType, reactive, ref } from 'vue'
  24. import { musicSheetCategoriesQueryTree, musicSheetDetail, musicSheetSave } from '../../api'
  25. import UploadFile from '@/components/upload-file'
  26. import styles from './index.module.less'
  27. import deepClone from '@/utils/deep.clone'
  28. import axios from 'axios'
  29. import { appKey, clientType, musicSheetSourceType, musicSheetType } from '@/utils/constant'
  30. import { getMapValueByKey, getSelectDataFromObj } from '@/utils/objectUtil'
  31. import { musicalInstrumentPage } from '@views/system-manage/subject-manage/api'
  32. import { subjectPage } from '@views/system-manage/api'
  33. import MusicSheetOwnerDialog from '@views/music-library/music-sheet/modal/musicSheetOwnerDialog'
  34. import { sysApplicationPage } from '@views/menu-manage/api'
  35. import { filterPointCategory } from '@views/teaching-manage/unit-test'
  36. import { api_uploadFile } from '@/plugins/uploadFile'
  37. import MusicCreateImg from './music-create-img'
  38. /**
  39. * 获取指定元素下一个Note元素
  40. * @param ele 指定元素
  41. * @param selectors 选择器
  42. */
  43. const getNextNote = (ele: any, selectors: any) => {
  44. let index = 0
  45. const parentEle = ele.closest(selectors)
  46. let pointer = parentEle
  47. const measure = parentEle?.closest('measure')
  48. let siblingNote = null
  49. // 查找到相邻的第一个note元素
  50. while (!siblingNote && index < (measure?.childNodes.length || 50)) {
  51. index++
  52. if (pointer?.nextElementSibling?.tagName === 'note') {
  53. siblingNote = pointer?.nextElementSibling
  54. }
  55. pointer = pointer?.nextElementSibling
  56. }
  57. return siblingNote
  58. }
  59. export const onlyVisible = (xml: any, partIndex: any) => {
  60. if (!xml) return ''
  61. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  62. const partList =
  63. xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
  64. const parts = xmlParse.getElementsByTagName('part')
  65. const visiblePartInfo = partList[partIndex]
  66. if (visiblePartInfo) {
  67. const id = visiblePartInfo.getAttribute('id')
  68. Array.from(parts).forEach((part) => {
  69. if (part && part.getAttribute('id') !== id) {
  70. part.parentNode?.removeChild(part)
  71. // 不等于第一行才添加避免重复添加
  72. }
  73. // 最后一个小节的结束线元素不在最后 调整
  74. if (part && part.getAttribute('id') === id) {
  75. const barlines = part.getElementsByTagName('barline')
  76. const lastParent = barlines[barlines.length - 1]?.parentElement
  77. if (lastParent?.lastElementChild?.tagName !== 'barline') {
  78. const children: any[] = (lastParent?.children as any) || []
  79. for (let el of children) {
  80. if (el.tagName === 'barline') {
  81. // 将结束线元素放到最后
  82. lastParent?.appendChild(el)
  83. break
  84. }
  85. }
  86. }
  87. }
  88. })
  89. Array.from(partList).forEach((part) => {
  90. if (part && part.getAttribute('id') !== id) {
  91. part.parentNode?.removeChild(part)
  92. }
  93. })
  94. // 处理装饰音问题
  95. const notes = xmlParse.getElementsByTagName('note')
  96. const getNextvNoteDuration = (i: any) => {
  97. let nextNote = notes[i + 1]
  98. // 可能存在多个装饰音问题,取下一个非装饰音时值
  99. for (let index = i; index < notes.length; index++) {
  100. const note = notes[index]
  101. if (!note.getElementsByTagName('grace')?.length) {
  102. nextNote = note
  103. break
  104. }
  105. }
  106. return nextNote?.getElementsByTagName('duration')[0]
  107. }
  108. Array.from(notes).forEach((note, i) => {
  109. const graces = note.getElementsByTagName('grace')
  110. if (graces && graces.length) {
  111. note.appendChild(getNextvNoteDuration(i)?.cloneNode(true))
  112. }
  113. })
  114. }
  115. return new XMLSerializer().serializeToString(xmlParse)
  116. }
  117. const speedInfo = {
  118. 'rall.': 1.333333333,
  119. 'poco rit.': 1.333333333,
  120. 'rit.': 1.333333333,
  121. 'molto rit.': 1.333333333,
  122. 'molto rall': 1.333333333,
  123. molto: 1.333333333,
  124. lentando: 1.333333333,
  125. allargando: 1.333333333,
  126. morendo: 1.333333333,
  127. 'accel.': 0.8,
  128. calando: 2,
  129. 'poco accel.': 0.8,
  130. 'gradually slowing': 1.333333333,
  131. slowing: 1.333333333,
  132. slow: 1.333333333,
  133. slowly: 1.333333333,
  134. faster: 1.333333333
  135. }
  136. /**
  137. * 按照xml进行减慢速度的计算
  138. * @param xml 始终按照第一分谱进行减慢速度的计算
  139. */
  140. export function getGradualLengthByXml(xml: string) {
  141. const firstPartXml = onlyVisible(xml, 0)
  142. const xmlParse = new DOMParser().parseFromString(firstPartXml, 'text/xml')
  143. const measures = Array.from(xmlParse.querySelectorAll('measure'))
  144. const notes = Array.from(xmlParse.querySelectorAll('note'))
  145. const words = Array.from(xmlParse.querySelectorAll('words'))
  146. const metronomes = Array.from(xmlParse.querySelectorAll('metronome'))
  147. const eles = []
  148. for (const ele of [...words, ...metronomes]) {
  149. const note = getNextNote(ele, 'direction')
  150. // console.log(ele, note)
  151. if (note) {
  152. const measure = note?.closest('measure')
  153. const measureNotes = Array.from(measure.querySelectorAll('note'))
  154. const noteInMeasureIndex = Array.from(measure.childNodes)
  155. .filter((item: any) => item.nodeName === 'note')
  156. .findIndex((item) => item === note)
  157. let allDuration = 0
  158. let leftDuration = 0
  159. for (let i = 0; i < measureNotes.length; i++) {
  160. const n: any = measureNotes[i]
  161. const duration = +(n.querySelector('duration')?.textContent || '0')
  162. allDuration += duration
  163. if (i < noteInMeasureIndex) {
  164. leftDuration = allDuration
  165. }
  166. }
  167. eles.push({
  168. ele,
  169. index: notes.indexOf(note),
  170. noteInMeasureIndex,
  171. textContent: ele.textContent,
  172. measureIndex: measures.indexOf(measure), //,measure?.getAttribute('number')
  173. type: ele.tagName,
  174. allDuration,
  175. leftDuration
  176. })
  177. }
  178. }
  179. // 结尾处手动插入一个音符节点
  180. eles.push({
  181. ele: notes[notes.length - 1],
  182. index: notes.length,
  183. noteInMeasureIndex: 0,
  184. textContent: '',
  185. type: 'metronome',
  186. allDuration: 1,
  187. leftDuration: 1,
  188. measureIndex: measures.length
  189. })
  190. const gradualNotes: any[] = []
  191. eles.sort((a, b) => a.index - b.index)
  192. const keys = Object.keys(speedInfo).map((w) => w.toLocaleLowerCase())
  193. let isLastNoteAndNotClosed = false
  194. for (const ele of eles) {
  195. const textContent: any = ele.textContent?.toLocaleLowerCase().trim()
  196. if (ele === eles[eles.length - 1]) {
  197. if (gradualNotes[gradualNotes.length - 1]?.length === 1) {
  198. isLastNoteAndNotClosed = true
  199. }
  200. }
  201. const isKeyWork = keys.find((k) => {
  202. const ks = k.split(' ')
  203. return textContent && ks.includes(textContent)
  204. })
  205. if (
  206. ele.type === 'metronome' ||
  207. (ele.type === 'words' && (textContent.startsWith('a tempo') || isKeyWork)) ||
  208. isLastNoteAndNotClosed
  209. ) {
  210. const indexOf = gradualNotes.findIndex((item) => item.length === 1)
  211. if (indexOf > -1 && ele.index > gradualNotes[indexOf]?.[0].start) {
  212. gradualNotes[indexOf][1] = {
  213. start: ele.index,
  214. measureIndex: ele.measureIndex,
  215. noteInMeasureIndex: ele.noteInMeasureIndex,
  216. allDuration: ele.allDuration,
  217. leftDuration: ele.leftDuration,
  218. type: textContent
  219. }
  220. }
  221. }
  222. if (ele.type === 'words' && isKeyWork) {
  223. gradualNotes.push([
  224. {
  225. start: ele.index,
  226. measureIndex: ele.measureIndex,
  227. noteInMeasureIndex: ele.noteInMeasureIndex,
  228. allDuration: ele.allDuration,
  229. leftDuration: ele.leftDuration,
  230. type: textContent
  231. }
  232. ])
  233. }
  234. }
  235. return gradualNotes
  236. }
  237. export default defineComponent({
  238. name: 'music-operation',
  239. props: {
  240. type: {
  241. type: String,
  242. default: 'add'
  243. },
  244. data: {
  245. type: Object as PropType<any>,
  246. default: () => {
  247. }
  248. },
  249. tagList: {
  250. type: Array as PropType<Array<SelectOption>>,
  251. default: () => []
  252. },
  253. subjectList: {
  254. type: Array as PropType<Array<SelectOption>>,
  255. default: () => []
  256. },
  257. // musicSheetCategories: {
  258. // type: Array as PropType<Array<SelectOption>>,
  259. // default: () => []
  260. // }
  261. },
  262. emits: ['close', 'getList'],
  263. setup(props, {slots, attrs, emit}) {
  264. const forms = reactive({
  265. graduals: {} as any, // 渐变速度
  266. playMode: 'MP3', // 播放类型
  267. xmlFileUrl: null, // XML
  268. midiUrl: null, // mid
  269. name: null, // 曲目名称
  270. // musicTag: [] as any, // 曲目标签
  271. composer: null, // 音乐人
  272. playSpeed: null as any, // 曲谱速度
  273. // showFingering: null as any, // 是否显示指法
  274. // canEvaluate: null as any, // 是否评测
  275. // notation: null as any, // 能否转和简谱
  276. // auditVersion: null as any, // 审核版本
  277. // sortNumber: null, // 排序
  278. musicCover: null, // 曲谱封面
  279. remark: null, // 曲谱描述
  280. musicSheetSoundList: [] as any, // 原音
  281. // musicSheetCategoriesId: null,
  282. status: false,
  283. musicSheetType: 'SINGLE', // 曲目类型
  284. sourceType: null as any, //来源类型/作者属性(PLATFORM: 平台; ORG: 机构; PERSON: 个人)
  285. // userId: null, // 所属人
  286. appAuditFlag: 0, // 是否审核版本
  287. midiFileUrl: null, // 伴奏文件 MIDI文件(保留字段)
  288. subjectIds: [] as any, // 可用声部
  289. musicalInstrumentIdList: [] as any, //可用乐器
  290. musicCategoryId: null, //曲目分类
  291. musicSheetAccompanimentList: [] as any, //曲目伴奏
  292. audioType: 'HOMEMODE', // 伴奏类型
  293. isPlayBeat: true, // 是否播放节拍器
  294. isUseSystemBeat: true, // 是否使用系统节拍器(0:否;1:是)
  295. repeatedBeats: false, // 是否重复节拍时长
  296. evaluationStandard: 'FREQUENCY', // 评分标准 节奏 AMPLITUDE 音准 FREQUENCY 分贝 DECIBELS
  297. multiTracksSelection: [] as any, // 声轨
  298. musicSheetExtend: {} as any, //所属人信息
  299. musicImg: '', // 五线谱图片
  300. musicSvg: '', //首调图片
  301. musicJianSvg: '' // 简谱固定调
  302. })
  303. const state = reactive({
  304. loading: false,
  305. previewMode: false,//是否是预览模式
  306. tagList: [...props.tagList] as any, // 标签列表
  307. xmlFirstSpeed: null as any, // 第一个音轨速度
  308. partListNames: [] as any, // 所有音轨声部列表
  309. musicSheetCategories: [] as any,
  310. musicSheetAccompanimentUrls: '' as any,
  311. musicSheetAccompanimentUrlList: [] as any,
  312. instrumentData: [],
  313. instrumentList: [],
  314. subjectList: [] as any,
  315. showMusicSheetOwnerDialog: false, //所属人弹框
  316. // musicSheetOwnerData: {}, //所属人信息
  317. multiTracks: null,
  318. appData: [], // 应用列表
  319. ownerName: null as any, // 所属人名称描述
  320. productOpen: false, // 是否打开自动生成图片
  321. productItem: {} as any,
  322. productIfameSrc: '',
  323. isAutoSave: false // 是否自动保存
  324. })
  325. const gradualData = reactive({
  326. list: [] as any[],
  327. gradualRefs: [] as any[]
  328. })
  329. const btnLoading = ref(false)
  330. const formsRef = ref()
  331. const message = useMessage()
  332. const dialog = useDialog()
  333. // 提交记录
  334. const onSubmit = async () => {
  335. formsRef.value.validate(async (error: any) => {
  336. if (error) {
  337. return
  338. }
  339. if (!state.isAutoSave) {
  340. state.isAutoSave = true
  341. state.productOpen = true
  342. return
  343. }
  344. try {
  345. //extConfigJson: {"repeatedBeats":0,"gradualTimes":{"75":"02:38:60","77":"02:43:39"}}
  346. const obj = {
  347. ...forms,
  348. musicTag: '-1',
  349. multiTracksSelection: forms.multiTracksSelection.join(','),
  350. musicSheetSoundList: forms.musicSheetSoundList.filter((next: any) => {
  351. return !!next.audioFileUrl && forms.multiTracksSelection.includes(next.track)
  352. }),
  353. musicalInstrumentIds: forms.musicalInstrumentIdList.join(','),
  354. extConfigJson: JSON.stringify({
  355. repeatedBeats: forms.repeatedBeats ? 1 : 0,
  356. gradualTimes: forms.graduals
  357. }),
  358. subjectIds: forms.subjectIds.join(',')
  359. }
  360. if (forms.audioType == 'MIDI') {
  361. obj.musicSheetSoundList = []
  362. }
  363. btnLoading.value = true
  364. if (props.type === 'add') {
  365. await musicSheetSave(obj)
  366. message.success('添加成功')
  367. } else if (props.type === 'edit') {
  368. await musicSheetSave({ ...obj, id: props.data.id })
  369. message.success('修改成功')
  370. }
  371. emit('getList')
  372. emit('close')
  373. } catch (e) {
  374. console.log(e)
  375. }
  376. setTimeout(() => {
  377. btnLoading.value = false
  378. }, 100)
  379. })
  380. }
  381. // 上传XML,初始化音轨 音轨速度 乐器、声部
  382. const readFileInputEventAsArrayBuffer = (file: any) => {
  383. const xmlRead = new FileReader()
  384. xmlRead.onload = (res) => {
  385. try {
  386. gradualData.list = getGradualLengthByXml(res?.target?.result as any).filter(
  387. (item: any) => item.length === 2
  388. )
  389. } catch (error) {}
  390. state.partListNames = getPartListNames(res?.target?.result as any) as any
  391. parseInstrumentAndSubject(res?.target?.result as any)
  392. // 这里是如果没有当前音轨就重新写
  393. for (let j = 0; j < state.partListNames.length; j++) {
  394. if (!forms.musicSheetSoundList[j]) {
  395. forms.musicSheetSoundList.push({ audioFileUrl: null, track: null })
  396. }
  397. forms.musicSheetSoundList[j].track = state.partListNames[j].value
  398. }
  399. // 循环添加所在音轨的原音
  400. for (
  401. let index = forms.musicSheetSoundList.length;
  402. index < state.partListNames.length;
  403. index++
  404. ) {
  405. const part = state.partListNames[index].value
  406. const sysData = {
  407. ...forms.musicSheetSoundList[0],
  408. track: part
  409. }
  410. if (!sysData.speed) {
  411. sysData.speed = state.xmlFirstSpeed
  412. }
  413. createSys(sysData)
  414. }
  415. if (forms.musicSheetSoundList.length == 0) {
  416. forms.musicSheetSoundList.push({ audioFileUrl: '', track: '' })
  417. }
  418. }
  419. xmlRead.readAsText(file)
  420. }
  421. const parseInstrumentAndSubject = (xml: any) => {
  422. if (!xml) return
  423. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  424. // 乐器
  425. const instrumentCodeList: any = [];
  426. const instrumentEle = xmlParse.getElementsByTagName('virtual-instrument');
  427. for (let index = 0; index < instrumentEle.length; index++) {
  428. const note = instrumentEle[index]
  429. const instrumentCode = note.getElementsByTagName('virtual-name')?.[0]?.textContent || '';
  430. if (instrumentCode && !instrumentCodeList.includes(instrumentCode)) {
  431. instrumentCodeList.push(instrumentCode);
  432. }
  433. }
  434. const codeIdMap = new Map<string, string>();
  435. state.instrumentData.forEach((data: any) => {
  436. codeIdMap.set(data.code, data.id + '');
  437. })
  438. forms.musicalInstrumentIdList = [];
  439. instrumentCodeList.forEach((code: string) => {
  440. if (codeIdMap.has(code)) {
  441. forms.musicalInstrumentIdList.push(codeIdMap.get(code));
  442. }
  443. })
  444. // 声部
  445. if (forms.musicalInstrumentIdList.length > 0) {
  446. showBackSubject(forms.musicalInstrumentIdList);
  447. }
  448. }
  449. // 获取xml中所有轨道 乐器
  450. const getPartListNames = (xml: any) => {
  451. if (!xml) return []
  452. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  453. const partList =
  454. xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
  455. let partListNames = Array.from(partList).map((item) => {
  456. const part = item.getElementsByTagName('part-name')?.[0].textContent || ''
  457. return {
  458. value: part,
  459. label: part
  460. }
  461. })
  462. partListNames = partListNames.filter(
  463. (n: any) => n.value?.toLocaleUpperCase?.() != 'COMMON'
  464. )
  465. if (partListNames.length > 0) {
  466. forms.musicSheetSoundList = forms.musicSheetSoundList.slice(0, partListNames.length)
  467. }
  468. state.xmlFirstSpeed = xmlParse.getElementsByTagName('per-minute')?.[0]?.textContent || ''
  469. if (!forms.playSpeed) {
  470. if (state.xmlFirstSpeed) {
  471. forms.playSpeed = Number.parseInt(state.xmlFirstSpeed)
  472. } else {
  473. // 速度默认给100
  474. forms.playSpeed = 100
  475. }
  476. }
  477. // 乐器
  478. const instrumentCodeList: any = []
  479. const instrumentEle = xmlParse.getElementsByTagName('virtual-instrument')
  480. for (let index = 0; index < instrumentEle.length; index++) {
  481. const note = instrumentEle[index]
  482. const instrumentCode = note.getElementsByTagName('virtual-name')?.[0]?.textContent || ''
  483. if (instrumentCode && !instrumentCodeList.includes(instrumentCode)) {
  484. instrumentCodeList.push(instrumentCode)
  485. }
  486. }
  487. const codeIdMap = new Map<string, string>()
  488. state.instrumentData.forEach((data: any) => {
  489. codeIdMap.set(data.code, data.id + '')
  490. })
  491. forms.musicalInstrumentIdList = []
  492. instrumentCodeList.forEach((code: string) => {
  493. if (codeIdMap.has(code)) {
  494. forms.musicalInstrumentIdList.push(codeIdMap.get(code))
  495. }
  496. })
  497. // 声部
  498. if (forms.musicalInstrumentIdList.length > 0) {
  499. showBackSubject(forms.musicalInstrumentIdList)
  500. }
  501. return partListNames
  502. }
  503. // 判断选择的音轨是否在选中
  504. const initPartsListStatus = (track: string): any => {
  505. // const _names = state.partListNames.filter(
  506. // (n: any) => n.value?.toLocaleUpperCase?.() != 'COMMON'
  507. // )
  508. const partListNames = deepClone(state.partListNames) || []
  509. partListNames.forEach((item: any) => {
  510. const index = forms.musicSheetSoundList.findIndex((ground: any) => item.value == ground.track)
  511. if (index > -1 && track == item.value) {
  512. item.disabled = false
  513. } else {
  514. item.disabled = true
  515. }
  516. // if (index > -1 && track != item.value) {
  517. // item.disabled = true
  518. // } else {
  519. // item.disabled = false
  520. // }
  521. })
  522. return partListNames || []
  523. }
  524. // 反显声部
  525. const showBackSubject = async (musicalInstrumentIdList: []) => {
  526. try {
  527. const { data } = await subjectPage({
  528. page: 1,
  529. rows: 999,
  530. musicalInstrumentIdList: musicalInstrumentIdList
  531. })
  532. const tempList = data.rows || []
  533. tempList.forEach((item: any) => {
  534. forms.subjectIds.push(item.id + '')
  535. })
  536. } catch {}
  537. }
  538. // 添加原音
  539. const createSys = (initData?: any) => {
  540. forms.musicSheetSoundList.push({
  541. audioFileUrl: null, // 原音
  542. track: null, // 轨道
  543. ...initData
  544. })
  545. }
  546. // 删除原音
  547. const removeSys = (index: number) => {
  548. dialog.warning({
  549. title: '警告',
  550. content: `是否确认删除此原音?`,
  551. positiveText: '确定',
  552. negativeText: '取消',
  553. onPositiveClick: async () => {
  554. forms.musicSheetSoundList.splice(index, 1)
  555. }
  556. })
  557. }
  558. const checkMultiTracks = (value: string) => {
  559. if (!value) {
  560. return
  561. }
  562. if (value === 'all') {
  563. forms.multiTracksSelection = []
  564. state.partListNames.forEach((next: any) => {
  565. forms.multiTracksSelection.push(next.value)
  566. })
  567. } else if (value === 'invert') {
  568. state.partListNames.forEach((next: any) => {
  569. const indexOf = forms.multiTracksSelection.indexOf(next.value)
  570. if (indexOf > -1) {
  571. forms.multiTracksSelection.splice(indexOf, 1)
  572. } else {
  573. forms.multiTracksSelection.push(next.value)
  574. }
  575. })
  576. } else if (value === 'allUncheck') {
  577. forms.multiTracksSelection = []
  578. }
  579. }
  580. const setOwnerName = () => {
  581. if (forms.sourceType == 'PLATFORM') {
  582. state.ownerName = ''
  583. return
  584. }
  585. if (!forms.musicSheetExtend || !forms.sourceType || !forms.musicSheetExtend?.userId) {
  586. return
  587. }
  588. const appId = forms.musicSheetExtend.applicationId
  589. const app = state.appData.filter((next: any) => {
  590. return next.id == appId
  591. }) as any
  592. if (app.length > 0) {
  593. state.ownerName = app[0].appName
  594. }
  595. if (forms.sourceType == 'ORG') {
  596. state.ownerName += '-' + forms.musicSheetExtend.organizationRole
  597. } else if (forms.sourceType == 'PERSON') {
  598. state.ownerName +=
  599. '-' +
  600. getMapValueByKey(forms.musicSheetExtend.clientType, new Map(Object.entries(clientType)))
  601. if (forms.musicSheetExtend.userName) {
  602. state.ownerName += '-' + forms.musicSheetExtend.userName
  603. }
  604. if (forms.musicSheetExtend.phone) {
  605. state.ownerName += '(' + forms.musicSheetExtend.phone + ')'
  606. }
  607. }
  608. }
  609. onMounted(async () => {
  610. state.loading = true
  611. if (props.type === 'preview') {
  612. state.previewMode = true
  613. }
  614. // 获取乐器信息
  615. {
  616. if (state.instrumentList && state.instrumentList.length > 0) {
  617. return
  618. }
  619. try {
  620. const { data } = await musicalInstrumentPage({ page: 1, rows: 999 })
  621. const tempList = data.rows || []
  622. state.instrumentData = tempList
  623. tempList.forEach((item: any) => {
  624. item.label = item.name
  625. item.value = item.id + ''
  626. item.disabled = !item.enableFlag
  627. })
  628. state.instrumentList = tempList
  629. } catch {}
  630. }
  631. state.subjectList = deepClone(props.subjectList)
  632. state.subjectList.forEach((subject: any) => {
  633. subject.disabled = !subject.enableFlag
  634. })
  635. // 初始化应用
  636. {
  637. const appKeys = Object.keys(appKey)
  638. const { data } = await sysApplicationPage({ page: 1, rows: 999, parentId: 0 })
  639. const tempList = data.rows || []
  640. const filter = tempList.filter((next: any) => {
  641. return appKeys.includes(next.appKey)
  642. })
  643. filter.forEach((item: any) => {
  644. item.label = item.appName
  645. item.value = item.id
  646. })
  647. state.appData = filter
  648. }
  649. // 获取分类信息
  650. {
  651. try {
  652. const { data } = await musicSheetCategoriesQueryTree({ enable: true })
  653. state.musicSheetCategories = filterPointCategory(data, 'musicSheetCategoriesList')
  654. } catch (e) {}
  655. }
  656. if (props.type === 'edit' || props.type === 'preview') {
  657. const detail = props.data
  658. try {
  659. const { data } = await musicSheetDetail({ id: detail.id })
  660. forms.audioType = data.audioType
  661. forms.musicSheetAccompanimentList = data.musicSheetAccompanimentList
  662. data.musicSheetAccompanimentList?.forEach((next: any) => {
  663. state.musicSheetAccompanimentUrlList.push(next.audioFileUrl)
  664. })
  665. forms.playMode = data.playMode
  666. forms.xmlFileUrl = data.xmlFileUrl
  667. forms.midiUrl = data.midiUrl
  668. forms.name = data.name
  669. // forms.musicTag = data.musicTag?.split(',')
  670. forms.composer = data.composer
  671. forms.playSpeed = data.playSpeed
  672. // forms.showFingering = Number(data.showFingering)
  673. // forms.canEvaluate = Number(data.canEvaluate)
  674. // forms.notation = Number(data.notation)
  675. // forms.auditVersion = Number(data.auditVersion)
  676. // forms.sortNumber = data.sortNumber
  677. forms.musicCover = data.musicCover
  678. forms.remark = data.remark
  679. forms.status = data.status
  680. forms.musicCategoryId = data.musicCategoryId
  681. forms.musicSheetType = data.musicSheetType || 'SINGLE'
  682. forms.evaluationStandard = data.evaluationStandard
  683. forms.musicalInstrumentIdList = data.musicalInstrumentIds.split(',') || []
  684. forms.subjectIds = data.subjectIds?.split(',') || []
  685. forms.sourceType = data.sourceType
  686. forms.musicSheetExtend = data.musicSheetExtend
  687. forms.repeatedBeats = data.isPlayBeat
  688. // 获取渐变 和 是否多声部
  689. try {
  690. const extConfigJson = data.extConfigJson ? JSON.parse(data.extConfigJson) : {}
  691. forms.graduals = extConfigJson.gradualTimes || {}
  692. } catch (error) {}
  693. setOwnerName()
  694. axios.get(data.xmlFileUrl).then((res: any) => {
  695. if (res?.data) {
  696. gradualData.list = getGradualLengthByXml(res?.data as any).filter(
  697. (item: any) => item.length === 2
  698. )
  699. state.partListNames = getPartListNames(res?.data as any) as any
  700. // 初始化音轨和原音
  701. forms.multiTracksSelection = data.multiTracksSelection?.split(',')||[]
  702. const existSoundList = data.musicSheetSoundList || [];
  703. state.partListNames.forEach((item: any) => {
  704. let audioFileUrl;
  705. existSoundList.forEach((next: any) => {
  706. if(next.track == item.value){
  707. audioFileUrl = next.audioFileUrl
  708. }
  709. })
  710. forms.musicSheetSoundList.push({
  711. audioFileUrl: audioFileUrl, // 原音
  712. track: item.value, // 轨道
  713. })
  714. })
  715. }
  716. })
  717. } catch (error) {}
  718. } else {
  719. // 新增只能使用启用状态的数据
  720. state.subjectList = state.subjectList.filter((next: any) => {
  721. return next.enableFlag == true
  722. })
  723. state.instrumentList = state.instrumentList.filter((next: any) => {
  724. return next.enableFlag == true
  725. })
  726. }
  727. state.loading = false
  728. })
  729. return () => (
  730. <div style="background: #fff; padding-top: 12px">
  731. <NSpin show={state.loading}>
  732. <NForm
  733. class={styles.formContainer}
  734. model={forms}
  735. ref={formsRef}
  736. label-placement="left"
  737. label-width="130"
  738. disabled={state.previewMode}
  739. >
  740. <NAlert showIcon={false} style={{ marginBottom: '12px' }}>
  741. 曲目信息
  742. </NAlert>
  743. <NGrid cols={2}>
  744. <NFormItemGi
  745. label="曲目名称"
  746. path="name"
  747. rule={[
  748. {
  749. required: true,
  750. message: '请输入曲目名称',
  751. trigger: ['input', 'blur']
  752. }
  753. ]}
  754. >
  755. <NInput
  756. v-model:value={forms.name}
  757. placeholder="请输入曲目名称"
  758. maxlength={25}
  759. showCount
  760. />
  761. </NFormItemGi>
  762. <NFormItemGi
  763. label="音乐人"
  764. path="composer"
  765. rule={[
  766. {
  767. required: true,
  768. message: '请输入音乐人',
  769. trigger: ['input', 'blur']
  770. }
  771. ]}
  772. >
  773. <NInput
  774. v-model:value={forms.composer}
  775. placeholder="请输入音乐人名称"
  776. showCount
  777. maxlength={14}
  778. />
  779. </NFormItemGi>
  780. </NGrid>
  781. <NGrid cols={2}>
  782. <NFormItemGi label="曲目描述" path="remark">
  783. <NInput
  784. placeholder="请输入曲目描述"
  785. type="textarea"
  786. rows={4}
  787. showCount
  788. maxlength={200}
  789. v-model:value={forms.remark}
  790. />
  791. </NFormItemGi>
  792. <NFormItemGi
  793. label="曲目封面"
  794. path="musicCover"
  795. rule={[
  796. {
  797. required: true,
  798. message: '请上传曲目封面',
  799. trigger:['input','blur']
  800. }
  801. ]}
  802. >
  803. <UploadFile
  804. desc={'封面图'}
  805. disabled={state.previewMode}
  806. accept=".jpg,.jpeg,.png"
  807. tips="请上传大小1M以内的JPG、PNG图片"
  808. size={1}
  809. v-model:fileList={forms.musicCover}
  810. cropper
  811. bucketName="cbs"
  812. options={{
  813. autoCrop: true, //是否默认生成截图框
  814. enlarge: 2, // 图片放大倍数
  815. autoCropWidth: 200, //默框高度
  816. fixedBox: true, //是否固定截图框大认生成截图框宽度
  817. autoCropHeight: 200, //默认生成截图小 不允许改变
  818. previewsCircle: false, //预览图是否是原圆形
  819. title: '曲目封面'
  820. }}
  821. />
  822. </NFormItemGi>
  823. </NGrid>
  824. <NGrid cols={2}>
  825. <NFormItemGi
  826. label="曲目类型"
  827. path="musicSheetType"
  828. rule={[
  829. {
  830. required: true,
  831. message: '请选择曲目类型',
  832. trigger:'change'
  833. }
  834. ]}
  835. >
  836. <NSelect
  837. placeholder="请选择曲目类型"
  838. v-model:value={forms.musicSheetType}
  839. options={getSelectDataFromObj(musicSheetType)}
  840. />
  841. </NFormItemGi>
  842. <NFormItemGi
  843. label="作者属性"
  844. path="sourceType"
  845. rule={[
  846. {
  847. required: true,
  848. message: '请选择作者属性',
  849. trigger:'change'
  850. }
  851. ]}
  852. >
  853. <NSelect
  854. v-model:value={forms.sourceType}
  855. options={getSelectDataFromObj(musicSheetSourceType)}
  856. placeholder="请选择作者属性"
  857. onUpdateValue={() => {
  858. // 发送变化,清理选择的所属人信息
  859. forms.musicSheetExtend = {}
  860. state.ownerName = null
  861. // forms.musicSheetExtend.userId = null
  862. // forms.musicSheetExtend.userName = null
  863. // forms.musicSheetExtend.applicationId = null
  864. // forms.musicSheetExtend.organizationRoleId = null
  865. }}
  866. />
  867. </NFormItemGi>
  868. </NGrid>
  869. <NGrid cols={2}>
  870. {forms.sourceType === 'PERSON' && (
  871. <NFormItemGi
  872. label="所属人"
  873. path="musicSheetExtend.userId"
  874. rule={[
  875. {
  876. required: true,
  877. message: '请选择曲目所属人',
  878. trigger:['input','change']
  879. }
  880. ]}
  881. >
  882. <NButton
  883. disabled={state.previewMode || !forms.sourceType}
  884. type="primary"
  885. size="small"
  886. text
  887. //v-auth="orchestraSubsidyStandard/update1597887579789053953"
  888. onClick={() => {
  889. state.showMusicSheetOwnerDialog = true
  890. }}
  891. >
  892. {state.ownerName ? state.ownerName : '请选择所属人'}
  893. </NButton>
  894. </NFormItemGi>
  895. )}
  896. {forms.sourceType === 'ORG' && (
  897. <NFormItemGi
  898. label="所属人"
  899. path="musicSheetExtend.organizationRoleId"
  900. rule={[
  901. {
  902. required: true,
  903. message: '请选择曲目所属机构',
  904. trigger:['input','change']
  905. }
  906. ]}
  907. >
  908. <NButton
  909. disabled={state.previewMode || !forms.sourceType}
  910. type="primary"
  911. size="small"
  912. text
  913. //v-auth="orchestraSubsidyStandard/update1597887579789053953"
  914. onClick={() => {
  915. state.showMusicSheetOwnerDialog = true
  916. }}
  917. >
  918. {state.ownerName ? state.ownerName : '请选择所属机构'}
  919. </NButton>
  920. </NFormItemGi>
  921. )}
  922. <NFormItemGi
  923. label="速度"
  924. path="playSpeed"
  925. rule={[
  926. {
  927. required: false,
  928. message: '请输入速度'
  929. }
  930. ]}
  931. >
  932. <NInputNumber
  933. placeholder="请输入速度"
  934. v-model:value={forms.playSpeed}
  935. style="width:100%"
  936. />
  937. </NFormItemGi>
  938. </NGrid>
  939. <NGrid cols={2}>
  940. <NFormItemGi
  941. label="审核版本"
  942. path="appAuditFlag"
  943. rule={[
  944. {
  945. required: true,
  946. message: '请选择审核版本',
  947. trigger:'change',
  948. type:'number'
  949. }
  950. ]}
  951. >
  952. <NSelect
  953. options={
  954. [
  955. {
  956. label: '是',
  957. value: 1
  958. },
  959. {
  960. label: '否',
  961. value: 0
  962. }
  963. ] as any
  964. }
  965. v-model:value={forms.appAuditFlag}
  966. />
  967. </NFormItemGi>
  968. <NFormItemGi label="曲目分类" path="musicCategoryId"
  969. rule={[
  970. {
  971. required: true,
  972. message: '请选择曲目分类',
  973. trigger: ['change']
  974. }
  975. ]}
  976. >
  977. <NCascader
  978. valueField="id"
  979. labelField="name"
  980. children-field="musicSheetCategoriesList"
  981. placeholder="请选择分类"
  982. v-model:value={forms.musicCategoryId}
  983. options={state.musicSheetCategories}
  984. clearable
  985. />
  986. </NFormItemGi>
  987. </NGrid>
  988. <NGrid cols={2}>
  989. <NFormItemGi label="重复节拍时长" path="repeatedBeats"
  990. rule={[
  991. {
  992. required: false,
  993. message: '请选择是否重复节拍时长'
  994. }
  995. ]}
  996. >
  997. <NRadioGroup v-model:value={forms.repeatedBeats}>
  998. <NRadio value={true}>是</NRadio>
  999. <NRadio value={false}>否</NRadio>
  1000. </NRadioGroup>
  1001. </NFormItemGi>
  1002. <NFormItemGi
  1003. label="评分标准"
  1004. path="evaluationStandard"
  1005. rule={[
  1006. {
  1007. required: true
  1008. }
  1009. ]}
  1010. >
  1011. <NRadioGroup v-model:value={forms.evaluationStandard}>
  1012. <NRadio value={'FREQUENCY'}>标准评测</NRadio>
  1013. <NRadio value={'AMPLITUDE'}>打击乐(振幅)</NRadio>
  1014. <NRadio value={'DECIBELS'}>节奏(分贝)</NRadio>
  1015. </NRadioGroup>
  1016. </NFormItemGi>
  1017. </NGrid>
  1018. <NAlert showIcon={false} style={{ marginBottom: '12px' }}>
  1019. 曲目上传
  1020. </NAlert>
  1021. <NGrid cols={2}>
  1022. <NFormItemGi
  1023. label="播放模式"
  1024. path="playMode"
  1025. rule={[
  1026. {
  1027. required: true,
  1028. message: '请选择播放模式'
  1029. }
  1030. ]}
  1031. >
  1032. <NRadioGroup
  1033. v-model:value={forms.playMode}
  1034. onUpdateValue={(value: string | number | boolean) => {
  1035. if (value === 'MP3') {
  1036. forms.playMode = 'MP3'
  1037. } else {
  1038. forms.playMode = 'MIDI'
  1039. }
  1040. }}
  1041. >
  1042. <NRadio value="MP3">MP3</NRadio>
  1043. <NRadio value="MIDI">MID</NRadio>
  1044. </NRadioGroup>
  1045. </NFormItemGi>
  1046. {forms.playMode === 'MP3' && (
  1047. <NFormItemGi
  1048. label="伴奏类型"
  1049. path="audioType"
  1050. rule={[
  1051. {
  1052. required: true,
  1053. message: '请选择伴奏类型'
  1054. }
  1055. ]}
  1056. >
  1057. <NRadioGroup v-model:value={forms.audioType}>
  1058. <NRadio value={'HOMEMODE'}>自制伴奏</NRadio>
  1059. <NRadio value={'COMMON'}>普通伴奏</NRadio>
  1060. </NRadioGroup>
  1061. </NFormItemGi>
  1062. )}
  1063. </NGrid>
  1064. <NGrid cols={2}>
  1065. {forms.playMode === 'MP3' && (
  1066. <NFormItemGi
  1067. label="上传伴奏"
  1068. path="musicSheetAccompanimentList"
  1069. rule={[
  1070. {
  1071. required: false,
  1072. message: '请选择上传.mp3'
  1073. }
  1074. ]}
  1075. >
  1076. <UploadFile
  1077. disabled={state.previewMode}
  1078. size={10}
  1079. v-model:imageList={state.musicSheetAccompanimentUrlList}
  1080. tips="仅支持上传.mp3格式文件"
  1081. listType="image"
  1082. accept=".mp3"
  1083. bucketName="cloud-coach"
  1084. text="点击上传伴奏文件"
  1085. max={10}
  1086. desc={'上传伴奏文件'}
  1087. onUpload:success={(file) => {
  1088. state.musicSheetAccompanimentUrls = [
  1089. state.musicSheetAccompanimentUrls,
  1090. file.url
  1091. ]
  1092. .filter(Boolean)
  1093. .join(',')
  1094. state.musicSheetAccompanimentUrlList = state.musicSheetAccompanimentUrls
  1095. ?.split(',')
  1096. .filter(Boolean)
  1097. forms.musicSheetAccompanimentList = []
  1098. for (let i = 0; i < state.musicSheetAccompanimentUrlList.length; i++) {
  1099. forms.musicSheetAccompanimentList.push({
  1100. audioFileUrl: state.musicSheetAccompanimentUrlList[i],
  1101. sortNumber: i + 1
  1102. })
  1103. }
  1104. }}
  1105. onRemove={() => {
  1106. state.musicSheetAccompanimentUrlList = []
  1107. state.musicSheetAccompanimentUrls = ''
  1108. }}
  1109. // onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
  1110. multiple={true}
  1111. />
  1112. </NFormItemGi>
  1113. )}
  1114. {forms.playMode === 'MIDI' && (
  1115. <NFormItemGi
  1116. label="上传MID"
  1117. path="midiFileUrl"
  1118. rule={[
  1119. {
  1120. required: true,
  1121. message: '请选择上传.MID格式文件'
  1122. }
  1123. ]}
  1124. >
  1125. <UploadFile
  1126. desc={'MIDI文件'}
  1127. disabled={state.previewMode}
  1128. size={10}
  1129. v-model:fileList={forms.midiFileUrl}
  1130. tips="仅支持上传.MID格式文件"
  1131. listType="image"
  1132. accept=".mid"
  1133. bucketName="cloud-coach"
  1134. text="点击上传MID文件"
  1135. // onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
  1136. />
  1137. </NFormItemGi>
  1138. )}
  1139. <NFormItemGi
  1140. label="上传XML"
  1141. path="xmlFileUrl"
  1142. rule={[
  1143. {
  1144. required: true,
  1145. message: '请选择上传XML',
  1146. trigger: ['change', 'input']
  1147. }
  1148. ]}
  1149. >
  1150. <UploadFile
  1151. desc={'XML文件'}
  1152. disabled={state.previewMode}
  1153. size={10}
  1154. key={'xmlFileUrl'}
  1155. v-model:fileList={forms.xmlFileUrl}
  1156. tips="仅支持上传.xml/.mxml格式文件"
  1157. listType="image"
  1158. accept=".xml,.mxml"
  1159. bucketName="cloud-coach"
  1160. text="点击上传XML文件"
  1161. onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
  1162. onRemove={() => {
  1163. forms.multiTracksSelection = []
  1164. state.partListNames = []
  1165. forms.musicSheetSoundList = []
  1166. forms.musicalInstrumentIdList = []
  1167. forms.subjectIds = []
  1168. }}
  1169. />
  1170. </NFormItemGi>
  1171. </NGrid>
  1172. <NGrid cols={2}>
  1173. <NFormItemGi
  1174. label="可用声部"
  1175. path="subjectIds"
  1176. rule={[
  1177. {
  1178. required: true,
  1179. message: '请选择可用声部',
  1180. trigger: 'change',
  1181. type: 'array'
  1182. }
  1183. ]}
  1184. >
  1185. <NSelect
  1186. v-model:value={forms.subjectIds}
  1187. options={state.subjectList}
  1188. multiple
  1189. filterable
  1190. clearable
  1191. placeholder="请选择可用声部"
  1192. maxTagCount={2}
  1193. />
  1194. </NFormItemGi>
  1195. <NFormItemGi
  1196. label="可用乐器"
  1197. path="musicalInstrumentIdList"
  1198. rule={[
  1199. {
  1200. required: true,
  1201. message: '请选择可用乐器',
  1202. trigger: 'change',
  1203. type: 'array'
  1204. }
  1205. ]}
  1206. >
  1207. <NSelect
  1208. placeholder="请选择可用乐器"
  1209. options={state.instrumentList}
  1210. v-model:value={forms.musicalInstrumentIdList}
  1211. clearable
  1212. multiple
  1213. maxTagCount={2}
  1214. />
  1215. </NFormItemGi>
  1216. </NGrid>
  1217. {forms.musicSheetType && (
  1218. <NGrid cols={1}>
  1219. <NFormItemGi
  1220. label={`${forms.musicSheetType === 'SINGLE' ? '页面渲染声轨' : '用户可切换声轨'}`}
  1221. path="multiTracksSelection"
  1222. rule={[
  1223. {
  1224. required: true,
  1225. message: `请选择${
  1226. forms.musicSheetType === 'SINGLE' ? '页面渲染声轨' : '用户可切换声轨'
  1227. }`,
  1228. trigger:'change',
  1229. type:'array'
  1230. }
  1231. ]}
  1232. >
  1233. <NGrid style="padding-top: 4px;">
  1234. <NGi span={24}>
  1235. <NRadioGroup
  1236. v-model:value={state.multiTracks}
  1237. onUpdateValue={(value) => {
  1238. checkMultiTracks(value)
  1239. }}
  1240. >
  1241. <NRadio value={'all'}>全选</NRadio>
  1242. <NRadio value={'allUncheck'}>重置</NRadio>
  1243. <NRadio value={'invert'}>反选</NRadio>
  1244. </NRadioGroup>
  1245. </NGi>
  1246. <NGi span={24} style={'margin-top:5px'}>
  1247. <NFormItemGi
  1248. label=""
  1249. path="multiTracksSelection"
  1250. rule={[
  1251. {
  1252. required: false
  1253. }
  1254. ]}
  1255. >
  1256. <NCheckboxGroup v-model:value={forms.multiTracksSelection}>
  1257. <NGrid yGap={2} cols={4}>
  1258. {state.partListNames.map((item: any) => (
  1259. <NGi>
  1260. <NCheckbox value={item.value} label={item.label} />
  1261. </NGi>
  1262. ))}
  1263. </NGrid>
  1264. </NCheckboxGroup>
  1265. </NFormItemGi>
  1266. </NGi>
  1267. </NGrid>
  1268. </NFormItemGi>
  1269. </NGrid>
  1270. )}
  1271. <NGrid cols={2}>
  1272. <NFormItemGi
  1273. label="是否播放节拍器"
  1274. path="isPlayBeat"
  1275. rule={[
  1276. {
  1277. required: true,
  1278. message: '请选择是否播放节拍器'
  1279. }
  1280. ]}
  1281. >
  1282. <NRadioGroup v-model:value={forms.isPlayBeat}>
  1283. <NRadio value={true}>是</NRadio>
  1284. <NRadio value={false}>否</NRadio>
  1285. </NRadioGroup>
  1286. </NFormItemGi>
  1287. {forms.isPlayBeat && (
  1288. <NFormItemGi
  1289. label="播放方式"
  1290. path="audioType"
  1291. rule={[
  1292. {
  1293. required: true,
  1294. message: '请选择播放方式'
  1295. }
  1296. ]}
  1297. >
  1298. <NRadioGroup v-model:value={forms.isUseSystemBeat}>
  1299. <NRadio value={true}>系统节拍器</NRadio>
  1300. <NRadio value={false}>MP3节拍器</NRadio>
  1301. </NRadioGroup>
  1302. </NFormItemGi>
  1303. )}
  1304. </NGrid>
  1305. {/* 只有播放类型为mp3时才会有原音 */}
  1306. {forms.playMode === 'MP3' && forms.musicSheetSoundList.length > 0 && (
  1307. <>
  1308. {forms.musicSheetSoundList.map((item: any, index: number) => (
  1309. <>
  1310. {item.track?.toLocaleUpperCase?.() != 'COMMON' &&
  1311. forms.multiTracksSelection.indexOf(item.track) > -1 && (
  1312. <NGrid
  1313. class={styles.audioSection}
  1314. // v-show={forms.multiTracksSelection.indexOf(item.track) > -1}
  1315. >
  1316. <NFormItemGi
  1317. span={12}
  1318. label="原音"
  1319. path={`musicSheetSoundList[${index}].audioFileUrl`}
  1320. rule={[
  1321. {
  1322. // required: forms.multiTracksSelection.indexOf(forms.musicSheetSoundList[index].audioFileUrl) > -1,
  1323. required: true,
  1324. message: `请上传${
  1325. item.track ? item.track + '的' : '第' + (index + 1) + '个'
  1326. }原音`
  1327. }
  1328. ]}
  1329. >
  1330. <UploadFile
  1331. desc={'原音文件'}
  1332. disabled={state.previewMode}
  1333. size={100}
  1334. v-model:fileList={item.audioFileUrl}
  1335. tips="仅支持上传.mp3格式文件"
  1336. listType="image"
  1337. accept=".mp3"
  1338. bucketName="cloud-coach"
  1339. />
  1340. </NFormItemGi>
  1341. {state.partListNames.length > 1 && (
  1342. <NFormItemGi
  1343. span={12}
  1344. label="所属轨道"
  1345. path={`musicSheetSoundList[${index}].track`}
  1346. rule={[
  1347. {
  1348. required: true,
  1349. message: '请选择所属轨道'
  1350. }
  1351. ]}
  1352. >
  1353. <NSelect
  1354. placeholder="请选择所属轨道"
  1355. v-model:value={item.track}
  1356. options={initPartsListStatus(item.track)}
  1357. />
  1358. </NFormItemGi>
  1359. )}
  1360. </NGrid>
  1361. )}
  1362. </>
  1363. ))}
  1364. </>
  1365. )}
  1366. </NForm>
  1367. </NSpin>
  1368. {props.type !== 'preview' && (
  1369. <NSpace justify="end" style="padding-top:12px">
  1370. <NButton type="default" onClick={() => emit('close')}>
  1371. 取消
  1372. </NButton>
  1373. <NButton
  1374. type="primary"
  1375. onClick={() => onSubmit()}
  1376. loading={btnLoading.value}
  1377. disabled={btnLoading.value}
  1378. >
  1379. 确认
  1380. </NButton>
  1381. </NSpace>
  1382. )}
  1383. <NModal
  1384. v-model:show={state.showMusicSheetOwnerDialog}
  1385. preset="dialog"
  1386. showIcon={false}
  1387. maskClosable={false}
  1388. title="所属人"
  1389. style={{ width: '800px' }}
  1390. >
  1391. <MusicSheetOwnerDialog
  1392. musicSheetExtend={forms.musicSheetExtend}
  1393. sourceType={forms.sourceType}
  1394. appData={state.appData}
  1395. onClose={() => {
  1396. state.showMusicSheetOwnerDialog = false
  1397. }}
  1398. onChoseMusicSheetOwnerData={(musicSheetOwnerData) => {
  1399. forms.musicSheetExtend = {
  1400. ...musicSheetOwnerData
  1401. }
  1402. setOwnerName()
  1403. }}
  1404. />
  1405. </NModal>
  1406. <NModal
  1407. class={styles.productModal}
  1408. title="自动生成曲谱图片"
  1409. v-model:show={state.productOpen}
  1410. preset="dialog"
  1411. closeOnEsc={false}
  1412. maskClosable={false}
  1413. showIcon={false}
  1414. >
  1415. <MusicCreateImg
  1416. xmlFileUrl={forms.xmlFileUrl || ''}
  1417. onClose={() => (state.productOpen = false)}
  1418. onConfirm={async (item: any) => {
  1419. // 保存
  1420. try {
  1421. forms.musicImg = item.musicImg
  1422. forms.musicSvg = item.musicSvg
  1423. forms.musicJianSvg = item.musicJianSvg
  1424. onSubmit()
  1425. } catch (e: any) {
  1426. //
  1427. console.log(e, 'e')
  1428. }
  1429. setTimeout(() => {
  1430. state.isAutoSave = false
  1431. }, 50)
  1432. }}
  1433. />
  1434. </NModal>
  1435. </div>
  1436. )
  1437. }
  1438. })