music-operationV2.tsx 85 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212
  1. import type {SelectOption} from 'naive-ui'
  2. import {
  3. NAlert,
  4. NButton,
  5. NCascader,
  6. NCheckbox,
  7. NCheckboxGroup,
  8. NForm,
  9. NFormItem,
  10. NFormItemGi,
  11. NGi,
  12. NGrid,
  13. NInput,
  14. NInputGroup,
  15. NInputGroupLabel,
  16. NInputNumber,
  17. NModal,
  18. NRadio,
  19. NRadioGroup,
  20. NSelect,
  21. NSpace,
  22. NSpin,
  23. useDialog,
  24. useMessage
  25. } from 'naive-ui'
  26. import {defineComponent, onMounted, PropType, reactive, ref} from 'vue'
  27. import {musicSheetCategoriesQueryTree, musicSheetDetail, musicSheetSave} from '../../api'
  28. import UploadFile from '@/components/upload-file'
  29. import styles from './index.module.less'
  30. import deepClone from '@/utils/deep.clone'
  31. import axios from 'axios'
  32. import {appKey, clientType, musicSheetSourceType} from '@/utils/constant'
  33. import {getMapValueByKey, getSelectDataFromObj} from '@/utils/objectUtil'
  34. import {musicalInstrumentPage} from '@views/system-manage/subject-manage/api'
  35. import {subjectPage} from '@views/system-manage/api'
  36. import MusicSheetOwnerDialog from '@views/music-library/music-sheet/modal/musicSheetOwnerDialog'
  37. import {sysApplicationPage} from '@views/menu-manage/api'
  38. import {filterPointCategory} from '@views/teaching-manage/unit-test'
  39. import MusicCreateImg from './music-create-img'
  40. import {TABS_ROUTES} from "@/store/mutation-types";
  41. /**
  42. * 获取指定元素下一个Note元素
  43. * @param ele 指定元素
  44. * @param selectors 选择器
  45. */
  46. const getNextNote = (ele: any, selectors: any) => {
  47. let index = 0
  48. const parentEle = ele.closest(selectors)
  49. let pointer = parentEle
  50. const measure = parentEle?.closest('measure')
  51. let siblingNote = null
  52. // 查找到相邻的第一个note元素
  53. while (!siblingNote && index < (measure?.childNodes.length || 50)) {
  54. index++
  55. if (pointer?.nextElementSibling?.tagName === 'note') {
  56. siblingNote = pointer?.nextElementSibling
  57. }
  58. pointer = pointer?.nextElementSibling
  59. }
  60. return siblingNote
  61. }
  62. export const onlyVisible = (xml: any, partIndex: any) => {
  63. if (!xml) return ''
  64. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  65. const partList =
  66. xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
  67. const parts = xmlParse.getElementsByTagName('part')
  68. const visiblePartInfo = partList[partIndex]
  69. if (visiblePartInfo) {
  70. const id = visiblePartInfo.getAttribute('id')
  71. Array.from(parts).forEach((part) => {
  72. if (part && part.getAttribute('id') !== id) {
  73. part.parentNode?.removeChild(part)
  74. // 不等于第一行才添加避免重复添加
  75. }
  76. // 最后一个小节的结束线元素不在最后 调整
  77. if (part && part.getAttribute('id') === id) {
  78. const barlines = part.getElementsByTagName('barline')
  79. const lastParent = barlines[barlines.length - 1]?.parentElement
  80. if (lastParent?.lastElementChild?.tagName !== 'barline') {
  81. const children: any[] = (lastParent?.children as any) || []
  82. for (let el of children) {
  83. if (el.tagName === 'barline') {
  84. // 将结束线元素放到最后
  85. lastParent?.appendChild(el)
  86. break
  87. }
  88. }
  89. }
  90. }
  91. })
  92. Array.from(partList).forEach((part) => {
  93. if (part && part.getAttribute('id') !== id) {
  94. part.parentNode?.removeChild(part)
  95. }
  96. })
  97. // 处理装饰音问题
  98. const notes = xmlParse.getElementsByTagName('note')
  99. const getNextvNoteDuration = (i: any) => {
  100. let nextNote = notes[i + 1]
  101. // 可能存在多个装饰音问题,取下一个非装饰音时值
  102. for (let index = i; index < notes.length; index++) {
  103. const note = notes[index]
  104. if (!note.getElementsByTagName('grace')?.length) {
  105. nextNote = note
  106. break
  107. }
  108. }
  109. return nextNote?.getElementsByTagName('duration')[0]
  110. }
  111. Array.from(notes).forEach((note, i) => {
  112. const graces = note.getElementsByTagName('grace')
  113. if (graces && graces.length) {
  114. note.appendChild(getNextvNoteDuration(i)?.cloneNode(true))
  115. }
  116. })
  117. }
  118. return new XMLSerializer().serializeToString(xmlParse)
  119. }
  120. const speedInfo = {
  121. 'rall.': 1.333333333,
  122. 'poco rit.': 1.333333333,
  123. 'rit.': 1.333333333,
  124. 'molto rit.': 1.333333333,
  125. 'molto rall': 1.333333333,
  126. molto: 1.333333333,
  127. lentando: 1.333333333,
  128. allargando: 1.333333333,
  129. morendo: 1.333333333,
  130. 'accel.': 0.8,
  131. calando: 2,
  132. 'poco accel.': 0.8,
  133. 'gradually slowing': 1.333333333,
  134. slowing: 1.333333333,
  135. slow: 1.333333333,
  136. slowly: 1.333333333,
  137. faster: 1.333333333
  138. }
  139. /**
  140. * 按照xml进行减慢速度的计算
  141. * @param xml 始终按照第一分谱进行减慢速度的计算
  142. */
  143. export function getGradualLengthByXml(xml: string) {
  144. const firstPartXml = onlyVisible(xml, 0)
  145. const xmlParse = new DOMParser().parseFromString(firstPartXml, 'text/xml')
  146. const measures = Array.from(xmlParse.querySelectorAll('measure'))
  147. const notes = Array.from(xmlParse.querySelectorAll('note'))
  148. const words = Array.from(xmlParse.querySelectorAll('words'))
  149. const metronomes = Array.from(xmlParse.querySelectorAll('metronome'))
  150. const eles = []
  151. for (const ele of [...words, ...metronomes]) {
  152. const note = getNextNote(ele, 'direction')
  153. // console.log(ele, note)
  154. if (note) {
  155. const measure = note?.closest('measure')
  156. const measureNotes = Array.from(measure.querySelectorAll('note'))
  157. const noteInMeasureIndex = Array.from(measure.childNodes)
  158. .filter((item: any) => item.nodeName === 'note')
  159. .findIndex((item) => item === note)
  160. let allDuration = 0
  161. let leftDuration = 0
  162. for (let i = 0; i < measureNotes.length; i++) {
  163. const n: any = measureNotes[i]
  164. const duration = +(n.querySelector('duration')?.textContent || '0')
  165. allDuration += duration
  166. if (i < noteInMeasureIndex) {
  167. leftDuration = allDuration
  168. }
  169. }
  170. eles.push({
  171. ele,
  172. index: notes.indexOf(note),
  173. noteInMeasureIndex,
  174. textContent: ele.textContent,
  175. measureIndex: measures.indexOf(measure), //,measure?.getAttribute('number')
  176. type: ele.tagName,
  177. allDuration,
  178. leftDuration
  179. })
  180. }
  181. }
  182. // 结尾处手动插入一个音符节点
  183. eles.push({
  184. ele: notes[notes.length - 1],
  185. index: notes.length,
  186. noteInMeasureIndex: 0,
  187. textContent: '',
  188. type: 'metronome',
  189. allDuration: 1,
  190. leftDuration: 1,
  191. measureIndex: measures.length
  192. })
  193. const gradualNotes: any[] = []
  194. eles.sort((a, b) => a.index - b.index)
  195. const keys = Object.keys(speedInfo).map((w) => w.toLocaleLowerCase())
  196. let isLastNoteAndNotClosed = false
  197. for (const ele of eles) {
  198. const textContent: any = ele.textContent?.toLocaleLowerCase().trim()
  199. if (ele === eles[eles.length - 1]) {
  200. if (gradualNotes[gradualNotes.length - 1]?.length === 1) {
  201. isLastNoteAndNotClosed = true
  202. }
  203. }
  204. const isKeyWork = keys.find((k) => {
  205. const ks = k.split(' ')
  206. return textContent && ks.includes(textContent)
  207. })
  208. if (
  209. ele.type === 'metronome' ||
  210. (ele.type === 'words' && (textContent.startsWith('a tempo') || isKeyWork)) ||
  211. isLastNoteAndNotClosed
  212. ) {
  213. const indexOf = gradualNotes.findIndex((item) => item.length === 1)
  214. if (indexOf > -1 && ele.index > gradualNotes[indexOf]?.[0].start) {
  215. gradualNotes[indexOf][1] = {
  216. start: ele.index,
  217. measureIndex: ele.measureIndex,
  218. noteInMeasureIndex: ele.noteInMeasureIndex,
  219. allDuration: ele.allDuration,
  220. leftDuration: ele.leftDuration,
  221. type: textContent
  222. }
  223. }
  224. }
  225. if (ele.type === 'words' && isKeyWork) {
  226. gradualNotes.push([
  227. {
  228. start: ele.index,
  229. measureIndex: ele.measureIndex,
  230. noteInMeasureIndex: ele.noteInMeasureIndex,
  231. allDuration: ele.allDuration,
  232. leftDuration: ele.leftDuration,
  233. type: textContent
  234. }
  235. ])
  236. }
  237. }
  238. return gradualNotes
  239. }
  240. export default defineComponent({
  241. name: 'music-operation',
  242. props: {
  243. type: {
  244. type: String,
  245. default: 'add'
  246. },
  247. data: {
  248. type: Object as PropType<any>,
  249. default: () => {
  250. }
  251. },
  252. tagList: {
  253. type: Array as PropType<Array<SelectOption>>,
  254. default: () => []
  255. },
  256. subjectList: {
  257. type: Array as PropType<Array<SelectOption>>,
  258. default: () => []
  259. }
  260. // musicSheetCategories: {
  261. // type: Array as PropType<Array<SelectOption>>,
  262. // default: () => []
  263. // }
  264. },
  265. emits: ['close', 'getList'],
  266. setup(props, {slots, attrs, emit}) {
  267. const forms = reactive({
  268. details: {} as any, // 曲目详情
  269. graduals: {} as any, // 渐变速度
  270. playMode: 'MP3', // 播放类型
  271. xmlFileUrl: null, // XML
  272. midiUrl: null, // mid
  273. name: null, // 曲目名称
  274. // musicTag: [] as any, // 曲目标签
  275. composer: null, // 音乐人
  276. playSpeed: null as any, // 曲谱速度
  277. // showFingering: null as any, // 是否显示指法
  278. // canEvaluate: null as any, // 是否评测
  279. // notation: null as any, // 能否转和简谱
  280. // auditVersion: null as any, // 审核版本
  281. // sortNumber: null, // 排序
  282. musicCover: null, // 曲谱封面
  283. remark: null, // 曲谱描述
  284. // musicSheetSoundList: [] as any, // 原音
  285. musicSheetSoundList_YY: [] as any, // 演唱原音
  286. musicSheetSoundList_YZ: [] as any, // 演奏演奏
  287. musicSheetSoundList_all_subject: null, // 全部声部原音
  288. // musicSheetCategoriesId: null,
  289. status: false,
  290. musicSheetType: 'CONCERT', // 曲目类型
  291. sourceType: 'PLATFORM' as any, //来源类型/作者属性(PLATFORM: 平台; ORG: 机构; PERSON: 个人)
  292. // userId: null, // 所属人
  293. appAuditFlag: 0, // 是否审核版本
  294. midiFileUrl: null, // 伴奏文件 MIDI文件(保留字段)
  295. subjectIds: [] as any, // 可用声部
  296. musicalInstrumentIdList: [] as any, //可用乐器
  297. musicCategoryId: null, //曲目分类
  298. musicSheetAccompanimentList: [] as any, //曲目伴奏
  299. audioType: 'HOMEMODE', // 伴奏类型
  300. isPlayBeat: true, // 是否播放节拍器
  301. isUseSystemBeat: true, // 是否使用系统节拍器(0:否;1:是)
  302. isUseSingSystemBeat: true, //演唱是否使用系统节拍器(0:否;1:是)
  303. repeatedBeats: false, // 是否重复节拍时长
  304. repeatedBeatsToSing: false, //演唱是否重复节拍时长
  305. isPlaySingBeat: true, //是否播入演唱节拍器(0: 否 1:是)
  306. evaluationStandard: 'FREQUENCY', // 评分标准 节奏 AMPLITUDE 音准 FREQUENCY 分贝 DECIBELS
  307. multiTracksSelection: [] as any, // 声轨
  308. musicSheetExtend: {} as any, //所属人信息
  309. musicImg: '', // 五线谱图片
  310. musicFirstImg: '', //首调图片
  311. musicJianImg: '', // 简谱固定调
  312. isEvxml: false, // 是否是evxml
  313. isShowFingering: true, // 是否显示指法
  314. solmizationFileUrl: null, // 唱名文件
  315. isAllSubject: false, // 适用声部
  316. })
  317. const state = reactive({
  318. loading: false,
  319. previewMode: false, //是否是预览模式
  320. tagList: [...props.tagList] as any, // 标签列表
  321. xmlFirstSpeed: null as any, // 第一个音轨速度
  322. partListNames: [] as any, // 所有音轨声部列表
  323. musicSheetCategories: [] as any,
  324. musicSheetAccompanimentUrls: '' as any,
  325. musicSheetAccompanimentUrlList: [] as any,
  326. instrumentData: [],
  327. instrumentList: [] as any,
  328. instrumentIdNameMap: new Map() as any,
  329. subjectList: [] as any,
  330. showMusicSheetOwnerDialog: false, //所属人弹框
  331. // musicSheetOwnerData: {}, //所属人信息
  332. multiTracks: null as any,
  333. multiInstruments: null,
  334. appData: [], // 应用列表
  335. ownerName: null as any, // 所属人名称描述
  336. productOpen: false, // 是否打开自动生成图片
  337. productItem: {} as any,
  338. productIfameSrc: '',
  339. isAutoSave: false, // 是否自动保存
  340. fSongFile: null as any, // 范唱
  341. bSongFile: null as any, // 伴唱
  342. musicSheetSoundList: [] as any,
  343. })
  344. const gradualData = reactive({
  345. list: [] as any[],
  346. gradualRefs: [] as any[]
  347. })
  348. const btnLoading = ref(false)
  349. const formsRef = ref()
  350. const message = useMessage()
  351. const dialog = useDialog()
  352. // 提交记录
  353. const onSubmit = async () => {
  354. formsRef.value.validate(async (error: any) => {
  355. if (error) {
  356. return
  357. }
  358. // 校验合奏时声轨与乐器是否存在不匹配情况
  359. if (forms.musicSheetType == 'CONCERT') {
  360. let set = [] as any;
  361. const {data} = await musicalInstrumentPage({page: 1, rows: 9999})
  362. data.rows.map((row: any) => {
  363. if (row.code) {
  364. row.code.split(',').forEach((code: string) => {
  365. let temp = code.replaceAll(' ', '')
  366. set.push(temp.toLocaleLowerCase())
  367. })
  368. }
  369. })
  370. let unDefinedTrack = [] as any
  371. forms.multiTracksSelection.forEach((item: any) => {
  372. if (item) {
  373. let contain = false;
  374. let code = item.trim().toLocaleLowerCase()
  375. for (let i = 0; i < set.length; i++) {
  376. if (set[i].startsWith(code) || set[i].endsWith(code)) {
  377. contain = true
  378. break
  379. }
  380. }
  381. if (!contain) {
  382. unDefinedTrack.push(item)
  383. }
  384. }
  385. })
  386. if (unDefinedTrack.length > 0) {
  387. dialog.warning({
  388. title: '提示',
  389. content: `声轨未配置:${unDefinedTrack.join(',')}`,
  390. positiveText: '确定',
  391. onPositiveClick: () => {
  392. },
  393. })
  394. return
  395. }
  396. }
  397. try {
  398. //extConfigJson: {"repeatedBeats":0,"gradualTimes":{"75":"02:38:60","77":"02:43:39"}}
  399. let audioPlayTypes = [] as any;
  400. let musicSheetSoundList = [];
  401. let musicSheetType = forms.musicSheetType;
  402. if (state.fSongFile) {
  403. musicSheetSoundList.push({
  404. musicSheetId: props.data.id,
  405. audioFileUrl: state.fSongFile,
  406. audioPlayType: 'SING',
  407. solmizationFileUrl: forms.solmizationFileUrl
  408. })
  409. audioPlayTypes.push("SING")
  410. }
  411. var existYzFile = false;
  412. if (musicSheetType == 'SINGLE') {
  413. if (forms.isAllSubject) {
  414. if (forms.musicSheetSoundList_all_subject) {
  415. audioPlayTypes.push("PLAY")
  416. musicSheetSoundList.push({
  417. audioFileUrl: forms.musicSheetSoundList_all_subject,
  418. musicSheetId: props.data.id,
  419. audioPlayType: 'PLAY'
  420. })
  421. existYzFile = true
  422. }
  423. } else {
  424. if (forms.musicSheetSoundList_YZ.length > 0 && forms.playMode == 'MP3') {
  425. audioPlayTypes.push("PLAY")
  426. forms.musicSheetSoundList_YZ.forEach((musicSheetSound: any) => {
  427. if (forms.musicalInstrumentIdList.includes(musicSheetSound.musicalInstrumentId) && musicSheetSound.audioFileUrl) {
  428. existYzFile = true
  429. musicSheetSoundList.push({
  430. ...musicSheetSound,
  431. musicSheetId: props.data.id,
  432. })
  433. }
  434. })
  435. }
  436. }
  437. } else if (musicSheetType == 'CONCERT') {
  438. audioPlayTypes.push("PLAY")
  439. forms.musicSheetSoundList_YY.forEach((musicSheetSound: any) => {
  440. if (musicSheetSound.audioFileUrl && musicSheetSound.track != null) {
  441. musicSheetSoundList.push({
  442. ...musicSheetSound,
  443. musicSheetId: props.data.id,
  444. })
  445. existYzFile = true
  446. }
  447. })
  448. }
  449. if (!state.fSongFile && !state.bSongFile && !existYzFile) {
  450. message.warning("请上传音频文件")
  451. return
  452. }
  453. if (state.bSongFile) {
  454. forms.musicSheetAccompanimentList.push({
  455. musicSheetId: props.data.id,
  456. audioFileUrl: state.bSongFile,
  457. audioPlayType: 'SING'
  458. })
  459. }
  460. // 生成图片
  461. if (!state.isAutoSave) {
  462. state.isAutoSave = true
  463. state.productOpen = true
  464. return
  465. }
  466. const obj = {
  467. musicCategoryId: forms.musicCategoryId,
  468. musicCover: forms.musicCover,
  469. name: forms.name,
  470. appAuditFlag: forms.appAuditFlag,
  471. subjectIds: forms.isAllSubject ? "" : forms.subjectIds.join(','),
  472. remark: forms.remark,
  473. audioPlayTypes: audioPlayTypes.join(','),
  474. musicalInstrumentIds: forms.isAllSubject ? "" : forms.musicalInstrumentIdList.join(','),
  475. composer: forms.composer,
  476. musicSheetType: forms.musicSheetType,
  477. isUseSystemBeat: forms.isUseSystemBeat,
  478. isShowFingering: forms.isShowFingering,
  479. isPlayBeat: forms.isPlayBeat,
  480. multiTracksSelection: forms.multiTracksSelection.join(','),
  481. playSpeed: forms.playSpeed,
  482. playMode: forms.playMode,
  483. xmlFileUrl: forms.xmlFileUrl,
  484. musicImg: forms.musicImg,
  485. musicFirstImg: forms.musicFirstImg,
  486. musicJianImg: forms.musicJianImg,
  487. extConfigJson: JSON.stringify({
  488. repeatedBeats: forms.repeatedBeats ? 1 : 0,
  489. gradualTimes: forms.graduals,
  490. isEvxml: forms.isEvxml ? 1 : 0,
  491. repeatedBeatsToSing: forms.repeatedBeatsToSing ? 1 : 0
  492. }),
  493. // availableType: forms.availableType,
  494. sourceType: forms.sourceType,
  495. audioType: forms.audioType,
  496. status: forms.status,
  497. evaluationStandard: forms.evaluationStandard,
  498. musicSheetAccompanimentList: forms.musicSheetAccompanimentList,
  499. musicSheetSoundList: musicSheetSoundList,
  500. musicTag: '-1',
  501. isUseSingSystemBeat: forms.isUseSingSystemBeat,
  502. isPlaySingBeat: forms.isPlaySingBeat,
  503. isAllSubject: forms.isAllSubject,
  504. musicSheetExtend: forms.sourceType == 'PLATFORM' ? null : forms.musicSheetExtend,
  505. }
  506. if (forms.audioType == 'MIDI') {
  507. obj.musicSheetSoundList = []
  508. }
  509. btnLoading.value = true
  510. if (props.type === 'add') {
  511. await musicSheetSave(obj)
  512. message.success('添加成功')
  513. } else if (props.type === 'edit') {
  514. await musicSheetSave({...obj, id: props.data.id})
  515. message.success('修改成功')
  516. }
  517. emit('getList')
  518. emit('close')
  519. } catch (e) {
  520. console.log(e)
  521. }
  522. setTimeout(() => {
  523. btnLoading.value = false
  524. state.isAutoSave = false
  525. }, 100)
  526. })
  527. }
  528. // 上传XML,初始化音轨 音轨速度 乐器、声部
  529. const readFileInputEventAsArrayBuffer = (file: any) => {
  530. // 是否是evxml
  531. forms.isEvxml = file?.name?.includes('.evxml') ? true : false;
  532. const xmlRead = new FileReader()
  533. xmlRead.onload = (res) => {
  534. try {
  535. gradualData.list = getGradualLengthByXml(res?.target?.result as any).filter(
  536. (item: any) => item.length === 2
  537. )
  538. } catch (error) {
  539. }
  540. forms.musicSheetSoundList_YY = forms.musicSheetSoundList_YY.filter((item: any) => {
  541. return (!item.track || !containOther(item.track) || (item.track?.toLocaleUpperCase?.() != 'COMMON' && forms.multiTracksSelection.includes(item.track)))
  542. })
  543. state.partListNames = getPartListNames(res?.target?.result as any) as any
  544. // parseInstrumentAndSubject(res?.target?.result as any)
  545. // 这里是如果没有当前音轨就重新写
  546. let map = new Map<String, String>()
  547. for (let i = 0; i < forms.musicSheetSoundList_YY.length; i++) {
  548. let track = forms.musicSheetSoundList_YY[i].track;
  549. if (track) {
  550. map.set(track, forms.musicSheetSoundList_YY[i])
  551. }
  552. }
  553. let newMusicSheetSoundList = []
  554. let tracks = [] as any
  555. for (let j = 0; j < state.partListNames.length; j++) {
  556. let track = state.partListNames[j].value;
  557. if (map.has(track)) {
  558. newMusicSheetSoundList.push(map.get(track))
  559. } else {
  560. newMusicSheetSoundList.push({audioFileUrl: null, track: track, musicalInstrumentId: null})
  561. }
  562. tracks.push(track)
  563. if(!forms.multiTracksSelection.includes(track)) {
  564. forms.multiTracksSelection.push(track)
  565. }
  566. }
  567. for (let i = 0; i < forms.musicSheetSoundList_YY.length; i++) {
  568. let track = forms.musicSheetSoundList_YY[i].track;
  569. if (!track || !tracks.includes(track)) {
  570. forms.musicSheetSoundList_YY[i].track = null
  571. newMusicSheetSoundList.push(forms.musicSheetSoundList_YY[i])
  572. }
  573. }
  574. forms.musicSheetSoundList_YY = newMusicSheetSoundList
  575. forms.multiTracksSelection = forms.multiTracksSelection.filter((track: any) => {
  576. return tracks.includes(track)
  577. })
  578. // 全选选中
  579. state.multiTracks = 'all'
  580. // 循环添加所在音轨的原音
  581. // for (let index = forms.musicSheetSoundList.length; index < state.partListNames.length; index++) {
  582. // const part = state.partListNames[index].value
  583. // const sysData = {
  584. // ...forms.musicSheetSoundList[0],
  585. // track: part
  586. // }
  587. // if (!sysData.speed) {
  588. // sysData.speed = state.xmlFirstSpeed
  589. // }
  590. // createSys(sysData)
  591. // }
  592. if (forms.musicSheetSoundList_YY.length == 0) {
  593. forms.musicSheetSoundList_YY.push({ audioFileUrl: '', track: '' })
  594. }
  595. }
  596. xmlRead.readAsText(file)
  597. }
  598. const containOther = (track: any) => {
  599. for (let i = 0; i < state.partListNames.length; i++) {
  600. if (state.partListNames[i].value == track) {
  601. return true
  602. }
  603. }
  604. return false
  605. }
  606. const validSoundNum = () => {
  607. return forms.musicSheetSoundList_YY.filter((item: any) => {
  608. return (!item.track || !containOther(item.track) || (item.track?.toLocaleUpperCase?.() != 'COMMON' && forms.multiTracksSelection.includes(item.track)))
  609. }).length;
  610. }
  611. const parseInstrumentAndSubject = (xml: any) => {
  612. if (!xml) return
  613. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  614. // 乐器
  615. const instrumentCodeList: any = []
  616. const instrumentEle = xmlParse.getElementsByTagName('virtual-instrument')
  617. for (let index = 0; index < instrumentEle.length; index++) {
  618. const note = instrumentEle[index]
  619. let instrumentCode = note.getElementsByTagName('virtual-name')?.[0]?.textContent || ''
  620. instrumentCode = instrumentCode.toLocaleLowerCase().trim()
  621. if (instrumentCode && !instrumentCodeList.includes(instrumentCode)) {
  622. instrumentCodeList.push(instrumentCode)
  623. }
  624. }
  625. // 乐器支持多编码,暂不开放
  626. // const codeIdMap = new Map<string, []>() as any
  627. // state.instrumentData.forEach((data: any) => {
  628. // const codes = data.code.split(/,|,/)
  629. // codes.forEach((code: string) => {
  630. // if (codeIdMap.has(code)) {
  631. // codeIdMap.get(code).push(data.id + '')
  632. // } else {
  633. // const arr = [] as any;
  634. // arr.push(data.id + '')
  635. // codeIdMap.set(code, arr)
  636. // }
  637. // // codeIdMap.set(code, data.id + '')
  638. // })
  639. // })
  640. // forms.musicalInstrumentIdList = []
  641. // instrumentCodeList.forEach((code: string) => {
  642. // if (codeIdMap.has(code)) {
  643. // codeIdMap.get(code).forEach((c: any) => {
  644. // forms.musicalInstrumentIdList.push(c)
  645. // })
  646. // }
  647. // })
  648. const codeIdMap = new Map<string, string>()
  649. state.instrumentData.forEach((data: any) => {
  650. if (!data.disabled) {
  651. codeIdMap.set(data.code.toLocaleLowerCase().trim(), data.id + '')
  652. }
  653. })
  654. forms.musicalInstrumentIdList = []
  655. instrumentCodeList.forEach((code: string) => {
  656. if (codeIdMap.has(code)) {
  657. forms.musicalInstrumentIdList.push(codeIdMap.get(code))
  658. }
  659. })
  660. // 声部
  661. if (forms.musicalInstrumentIdList.length > 0) {
  662. showBackSubject(forms.musicalInstrumentIdList)
  663. }
  664. }
  665. // 获取xml中所有轨道 乐器
  666. const getPartListNames = (xml: any) => {
  667. if (!xml) return []
  668. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  669. const partList: any =
  670. xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
  671. let partListNames = Array.from(partList).map((item: any) => {
  672. let part = item.getElementsByTagName('part-name')?.[0]
  673. // evxml没有分轨,需要手动设置一个默认的名称,用于上传原音
  674. // if (forms.isEvxml) {
  675. // part = part || 'noPartName'
  676. // }
  677. // 优先解析声轨,没有就取id值
  678. let track = ''
  679. if (part) {
  680. track = part.textContent || ''
  681. } else {
  682. let id = item.getAttribute('id')
  683. if (id) {
  684. track = id
  685. }
  686. }
  687. return {
  688. value: track.trim(),
  689. label: track.trim()
  690. }
  691. })
  692. // 处理空数据
  693. // if (partListNames.length === 1 && forms.details.id && !partListNames[0].value) {
  694. // partListNames[0] = {
  695. // value: forms.details.multiTracksSelection,
  696. // label: forms.details.multiTracksSelection
  697. // }
  698. // }
  699. partListNames = partListNames.filter((n: any) => n.value?.toLocaleUpperCase?.() != 'COMMON')
  700. // if (partListNames.length > 0) {
  701. // forms.musicSheetSoundList = forms.musicSheetSoundList.slice(0, partListNames.length)
  702. // }
  703. state.xmlFirstSpeed = xmlParse.getElementsByTagName('per-minute')?.[0]?.textContent || ''
  704. if (!forms.playSpeed) {
  705. if (state.xmlFirstSpeed) {
  706. forms.playSpeed = Number.parseFloat(state.xmlFirstSpeed)
  707. } else {
  708. // 速度默认给100
  709. forms.playSpeed = 100
  710. }
  711. }
  712. // console.log('xml声轨',partListNames,state.xmlFirstSpeed)
  713. return partListNames
  714. }
  715. // 判断选择的音轨是否在选中
  716. const initPartsListStatus = (track: string): any => {
  717. // const _names = state.partListNames.filter(
  718. // (n: any) => n.value?.toLocaleUpperCase?.() != 'COMMON'
  719. // )
  720. const partListNames = deepClone(state.partListNames) || []
  721. const multiTracksSelection = forms.multiTracksSelection
  722. partListNames.forEach((item: any) => {
  723. if (multiTracksSelection.includes(item.value)) {
  724. item.disabled = true
  725. } else {
  726. item.disabled = false
  727. }
  728. // const index = forms.musicSheetSoundList.findIndex(
  729. // (ground: any) => item.value == ground.track
  730. // )
  731. // if (index > -1 && track == item.value) {
  732. // item.disabled = false
  733. // } else {
  734. // item.disabled = true
  735. // }
  736. })
  737. return partListNames || []
  738. }
  739. // 反显声部
  740. const showBackSubject = async (musicalInstrumentIdList: []) => {
  741. try {
  742. const { data } = await subjectPage({
  743. page: 1,
  744. rows: 999,
  745. musicalInstrumentIdList: musicalInstrumentIdList
  746. })
  747. const tempList = data.rows || []
  748. tempList.forEach((item: any) => {
  749. forms.subjectIds.push(item.id + '')
  750. })
  751. } catch {
  752. }
  753. }
  754. // 添加原音
  755. const createSys = (initData?: any) => {
  756. forms.musicSheetSoundList_YY.push({
  757. audioFileUrl: null, // 原音
  758. track: null, // 轨道
  759. ...initData
  760. })
  761. }
  762. // 删除原音
  763. const removeSys = (index: number) => {
  764. dialog.warning({
  765. title: '提示',
  766. content: `是否确认删除此原音?`,
  767. positiveText: '确定',
  768. negativeText: '取消',
  769. onPositiveClick: async () => {
  770. const sound = forms.musicSheetSoundList_YY[index]
  771. let track = sound.track
  772. // if (!track) {
  773. // track = ''
  774. // }
  775. // if (track) {
  776. const selectIndex = forms.multiTracksSelection.indexOf(track)
  777. if (selectIndex > -1) {
  778. forms.multiTracksSelection.splice(selectIndex, 1)
  779. } else {
  780. forms.musicSheetSoundList_YY.splice(index, 1)
  781. }
  782. if (state.multiTracks == 'all' && state.partListNames.length != forms.multiTracksSelection.length) {
  783. state.multiTracks = null
  784. }
  785. }
  786. })
  787. }
  788. const checkMultiTracks = (value: string) => {
  789. if (!value) {
  790. return
  791. }
  792. if (value === 'all') {
  793. forms.multiTracksSelection = []
  794. state.partListNames.forEach((next: any) => {
  795. forms.multiTracksSelection.push(next.value)
  796. })
  797. } else if (value === 'invert') {
  798. state.partListNames.forEach((next: any) => {
  799. const indexOf = forms.multiTracksSelection.indexOf(next.value)
  800. if (indexOf > -1) {
  801. forms.multiTracksSelection.splice(indexOf, 1)
  802. } else {
  803. forms.multiTracksSelection.push(next.value)
  804. }
  805. })
  806. } else if (value === 'allUncheck') {
  807. forms.multiTracksSelection = []
  808. }
  809. }
  810. const setOwnerName = () => {
  811. if (forms.sourceType == 'PLATFORM') {
  812. state.ownerName = ''
  813. return
  814. }
  815. if (!forms.sourceType || !forms.musicSheetExtend?.userId) {
  816. return
  817. }
  818. const appId = forms.musicSheetExtend.applicationId
  819. const app = state.appData.filter((next: any) => {
  820. return next.id == appId
  821. }) as any
  822. if (app.length > 0) {
  823. state.ownerName = app[0].appName
  824. }
  825. if (forms.sourceType == 'ORG') {
  826. state.ownerName += '-' + forms.musicSheetExtend.organizationRole
  827. } else if (forms.sourceType == 'PERSON') {
  828. state.ownerName +=
  829. '-' +
  830. getMapValueByKey(forms.musicSheetExtend.clientType, new Map(Object.entries(clientType)))
  831. if (forms.musicSheetExtend.userName) {
  832. state.ownerName += '-' + forms.musicSheetExtend.userName
  833. }
  834. if (forms.musicSheetExtend.phone) {
  835. state.ownerName += '(' + forms.musicSheetExtend.phone + ')'
  836. }
  837. }
  838. }
  839. // 声轨数据兼容
  840. const formatTrack = (track: string) => {
  841. if (!track) {
  842. return '';
  843. }
  844. const trim = track.trim().toUpperCase();
  845. // 导入后的脏数据兼容
  846. if (trim == 'P1' || trim == 'NULL') {
  847. return '';
  848. }
  849. return track.trim();
  850. }
  851. const changeSubject = async (subjectIdList: []) => {
  852. state.instrumentList = []
  853. if (!subjectIdList || subjectIdList.length == 0) {
  854. forms.musicalInstrumentIdList = []
  855. return
  856. }
  857. let enableFlag = null;
  858. if (props.type === 'add') {
  859. enableFlag = true;
  860. }
  861. let tempMusicalInstrumentIdList = [] as any
  862. const {data} = await musicalInstrumentPage({page: 1, rows: 999, subjectIds: subjectIdList, enableFlag: enableFlag});
  863. data.rows.map((item: any) => {
  864. tempMusicalInstrumentIdList.push(item.id + '')
  865. state.instrumentList.push(
  866. {
  867. label: item.name,
  868. value: item.id + '',
  869. disabled: !item.enableFlag
  870. }
  871. )
  872. })
  873. forms.musicalInstrumentIdList = forms.musicalInstrumentIdList.filter((item: any) => {
  874. return tempMusicalInstrumentIdList.includes(item)
  875. })
  876. }
  877. onMounted(async () => {
  878. state.loading = true
  879. if (props.type === 'preview') {
  880. state.previewMode = true
  881. }
  882. // 获取乐器信息
  883. {
  884. if (state.instrumentList && state.instrumentList.length > 0) {
  885. return
  886. }
  887. try {
  888. const {data} = await musicalInstrumentPage({page: 1, rows: 999})
  889. const tempList = data.rows || []
  890. tempList.forEach((item: any) => {
  891. item.label = item.name
  892. item.value = item.id + ''
  893. item.disabled = !item.enableFlag
  894. state.instrumentIdNameMap.set(item.id + '', item.name)
  895. forms.musicSheetSoundList_YZ.push({
  896. 'musicSheetId': props.data.id,
  897. 'musicalInstrumentId': item.id + '',
  898. 'musicalInstrumentName': item.name,
  899. 'audioFileUrl': null,
  900. 'audioPlayType': 'PLAY'
  901. });
  902. })
  903. state.instrumentData = tempList
  904. // state.instrumentList = tempList
  905. } catch {
  906. }
  907. }
  908. state.subjectList = deepClone(props.subjectList)
  909. state.subjectList.forEach((subject: any) => {
  910. subject.disabled = !subject.enableFlag
  911. })
  912. // 初始化应用
  913. {
  914. const appKeys = Object.keys(appKey)
  915. const {data} = await sysApplicationPage({page: 1, rows: 999, parentId: 0})
  916. const tempList = data.rows || []
  917. const filter = tempList.filter((next: any) => {
  918. return appKeys.includes(next.appKey)
  919. })
  920. filter.forEach((item: any) => {
  921. item.label = item.appName
  922. item.value = item.id
  923. })
  924. state.appData = filter
  925. }
  926. // 获取分类信息
  927. {
  928. try {
  929. const {data} = await musicSheetCategoriesQueryTree({enable: true})
  930. state.musicSheetCategories = filterPointCategory(data, 'musicSheetCategoriesList')
  931. } catch (e) {
  932. }
  933. }
  934. if (props.type === 'edit' || props.type === 'preview') {
  935. const detail = props.data
  936. try {
  937. const {data} = await musicSheetDetail({id: detail.id})
  938. forms.details = data
  939. forms.playMode = data.playMode
  940. forms.xmlFileUrl = data.xmlFileUrl
  941. forms.midiUrl = data.midiUrl
  942. forms.name = data.name
  943. // forms.musicTag = data.musicTag?.split(',')
  944. forms.composer = data.composer
  945. forms.playSpeed = data.playSpeed ? Number.parseFloat(data.playSpeed) : 100
  946. // forms.showFingering = Number(data.showFingering)
  947. // forms.canEvaluate = Number(data.canEvaluate)
  948. // forms.notation = Number(data.notation)
  949. // forms.auditVersion = Number(data.auditVersion)
  950. // forms.sortNumber = data.sortNumber
  951. forms.musicCover = data.musicCover
  952. forms.remark = data.remark
  953. forms.status = data.status
  954. forms.musicSheetType = data.musicSheetType || 'SINGLE'
  955. forms.sourceType = data.sourceType
  956. forms.appAuditFlag = data.appAuditFlag ? 1 : 0
  957. forms.midiFileUrl = data.midiFileUrl
  958. forms.isShowFingering = data.isShowFingering
  959. forms.isAllSubject = data.isAllSubject
  960. forms.isUseSingSystemBeat = data.isUseSingSystemBeat
  961. forms.isPlaySingBeat = data.isPlaySingBeat
  962. forms.subjectIds = []
  963. if (data.subjectIds) {
  964. const subjectIds = data.subjectIds.split(',') || []
  965. subjectIds.forEach((subjectId: any) => {
  966. if (!forms.subjectIds.includes(subjectId)) {
  967. forms.subjectIds.push(subjectId)
  968. }
  969. })
  970. state.subjectList = state.subjectList.filter((subject: any) => {
  971. return (!subject.disabled || subjectIds.includes(subject.value));
  972. })
  973. }
  974. forms.musicalInstrumentIdList = data.musicalInstrumentIds ? data.musicalInstrumentIds.split(',') : []
  975. await changeSubject(forms.subjectIds)
  976. forms.musicCategoryId = data.musicCategoryId
  977. forms.audioType = data.audioType
  978. forms.isPlayBeat = data.isPlayBeat
  979. forms.isUseSystemBeat = data.isUseSystemBeat
  980. // 获取渐变 和 是否多声部
  981. try {
  982. const extConfigJson = data.extConfigJson ? JSON.parse(data.extConfigJson) : {}
  983. forms.graduals = extConfigJson.gradualTimes || {}
  984. forms.repeatedBeats = !!extConfigJson.repeatedBeats
  985. forms.isEvxml = !!extConfigJson.isEvxml
  986. forms.repeatedBeatsToSing = !!extConfigJson.repeatedBeatsToSing
  987. } catch (error) {
  988. }
  989. forms.evaluationStandard = data.evaluationStandard
  990. forms.musicSheetExtend = data.musicSheetExtend
  991. state.musicSheetSoundList = data.musicSheetSoundList ? data.musicSheetSoundList : []
  992. let musicSheetAccompanimentList = data.musicSheetAccompanimentList ? data.musicSheetAccompanimentList : []
  993. musicSheetAccompanimentList.forEach((next: any) => {
  994. let audioPlayType = next.audioPlayType;
  995. if (audioPlayType && audioPlayType == 'SING') {
  996. state.bSongFile = next.audioFileUrl;
  997. } else {
  998. state.musicSheetAccompanimentUrlList.push(next.audioFileUrl)
  999. forms.musicSheetAccompanimentList.push(next)
  1000. }
  1001. })
  1002. // 初始化演奏
  1003. for (let i = 0; i < state.musicSheetSoundList.length; i++) {
  1004. if (state.musicSheetSoundList[i].audioPlayType == 'SING') {
  1005. // 范唱 唱名
  1006. state.fSongFile = state.musicSheetSoundList[i].audioFileUrl
  1007. forms.solmizationFileUrl = state.musicSheetSoundList[i].solmizationFileUrl
  1008. } else {
  1009. if (forms.isAllSubject) {
  1010. forms.musicSheetSoundList_all_subject = state.musicSheetSoundList[i].audioFileUrl
  1011. } else {
  1012. // 乐器演奏原音
  1013. for (let j = 0; j < forms.musicSheetSoundList_YZ.length; j++) {
  1014. let musicalInstrumentId = state.musicSheetSoundList[i].musicalInstrumentId;
  1015. if (musicalInstrumentId && musicalInstrumentId == forms.musicSheetSoundList_YZ[j].musicalInstrumentId) {
  1016. forms.musicSheetSoundList_YZ[j].audioFileUrl = state.musicSheetSoundList[i].audioFileUrl
  1017. }
  1018. }
  1019. }
  1020. }
  1021. }
  1022. setOwnerName()
  1023. axios.get(data.xmlFileUrl).then((res: any) => {
  1024. if (res?.data) {
  1025. gradualData.list = getGradualLengthByXml(res?.data as any).filter(
  1026. (item: any) => item.length === 2
  1027. )
  1028. state.partListNames = getPartListNames(res?.data as any) as any
  1029. // 初始化音轨和原音
  1030. if (data.multiTracksSelection) {
  1031. data.multiTracksSelection = data.multiTracksSelection.toLocaleUpperCase()
  1032. }
  1033. if (!data.multiTracksSelection || data.multiTracksSelection.trim() == '' || data.multiTracksSelection.trim() == 'NULL') {
  1034. forms.multiTracksSelection = ['']
  1035. } else {
  1036. forms.multiTracksSelection = data.multiTracksSelection.split(',')
  1037. }
  1038. let names = state.partListNames.map((next: any) => next.label)
  1039. forms.multiTracksSelection = names.filter((next: any) => forms.multiTracksSelection.includes(next.toLocaleUpperCase()))
  1040. const existSoundList = data.musicSheetSoundList ? data.musicSheetSoundList : []
  1041. // 如果只有一个原音文件,并且原音没有对应声轨,取xml解析中的第一个声轨绑定当当前原音
  1042. // if (existSoundList.length === 1 && !formatTrack(existSoundList[0].track)) {
  1043. // let track = state.partListNames.length > 0 ? state.partListNames[0].value : null;
  1044. // forms.musicSheetSoundList_YY.push({
  1045. // audioFileUrl: existSoundList[0].audioFileUrl, // 原音
  1046. // musicalInstrumentId: existSoundList[0].musicalInstrumentId,
  1047. // track: track, // 轨道
  1048. // audioPlayType: 'PLAY'
  1049. // })
  1050. // if (track && !forms.multiTracksSelection.includes(track)) {
  1051. // forms.multiTracksSelection.push(track)
  1052. // }
  1053. // } else {
  1054. const tracks = [] as any
  1055. state.partListNames.forEach((item: any) => {
  1056. let audioFileUrl = null
  1057. let musicalInstrumentId = null
  1058. if (forms.musicSheetType == 'CONCERT') {
  1059. existSoundList.forEach((next: any) => {
  1060. if (next.audioPlayType == 'PLAY') {
  1061. if (!next.track || next.track.trim() == '') {
  1062. next.track = ''
  1063. }
  1064. if (next.track == item.value) {
  1065. audioFileUrl = next.audioFileUrl
  1066. musicalInstrumentId = next.musicalInstrumentId
  1067. }
  1068. }
  1069. })
  1070. }
  1071. forms.musicSheetSoundList_YY.push({
  1072. audioFileUrl: audioFileUrl, // 原音
  1073. musicalInstrumentId: musicalInstrumentId, // 乐器
  1074. track: item.value, // 轨道
  1075. audioPlayType: 'PLAY'
  1076. })
  1077. tracks.push(item.value)
  1078. })
  1079. if (tracks.length == forms.multiTracksSelection.length) {
  1080. state.multiTracks = 'all'
  1081. }
  1082. // 处理没有声轨,但有原音
  1083. if (data.musicSheetType == 'CONCERT') {
  1084. state.musicSheetSoundList.forEach((next: any) => {
  1085. if (next.audioPlayType == 'PLAY') {
  1086. if (next.track && !tracks.includes(next.track.trim()) && next.audioPlayType == 'PLAY') {
  1087. forms.musicSheetSoundList_YY.push({
  1088. audioFileUrl: next.audioFileUrl, // 原音
  1089. musicalInstrumentId: next.musicalInstrumentId,
  1090. track: next.track ? next.track.trim() : '', // 轨道
  1091. audioPlayType: 'PLAY'
  1092. })
  1093. }
  1094. }
  1095. })
  1096. // }
  1097. }
  1098. }
  1099. })
  1100. } catch (error) {
  1101. }
  1102. } else {
  1103. // 新增只能使用启用状态的数据
  1104. state.subjectList = state.subjectList.filter((next: any) => {
  1105. return next.enableFlag == true
  1106. })
  1107. state.instrumentList = state.instrumentList.filter((next: any) => {
  1108. return next.enableFlag == true
  1109. })
  1110. }
  1111. state.loading = false
  1112. })
  1113. return () => (
  1114. <div style="background: #fff; padding-top: 12px">
  1115. <NSpin show={state.loading}>
  1116. <NForm
  1117. class={styles.formContainer}
  1118. model={forms}
  1119. ref={formsRef}
  1120. label-placement="left"
  1121. label-width="150"
  1122. disabled={state.previewMode}
  1123. >
  1124. <NAlert showIcon={false} style={{marginBottom: '12px'}}>
  1125. 基本信息
  1126. </NAlert>
  1127. <NGrid cols={2}>
  1128. <NFormItemGi
  1129. label="曲目名称"
  1130. path="name"
  1131. rule={[
  1132. {
  1133. required: true,
  1134. message: '请输入曲目名称',
  1135. trigger: ['input', 'blur']
  1136. }
  1137. ]}
  1138. >
  1139. <NInput
  1140. v-model:value={forms.name}
  1141. placeholder="请输入曲目名称"
  1142. maxlength={50}
  1143. showCount
  1144. />
  1145. </NFormItemGi>
  1146. <NFormItemGi
  1147. label="音乐人"
  1148. path="composer"
  1149. rule={[
  1150. {
  1151. required: true,
  1152. message: '请输入音乐人',
  1153. trigger: ['input', 'blur']
  1154. }
  1155. ]}
  1156. >
  1157. <NInput
  1158. v-model:value={forms.composer}
  1159. placeholder="请输入音乐人名称"
  1160. showCount
  1161. maxlength={14}
  1162. />
  1163. </NFormItemGi>
  1164. </NGrid>
  1165. <NGrid cols={2}>
  1166. <NFormItemGi
  1167. label="曲目封面"
  1168. path="musicCover"
  1169. rule={[
  1170. {
  1171. required: false,
  1172. message: '请上传曲目封面',
  1173. trigger: ['input', 'blur']
  1174. }
  1175. ]}
  1176. >
  1177. <UploadFile
  1178. desc={'封面图'}
  1179. disabled={state.previewMode}
  1180. accept=".jpg,.jpeg,.png"
  1181. tips="请上传大小1M以内的JPG、PNG图片"
  1182. size={1}
  1183. v-model:fileList={forms.musicCover}
  1184. cropper
  1185. bucketName="cbs"
  1186. options={{
  1187. autoCrop: true, //是否默认生成截图框
  1188. enlarge: 2, // 图片放大倍数
  1189. autoCropWidth: 200, //默框高度
  1190. fixedBox: true, //是否固定截图框大认生成截图框宽度
  1191. autoCropHeight: 200, //默认生成截图小 不允许改变
  1192. previewsCircle: false, //预览图是否是原圆形
  1193. title: '曲目封面'
  1194. }}
  1195. />
  1196. </NFormItemGi>
  1197. <NFormItemGi
  1198. label="曲目分类"
  1199. path="musicCategoryId"
  1200. rule={[
  1201. {
  1202. required: true,
  1203. message: '请选择曲目分类',
  1204. trigger: ['change']
  1205. }
  1206. ]}
  1207. >
  1208. <NCascader
  1209. valueField="id"
  1210. labelField="name"
  1211. children-field="musicSheetCategoriesList"
  1212. placeholder="请选择分类"
  1213. v-model:value={forms.musicCategoryId}
  1214. options={state.musicSheetCategories}
  1215. clearable
  1216. />
  1217. </NFormItemGi>
  1218. </NGrid>
  1219. <NGrid cols={2}>
  1220. <NFormItemGi
  1221. label="作者属性"
  1222. path="sourceType"
  1223. rule={[
  1224. {
  1225. required: true,
  1226. message: '请选择作者属性',
  1227. trigger: 'change'
  1228. }
  1229. ]}
  1230. >
  1231. <NSelect
  1232. v-model:value={forms.sourceType}
  1233. options={getSelectDataFromObj(musicSheetSourceType)}
  1234. placeholder="请选择作者属性"
  1235. onUpdateValue={() => {
  1236. // 发送变化,清理选择的所属人信息
  1237. forms.musicSheetExtend = {}
  1238. state.ownerName = null
  1239. // forms.musicSheetExtend.userId = null
  1240. // forms.musicSheetExtend.userName = null
  1241. // forms.musicSheetExtend.applicationId = null
  1242. // forms.musicSheetExtend.organizationRoleId = null
  1243. }}
  1244. />
  1245. </NFormItemGi>
  1246. {forms.sourceType === 'PERSON' && (
  1247. <NFormItemGi
  1248. label="所属人"
  1249. path="musicSheetExtend.userId"
  1250. rule={[
  1251. {
  1252. required: true,
  1253. message: '请选择曲目所属人',
  1254. trigger: ['input', 'change']
  1255. }
  1256. ]}
  1257. >
  1258. <NButton
  1259. disabled={state.previewMode || !forms.sourceType}
  1260. type="primary"
  1261. size="small"
  1262. text
  1263. //v-auth="orchestraSubsidyStandard/update1597887579789053953"
  1264. onClick={() => {
  1265. state.showMusicSheetOwnerDialog = true
  1266. }}
  1267. >
  1268. {state.ownerName ? state.ownerName : '请选择所属人'}
  1269. </NButton>
  1270. </NFormItemGi>
  1271. )}
  1272. {forms.sourceType === 'ORG' && (
  1273. <NFormItemGi
  1274. label="所属人"
  1275. path="musicSheetExtend.organizationRoleId"
  1276. rule={[
  1277. {
  1278. required: true,
  1279. message: '请选择曲目所属机构',
  1280. trigger: ['input', 'change']
  1281. }
  1282. ]}
  1283. >
  1284. <NButton
  1285. disabled={state.previewMode || !forms.sourceType}
  1286. type="primary"
  1287. size="small"
  1288. text
  1289. //v-auth="orchestraSubsidyStandard/update1597887579789053953"
  1290. onClick={() => {
  1291. state.showMusicSheetOwnerDialog = true
  1292. }}
  1293. >
  1294. {state.ownerName ? state.ownerName : '请选择所属机构'}
  1295. </NButton>
  1296. </NFormItemGi>
  1297. )}
  1298. </NGrid>
  1299. <NGrid cols={2}>
  1300. <NFormItemGi
  1301. label="审核版本"
  1302. path="appAuditFlag"
  1303. rule={[
  1304. {
  1305. required: true,
  1306. message: '请选择审核版本',
  1307. trigger: 'change',
  1308. type: 'number'
  1309. }
  1310. ]}
  1311. >
  1312. <NSelect
  1313. options={
  1314. [
  1315. {
  1316. label: '是',
  1317. value: 1
  1318. },
  1319. {
  1320. label: '否',
  1321. value: 0
  1322. }
  1323. ] as any
  1324. }
  1325. v-model:value={forms.appAuditFlag}
  1326. />
  1327. </NFormItemGi>
  1328. </NGrid>
  1329. <NAlert showIcon={false} style={{marginBottom: '12px'}}>
  1330. 曲目设置
  1331. </NAlert>
  1332. <NGrid cols={2}>
  1333. <NFormItemGi
  1334. label="播放模式"
  1335. path="playMode"
  1336. rule={[
  1337. {
  1338. required: true,
  1339. message: '请选择播放模式'
  1340. }
  1341. ]}
  1342. >
  1343. <NRadioGroup
  1344. v-model:value={forms.playMode}
  1345. onUpdateValue={(value: string | number | boolean) => {
  1346. if (value === 'MP3') {
  1347. forms.playMode = 'MP3'
  1348. } else {
  1349. forms.playMode = 'MIDI'
  1350. }
  1351. }}
  1352. >
  1353. <NRadio value="MP3">MP3</NRadio>
  1354. {/*{forms.playMode == 'MIDI' && (<NRadio value="MIDI">MID</NRadio>)}*/}
  1355. </NRadioGroup>
  1356. </NFormItemGi>
  1357. {forms.playMode === 'MP3' && (
  1358. <NFormItemGi
  1359. label="伴奏类型"
  1360. path="audioType"
  1361. rule={[
  1362. {
  1363. required: true,
  1364. message: '请选择伴奏类型'
  1365. }
  1366. ]}
  1367. >
  1368. <NRadioGroup v-model:value={forms.audioType}>
  1369. <NRadio value={'HOMEMODE'}>自制伴奏</NRadio>
  1370. <NRadio value={'COMMON'}>普通伴奏</NRadio>
  1371. </NRadioGroup>
  1372. </NFormItemGi>
  1373. )}
  1374. </NGrid>
  1375. <NGrid cols={2}>
  1376. <NFormItemGi
  1377. label="上传XML"
  1378. path="xmlFileUrl"
  1379. rule={[
  1380. {
  1381. required: true,
  1382. message: '请选择上传XML',
  1383. trigger: ['change', 'input']
  1384. }
  1385. ]}
  1386. >
  1387. <UploadFile
  1388. desc={'XML文件'}
  1389. disabled={state.previewMode}
  1390. size={30}
  1391. key={'xmlFileUrl'}
  1392. v-model:fileList={forms.xmlFileUrl}
  1393. tips="仅支持上传.xml/.mxml格式文件"
  1394. listType="image"
  1395. accept=".xml,.mxml,.evxml"
  1396. bucketName="cloud-coach"
  1397. text="点击上传XML文件"
  1398. onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
  1399. onRemove={() => {
  1400. // forms.multiTracksSelection = []
  1401. // state.partListNames = []
  1402. // forms.musicSheetSoundList_YY = []
  1403. // forms.musicalInstrumentIdList = []
  1404. // forms.subjectIds = []
  1405. }}
  1406. />
  1407. </NFormItemGi>
  1408. <NFormItemGi
  1409. label="评分标准"
  1410. path="evaluationStandard"
  1411. rule={[
  1412. {
  1413. required: true
  1414. }
  1415. ]}
  1416. >
  1417. <NRadioGroup v-model:value={forms.evaluationStandard}>
  1418. <NRadio value={'FREQUENCY'}>标准评测</NRadio>
  1419. <NRadio value={'AMPLITUDE'}>打击乐(振幅)</NRadio>
  1420. <NRadio value={'DECIBELS'}>节奏(分贝)</NRadio>
  1421. </NRadioGroup>
  1422. </NFormItemGi>
  1423. </NGrid>
  1424. <NGrid cols={2}>
  1425. <NFormItemGi
  1426. label="谱面渲染"
  1427. path="musicSheetType"
  1428. rule={[
  1429. {
  1430. required: true,
  1431. message: '请选择谱面渲染',
  1432. trigger: 'change'
  1433. }
  1434. ]}
  1435. >
  1436. <NRadioGroup v-model:value={forms.musicSheetType}>
  1437. <NRadio value={'SINGLE'}>多声轨</NRadio>
  1438. <NRadio value={'CONCERT'}>单声轨</NRadio>
  1439. </NRadioGroup>
  1440. </NFormItemGi>
  1441. <NFormItemGi
  1442. label="适用声部"
  1443. path="isAllSubject"
  1444. rule={[
  1445. {
  1446. required: true,
  1447. message: '请选择适用声部',
  1448. }
  1449. ]}
  1450. >
  1451. <NRadioGroup v-model:value={forms.isAllSubject}>
  1452. <NRadio value={false}>部分声部</NRadio>
  1453. <NRadio value={true}>全部声部</NRadio>
  1454. </NRadioGroup>
  1455. </NFormItemGi>
  1456. </NGrid>
  1457. {!forms.isAllSubject && (
  1458. <NGrid cols={2}>
  1459. <NFormItemGi
  1460. label="可用声部"
  1461. path="subjectIds"
  1462. rule={[
  1463. {
  1464. required: true,
  1465. message: '请选择可用声部',
  1466. trigger: 'change',
  1467. type: 'array'
  1468. }
  1469. ]}
  1470. >
  1471. <NSelect
  1472. v-model:value={forms.subjectIds}
  1473. options={state.subjectList}
  1474. multiple
  1475. filterable
  1476. clearable
  1477. placeholder="请选择可用声部"
  1478. maxTagCount={2}
  1479. onUpdateValue={async (val: any) => {
  1480. await changeSubject(val)
  1481. }}
  1482. />
  1483. </NFormItemGi>
  1484. <NFormItemGi
  1485. label="可用乐器"
  1486. path="musicalInstrumentIdList"
  1487. rule={[
  1488. {
  1489. required: true,
  1490. message: '请选择可用乐器',
  1491. trigger: 'change',
  1492. type: 'array'
  1493. }
  1494. ]}
  1495. >
  1496. <NSelect
  1497. placeholder="请选择可用乐器"
  1498. options={state.instrumentList}
  1499. v-model:value={forms.musicalInstrumentIdList}
  1500. clearable
  1501. multiple
  1502. filterable
  1503. maxTagCount={2}
  1504. onUpdateValue={async (value: any) => {
  1505. }}
  1506. />
  1507. </NFormItemGi>
  1508. </NGrid>
  1509. )}
  1510. <NGrid cols={2}>
  1511. <NFormItemGi
  1512. label="速度"
  1513. path="playSpeed"
  1514. rule={[
  1515. {
  1516. required: false,
  1517. message: '请输入速度'
  1518. }
  1519. ]}
  1520. >
  1521. <NInputNumber
  1522. placeholder="请输入速度"
  1523. v-model:value={forms.playSpeed}
  1524. min={0}
  1525. style="width:100%"
  1526. />
  1527. </NFormItemGi>
  1528. </NGrid>
  1529. <NGrid cols={1}>
  1530. <NFormItemGi
  1531. label={`${forms.musicSheetType === 'SINGLE' ? '页面渲染声轨' : '用户可切换声轨'}`}
  1532. path="multiTracksSelection"
  1533. rule={[
  1534. {
  1535. required: true,
  1536. message: `请选择${forms.musicSheetType === 'SINGLE' ? '页面渲染声轨' : '用户可切换声轨'}`,
  1537. trigger: 'change',
  1538. type: 'array'
  1539. }
  1540. ]}
  1541. >
  1542. <NGrid style="padding-top: 4px;">
  1543. <NGi span={24}>
  1544. <NRadioGroup
  1545. v-model:value={state.multiTracks}
  1546. onUpdateValue={(value) => {
  1547. checkMultiTracks(value)
  1548. }}
  1549. >
  1550. <NRadio value={'all'}>全选</NRadio>
  1551. <NRadio value={'allUncheck'}>重置</NRadio>
  1552. <NRadio value={'invert'}>反选</NRadio>
  1553. </NRadioGroup>
  1554. </NGi>
  1555. {state.partListNames && state.partListNames.length > 0 && (
  1556. <NGi span={24} style={'margin-top:5px'}>
  1557. <NFormItemGi
  1558. label=""
  1559. path="multiTracksSelection"
  1560. rule={[
  1561. {
  1562. required: false
  1563. }
  1564. ]}
  1565. >
  1566. <NCheckboxGroup v-model:value={forms.multiTracksSelection}
  1567. onUpdateValue={(val: any) => {
  1568. if (state.partListNames.length != val.length) {
  1569. state.multiTracks = null
  1570. } else {
  1571. state.multiTracks = 'all'
  1572. }
  1573. }}
  1574. >
  1575. <NGrid yGap={2} cols={4}>
  1576. {state.partListNames.map((item: any) => (
  1577. <NGi>
  1578. <NCheckbox value={item.value} label={item.label}/>
  1579. </NGi>
  1580. ))}
  1581. </NGrid>
  1582. </NCheckboxGroup>
  1583. </NFormItemGi>
  1584. </NGi>
  1585. )}
  1586. </NGrid>
  1587. </NFormItemGi>
  1588. </NGrid>
  1589. <NAlert showIcon={false} style={{marginBottom: '12px'}}>
  1590. 演唱文件
  1591. </NAlert>
  1592. <NGrid cols={2}>
  1593. <NFormItemGi
  1594. label="是否播放节拍器"
  1595. path="isPlaySingBeat"
  1596. rule={[
  1597. {
  1598. required: true,
  1599. message: '请选择是否播放节拍器'
  1600. }
  1601. ]}
  1602. >
  1603. <NRadioGroup v-model:value={forms.isPlaySingBeat}>
  1604. <NRadio value={true}>是</NRadio>
  1605. <NRadio value={false}>否</NRadio>
  1606. </NRadioGroup>
  1607. </NFormItemGi>
  1608. {forms.isPlaySingBeat && (
  1609. <NFormItemGi
  1610. label="播放方式"
  1611. path="isUseSingSystemBeat"
  1612. rule={[
  1613. {
  1614. required: true,
  1615. message: '请选择播放方式'
  1616. }
  1617. ]}
  1618. >
  1619. <NRadioGroup v-model:value={forms.isUseSingSystemBeat}>
  1620. <NRadio value={true}>系统节拍器</NRadio>
  1621. <NRadio value={false}>MP3节拍器</NRadio>
  1622. </NRadioGroup>
  1623. </NFormItemGi>
  1624. )}
  1625. </NGrid>
  1626. <NGrid cols={2}>
  1627. <NFormItemGi
  1628. label="重复节拍时长"
  1629. path="repeatedBeatsToSing"
  1630. rule={[
  1631. {
  1632. required: false,
  1633. message: '请选择是否重复节拍时长'
  1634. }
  1635. ]}
  1636. >
  1637. <NRadioGroup v-model:value={forms.repeatedBeatsToSing}>
  1638. <NRadio value={true}>是</NRadio>
  1639. <NRadio value={false}>否</NRadio>
  1640. </NRadioGroup>
  1641. </NFormItemGi>
  1642. </NGrid>
  1643. <NGrid cols={2}>
  1644. <NFormItemGi
  1645. label="上传范唱"
  1646. path="fSongFile"
  1647. rule={[
  1648. {
  1649. required: false,
  1650. message: '请选择上传范唱',
  1651. trigger: ['change', 'input']
  1652. }
  1653. ]}
  1654. >
  1655. <UploadFile
  1656. desc={'上传范唱'}
  1657. disabled={state.previewMode}
  1658. size={30}
  1659. key={'xmlFileUrl'}
  1660. v-model:fileList={state.fSongFile}
  1661. tips="仅支持上传.mp3格式文件"
  1662. listType="image"
  1663. accept=".mp3"
  1664. bucketName="cloud-coach"
  1665. text="点击上传范唱文件"
  1666. onRemove={() => {
  1667. }}
  1668. />
  1669. </NFormItemGi>
  1670. <NFormItemGi
  1671. label="上传伴唱"
  1672. path="bSongFile"
  1673. rule={[
  1674. {
  1675. required: false,
  1676. message: '请选择上传.MID格式文件'
  1677. }
  1678. ]}
  1679. >
  1680. <UploadFile
  1681. desc={'上传伴唱'}
  1682. disabled={state.previewMode}
  1683. size={30}
  1684. v-model:fileList={state.bSongFile}
  1685. tips="仅支持上传.mp3格式文件"
  1686. listType="image"
  1687. accept=".mp3"
  1688. bucketName="cloud-coach"
  1689. text="点击上传伴唱文件"
  1690. />
  1691. </NFormItemGi>
  1692. </NGrid>
  1693. <NGrid cols={2}>
  1694. <NFormItemGi
  1695. label="上传唱名"
  1696. path="solmizationFileUrl"
  1697. rule={[
  1698. {
  1699. required: false,
  1700. message: '请选择上传唱名',
  1701. trigger: ['change', 'input']
  1702. }
  1703. ]}
  1704. >
  1705. <UploadFile
  1706. desc={'上传唱名'}
  1707. disabled={state.previewMode}
  1708. size={30}
  1709. key={'xmlFileUrl'}
  1710. v-model:fileList={forms.solmizationFileUrl}
  1711. tips="仅支持上传.mp3格式文件"
  1712. listType="image"
  1713. accept=".mp3"
  1714. bucketName="cloud-coach"
  1715. text="点击上传唱名文件"
  1716. onRemove={() => {
  1717. }}
  1718. />
  1719. </NFormItemGi>
  1720. </NGrid>
  1721. <NAlert showIcon={false} style={{marginBottom: '12px'}}>
  1722. 演奏文件
  1723. </NAlert>
  1724. <NGrid cols={2}>
  1725. <NFormItemGi
  1726. label="是否播放节拍器"
  1727. path="isPlayBeat"
  1728. rule={[
  1729. {
  1730. required: true,
  1731. message: '请选择是否播放节拍器'
  1732. }
  1733. ]}
  1734. >
  1735. <NRadioGroup v-model:value={forms.isPlayBeat}>
  1736. <NRadio value={true}>是</NRadio>
  1737. <NRadio value={false}>否</NRadio>
  1738. </NRadioGroup>
  1739. </NFormItemGi>
  1740. {forms.isPlayBeat && (
  1741. <NFormItemGi
  1742. label="播放方式"
  1743. path="isUseSystemBeat"
  1744. rule={[
  1745. {
  1746. required: true,
  1747. message: '请选择播放方式'
  1748. }
  1749. ]}
  1750. >
  1751. <NRadioGroup v-model:value={forms.isUseSystemBeat}>
  1752. <NRadio value={true}>系统节拍器</NRadio>
  1753. <NRadio value={false}>MP3节拍器</NRadio>
  1754. </NRadioGroup>
  1755. </NFormItemGi>
  1756. )}
  1757. </NGrid>
  1758. <NGrid cols={2}>
  1759. <NFormItemGi
  1760. label="重复节拍时长"
  1761. path="repeatedBeats"
  1762. rule={[
  1763. {
  1764. required: false,
  1765. message: '请选择是否重复节拍时长'
  1766. }
  1767. ]}
  1768. >
  1769. <NRadioGroup v-model:value={forms.repeatedBeats}>
  1770. <NRadio value={true}>是</NRadio>
  1771. <NRadio value={false}>否</NRadio>
  1772. </NRadioGroup>
  1773. </NFormItemGi>
  1774. <NFormItemGi
  1775. label="是否显示指法"
  1776. path="isShowFingering"
  1777. rule={[
  1778. {
  1779. required: true,
  1780. message: '请选择是否显示指法'
  1781. }
  1782. ]}
  1783. >
  1784. <NRadioGroup v-model:value={forms.isShowFingering}>
  1785. <NRadio value={true}>是</NRadio>
  1786. <NRadio value={false}>否</NRadio>
  1787. </NRadioGroup>
  1788. </NFormItemGi>
  1789. </NGrid>
  1790. <NGrid cols={2}>
  1791. {forms.playMode === 'MP3' && (
  1792. <NFormItemGi
  1793. label="上传伴奏"
  1794. path="musicSheetAccompanimentList"
  1795. rule={[
  1796. {
  1797. required: false,
  1798. message: '请选择上传.mp3'
  1799. }
  1800. ]}
  1801. >
  1802. <UploadFile
  1803. disabled={state.previewMode}
  1804. size={30}
  1805. v-model:imageList={state.musicSheetAccompanimentUrlList}
  1806. tips="仅支持上传.mp3格式文件"
  1807. listType="image"
  1808. accept=".mp3"
  1809. bucketName="cloud-coach"
  1810. text="点击上传伴奏文件"
  1811. max={1}
  1812. desc={'上传伴奏文件'}
  1813. onUpload:success={(file) => {
  1814. state.musicSheetAccompanimentUrls = [state.musicSheetAccompanimentUrls, file.url].filter(Boolean).join(',')
  1815. state.musicSheetAccompanimentUrlList = state.musicSheetAccompanimentUrls?.split(',').filter(Boolean)
  1816. // 清除伴奏
  1817. // forms.musicSheetAccompanimentList = forms.musicSheetAccompanimentList.filter((next: any) => {
  1818. // return next.audioPlayType == 'SING'
  1819. // })
  1820. forms.musicSheetAccompanimentList = []
  1821. for (let i = 0; i < state.musicSheetAccompanimentUrlList.length; i++) {
  1822. forms.musicSheetAccompanimentList.push({
  1823. audioFileUrl: state.musicSheetAccompanimentUrlList[i],
  1824. sortNumber: i + 1,
  1825. audioPlayType: 'PLAY'
  1826. })
  1827. }
  1828. }}
  1829. onRemove={() => {
  1830. state.musicSheetAccompanimentUrlList = []
  1831. state.musicSheetAccompanimentUrls = ''
  1832. }}
  1833. // onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
  1834. multiple={true}
  1835. />
  1836. </NFormItemGi>
  1837. )}
  1838. {forms.isAllSubject && forms.musicSheetType == 'SINGLE' && (
  1839. <NFormItemGi
  1840. label="上传原音"
  1841. path="musicSheetSoundList_all_subject"
  1842. rule={[
  1843. {
  1844. required: false,
  1845. message: '请选择上传原音',
  1846. trigger: ['change', 'input']
  1847. }
  1848. ]}
  1849. >
  1850. <UploadFile
  1851. desc={'上传原音'}
  1852. disabled={state.previewMode}
  1853. size={30}
  1854. max={1}
  1855. v-model:fileList={forms.musicSheetSoundList_all_subject}
  1856. tips="仅支持上传.mp3格式文件"
  1857. listType="image"
  1858. accept=".mp3"
  1859. bucketName="cloud-coach"
  1860. text="点击上传原音"
  1861. onRemove={() => {
  1862. }}
  1863. />
  1864. </NFormItemGi>
  1865. )}
  1866. </NGrid>
  1867. {/*渐变速*/}
  1868. {!!gradualData.list.length && (
  1869. <>
  1870. <NAlert showIcon={false} type="info">
  1871. 识别到共1处渐变速度,请输入Dorico对应小节时间信息
  1872. </NAlert>
  1873. <NFormItem label="rit." required style={{marginTop: '10px'}}>
  1874. <NSpace vertical>
  1875. {gradualData.list.map((n: any, ni: number) => (
  1876. <NInputGroup>
  1877. <NFormItem
  1878. path={`graduals.${n[0].measureIndex}`}
  1879. rule={[
  1880. {required: true, message: '请输入合奏曲目时间'},
  1881. {
  1882. pattern: /^((\d{2}):?){2,3}$/,
  1883. message: '请输入正确的曲目时间',
  1884. trigger: 'blur'
  1885. }
  1886. ]}
  1887. >
  1888. <NInputGroup>
  1889. <NInputGroupLabel>{n[0].measureIndex}小节开始</NInputGroupLabel>
  1890. <NInput
  1891. placeholder="00:00:00"
  1892. v-model:value={forms.graduals[n[0].measureIndex]}
  1893. ></NInput>
  1894. </NInputGroup>
  1895. </NFormItem>
  1896. <div style={{lineHeight: '30px', padding: '0 4px'}}>~</div>
  1897. <NFormItem
  1898. path={`graduals.${n[1].measureIndex}`}
  1899. rule={[
  1900. {required: true, message: '请输入合奏曲目时间'},
  1901. {
  1902. pattern: /^((\d{2}):?){2,3}$/,
  1903. message: '请输入正确的曲目时间',
  1904. trigger: 'blur'
  1905. }
  1906. ]}
  1907. >
  1908. <NInputGroup>
  1909. <NInput
  1910. placeholder="00:00:00"
  1911. v-model:value={forms.graduals[n[1].measureIndex]}
  1912. ></NInput>
  1913. <NInputGroupLabel>{n[1].measureIndex}小节结束</NInputGroupLabel>
  1914. </NInputGroup>
  1915. </NFormItem>
  1916. </NInputGroup>
  1917. ))}
  1918. </NSpace>
  1919. </NFormItem>
  1920. </>
  1921. )}
  1922. {/*独奏*/}
  1923. {forms.musicSheetType == 'SINGLE' && forms.playMode === 'MP3' && !forms.isAllSubject && forms.musicSheetSoundList_YZ.map((item: any, index: any) => {
  1924. return (
  1925. <>
  1926. {forms.musicalInstrumentIdList.includes(item.musicalInstrumentId) && (
  1927. <NGrid class={styles.audioSection}>
  1928. <NFormItemGi
  1929. span={12}
  1930. label={item.musicalInstrumentName}
  1931. path={`musicSheetSoundList_YZ[${index}].audioFileUrl`}
  1932. rule={[
  1933. {
  1934. required: false,
  1935. message: `请上传乐器演奏文件`
  1936. }
  1937. ]}
  1938. >
  1939. <UploadFile
  1940. desc={'乐器演奏文件'}
  1941. disabled={state.previewMode}
  1942. size={100}
  1943. v-model:fileList={item.audioFileUrl}
  1944. tips="仅支持上传.mp3格式文件"
  1945. listType="image"
  1946. accept=".mp3"
  1947. text={"点击上传MP3文件"}
  1948. bucketName="cloud-coach"
  1949. />
  1950. </NFormItemGi>
  1951. </NGrid>
  1952. )}
  1953. </>
  1954. )
  1955. })
  1956. }
  1957. {/*合奏*/}
  1958. {forms.musicSheetType == 'CONCERT' && forms.playMode === 'MP3' && forms.musicSheetSoundList_YY.length > 0 && (
  1959. <>
  1960. {forms.musicSheetSoundList_YY.map((item: any, index: number) => {
  1961. return (
  1962. <>
  1963. {(!containOther(item.track) ||
  1964. (item.track?.toLocaleUpperCase?.() != 'COMMON' &&
  1965. forms.multiTracksSelection.includes(item.track))) && (
  1966. <NGrid
  1967. class={styles.audioSection}
  1968. // v-show={forms.multiTracksSelection.indexOf(item.track) > -1}
  1969. >
  1970. <NFormItemGi
  1971. span={12}
  1972. label="原音"
  1973. path={`musicSheetSoundList_YY[${index}].audioFileUrl`}
  1974. rule={[
  1975. {
  1976. // required: forms.multiTracksSelection.indexOf(forms.musicSheetSoundList_YY[index].audioFileUrl) > -1,
  1977. required: false,
  1978. message: `请上传${
  1979. item.track ? item.track + '的' : '第' + (index + 1) + '个'
  1980. }原音`
  1981. }
  1982. ]}
  1983. >
  1984. <UploadFile
  1985. desc={'原音文件'}
  1986. disabled={state.previewMode}
  1987. size={100}
  1988. v-model:fileList={item.audioFileUrl}
  1989. tips="仅支持上传.mp3格式文件"
  1990. listType="image"
  1991. accept=".mp3"
  1992. bucketName="cloud-coach"
  1993. />
  1994. </NFormItemGi>
  1995. {state.partListNames.length > 0 && (
  1996. <NFormItemGi
  1997. span={12}
  1998. label="所属轨道"
  1999. path={`musicSheetSoundList_YY[${index}].track`}
  2000. rule={[
  2001. {
  2002. required: false,
  2003. message: '请选择所属轨道'
  2004. }
  2005. ]}
  2006. >
  2007. <NSelect
  2008. placeholder="请选择所属轨道"
  2009. value={item.track}
  2010. options={initPartsListStatus(item.track)}
  2011. onUpdateValue={(value: any) => {
  2012. const track = item.track
  2013. if (track) {
  2014. // 声轨交换
  2015. forms.musicSheetSoundList_YY.forEach((next: any) => {
  2016. if (next.track == value) {
  2017. next.track = track
  2018. }
  2019. })
  2020. const index = forms.multiTracksSelection.indexOf(item.track)
  2021. forms.multiTracksSelection.splice(index, 1)
  2022. } else {
  2023. forms.musicSheetSoundList_YY = forms.musicSheetSoundList_YY.filter(
  2024. (next: any) => {
  2025. return next.track != value
  2026. }
  2027. )
  2028. }
  2029. if (value != null && !forms.multiTracksSelection.includes(value)) {
  2030. forms.multiTracksSelection.push(value)
  2031. }
  2032. item.track = value
  2033. if (state.partListNames.length == forms.multiTracksSelection.length) {
  2034. state.multiTracks = 'all'
  2035. }
  2036. }}
  2037. />
  2038. </NFormItemGi>
  2039. )}
  2040. <NGi class={styles.btnRemove}>
  2041. <NButton
  2042. type="primary"
  2043. text
  2044. // disabled={forms.musicSheetSoundList_YY.length === 1}
  2045. onClick={() => removeSys(index)}
  2046. >
  2047. 删除
  2048. </NButton>
  2049. </NGi>
  2050. </NGrid>
  2051. )}
  2052. </>
  2053. )
  2054. })}
  2055. </>
  2056. )}
  2057. </NForm>
  2058. </NSpin>
  2059. {props.type !== 'preview' && (
  2060. <NSpace justify="end" style="padding-top:12px">
  2061. <NButton type="default" onClick={() => emit('close')}>
  2062. 取消
  2063. </NButton>
  2064. <NButton
  2065. type="primary"
  2066. onClick={() => onSubmit()}
  2067. loading={btnLoading.value}
  2068. disabled={btnLoading.value}
  2069. >
  2070. 确认
  2071. </NButton>
  2072. </NSpace>
  2073. )}
  2074. <NModal
  2075. v-model:show={state.showMusicSheetOwnerDialog}
  2076. preset="dialog"
  2077. showIcon={false}
  2078. maskClosable={false}
  2079. title="所属人"
  2080. style={{width: '800px'}}
  2081. >
  2082. <MusicSheetOwnerDialog
  2083. musicSheetExtend={forms.musicSheetExtend}
  2084. sourceType={forms.sourceType}
  2085. appData={state.appData}
  2086. onClose={() => {
  2087. state.showMusicSheetOwnerDialog = false
  2088. }}
  2089. onChoseMusicSheetOwnerData={(musicSheetOwnerData) => {
  2090. forms.musicSheetExtend = {
  2091. ...musicSheetOwnerData
  2092. }
  2093. setOwnerName()
  2094. }}
  2095. />
  2096. </NModal>
  2097. <NModal
  2098. class={styles.productModal}
  2099. title="自动生成曲谱图片"
  2100. v-model:show={state.productOpen}
  2101. preset="dialog"
  2102. closeOnEsc={false}
  2103. maskClosable={false}
  2104. onClose={()=> {
  2105. state.isAutoSave = false
  2106. }}
  2107. showIcon={false}
  2108. >
  2109. <MusicCreateImg
  2110. xmlFileUrl={forms.xmlFileUrl || ''}
  2111. onClose={() => (state.productOpen = false)}
  2112. onConfirm={async (item: any) => {
  2113. // 保存
  2114. try {
  2115. forms.musicImg = item.musicImg
  2116. forms.musicFirstImg = item.musicFirstImg
  2117. forms.musicJianImg = item.musicJianImg
  2118. await onSubmit()
  2119. } catch (e: any) {
  2120. //
  2121. console.log(e, 'e')
  2122. }
  2123. // setTimeout(() => {
  2124. // state.isAutoSave = false
  2125. // }, 50)
  2126. }}
  2127. />
  2128. </NModal>
  2129. </div>
  2130. )
  2131. }
  2132. })