music-operationV2.tsx 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214
  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.replaceAll(' ', '').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. }
  422. if (forms.musicSheetSoundList_all_subject) {
  423. existYzFile = true
  424. }
  425. } else {
  426. if (forms.musicSheetSoundList_YZ.length > 0) {
  427. audioPlayTypes.push("PLAY")
  428. forms.musicSheetSoundList_YZ.forEach((musicSheetSound: any) => {
  429. if (forms.musicalInstrumentIdList.includes(musicSheetSound.musicalInstrumentId) && musicSheetSound.audioFileUrl) {
  430. existYzFile = true
  431. musicSheetSoundList.push({
  432. ...musicSheetSound,
  433. musicSheetId: props.data.id,
  434. })
  435. }
  436. })
  437. }
  438. }
  439. } else if (musicSheetType == 'CONCERT') {
  440. audioPlayTypes.push("PLAY")
  441. forms.musicSheetSoundList_YY.forEach((musicSheetSound: any) => {
  442. if (musicSheetSound.audioFileUrl && musicSheetSound.track != null) {
  443. musicSheetSoundList.push({
  444. ...musicSheetSound,
  445. musicSheetId: props.data.id,
  446. })
  447. existYzFile = true
  448. }
  449. })
  450. }
  451. if (!state.fSongFile && !state.bSongFile && !existYzFile) {
  452. message.warning("请上传音频文件")
  453. return
  454. }
  455. if (state.bSongFile) {
  456. forms.musicSheetAccompanimentList.push({
  457. musicSheetId: props.data.id,
  458. audioFileUrl: state.bSongFile,
  459. audioPlayType: 'SING'
  460. })
  461. }
  462. // 生成图片
  463. if (!state.isAutoSave) {
  464. state.isAutoSave = true
  465. state.productOpen = true
  466. return
  467. }
  468. const obj = {
  469. musicCategoryId: forms.musicCategoryId,
  470. musicCover: forms.musicCover,
  471. name: forms.name,
  472. appAuditFlag: forms.appAuditFlag,
  473. subjectIds: forms.isAllSubject ? "" : forms.subjectIds.join(','),
  474. remark: forms.remark,
  475. audioPlayTypes: audioPlayTypes.join(','),
  476. musicalInstrumentIds: forms.isAllSubject ? "" : forms.musicalInstrumentIdList.join(','),
  477. composer: forms.composer,
  478. musicSheetType: forms.musicSheetType,
  479. isUseSystemBeat: forms.isUseSystemBeat,
  480. isShowFingering: forms.isShowFingering,
  481. isPlayBeat: forms.isPlayBeat,
  482. multiTracksSelection: forms.isAllSubject ? "" : forms.multiTracksSelection.join(','),
  483. playSpeed: forms.playSpeed,
  484. playMode: forms.playMode,
  485. xmlFileUrl: forms.xmlFileUrl,
  486. musicImg: forms.musicImg,
  487. musicFirstImg: forms.musicFirstImg,
  488. musicJianImg: forms.musicJianImg,
  489. extConfigJson: JSON.stringify({
  490. repeatedBeats: forms.repeatedBeats ? 1 : 0,
  491. gradualTimes: forms.graduals,
  492. isEvxml: forms.isEvxml ? 1 : 0,
  493. repeatedBeatsToSing: forms.repeatedBeatsToSing ? 1 : 0
  494. }),
  495. // availableType: forms.availableType,
  496. sourceType: forms.sourceType,
  497. audioType: forms.audioType,
  498. status: forms.status,
  499. evaluationStandard: forms.evaluationStandard,
  500. musicSheetAccompanimentList: forms.musicSheetAccompanimentList,
  501. musicSheetSoundList: musicSheetSoundList,
  502. musicTag: '-1',
  503. isUseSingSystemBeat: forms.isUseSingSystemBeat,
  504. isPlaySingBeat: forms.isPlaySingBeat,
  505. isAllSubject: forms.isAllSubject,
  506. musicSheetExtend: forms.sourceType == 'PLATFORM' ? null : forms.musicSheetExtend,
  507. }
  508. if (forms.audioType == 'MIDI') {
  509. obj.musicSheetSoundList = []
  510. }
  511. btnLoading.value = true
  512. if (props.type === 'add') {
  513. await musicSheetSave(obj)
  514. message.success('添加成功')
  515. } else if (props.type === 'edit') {
  516. await musicSheetSave({...obj, id: props.data.id})
  517. message.success('修改成功')
  518. }
  519. emit('getList')
  520. emit('close')
  521. } catch (e) {
  522. console.log(e)
  523. }
  524. setTimeout(() => {
  525. btnLoading.value = false
  526. state.isAutoSave = false
  527. }, 100)
  528. })
  529. }
  530. // 上传XML,初始化音轨 音轨速度 乐器、声部
  531. const readFileInputEventAsArrayBuffer = (file: any) => {
  532. // 是否是evxml
  533. forms.isEvxml = file?.name?.includes('.evxml') ? true : false;
  534. const xmlRead = new FileReader()
  535. xmlRead.onload = (res) => {
  536. try {
  537. gradualData.list = getGradualLengthByXml(res?.target?.result as any).filter(
  538. (item: any) => item.length === 2
  539. )
  540. } catch (error) {
  541. }
  542. forms.musicSheetSoundList_YY = forms.musicSheetSoundList_YY.filter((item: any) => {
  543. return (!item.track || !containOther(item.track) || (item.track?.toLocaleUpperCase?.() != 'COMMON' && forms.multiTracksSelection.includes(item.track)))
  544. })
  545. state.partListNames = getPartListNames(res?.target?.result as any) as any
  546. // parseInstrumentAndSubject(res?.target?.result as any)
  547. // 这里是如果没有当前音轨就重新写
  548. let map = new Map<String, String>()
  549. for (let i = 0; i < forms.musicSheetSoundList_YY.length; i++) {
  550. let track = forms.musicSheetSoundList_YY[i].track;
  551. if (track) {
  552. map.set(track, forms.musicSheetSoundList_YY[i])
  553. }
  554. }
  555. let newMusicSheetSoundList = []
  556. let tracks = [] as any
  557. for (let j = 0; j < state.partListNames.length; j++) {
  558. let track = state.partListNames[j].value;
  559. if (map.has(track)) {
  560. newMusicSheetSoundList.push(map.get(track))
  561. } else {
  562. newMusicSheetSoundList.push({audioFileUrl: null, track: track, musicalInstrumentId: null})
  563. }
  564. tracks.push(track)
  565. if(!forms.multiTracksSelection.includes(track)) {
  566. forms.multiTracksSelection.push(track)
  567. }
  568. }
  569. for (let i = 0; i < forms.musicSheetSoundList_YY.length; i++) {
  570. let track = forms.musicSheetSoundList_YY[i].track;
  571. if (!track || !tracks.includes(track)) {
  572. forms.musicSheetSoundList_YY[i].track = null
  573. newMusicSheetSoundList.push(forms.musicSheetSoundList_YY[i])
  574. }
  575. }
  576. forms.musicSheetSoundList_YY = newMusicSheetSoundList
  577. forms.multiTracksSelection = forms.multiTracksSelection.filter((track: any) => {
  578. return tracks.includes(track)
  579. })
  580. // 全选选中
  581. state.multiTracks = 'all'
  582. // 循环添加所在音轨的原音
  583. // for (let index = forms.musicSheetSoundList.length; index < state.partListNames.length; index++) {
  584. // const part = state.partListNames[index].value
  585. // const sysData = {
  586. // ...forms.musicSheetSoundList[0],
  587. // track: part
  588. // }
  589. // if (!sysData.speed) {
  590. // sysData.speed = state.xmlFirstSpeed
  591. // }
  592. // createSys(sysData)
  593. // }
  594. if (forms.musicSheetSoundList_YY.length == 0) {
  595. forms.musicSheetSoundList_YY.push({ audioFileUrl: '', track: '' })
  596. }
  597. }
  598. xmlRead.readAsText(file)
  599. }
  600. const containOther = (track: any) => {
  601. for (let i = 0; i < state.partListNames.length; i++) {
  602. if (state.partListNames[i].value == track) {
  603. return true
  604. }
  605. }
  606. return false
  607. }
  608. const validSoundNum = () => {
  609. return forms.musicSheetSoundList_YY.filter((item: any) => {
  610. return (!item.track || !containOther(item.track) || (item.track?.toLocaleUpperCase?.() != 'COMMON' && forms.multiTracksSelection.includes(item.track)))
  611. }).length;
  612. }
  613. const parseInstrumentAndSubject = (xml: any) => {
  614. if (!xml) return
  615. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  616. // 乐器
  617. const instrumentCodeList: any = []
  618. const instrumentEle = xmlParse.getElementsByTagName('virtual-instrument')
  619. for (let index = 0; index < instrumentEle.length; index++) {
  620. const note = instrumentEle[index]
  621. let instrumentCode = note.getElementsByTagName('virtual-name')?.[0]?.textContent || ''
  622. instrumentCode = instrumentCode.toLocaleLowerCase().trim()
  623. if (instrumentCode && !instrumentCodeList.includes(instrumentCode)) {
  624. instrumentCodeList.push(instrumentCode)
  625. }
  626. }
  627. // 乐器支持多编码,暂不开放
  628. // const codeIdMap = new Map<string, []>() as any
  629. // state.instrumentData.forEach((data: any) => {
  630. // const codes = data.code.split(/,|,/)
  631. // codes.forEach((code: string) => {
  632. // if (codeIdMap.has(code)) {
  633. // codeIdMap.get(code).push(data.id + '')
  634. // } else {
  635. // const arr = [] as any;
  636. // arr.push(data.id + '')
  637. // codeIdMap.set(code, arr)
  638. // }
  639. // // codeIdMap.set(code, data.id + '')
  640. // })
  641. // })
  642. // forms.musicalInstrumentIdList = []
  643. // instrumentCodeList.forEach((code: string) => {
  644. // if (codeIdMap.has(code)) {
  645. // codeIdMap.get(code).forEach((c: any) => {
  646. // forms.musicalInstrumentIdList.push(c)
  647. // })
  648. // }
  649. // })
  650. const codeIdMap = new Map<string, string>()
  651. state.instrumentData.forEach((data: any) => {
  652. if (!data.disabled) {
  653. codeIdMap.set(data.code.toLocaleLowerCase().trim(), data.id + '')
  654. }
  655. })
  656. forms.musicalInstrumentIdList = []
  657. instrumentCodeList.forEach((code: string) => {
  658. if (codeIdMap.has(code)) {
  659. forms.musicalInstrumentIdList.push(codeIdMap.get(code))
  660. }
  661. })
  662. // 声部
  663. if (forms.musicalInstrumentIdList.length > 0) {
  664. showBackSubject(forms.musicalInstrumentIdList)
  665. }
  666. }
  667. // 获取xml中所有轨道 乐器
  668. const getPartListNames = (xml: any) => {
  669. if (!xml) return []
  670. const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
  671. const partList: any =
  672. xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
  673. let partListNames = Array.from(partList).map((item: any) => {
  674. let part = item.getElementsByTagName('part-name')?.[0]
  675. // evxml没有分轨,需要手动设置一个默认的名称,用于上传原音
  676. // if (forms.isEvxml) {
  677. // part = part || 'noPartName'
  678. // }
  679. // 优先解析声轨,没有就取id值
  680. let track = ''
  681. if (part) {
  682. track = part.textContent || ''
  683. } else {
  684. let id = item.getAttribute('id')
  685. if (id) {
  686. track = id
  687. }
  688. }
  689. return {
  690. value: track.trim(),
  691. label: track.trim()
  692. }
  693. })
  694. // 处理空数据
  695. // if (partListNames.length === 1 && forms.details.id && !partListNames[0].value) {
  696. // partListNames[0] = {
  697. // value: forms.details.multiTracksSelection,
  698. // label: forms.details.multiTracksSelection
  699. // }
  700. // }
  701. partListNames = partListNames.filter((n: any) => n.value?.toLocaleUpperCase?.() != 'COMMON')
  702. // if (partListNames.length > 0) {
  703. // forms.musicSheetSoundList = forms.musicSheetSoundList.slice(0, partListNames.length)
  704. // }
  705. state.xmlFirstSpeed = xmlParse.getElementsByTagName('per-minute')?.[0]?.textContent || ''
  706. if (!forms.playSpeed) {
  707. if (state.xmlFirstSpeed) {
  708. forms.playSpeed = Number.parseFloat(state.xmlFirstSpeed)
  709. } else {
  710. // 速度默认给100
  711. forms.playSpeed = 100
  712. }
  713. }
  714. // console.log('xml声轨',partListNames,state.xmlFirstSpeed)
  715. return partListNames
  716. }
  717. // 判断选择的音轨是否在选中
  718. const initPartsListStatus = (track: string): any => {
  719. // const _names = state.partListNames.filter(
  720. // (n: any) => n.value?.toLocaleUpperCase?.() != 'COMMON'
  721. // )
  722. const partListNames = deepClone(state.partListNames) || []
  723. const multiTracksSelection = forms.multiTracksSelection
  724. partListNames.forEach((item: any) => {
  725. if (multiTracksSelection.includes(item.value)) {
  726. item.disabled = true
  727. } else {
  728. item.disabled = false
  729. }
  730. // const index = forms.musicSheetSoundList.findIndex(
  731. // (ground: any) => item.value == ground.track
  732. // )
  733. // if (index > -1 && track == item.value) {
  734. // item.disabled = false
  735. // } else {
  736. // item.disabled = true
  737. // }
  738. })
  739. return partListNames || []
  740. }
  741. // 反显声部
  742. const showBackSubject = async (musicalInstrumentIdList: []) => {
  743. try {
  744. const { data } = await subjectPage({
  745. page: 1,
  746. rows: 999,
  747. musicalInstrumentIdList: musicalInstrumentIdList
  748. })
  749. const tempList = data.rows || []
  750. tempList.forEach((item: any) => {
  751. forms.subjectIds.push(item.id + '')
  752. })
  753. } catch {
  754. }
  755. }
  756. // 添加原音
  757. const createSys = (initData?: any) => {
  758. forms.musicSheetSoundList_YY.push({
  759. audioFileUrl: null, // 原音
  760. track: null, // 轨道
  761. ...initData
  762. })
  763. }
  764. // 删除原音
  765. const removeSys = (index: number) => {
  766. dialog.warning({
  767. title: '提示',
  768. content: `是否确认删除此原音?`,
  769. positiveText: '确定',
  770. negativeText: '取消',
  771. onPositiveClick: async () => {
  772. const sound = forms.musicSheetSoundList_YY[index]
  773. let track = sound.track
  774. // if (!track) {
  775. // track = ''
  776. // }
  777. // if (track) {
  778. const selectIndex = forms.multiTracksSelection.indexOf(track)
  779. if (selectIndex > -1) {
  780. forms.multiTracksSelection.splice(selectIndex, 1)
  781. } else {
  782. forms.musicSheetSoundList_YY.splice(index, 1)
  783. }
  784. if (state.multiTracks == 'all' && state.partListNames.length != forms.multiTracksSelection.length) {
  785. state.multiTracks = null
  786. }
  787. }
  788. })
  789. }
  790. const checkMultiTracks = (value: string) => {
  791. if (!value) {
  792. return
  793. }
  794. if (value === 'all') {
  795. forms.multiTracksSelection = []
  796. state.partListNames.forEach((next: any) => {
  797. forms.multiTracksSelection.push(next.value)
  798. })
  799. } else if (value === 'invert') {
  800. state.partListNames.forEach((next: any) => {
  801. const indexOf = forms.multiTracksSelection.indexOf(next.value)
  802. if (indexOf > -1) {
  803. forms.multiTracksSelection.splice(indexOf, 1)
  804. } else {
  805. forms.multiTracksSelection.push(next.value)
  806. }
  807. })
  808. } else if (value === 'allUncheck') {
  809. forms.multiTracksSelection = []
  810. }
  811. }
  812. const setOwnerName = () => {
  813. if (forms.sourceType == 'PLATFORM') {
  814. state.ownerName = ''
  815. return
  816. }
  817. if (!forms.sourceType || !forms.musicSheetExtend?.userId) {
  818. return
  819. }
  820. const appId = forms.musicSheetExtend.applicationId
  821. const app = state.appData.filter((next: any) => {
  822. return next.id == appId
  823. }) as any
  824. if (app.length > 0) {
  825. state.ownerName = app[0].appName
  826. }
  827. if (forms.sourceType == 'ORG') {
  828. state.ownerName += '-' + forms.musicSheetExtend.organizationRole
  829. } else if (forms.sourceType == 'PERSON') {
  830. state.ownerName +=
  831. '-' +
  832. getMapValueByKey(forms.musicSheetExtend.clientType, new Map(Object.entries(clientType)))
  833. if (forms.musicSheetExtend.userName) {
  834. state.ownerName += '-' + forms.musicSheetExtend.userName
  835. }
  836. if (forms.musicSheetExtend.phone) {
  837. state.ownerName += '(' + forms.musicSheetExtend.phone + ')'
  838. }
  839. }
  840. }
  841. // 声轨数据兼容
  842. const formatTrack = (track: string) => {
  843. if (!track) {
  844. return '';
  845. }
  846. const trim = track.trim().toUpperCase();
  847. // 导入后的脏数据兼容
  848. if (trim == 'P1' || trim == 'NULL') {
  849. return '';
  850. }
  851. return track.trim();
  852. }
  853. const changeSubject = async (subjectIdList: []) => {
  854. state.instrumentList = []
  855. if (!subjectIdList || subjectIdList.length == 0) {
  856. forms.musicalInstrumentIdList = []
  857. return
  858. }
  859. let enableFlag = null;
  860. if (props.type === 'add') {
  861. enableFlag = true;
  862. }
  863. let tempMusicalInstrumentIdList = [] as any
  864. const {data} = await musicalInstrumentPage({page: 1, rows: 999, subjectIds: subjectIdList, enableFlag: enableFlag});
  865. data.rows.map((item: any) => {
  866. tempMusicalInstrumentIdList.push(item.id + '')
  867. state.instrumentList.push(
  868. {
  869. label: item.name,
  870. value: item.id + '',
  871. disabled: !item.enableFlag
  872. }
  873. )
  874. })
  875. forms.musicalInstrumentIdList = forms.musicalInstrumentIdList.filter((item: any) => {
  876. return tempMusicalInstrumentIdList.includes(item)
  877. })
  878. }
  879. onMounted(async () => {
  880. state.loading = true
  881. if (props.type === 'preview') {
  882. state.previewMode = true
  883. }
  884. // 获取乐器信息
  885. {
  886. if (state.instrumentList && state.instrumentList.length > 0) {
  887. return
  888. }
  889. try {
  890. const {data} = await musicalInstrumentPage({page: 1, rows: 999})
  891. const tempList = data.rows || []
  892. tempList.forEach((item: any) => {
  893. item.label = item.name
  894. item.value = item.id + ''
  895. item.disabled = !item.enableFlag
  896. state.instrumentIdNameMap.set(item.id + '', item.name)
  897. forms.musicSheetSoundList_YZ.push({
  898. 'musicSheetId': props.data.id,
  899. 'musicalInstrumentId': item.id + '',
  900. 'musicalInstrumentName': item.name,
  901. 'audioFileUrl': null,
  902. 'audioPlayType': 'PLAY'
  903. });
  904. })
  905. state.instrumentData = tempList
  906. // state.instrumentList = tempList
  907. } catch {
  908. }
  909. }
  910. state.subjectList = deepClone(props.subjectList)
  911. state.subjectList.forEach((subject: any) => {
  912. subject.disabled = !subject.enableFlag
  913. })
  914. // 初始化应用
  915. {
  916. const appKeys = Object.keys(appKey)
  917. const {data} = await sysApplicationPage({page: 1, rows: 999, parentId: 0})
  918. const tempList = data.rows || []
  919. const filter = tempList.filter((next: any) => {
  920. return appKeys.includes(next.appKey)
  921. })
  922. filter.forEach((item: any) => {
  923. item.label = item.appName
  924. item.value = item.id
  925. })
  926. state.appData = filter
  927. }
  928. // 获取分类信息
  929. {
  930. try {
  931. const {data} = await musicSheetCategoriesQueryTree({enable: true})
  932. state.musicSheetCategories = filterPointCategory(data, 'musicSheetCategoriesList')
  933. } catch (e) {
  934. }
  935. }
  936. if (props.type === 'edit' || props.type === 'preview') {
  937. const detail = props.data
  938. try {
  939. const {data} = await musicSheetDetail({id: detail.id})
  940. forms.details = data
  941. forms.playMode = data.playMode
  942. forms.xmlFileUrl = data.xmlFileUrl
  943. forms.midiUrl = data.midiUrl
  944. forms.name = data.name
  945. // forms.musicTag = data.musicTag?.split(',')
  946. forms.composer = data.composer
  947. forms.playSpeed = data.playSpeed ? Number.parseFloat(data.playSpeed) : 100
  948. // forms.showFingering = Number(data.showFingering)
  949. // forms.canEvaluate = Number(data.canEvaluate)
  950. // forms.notation = Number(data.notation)
  951. // forms.auditVersion = Number(data.auditVersion)
  952. // forms.sortNumber = data.sortNumber
  953. forms.musicCover = data.musicCover
  954. forms.remark = data.remark
  955. forms.status = data.status
  956. forms.musicSheetType = data.musicSheetType || 'SINGLE'
  957. forms.sourceType = data.sourceType
  958. forms.appAuditFlag = data.appAuditFlag ? 1 : 0
  959. forms.midiFileUrl = data.midiFileUrl
  960. forms.isShowFingering = data.isShowFingering
  961. forms.isAllSubject = data.isAllSubject
  962. forms.isUseSingSystemBeat = data.isUseSingSystemBeat
  963. forms.isPlaySingBeat = data.isPlaySingBeat
  964. forms.subjectIds = []
  965. if (data.subjectIds) {
  966. const subjectIds = data.subjectIds.split(',') || []
  967. subjectIds.forEach((subjectId: any) => {
  968. if (!forms.subjectIds.includes(subjectId)) {
  969. forms.subjectIds.push(subjectId)
  970. }
  971. })
  972. state.subjectList = state.subjectList.filter((subject: any) => {
  973. return (!subject.disabled || subjectIds.includes(subject.value));
  974. })
  975. }
  976. forms.musicalInstrumentIdList = data.musicalInstrumentIds ? data.musicalInstrumentIds.split(',') : []
  977. await changeSubject(forms.subjectIds)
  978. forms.musicCategoryId = data.musicCategoryId
  979. forms.audioType = data.audioType
  980. forms.isPlayBeat = data.isPlayBeat
  981. forms.isUseSystemBeat = data.isUseSystemBeat
  982. // 获取渐变 和 是否多声部
  983. try {
  984. const extConfigJson = data.extConfigJson ? JSON.parse(data.extConfigJson) : {}
  985. forms.graduals = extConfigJson.gradualTimes || {}
  986. forms.repeatedBeats = !!extConfigJson.repeatedBeats
  987. forms.isEvxml = !!extConfigJson.isEvxml
  988. forms.repeatedBeatsToSing = !!extConfigJson.repeatedBeatsToSing
  989. } catch (error) {
  990. }
  991. forms.evaluationStandard = data.evaluationStandard
  992. forms.musicSheetExtend = data.musicSheetExtend
  993. state.musicSheetSoundList = data.musicSheetSoundList ? data.musicSheetSoundList : []
  994. let musicSheetAccompanimentList = data.musicSheetAccompanimentList ? data.musicSheetAccompanimentList : []
  995. musicSheetAccompanimentList.forEach((next: any) => {
  996. let audioPlayType = next.audioPlayType;
  997. if (audioPlayType && audioPlayType == 'SING') {
  998. state.bSongFile = next.audioFileUrl;
  999. } else {
  1000. state.musicSheetAccompanimentUrlList.push(next.audioFileUrl)
  1001. forms.musicSheetAccompanimentList.push(next)
  1002. }
  1003. })
  1004. // 初始化演奏
  1005. for (let i = 0; i < state.musicSheetSoundList.length; i++) {
  1006. if (state.musicSheetSoundList[i].audioPlayType == 'SING') {
  1007. // 范唱 唱名
  1008. state.fSongFile = state.musicSheetSoundList[i].audioFileUrl
  1009. forms.solmizationFileUrl = state.musicSheetSoundList[i].solmizationFileUrl
  1010. } else {
  1011. if (forms.isAllSubject) {
  1012. forms.musicSheetSoundList_all_subject = state.musicSheetSoundList[i].audioFileUrl
  1013. } else {
  1014. // 乐器演奏原音
  1015. for (let j = 0; j < forms.musicSheetSoundList_YZ.length; j++) {
  1016. let musicalInstrumentId = state.musicSheetSoundList[i].musicalInstrumentId;
  1017. if (musicalInstrumentId && musicalInstrumentId == forms.musicSheetSoundList_YZ[j].musicalInstrumentId) {
  1018. forms.musicSheetSoundList_YZ[j].audioFileUrl = state.musicSheetSoundList[i].audioFileUrl
  1019. }
  1020. }
  1021. }
  1022. }
  1023. }
  1024. setOwnerName()
  1025. axios.get(data.xmlFileUrl).then((res: any) => {
  1026. if (res?.data) {
  1027. gradualData.list = getGradualLengthByXml(res?.data as any).filter(
  1028. (item: any) => item.length === 2
  1029. )
  1030. state.partListNames = getPartListNames(res?.data as any) as any
  1031. // 初始化音轨和原音
  1032. if (data.multiTracksSelection) {
  1033. data.multiTracksSelection = data.multiTracksSelection.toLocaleUpperCase()
  1034. }
  1035. if (!data.multiTracksSelection || data.multiTracksSelection.trim() == '' || data.multiTracksSelection.trim() == 'NULL') {
  1036. forms.multiTracksSelection = ['']
  1037. } else {
  1038. forms.multiTracksSelection = data.multiTracksSelection.split(',')
  1039. }
  1040. let names = state.partListNames.map((next: any) => next.label)
  1041. forms.multiTracksSelection = names.filter((next: any) => forms.multiTracksSelection.includes(next.toLocaleUpperCase()))
  1042. const existSoundList = data.musicSheetSoundList ? data.musicSheetSoundList : []
  1043. // 如果只有一个原音文件,并且原音没有对应声轨,取xml解析中的第一个声轨绑定当当前原音
  1044. // if (existSoundList.length === 1 && !formatTrack(existSoundList[0].track)) {
  1045. // let track = state.partListNames.length > 0 ? state.partListNames[0].value : null;
  1046. // forms.musicSheetSoundList_YY.push({
  1047. // audioFileUrl: existSoundList[0].audioFileUrl, // 原音
  1048. // musicalInstrumentId: existSoundList[0].musicalInstrumentId,
  1049. // track: track, // 轨道
  1050. // audioPlayType: 'PLAY'
  1051. // })
  1052. // if (track && !forms.multiTracksSelection.includes(track)) {
  1053. // forms.multiTracksSelection.push(track)
  1054. // }
  1055. // } else {
  1056. const tracks = [] as any
  1057. state.partListNames.forEach((item: any) => {
  1058. let audioFileUrl = null
  1059. let musicalInstrumentId = null
  1060. if (forms.musicSheetType == 'CONCERT') {
  1061. existSoundList.forEach((next: any) => {
  1062. if (next.audioPlayType == 'PLAY') {
  1063. if (!next.track || next.track.trim() == '') {
  1064. next.track = ''
  1065. }
  1066. if (next.track == item.value) {
  1067. audioFileUrl = next.audioFileUrl
  1068. musicalInstrumentId = next.musicalInstrumentId
  1069. }
  1070. }
  1071. })
  1072. }
  1073. forms.musicSheetSoundList_YY.push({
  1074. audioFileUrl: audioFileUrl, // 原音
  1075. musicalInstrumentId: musicalInstrumentId, // 乐器
  1076. track: item.value, // 轨道
  1077. audioPlayType: 'PLAY'
  1078. })
  1079. tracks.push(item.value)
  1080. })
  1081. if (tracks.length == forms.multiTracksSelection.length) {
  1082. state.multiTracks = 'all'
  1083. }
  1084. // 处理没有声轨,但有原音
  1085. if (data.musicSheetType == 'CONCERT') {
  1086. state.musicSheetSoundList.forEach((next: any) => {
  1087. if (next.audioPlayType == 'PLAY') {
  1088. if (next.track && !tracks.includes(next.track.trim()) && next.audioPlayType == 'PLAY') {
  1089. forms.musicSheetSoundList_YY.push({
  1090. audioFileUrl: next.audioFileUrl, // 原音
  1091. musicalInstrumentId: next.musicalInstrumentId,
  1092. track: next.track ? next.track.trim() : '', // 轨道
  1093. audioPlayType: 'PLAY'
  1094. })
  1095. }
  1096. }
  1097. })
  1098. // }
  1099. }
  1100. }
  1101. })
  1102. } catch (error) {
  1103. }
  1104. } else {
  1105. // 新增只能使用启用状态的数据
  1106. state.subjectList = state.subjectList.filter((next: any) => {
  1107. return next.enableFlag == true
  1108. })
  1109. state.instrumentList = state.instrumentList.filter((next: any) => {
  1110. return next.enableFlag == true
  1111. })
  1112. }
  1113. state.loading = false
  1114. })
  1115. return () => (
  1116. <div style="background: #fff; padding-top: 12px">
  1117. <NSpin show={state.loading}>
  1118. <NForm
  1119. class={styles.formContainer}
  1120. model={forms}
  1121. ref={formsRef}
  1122. label-placement="left"
  1123. label-width="150"
  1124. disabled={state.previewMode}
  1125. >
  1126. <NAlert showIcon={false} style={{marginBottom: '12px'}}>
  1127. 基本信息
  1128. </NAlert>
  1129. <NGrid cols={2}>
  1130. <NFormItemGi
  1131. label="曲目名称"
  1132. path="name"
  1133. rule={[
  1134. {
  1135. required: true,
  1136. message: '请输入曲目名称',
  1137. trigger: ['input', 'blur']
  1138. }
  1139. ]}
  1140. >
  1141. <NInput
  1142. v-model:value={forms.name}
  1143. placeholder="请输入曲目名称"
  1144. maxlength={50}
  1145. showCount
  1146. />
  1147. </NFormItemGi>
  1148. <NFormItemGi
  1149. label="音乐人"
  1150. path="composer"
  1151. rule={[
  1152. {
  1153. required: true,
  1154. message: '请输入音乐人',
  1155. trigger: ['input', 'blur']
  1156. }
  1157. ]}
  1158. >
  1159. <NInput
  1160. v-model:value={forms.composer}
  1161. placeholder="请输入音乐人名称"
  1162. showCount
  1163. maxlength={14}
  1164. />
  1165. </NFormItemGi>
  1166. </NGrid>
  1167. <NGrid cols={2}>
  1168. <NFormItemGi
  1169. label="曲目封面"
  1170. path="musicCover"
  1171. rule={[
  1172. {
  1173. required: false,
  1174. message: '请上传曲目封面',
  1175. trigger: ['input', 'blur']
  1176. }
  1177. ]}
  1178. >
  1179. <UploadFile
  1180. desc={'封面图'}
  1181. disabled={state.previewMode}
  1182. accept=".jpg,.jpeg,.png"
  1183. tips="请上传大小1M以内的JPG、PNG图片"
  1184. size={1}
  1185. v-model:fileList={forms.musicCover}
  1186. cropper
  1187. bucketName="cbs"
  1188. options={{
  1189. autoCrop: true, //是否默认生成截图框
  1190. enlarge: 2, // 图片放大倍数
  1191. autoCropWidth: 200, //默框高度
  1192. fixedBox: true, //是否固定截图框大认生成截图框宽度
  1193. autoCropHeight: 200, //默认生成截图小 不允许改变
  1194. previewsCircle: false, //预览图是否是原圆形
  1195. title: '曲目封面'
  1196. }}
  1197. />
  1198. </NFormItemGi>
  1199. <NFormItemGi
  1200. label="曲目分类"
  1201. path="musicCategoryId"
  1202. rule={[
  1203. {
  1204. required: true,
  1205. message: '请选择曲目分类',
  1206. trigger: ['change']
  1207. }
  1208. ]}
  1209. >
  1210. <NCascader
  1211. valueField="id"
  1212. labelField="name"
  1213. children-field="musicSheetCategoriesList"
  1214. placeholder="请选择分类"
  1215. v-model:value={forms.musicCategoryId}
  1216. options={state.musicSheetCategories}
  1217. clearable
  1218. />
  1219. </NFormItemGi>
  1220. </NGrid>
  1221. <NGrid cols={2}>
  1222. <NFormItemGi
  1223. label="作者属性"
  1224. path="sourceType"
  1225. rule={[
  1226. {
  1227. required: true,
  1228. message: '请选择作者属性',
  1229. trigger: 'change'
  1230. }
  1231. ]}
  1232. >
  1233. <NSelect
  1234. v-model:value={forms.sourceType}
  1235. options={getSelectDataFromObj(musicSheetSourceType)}
  1236. placeholder="请选择作者属性"
  1237. onUpdateValue={() => {
  1238. // 发送变化,清理选择的所属人信息
  1239. forms.musicSheetExtend = {}
  1240. state.ownerName = null
  1241. // forms.musicSheetExtend.userId = null
  1242. // forms.musicSheetExtend.userName = null
  1243. // forms.musicSheetExtend.applicationId = null
  1244. // forms.musicSheetExtend.organizationRoleId = null
  1245. }}
  1246. />
  1247. </NFormItemGi>
  1248. {forms.sourceType === 'PERSON' && (
  1249. <NFormItemGi
  1250. label="所属人"
  1251. path="musicSheetExtend.userId"
  1252. rule={[
  1253. {
  1254. required: true,
  1255. message: '请选择曲目所属人',
  1256. trigger: ['input', 'change']
  1257. }
  1258. ]}
  1259. >
  1260. <NButton
  1261. disabled={state.previewMode || !forms.sourceType}
  1262. type="primary"
  1263. size="small"
  1264. text
  1265. //v-auth="orchestraSubsidyStandard/update1597887579789053953"
  1266. onClick={() => {
  1267. state.showMusicSheetOwnerDialog = true
  1268. }}
  1269. >
  1270. {state.ownerName ? state.ownerName : '请选择所属人'}
  1271. </NButton>
  1272. </NFormItemGi>
  1273. )}
  1274. {forms.sourceType === 'ORG' && (
  1275. <NFormItemGi
  1276. label="所属人"
  1277. path="musicSheetExtend.organizationRoleId"
  1278. rule={[
  1279. {
  1280. required: true,
  1281. message: '请选择曲目所属机构',
  1282. trigger: ['input', 'change']
  1283. }
  1284. ]}
  1285. >
  1286. <NButton
  1287. disabled={state.previewMode || !forms.sourceType}
  1288. type="primary"
  1289. size="small"
  1290. text
  1291. //v-auth="orchestraSubsidyStandard/update1597887579789053953"
  1292. onClick={() => {
  1293. state.showMusicSheetOwnerDialog = true
  1294. }}
  1295. >
  1296. {state.ownerName ? state.ownerName : '请选择所属机构'}
  1297. </NButton>
  1298. </NFormItemGi>
  1299. )}
  1300. </NGrid>
  1301. <NGrid cols={2}>
  1302. <NFormItemGi
  1303. label="审核版本"
  1304. path="appAuditFlag"
  1305. rule={[
  1306. {
  1307. required: true,
  1308. message: '请选择审核版本',
  1309. trigger: 'change',
  1310. type: 'number'
  1311. }
  1312. ]}
  1313. >
  1314. <NSelect
  1315. options={
  1316. [
  1317. {
  1318. label: '是',
  1319. value: 1
  1320. },
  1321. {
  1322. label: '否',
  1323. value: 0
  1324. }
  1325. ] as any
  1326. }
  1327. v-model:value={forms.appAuditFlag}
  1328. />
  1329. </NFormItemGi>
  1330. </NGrid>
  1331. <NAlert showIcon={false} style={{marginBottom: '12px'}}>
  1332. 曲目设置
  1333. </NAlert>
  1334. <NGrid cols={2}>
  1335. <NFormItemGi
  1336. label="播放模式"
  1337. path="playMode"
  1338. rule={[
  1339. {
  1340. required: true,
  1341. message: '请选择播放模式'
  1342. }
  1343. ]}
  1344. >
  1345. <NRadioGroup
  1346. v-model:value={forms.playMode}
  1347. onUpdateValue={(value: string | number | boolean) => {
  1348. if (value === 'MP3') {
  1349. forms.playMode = 'MP3'
  1350. } else {
  1351. forms.playMode = 'MIDI'
  1352. }
  1353. }}
  1354. >
  1355. <NRadio value="MP3">MP3</NRadio>
  1356. <NRadio value="MIDI">MID</NRadio>
  1357. </NRadioGroup>
  1358. </NFormItemGi>
  1359. {forms.playMode === 'MP3' && (
  1360. <NFormItemGi
  1361. label="伴奏类型"
  1362. path="audioType"
  1363. rule={[
  1364. {
  1365. required: true,
  1366. message: '请选择伴奏类型'
  1367. }
  1368. ]}
  1369. >
  1370. <NRadioGroup v-model:value={forms.audioType}>
  1371. <NRadio value={'HOMEMODE'}>自制伴奏</NRadio>
  1372. <NRadio value={'COMMON'}>普通伴奏</NRadio>
  1373. </NRadioGroup>
  1374. </NFormItemGi>
  1375. )}
  1376. </NGrid>
  1377. <NGrid cols={2}>
  1378. <NFormItemGi
  1379. label="上传XML"
  1380. path="xmlFileUrl"
  1381. rule={[
  1382. {
  1383. required: true,
  1384. message: '请选择上传XML',
  1385. trigger: ['change', 'input']
  1386. }
  1387. ]}
  1388. >
  1389. <UploadFile
  1390. desc={'XML文件'}
  1391. disabled={state.previewMode}
  1392. size={30}
  1393. key={'xmlFileUrl'}
  1394. v-model:fileList={forms.xmlFileUrl}
  1395. tips="仅支持上传.xml/.mxml格式文件"
  1396. listType="image"
  1397. accept=".xml,.mxml,.evxml"
  1398. bucketName="cloud-coach"
  1399. text="点击上传XML文件"
  1400. onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
  1401. onRemove={() => {
  1402. // forms.multiTracksSelection = []
  1403. // state.partListNames = []
  1404. // forms.musicSheetSoundList_YY = []
  1405. // forms.musicalInstrumentIdList = []
  1406. // forms.subjectIds = []
  1407. }}
  1408. />
  1409. </NFormItemGi>
  1410. <NFormItemGi
  1411. label="评分标准"
  1412. path="evaluationStandard"
  1413. rule={[
  1414. {
  1415. required: true
  1416. }
  1417. ]}
  1418. >
  1419. <NRadioGroup v-model:value={forms.evaluationStandard}>
  1420. <NRadio value={'FREQUENCY'}>标准评测</NRadio>
  1421. <NRadio value={'AMPLITUDE'}>打击乐(振幅)</NRadio>
  1422. <NRadio value={'DECIBELS'}>节奏(分贝)</NRadio>
  1423. </NRadioGroup>
  1424. </NFormItemGi>
  1425. </NGrid>
  1426. <NGrid cols={2}>
  1427. <NFormItemGi
  1428. label="谱面渲染"
  1429. path="musicSheetType"
  1430. rule={[
  1431. {
  1432. required: true,
  1433. message: '请选择谱面渲染',
  1434. trigger: 'change'
  1435. }
  1436. ]}
  1437. >
  1438. <NRadioGroup v-model:value={forms.musicSheetType}>
  1439. <NRadio value={'SINGLE'}>多声轨</NRadio>
  1440. <NRadio value={'CONCERT'}>单声轨</NRadio>
  1441. </NRadioGroup>
  1442. </NFormItemGi>
  1443. <NFormItemGi
  1444. label="适用声部"
  1445. path="isAllSubject"
  1446. rule={[
  1447. {
  1448. required: true,
  1449. message: '请选择适用声部',
  1450. }
  1451. ]}
  1452. >
  1453. <NRadioGroup v-model:value={forms.isAllSubject}>
  1454. <NRadio value={false}>部分声部</NRadio>
  1455. <NRadio value={true}>全部声部</NRadio>
  1456. </NRadioGroup>
  1457. </NFormItemGi>
  1458. </NGrid>
  1459. {!forms.isAllSubject && (
  1460. <NGrid cols={2}>
  1461. <NFormItemGi
  1462. label="可用声部"
  1463. path="subjectIds"
  1464. rule={[
  1465. {
  1466. required: true,
  1467. message: '请选择可用声部',
  1468. trigger: 'change',
  1469. type: 'array'
  1470. }
  1471. ]}
  1472. >
  1473. <NSelect
  1474. v-model:value={forms.subjectIds}
  1475. options={state.subjectList}
  1476. multiple
  1477. filterable
  1478. clearable
  1479. placeholder="请选择可用声部"
  1480. maxTagCount={2}
  1481. onUpdateValue={async (val: any) => {
  1482. await changeSubject(val)
  1483. }}
  1484. />
  1485. </NFormItemGi>
  1486. <NFormItemGi
  1487. label="可用乐器"
  1488. path="musicalInstrumentIdList"
  1489. rule={[
  1490. {
  1491. required: true,
  1492. message: '请选择可用乐器',
  1493. trigger: 'change',
  1494. type: 'array'
  1495. }
  1496. ]}
  1497. >
  1498. <NSelect
  1499. placeholder="请选择可用乐器"
  1500. options={state.instrumentList}
  1501. v-model:value={forms.musicalInstrumentIdList}
  1502. clearable
  1503. multiple
  1504. filterable
  1505. maxTagCount={2}
  1506. onUpdateValue={async (value: any) => {
  1507. }}
  1508. />
  1509. </NFormItemGi>
  1510. </NGrid>
  1511. )}
  1512. <NGrid cols={2}>
  1513. <NFormItemGi
  1514. label="速度"
  1515. path="playSpeed"
  1516. rule={[
  1517. {
  1518. required: false,
  1519. message: '请输入速度'
  1520. }
  1521. ]}
  1522. >
  1523. <NInputNumber
  1524. placeholder="请输入速度"
  1525. v-model:value={forms.playSpeed}
  1526. min={0}
  1527. style="width:100%"
  1528. />
  1529. </NFormItemGi>
  1530. </NGrid>
  1531. <NGrid cols={1}>
  1532. <NFormItemGi
  1533. label={`${forms.musicSheetType === 'SINGLE' ? '页面渲染声轨' : '用户可切换声轨'}`}
  1534. path="multiTracksSelection"
  1535. rule={[
  1536. {
  1537. required: true,
  1538. message: `请选择${forms.musicSheetType === 'SINGLE' ? '页面渲染声轨' : '用户可切换声轨'}`,
  1539. trigger: 'change',
  1540. type: 'array'
  1541. }
  1542. ]}
  1543. >
  1544. <NGrid style="padding-top: 4px;">
  1545. <NGi span={24}>
  1546. <NRadioGroup
  1547. v-model:value={state.multiTracks}
  1548. onUpdateValue={(value) => {
  1549. checkMultiTracks(value)
  1550. }}
  1551. >
  1552. <NRadio value={'all'}>全选</NRadio>
  1553. <NRadio value={'allUncheck'}>重置</NRadio>
  1554. <NRadio value={'invert'}>反选</NRadio>
  1555. </NRadioGroup>
  1556. </NGi>
  1557. {state.partListNames && state.partListNames.length > 0 && (
  1558. <NGi span={24} style={'margin-top:5px'}>
  1559. <NFormItemGi
  1560. label=""
  1561. path="multiTracksSelection"
  1562. rule={[
  1563. {
  1564. required: false
  1565. }
  1566. ]}
  1567. >
  1568. <NCheckboxGroup v-model:value={forms.multiTracksSelection}
  1569. onUpdateValue={(val: any) => {
  1570. if (state.partListNames.length != val.length) {
  1571. state.multiTracks = null
  1572. } else {
  1573. state.multiTracks = 'all'
  1574. }
  1575. }}
  1576. >
  1577. <NGrid yGap={2} cols={4}>
  1578. {state.partListNames.map((item: any) => (
  1579. <NGi>
  1580. <NCheckbox value={item.value} label={item.label}/>
  1581. </NGi>
  1582. ))}
  1583. </NGrid>
  1584. </NCheckboxGroup>
  1585. </NFormItemGi>
  1586. </NGi>
  1587. )}
  1588. </NGrid>
  1589. </NFormItemGi>
  1590. </NGrid>
  1591. <NAlert showIcon={false} style={{marginBottom: '12px'}}>
  1592. 演唱文件
  1593. </NAlert>
  1594. <NGrid cols={2}>
  1595. <NFormItemGi
  1596. label="是否播放节拍器"
  1597. path="isPlaySingBeat"
  1598. rule={[
  1599. {
  1600. required: true,
  1601. message: '请选择是否播放节拍器'
  1602. }
  1603. ]}
  1604. >
  1605. <NRadioGroup v-model:value={forms.isPlaySingBeat}>
  1606. <NRadio value={true}>是</NRadio>
  1607. <NRadio value={false}>否</NRadio>
  1608. </NRadioGroup>
  1609. </NFormItemGi>
  1610. {forms.isPlaySingBeat && (
  1611. <NFormItemGi
  1612. label="播放方式"
  1613. path="isUseSingSystemBeat"
  1614. rule={[
  1615. {
  1616. required: true,
  1617. message: '请选择播放方式'
  1618. }
  1619. ]}
  1620. >
  1621. <NRadioGroup v-model:value={forms.isUseSingSystemBeat}>
  1622. <NRadio value={true}>系统节拍器</NRadio>
  1623. <NRadio value={false}>MP3节拍器</NRadio>
  1624. </NRadioGroup>
  1625. </NFormItemGi>
  1626. )}
  1627. </NGrid>
  1628. <NGrid cols={2}>
  1629. <NFormItemGi
  1630. label="重复节拍时长"
  1631. path="repeatedBeatsToSing"
  1632. rule={[
  1633. {
  1634. required: false,
  1635. message: '请选择是否重复节拍时长'
  1636. }
  1637. ]}
  1638. >
  1639. <NRadioGroup v-model:value={forms.repeatedBeatsToSing}>
  1640. <NRadio value={true}>是</NRadio>
  1641. <NRadio value={false}>否</NRadio>
  1642. </NRadioGroup>
  1643. </NFormItemGi>
  1644. </NGrid>
  1645. <NGrid cols={2}>
  1646. <NFormItemGi
  1647. label="上传范唱"
  1648. path="fSongFile"
  1649. rule={[
  1650. {
  1651. required: false,
  1652. message: '请选择上传范唱',
  1653. trigger: ['change', 'input']
  1654. }
  1655. ]}
  1656. >
  1657. <UploadFile
  1658. desc={'上传范唱'}
  1659. disabled={state.previewMode}
  1660. size={30}
  1661. key={'xmlFileUrl'}
  1662. v-model:fileList={state.fSongFile}
  1663. tips="仅支持上传.mp3格式文件"
  1664. listType="image"
  1665. accept=".mp3"
  1666. bucketName="cloud-coach"
  1667. text="点击上传范唱文件"
  1668. onRemove={() => {
  1669. }}
  1670. />
  1671. </NFormItemGi>
  1672. <NFormItemGi
  1673. label="上传伴唱"
  1674. path="bSongFile"
  1675. rule={[
  1676. {
  1677. required: false,
  1678. message: '请选择上传.MID格式文件'
  1679. }
  1680. ]}
  1681. >
  1682. <UploadFile
  1683. desc={'上传伴唱'}
  1684. disabled={state.previewMode}
  1685. size={30}
  1686. v-model:fileList={state.bSongFile}
  1687. tips="仅支持上传.mp3格式文件"
  1688. listType="image"
  1689. accept=".mp3"
  1690. bucketName="cloud-coach"
  1691. text="点击上传伴唱文件"
  1692. />
  1693. </NFormItemGi>
  1694. </NGrid>
  1695. <NGrid cols={2}>
  1696. <NFormItemGi
  1697. label="上传唱名"
  1698. path="solmizationFileUrl"
  1699. rule={[
  1700. {
  1701. required: false,
  1702. message: '请选择上传唱名',
  1703. trigger: ['change', 'input']
  1704. }
  1705. ]}
  1706. >
  1707. <UploadFile
  1708. desc={'上传唱名'}
  1709. disabled={state.previewMode}
  1710. size={30}
  1711. key={'xmlFileUrl'}
  1712. v-model:fileList={forms.solmizationFileUrl}
  1713. tips="仅支持上传.mp3格式文件"
  1714. listType="image"
  1715. accept=".mp3"
  1716. bucketName="cloud-coach"
  1717. text="点击上传唱名文件"
  1718. onRemove={() => {
  1719. }}
  1720. />
  1721. </NFormItemGi>
  1722. </NGrid>
  1723. <NAlert showIcon={false} style={{marginBottom: '12px'}}>
  1724. 演奏文件
  1725. </NAlert>
  1726. <NGrid cols={2}>
  1727. <NFormItemGi
  1728. label="是否播放节拍器"
  1729. path="isPlayBeat"
  1730. rule={[
  1731. {
  1732. required: true,
  1733. message: '请选择是否播放节拍器'
  1734. }
  1735. ]}
  1736. >
  1737. <NRadioGroup v-model:value={forms.isPlayBeat}>
  1738. <NRadio value={true}>是</NRadio>
  1739. <NRadio value={false}>否</NRadio>
  1740. </NRadioGroup>
  1741. </NFormItemGi>
  1742. {forms.isPlayBeat && (
  1743. <NFormItemGi
  1744. label="播放方式"
  1745. path="isUseSystemBeat"
  1746. rule={[
  1747. {
  1748. required: true,
  1749. message: '请选择播放方式'
  1750. }
  1751. ]}
  1752. >
  1753. <NRadioGroup v-model:value={forms.isUseSystemBeat}>
  1754. <NRadio value={true}>系统节拍器</NRadio>
  1755. <NRadio value={false}>MP3节拍器</NRadio>
  1756. </NRadioGroup>
  1757. </NFormItemGi>
  1758. )}
  1759. </NGrid>
  1760. <NGrid cols={2}>
  1761. <NFormItemGi
  1762. label="重复节拍时长"
  1763. path="repeatedBeats"
  1764. rule={[
  1765. {
  1766. required: false,
  1767. message: '请选择是否重复节拍时长'
  1768. }
  1769. ]}
  1770. >
  1771. <NRadioGroup v-model:value={forms.repeatedBeats}>
  1772. <NRadio value={true}>是</NRadio>
  1773. <NRadio value={false}>否</NRadio>
  1774. </NRadioGroup>
  1775. </NFormItemGi>
  1776. <NFormItemGi
  1777. label="是否显示指法"
  1778. path="isShowFingering"
  1779. rule={[
  1780. {
  1781. required: true,
  1782. message: '请选择是否显示指法'
  1783. }
  1784. ]}
  1785. >
  1786. <NRadioGroup v-model:value={forms.isShowFingering}>
  1787. <NRadio value={true}>是</NRadio>
  1788. <NRadio value={false}>否</NRadio>
  1789. </NRadioGroup>
  1790. </NFormItemGi>
  1791. </NGrid>
  1792. <NGrid cols={2}>
  1793. {forms.playMode === 'MP3' && (
  1794. <NFormItemGi
  1795. label="上传伴奏"
  1796. path="musicSheetAccompanimentList"
  1797. rule={[
  1798. {
  1799. required: false,
  1800. message: '请选择上传.mp3'
  1801. }
  1802. ]}
  1803. >
  1804. <UploadFile
  1805. disabled={state.previewMode}
  1806. size={30}
  1807. v-model:imageList={state.musicSheetAccompanimentUrlList}
  1808. tips="仅支持上传.mp3格式文件"
  1809. listType="image"
  1810. accept=".mp3"
  1811. bucketName="cloud-coach"
  1812. text="点击上传伴奏文件"
  1813. max={1}
  1814. desc={'上传伴奏文件'}
  1815. onUpload:success={(file) => {
  1816. state.musicSheetAccompanimentUrls = [state.musicSheetAccompanimentUrls, file.url].filter(Boolean).join(',')
  1817. state.musicSheetAccompanimentUrlList = state.musicSheetAccompanimentUrls?.split(',').filter(Boolean)
  1818. // 清除伴奏
  1819. // forms.musicSheetAccompanimentList = forms.musicSheetAccompanimentList.filter((next: any) => {
  1820. // return next.audioPlayType == 'SING'
  1821. // })
  1822. forms.musicSheetAccompanimentList = []
  1823. for (let i = 0; i < state.musicSheetAccompanimentUrlList.length; i++) {
  1824. forms.musicSheetAccompanimentList.push({
  1825. audioFileUrl: state.musicSheetAccompanimentUrlList[i],
  1826. sortNumber: i + 1,
  1827. audioPlayType: 'PLAY'
  1828. })
  1829. }
  1830. }}
  1831. onRemove={() => {
  1832. state.musicSheetAccompanimentUrlList = []
  1833. state.musicSheetAccompanimentUrls = ''
  1834. }}
  1835. // onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
  1836. multiple={true}
  1837. />
  1838. </NFormItemGi>
  1839. )}
  1840. {forms.isAllSubject && forms.musicSheetType == 'SINGLE' && (
  1841. <NFormItemGi
  1842. label="上传原音"
  1843. path="musicSheetSoundList_all_subject"
  1844. rule={[
  1845. {
  1846. required: false,
  1847. message: '请选择上传原音',
  1848. trigger: ['change', 'input']
  1849. }
  1850. ]}
  1851. >
  1852. <UploadFile
  1853. desc={'上传原音'}
  1854. disabled={state.previewMode}
  1855. size={30}
  1856. max={1}
  1857. v-model:fileList={forms.musicSheetSoundList_all_subject}
  1858. tips="仅支持上传.mp3格式文件"
  1859. listType="image"
  1860. accept=".mp3"
  1861. bucketName="cloud-coach"
  1862. text="点击上传原音"
  1863. onRemove={() => {
  1864. }}
  1865. />
  1866. </NFormItemGi>
  1867. )}
  1868. </NGrid>
  1869. {/*渐变速*/}
  1870. {!!gradualData.list.length && (
  1871. <>
  1872. <NAlert showIcon={false} type="info">
  1873. 识别到共1处渐变速度,请输入Dorico对应小节时间信息
  1874. </NAlert>
  1875. <NFormItem label="rit." required style={{marginTop: '10px'}}>
  1876. <NSpace vertical>
  1877. {gradualData.list.map((n: any, ni: number) => (
  1878. <NInputGroup>
  1879. <NFormItem
  1880. path={`graduals.${n[0].measureIndex}`}
  1881. rule={[
  1882. {required: true, message: '请输入合奏曲目时间'},
  1883. {
  1884. pattern: /^((\d{2}):?){2,3}$/,
  1885. message: '请输入正确的曲目时间',
  1886. trigger: 'blur'
  1887. }
  1888. ]}
  1889. >
  1890. <NInputGroup>
  1891. <NInputGroupLabel>{n[0].measureIndex}小节开始</NInputGroupLabel>
  1892. <NInput
  1893. placeholder="00:00:00"
  1894. v-model:value={forms.graduals[n[0].measureIndex]}
  1895. ></NInput>
  1896. </NInputGroup>
  1897. </NFormItem>
  1898. <div style={{lineHeight: '30px', padding: '0 4px'}}>~</div>
  1899. <NFormItem
  1900. path={`graduals.${n[1].measureIndex}`}
  1901. rule={[
  1902. {required: true, message: '请输入合奏曲目时间'},
  1903. {
  1904. pattern: /^((\d{2}):?){2,3}$/,
  1905. message: '请输入正确的曲目时间',
  1906. trigger: 'blur'
  1907. }
  1908. ]}
  1909. >
  1910. <NInputGroup>
  1911. <NInput
  1912. placeholder="00:00:00"
  1913. v-model:value={forms.graduals[n[1].measureIndex]}
  1914. ></NInput>
  1915. <NInputGroupLabel>{n[1].measureIndex}小节结束</NInputGroupLabel>
  1916. </NInputGroup>
  1917. </NFormItem>
  1918. </NInputGroup>
  1919. ))}
  1920. </NSpace>
  1921. </NFormItem>
  1922. </>
  1923. )}
  1924. {/*独奏*/}
  1925. {forms.musicSheetType == 'SINGLE' && forms.playMode === 'MP3' && !forms.isAllSubject && forms.musicSheetSoundList_YZ.map((item: any, index: any) => {
  1926. return (
  1927. <>
  1928. {forms.musicalInstrumentIdList.includes(item.musicalInstrumentId) && (
  1929. <NGrid class={styles.audioSection}>
  1930. <NFormItemGi
  1931. span={12}
  1932. label={item.musicalInstrumentName}
  1933. path={`musicSheetSoundList_YZ[${index}].audioFileUrl`}
  1934. rule={[
  1935. {
  1936. required: false,
  1937. message: `请上传乐器演奏文件`
  1938. }
  1939. ]}
  1940. >
  1941. <UploadFile
  1942. desc={'乐器演奏文件'}
  1943. disabled={state.previewMode}
  1944. size={100}
  1945. v-model:fileList={item.audioFileUrl}
  1946. tips="仅支持上传.mp3格式文件"
  1947. listType="image"
  1948. accept=".mp3"
  1949. text={"点击上传MP3文件"}
  1950. bucketName="cloud-coach"
  1951. />
  1952. </NFormItemGi>
  1953. </NGrid>
  1954. )}
  1955. </>
  1956. )
  1957. })
  1958. }
  1959. {/*合奏*/}
  1960. {forms.musicSheetType == 'CONCERT' && forms.playMode === 'MP3' && forms.musicSheetSoundList_YY.length > 0 && (
  1961. <>
  1962. {forms.musicSheetSoundList_YY.map((item: any, index: number) => {
  1963. return (
  1964. <>
  1965. {(!containOther(item.track) ||
  1966. (item.track?.toLocaleUpperCase?.() != 'COMMON' &&
  1967. forms.multiTracksSelection.includes(item.track))) && (
  1968. <NGrid
  1969. class={styles.audioSection}
  1970. // v-show={forms.multiTracksSelection.indexOf(item.track) > -1}
  1971. >
  1972. <NFormItemGi
  1973. span={12}
  1974. label="原音"
  1975. path={`musicSheetSoundList_YY[${index}].audioFileUrl`}
  1976. rule={[
  1977. {
  1978. // required: forms.multiTracksSelection.indexOf(forms.musicSheetSoundList_YY[index].audioFileUrl) > -1,
  1979. required: false,
  1980. message: `请上传${
  1981. item.track ? item.track + '的' : '第' + (index + 1) + '个'
  1982. }原音`
  1983. }
  1984. ]}
  1985. >
  1986. <UploadFile
  1987. desc={'原音文件'}
  1988. disabled={state.previewMode}
  1989. size={100}
  1990. v-model:fileList={item.audioFileUrl}
  1991. tips="仅支持上传.mp3格式文件"
  1992. listType="image"
  1993. accept=".mp3"
  1994. bucketName="cloud-coach"
  1995. />
  1996. </NFormItemGi>
  1997. {state.partListNames.length > 0 && (
  1998. <NFormItemGi
  1999. span={12}
  2000. label="所属轨道"
  2001. path={`musicSheetSoundList_YY[${index}].track`}
  2002. rule={[
  2003. {
  2004. required: false,
  2005. message: '请选择所属轨道'
  2006. }
  2007. ]}
  2008. >
  2009. <NSelect
  2010. placeholder="请选择所属轨道"
  2011. value={item.track}
  2012. options={initPartsListStatus(item.track)}
  2013. onUpdateValue={(value: any) => {
  2014. const track = item.track
  2015. if (track) {
  2016. // 声轨交换
  2017. forms.musicSheetSoundList_YY.forEach((next: any) => {
  2018. if (next.track == value) {
  2019. next.track = track
  2020. }
  2021. })
  2022. const index = forms.multiTracksSelection.indexOf(item.track)
  2023. forms.multiTracksSelection.splice(index, 1)
  2024. } else {
  2025. forms.musicSheetSoundList_YY = forms.musicSheetSoundList_YY.filter(
  2026. (next: any) => {
  2027. return next.track != value
  2028. }
  2029. )
  2030. }
  2031. if (value != null && !forms.multiTracksSelection.includes(value)) {
  2032. forms.multiTracksSelection.push(value)
  2033. }
  2034. item.track = value
  2035. if (state.partListNames.length == forms.multiTracksSelection.length) {
  2036. state.multiTracks = 'all'
  2037. }
  2038. }}
  2039. />
  2040. </NFormItemGi>
  2041. )}
  2042. <NGi class={styles.btnRemove}>
  2043. <NButton
  2044. type="primary"
  2045. text
  2046. // disabled={forms.musicSheetSoundList_YY.length === 1}
  2047. onClick={() => removeSys(index)}
  2048. >
  2049. 删除
  2050. </NButton>
  2051. </NGi>
  2052. </NGrid>
  2053. )}
  2054. </>
  2055. )
  2056. })}
  2057. </>
  2058. )}
  2059. </NForm>
  2060. </NSpin>
  2061. {props.type !== 'preview' && (
  2062. <NSpace justify="end" style="padding-top:12px">
  2063. <NButton type="default" onClick={() => emit('close')}>
  2064. 取消
  2065. </NButton>
  2066. <NButton
  2067. type="primary"
  2068. onClick={() => onSubmit()}
  2069. loading={btnLoading.value}
  2070. disabled={btnLoading.value}
  2071. >
  2072. 确认
  2073. </NButton>
  2074. </NSpace>
  2075. )}
  2076. <NModal
  2077. v-model:show={state.showMusicSheetOwnerDialog}
  2078. preset="dialog"
  2079. showIcon={false}
  2080. maskClosable={false}
  2081. title="所属人"
  2082. style={{width: '800px'}}
  2083. >
  2084. <MusicSheetOwnerDialog
  2085. musicSheetExtend={forms.musicSheetExtend}
  2086. sourceType={forms.sourceType}
  2087. appData={state.appData}
  2088. onClose={() => {
  2089. state.showMusicSheetOwnerDialog = false
  2090. }}
  2091. onChoseMusicSheetOwnerData={(musicSheetOwnerData) => {
  2092. forms.musicSheetExtend = {
  2093. ...musicSheetOwnerData
  2094. }
  2095. setOwnerName()
  2096. }}
  2097. />
  2098. </NModal>
  2099. <NModal
  2100. class={styles.productModal}
  2101. title="自动生成曲谱图片"
  2102. v-model:show={state.productOpen}
  2103. preset="dialog"
  2104. closeOnEsc={false}
  2105. maskClosable={false}
  2106. onClose={()=> {
  2107. state.isAutoSave = false
  2108. }}
  2109. showIcon={false}
  2110. >
  2111. <MusicCreateImg
  2112. xmlFileUrl={forms.xmlFileUrl || ''}
  2113. onClose={() => (state.productOpen = false)}
  2114. onConfirm={async (item: any) => {
  2115. // 保存
  2116. try {
  2117. forms.musicImg = item.musicImg
  2118. forms.musicFirstImg = item.musicFirstImg
  2119. forms.musicJianImg = item.musicJianImg
  2120. await onSubmit()
  2121. } catch (e: any) {
  2122. //
  2123. console.log(e, 'e')
  2124. }
  2125. // setTimeout(() => {
  2126. // state.isAutoSave = false
  2127. // }, 50)
  2128. }}
  2129. />
  2130. </NModal>
  2131. </div>
  2132. )
  2133. }
  2134. })