| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974 |
- import {
- NForm,
- NFormItem,
- NInput,
- NSelect,
- NSpace,
- NButton,
- useMessage,
- NRadioGroup,
- NRadio,
- NGrid,
- NFormItemGi,
- NInputNumber,
- NGi,
- useDialog,
- NCascader,
- NAlert,
- NInputGroup,
- NInputGroupLabel, NCheckbox
- } from 'naive-ui'
- import type {SelectOption} from 'naive-ui'
- import {defineComponent, onMounted, PropType, reactive, ref} from 'vue'
- import {musicSheetDetail, musicSheetSave, musicSheetUpdate} from '../../api'
- import UploadFile from '@/components/upload-file'
- import styles from './index.module.less'
- import deepClone from '@/utils/deep.clone'
- import axios from 'axios'
- import {Checkbox, CheckboxGroup} from "vant";
- import {releaseCandidate} from "@/utils/constant";
- import {getSelectDataFromObj} from "@/utils/objectUtil";
- /**
- * 获取指定元素下一个Note元素
- * @param ele 指定元素
- * @param selectors 选择器
- */
- const getNextNote = (ele: any, selectors: any) => {
- let index = 0
- const parentEle = ele.closest(selectors)
- let pointer = parentEle
- const measure = parentEle?.closest('measure')
- let siblingNote = null
- // 查找到相邻的第一个note元素
- while (!siblingNote && index < (measure?.childNodes.length || 50)) {
- index++
- if (pointer?.nextElementSibling?.tagName === 'note') {
- siblingNote = pointer?.nextElementSibling
- }
- pointer = pointer?.nextElementSibling
- }
- return siblingNote
- }
- export const onlyVisible = (xml: any, partIndex: any) => {
- if (!xml) return ''
- const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
- const partList =
- xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
- const parts = xmlParse.getElementsByTagName('part')
- const visiblePartInfo = partList[partIndex]
- if (visiblePartInfo) {
- const id = visiblePartInfo.getAttribute('id')
- Array.from(parts).forEach((part) => {
- if (part && part.getAttribute('id') !== id) {
- part.parentNode?.removeChild(part)
- // 不等于第一行才添加避免重复添加
- }
- // 最后一个小节的结束线元素不在最后 调整
- if (part && part.getAttribute('id') === id) {
- const barlines = part.getElementsByTagName('barline')
- const lastParent = barlines[barlines.length - 1]?.parentElement
- if (lastParent?.lastElementChild?.tagName !== 'barline') {
- const children: any[] = (lastParent?.children as any) || []
- for (let el of children) {
- if (el.tagName === 'barline') {
- // 将结束线元素放到最后
- lastParent?.appendChild(el)
- break
- }
- }
- }
- }
- })
- Array.from(partList).forEach((part) => {
- if (part && part.getAttribute('id') !== id) {
- part.parentNode?.removeChild(part)
- }
- })
- // 处理装饰音问题
- const notes = xmlParse.getElementsByTagName('note')
- const getNextvNoteDuration = (i: any) => {
- let nextNote = notes[i + 1]
- // 可能存在多个装饰音问题,取下一个非装饰音时值
- for (let index = i; index < notes.length; index++) {
- const note = notes[index]
- if (!note.getElementsByTagName('grace')?.length) {
- nextNote = note
- break
- }
- }
- const nextNoteDuration = nextNote?.getElementsByTagName('duration')[0]
- return nextNoteDuration
- }
- Array.from(notes).forEach((note, i) => {
- const graces = note.getElementsByTagName('grace')
- if (graces && graces.length) {
- note.appendChild(getNextvNoteDuration(i)?.cloneNode(true))
- }
- })
- }
- return new XMLSerializer().serializeToString(xmlParse)
- }
- const speedInfo = {
- 'rall.': 1.333333333,
- 'poco rit.': 1.333333333,
- 'rit.': 1.333333333,
- 'molto rit.': 1.333333333,
- 'molto rall': 1.333333333,
- molto: 1.333333333,
- lentando: 1.333333333,
- allargando: 1.333333333,
- morendo: 1.333333333,
- 'accel.': 0.8,
- calando: 2,
- 'poco accel.': 0.8,
- 'gradually slowing': 1.333333333,
- slowing: 1.333333333,
- slow: 1.333333333,
- slowly: 1.333333333,
- faster: 1.333333333
- }
- /**
- * 按照xml进行减慢速度的计算
- * @param xml 始终按照第一分谱进行减慢速度的计算
- */
- export function getGradualLengthByXml(xml: string) {
- const firstPartXml = onlyVisible(xml, 0)
- const xmlParse = new DOMParser().parseFromString(firstPartXml, 'text/xml')
- const measures = Array.from(xmlParse.querySelectorAll('measure'))
- const notes = Array.from(xmlParse.querySelectorAll('note'))
- const words = Array.from(xmlParse.querySelectorAll('words'))
- const metronomes = Array.from(xmlParse.querySelectorAll('metronome'))
- const eles = []
- for (const ele of [...words, ...metronomes]) {
- const note = getNextNote(ele, 'direction')
- // console.log(ele, note)
- if (note) {
- const measure = note?.closest('measure')
- const measureNotes = Array.from(measure.querySelectorAll('note'))
- const noteInMeasureIndex = Array.from(measure.childNodes)
- .filter((item: any) => item.nodeName === 'note')
- .findIndex((item) => item === note)
- let allDuration = 0
- let leftDuration = 0
- for (let i = 0; i < measureNotes.length; i++) {
- const n: any = measureNotes[i]
- const duration = +(n.querySelector('duration')?.textContent || '0')
- allDuration += duration
- if (i < noteInMeasureIndex) {
- leftDuration = allDuration
- }
- }
- eles.push({
- ele,
- index: notes.indexOf(note),
- noteInMeasureIndex,
- textContent: ele.textContent,
- measureIndex: measures.indexOf(measure), //,measure?.getAttribute('number')
- type: ele.tagName,
- allDuration,
- leftDuration
- })
- }
- }
- // 结尾处手动插入一个音符节点
- eles.push({
- ele: notes[notes.length - 1],
- index: notes.length,
- noteInMeasureIndex: 0,
- textContent: '',
- type: 'metronome',
- allDuration: 1,
- leftDuration: 1,
- measureIndex: measures.length
- })
- const gradualNotes: any[] = []
- eles.sort((a, b) => a.index - b.index)
- const keys = Object.keys(speedInfo).map((w) => w.toLocaleLowerCase())
- let isLastNoteAndNotClosed = false
- for (const ele of eles) {
- const textContent: any = ele.textContent?.toLocaleLowerCase().trim()
- if (ele === eles[eles.length - 1]) {
- if (gradualNotes[gradualNotes.length - 1]?.length === 1) {
- isLastNoteAndNotClosed = true
- }
- }
- const isKeyWork = keys.find((k) => {
- const ks = k.split(' ')
- return textContent && ks.includes(textContent)
- })
- if (
- ele.type === 'metronome' ||
- (ele.type === 'words' && (textContent.startsWith('a tempo') || isKeyWork)) ||
- isLastNoteAndNotClosed
- ) {
- const indexOf = gradualNotes.findIndex((item) => item.length === 1)
- if (indexOf > -1 && ele.index > gradualNotes[indexOf]?.[0].start) {
- gradualNotes[indexOf][1] = {
- start: ele.index,
- measureIndex: ele.measureIndex,
- noteInMeasureIndex: ele.noteInMeasureIndex,
- allDuration: ele.allDuration,
- leftDuration: ele.leftDuration,
- type: textContent
- }
- }
- }
- if (ele.type === 'words' && isKeyWork) {
- gradualNotes.push([
- {
- start: ele.index,
- measureIndex: ele.measureIndex,
- noteInMeasureIndex: ele.noteInMeasureIndex,
- allDuration: ele.allDuration,
- leftDuration: ele.leftDuration,
- type: textContent
- }
- ])
- }
- }
- return gradualNotes
- }
- export default defineComponent({
- name: 'music-operation',
- props: {
- type: {
- type: String,
- default: 'add'
- },
- data: {
- type: Object as PropType<any>,
- default: () => {
- }
- },
- tagList: {
- type: Array as PropType<Array<SelectOption>>,
- default: () => []
- },
- subjectList: {
- type: Array as PropType<Array<SelectOption>>,
- default: () => []
- },
- musicSheetCategories: {
- type: Array as PropType<Array<SelectOption>>,
- default: () => []
- }
- },
- emits: ['close', 'getList'],
- setup(props, {slots, attrs, emit}) {
- const forms = reactive({
- graduals: {} as any, // 渐变速度
- audioType: 'MP3', // 播放类型
- mp3Type: 'MP3', // 是否含节拍器
- xmlFileUrl: null, // XML
- midiUrl: null, // mid
- metronomeUrl: null, // 伴奏 根据mp3Type 是否是为包含节拍器
- musicSheetName: null, // 曲目名称
- musicTag: [] as any, // 曲目标签
- composer: null, // 音乐人
- playSpeed: null, // 曲谱速度
- showFingering: null as any, // 是否显示指法
- canEvaluate: null as any, // 是否评测
- musicSubject: null as any, // 可用声部
- notation: null as any, // 能否转和简谱
- auditVersion: null as any, // 审核版本
- accompanimentType: 1, // 伴奏类型
- sortNumber: null, // 排序
- titleImg: null, // 曲谱封面
- remark: null, // 曲谱描述
- background: [] as any, // 原音
- musicSheetCategoriesId: null,
- status: false,
- musicSheetType: 'SINGLE' as 'SINGLE' | 'CONCERT',
- author: null, //音乐人
- authorFrom: undefined,
- belongTo: undefined,
- speed: undefined,
- playMetronome: 1,
- playStyle: undefined, // 播放方式
- releaseCandidate: 'NO' // 是否审核版本
- })
- const state = reactive({
- tagList: [...props.tagList] as any, // 标签列表
- xmlFirstSpeed: null as any, // 第一个音轨速度
- partListNames: [] as any, // 所有音轨声部列表
- musicSheetCategories: [...props.musicSheetCategories] as any
- })
- const gradualData = reactive({
- list: [] as any[],
- gradualRefs: [] as any[]
- })
- const btnLoading = ref(false)
- const formsRef = ref()
- const message = useMessage()
- const dialog = useDialog()
- // 提交记录
- const onSubmit = async () => {
- formsRef.value.validate(async (error: any) => {
- console.log(error, 'error')
- if (error) {
- message.error(error[0]?.[0]?.message)
- return
- }
- try {
- const obj = {
- ...forms,
- musicTag: '-1',
- extConfigJson: JSON.stringify({gradualTimes: forms.graduals})
- }
- if (forms.audioType == 'MIDI') {
- obj.background = []
- }
- btnLoading.value = true
- if (props.type === 'add') {
- await musicSheetSave({...obj})
- message.success('添加成功')
- } else if (props.type === 'edit') {
- await musicSheetUpdate({...obj, id: props.data.id})
- message.success('修改成功')
- }
- emit('getList')
- emit('close')
- } catch (e) {
- console.log(e)
- }
- setTimeout(() => {
- btnLoading.value = false
- }, 100)
- })
- }
- // 上传XML,初始化音轨 音轨速度
- const readFileInputEventAsArrayBuffer = (file: any) => {
- const xmlRead = new FileReader()
- xmlRead.onload = (res) => {
- try {
- gradualData.list = getGradualLengthByXml(res?.target?.result as any).filter(
- (item: any) => item.length === 2
- )
- } catch (error) {
- }
- state.partListNames = getPartListNames(res?.target?.result as any) as any
- // 这里是如果没有当前音轨就重新写
- for (let j = 0; j < state.partListNames.length; j++) {
- if (!forms.background[j]) {
- forms.background.push({audioFileUrl: null, track: null})
- }
- forms.background[j].track = state.partListNames[j].value
- }
- // 循环添加所在音轨的原音
- for (let index = forms.background.length; index < state.partListNames.length; index++) {
- const part = state.partListNames[index].value
- const sysData = {
- ...forms.background[0],
- track: part
- }
- if (!sysData.speed) {
- sysData.speed = state.xmlFirstSpeed
- }
- createSys(sysData)
- }
- if (forms.background.length == 0) {
- forms.background.push({audioFileUrl: '', track: ''})
- }
- }
- xmlRead.readAsText(file)
- }
- // 获取xml中所有轨道
- const getPartListNames = (xml: any) => {
- if (!xml) return []
- const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
- const partList =
- xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
- const partListNames = Array.from(partList).map((item) => {
- const part = item.getElementsByTagName('part-name')?.[0].textContent || ''
- return {
- value: part,
- label: part
- }
- })
- if (partListNames.length > 0) {
- forms.background = forms.background.slice(0, partListNames.length)
- }
- state.xmlFirstSpeed = xmlParse.getElementsByTagName('per-minute')?.[0]?.textContent || ''
- if (!forms.playSpeed) {
- forms.playSpeed = state.xmlFirstSpeed
- }
- return partListNames
- }
- // 判断选择的音轨是否在选中
- const initPartsListStatus = (track: string): any => {
- const _names = state.partListNames.filter((n: any) => n.value?.toLocaleUpperCase?.() != 'COMMON')
- const partListNames = deepClone(_names) || []
- partListNames.forEach((item: any) => {
- const index = forms.background.findIndex((ground: any) => item.value == ground.track)
- if (index > -1 && track != item.value) {
- item.disabled = true
- } else {
- item.disabled = false
- }
- })
- return partListNames || []
- }
- // 添加原音
- const createSys = (initData?: any) => {
- forms.background.push({
- audioFileUrl: null, // 原音
- track: null, // 轨道
- ...initData
- })
- }
- // 删除原音
- const removeSys = (index: number) => {
- dialog.warning({
- title: '警告',
- content: `是否确认删除此原音?`,
- positiveText: '确定',
- negativeText: '取消',
- onPositiveClick: async () => {
- forms.background.splice(index, 1)
- }
- })
- }
- onMounted(async () => {
- if (props.type === 'edit' || props.type === 'preview') {
- const detail = props.data
- try {
- const {data} = await musicSheetDetail({id: detail.id})
- forms.audioType = data.audioType
- forms.mp3Type = data.mp3Type
- forms.xmlFileUrl = data.xmlFileUrl
- forms.midiUrl = data.midiUrl
- forms.metronomeUrl = data.metronomeUrl
- forms.musicSheetName = data.musicSheetName
- forms.musicTag = data.musicTag?.split(',')
- forms.composer = data.composer
- forms.playSpeed = data.playSpeed
- forms.showFingering = Number(data.showFingering)
- forms.canEvaluate = Number(data.canEvaluate)
- forms.musicSubject = data.musicSubject ? Number(data.musicSubject) : null
- forms.notation = Number(data.notation)
- forms.auditVersion = Number(data.auditVersion)
- forms.accompanimentType = data.accompanimentType
- forms.sortNumber = data.sortNumber
- forms.titleImg = data.titleImg
- forms.remark = data.remark
- forms.status = data.status
- forms.musicSheetCategoriesId = data.musicSheetCategoriesId
- forms.background = data.background || []
- forms.musicSheetType = data.musicSheetType || "SINGLE"
- // 获取渐变 和 是否多声部
- try {
- const extConfigJson = data.extConfigJson ? JSON.parse(data.extConfigJson) : {}
- forms.graduals = extConfigJson.gradualTimes || {}
- } catch (error) {
- }
- axios.get(data.xmlFileUrl).then((res: any) => {
- if (res?.data) {
- gradualData.list = getGradualLengthByXml(res?.data as any).filter(
- (item: any) => item.length === 2
- )
- state.partListNames = getPartListNames(res?.data as any) as any
- }
- })
- } catch (error) {
- console.log(error)
- }
- }
- })
- return () => (
- <div style="background: #fff; padding-top: 12px">
- <NForm
- class={styles.formContainer}
- model={forms}
- ref={formsRef}
- label-placement="left"
- label-width="130"
- >
- <NAlert showIcon={false} style={{marginBottom:"12px" }}>曲目信息</NAlert>
- <NGrid cols={2}>
- <NFormItemGi
- label="曲目名称"
- path="musicSheetName"
- rule={[
- {
- required: true,
- message: '请输入曲目名称'
- }
- ]}
- >
- <NInput v-model:value={forms.musicSheetName} placeholder="请输入曲目名称"/>
- </NFormItemGi>
- <NFormItemGi
- label="音乐人"
- path="author"
- rule={[
- {
- required: true,
- message: '请输入音乐人'
- }
- ]}
- >
- <NInput v-model:value={forms.author} placeholder="请输入音乐人"
- showCount
- maxlength={14}
- />
- </NFormItemGi>
- </NGrid>
- <NGrid cols={2}>
- <NFormItemGi label="曲目描述" path="remark">
- <NInput
- placeholder="请输入曲目描述"
- type="textarea"
- rows={4}
- showCount
- maxlength={200}
- v-model:value={forms.remark}
- />
- </NFormItemGi>
- <NFormItemGi label="曲谱封面" path="titleImg"
- rule={[
- {
- required: true
- }
- ]}>
- <UploadFile
- accept=".jpg,.jpeg,.png"
- tips="请上传大小2M以内的JPG、PNG图片"
- v-model:fileList={forms.titleImg}
- cropper
- bucketName="cloud-coach"
- options={{
- autoCrop: true, //是否默认生成截图框
- enlarge: 2, // 图片放大倍数
- autoCropWidth: 200, //默框高度
- fixedBox: true, //是否固定截图框大认生成截图框宽度
- autoCropHeight: 200, //默认生成截图小 不允许改变
- previewsCircle: false, //预览图是否是原圆形
- title: '曲谱封面'
- }}
- />
- </NFormItemGi>
- </NGrid>
- <NGrid cols={2}>
- <NFormItemGi
- label="曲目类型"
- path="musicSheetType"
- rule={[
- {
- required: true,
- message: '请选择曲目类型'
- }
- ]}
- >
- <NRadioGroup v-model:value={forms.musicSheetType}>
- <NRadio value="SINGLE">独奏</NRadio>
- <NRadio value="CONCERT">合奏</NRadio>
- </NRadioGroup>
- </NFormItemGi>
- <NFormItemGi
- label="作者属性"
- path="authorFrom"
- rule={[
- {
- required: true,
- message: '请选择作者属性'
- }
- ]}
- >
- <NSelect
- v-model:value={forms.authorFrom}
- options={
- [
- {
- label: '平台',
- value: 'PALTFORM'
- },
- {
- label: '机构',
- value: 'TENANT'
- },
- {
- label: '个人',
- value: 'SELF'
- }
- ] as any
- }
- filterable
- clearable
- placeholder="请选择作者属性"
- />
- </NFormItemGi>
- </NGrid>
- <NGrid cols={2}>
- <NFormItemGi
- label="所属人"
- path="belongTo"
- rule={[
- {
- required: true,
- message: '请选择曲目所属人'
- }
- ]}
- >
- <NSelect
- v-model:value={forms.belongTo}
- options={
- [
- {
- label: '小A',
- value: '小A'
- }
- ] as any
- }
- filterable
- clearable
- placeholder="请选择曲目所属人"
- />
- </NFormItemGi>
- <NFormItemGi label="速度" path="speed">
- <NInputNumber
- placeholder="请输入速度"
- v-model:value={forms.speed}
- />
- </NFormItemGi>
- </NGrid>
- <NGrid cols={2}>
- <NFormItemGi label="审核版本" path="speed">
- <NSelect
- options={getSelectDataFromObj(releaseCandidate)}
- v-model:value={forms.releaseCandidate}
- defaultValue={'NO'}
- />
- </NFormItemGi>
- </NGrid>
- <NAlert showIcon={false} style={{marginBottom:"12px" }}>曲目上传</NAlert>
- <NGrid cols={2}>
- <NFormItemGi label="播放模式" path="audioType"
- rule={[
- {
- required: true,
- message: '请选择播放模式'
- }
- ]}
- >
- <NRadioGroup
- v-model:value={forms.audioType}
- onUpdateValue={(value: string | number | boolean) => {
- if (value === 'MP3') {
- forms.mp3Type = 'MP3'
- } else {
- forms.mp3Type = 'MIDI'
- }
- }}
- >
- <NRadio value="MP3">MP3</NRadio>
- <NRadio value="MIDI">MIDI</NRadio>
- </NRadioGroup>
- </NFormItemGi>
- {forms.mp3Type === 'MP3' && (
- <NFormItemGi
- label="伴奏类型"
- path="audioType"
- rule={[
- {
- required: true
- }
- ]}
- >
- <NRadioGroup
- v-model:value={forms.accompanimentType}
- >
- <NRadio value={1}>自制伴奏</NRadio>
- <NRadio value={2}>普通伴奏</NRadio>
- </NRadioGroup>
- </NFormItemGi>
- )}
- </NGrid>
- <NGrid cols={2}>
- {forms.mp3Type === 'MP3' && (
- <NFormItemGi
- label="上传伴奏"
- path="xmlFileUrl"
- rule={[
- {
- required: true,
- message: '请选择上传.mp3/.aac'
- }
- ]}
- >
- <UploadFile
- size={10}
- v-model:fileList={forms.xmlFileUrl}
- tips="仅支持上传.mp3/.aac格式文件"
- listType="image"
- accept=".mp3,.aac"
- bucketName="cloud-coach"
- text="点击上传伴奏文件"
- onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
- />
- </NFormItemGi>
- )}
- {forms.mp3Type === 'MIDI' && (
- <NFormItemGi
- label="上传MIDI"
- path="xmlFileUrl"
- rule={[
- {
- required: true,
- message: '请选择上传.MIDI格式文件'
- }
- ]}
- >
- <UploadFile
- size={10}
- v-model:fileList={forms.xmlFileUrl}
- tips="仅支持上传.MIDI格式文件"
- listType="image"
- accept=".mp3,.aac"
- bucketName="cloud-coach"
- text="点击上传MIDI文件"
- onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
- />
- </NFormItemGi>
- )}
- <NFormItemGi
- label="上传XML"
- path="xmlFileUrl"
- rule={[
- {
- required: true,
- message: '请选择上传XML'
- }
- ]}
- >
- <UploadFile
- size={10}
- v-model:fileList={forms.xmlFileUrl}
- tips="仅支持上传.xml格式文件"
- listType="image"
- accept=".xml"
- bucketName="cloud-coach"
- text="点击上传XML文件"
- onReadFileInputEventAsArrayBuffer={readFileInputEventAsArrayBuffer}
- />
- </NFormItemGi>
- </NGrid>
- <NGrid cols={2}>
- <NFormItemGi label="可用声部" path="musicSubject"
- rule={[
- {
- required: true
- }
- ]}
- >
- <NSelect
- v-model:value={forms.musicSubject}
- options={props.subjectList}
- multiple
- filterable
- clearable
- placeholder="请选择可用声部"
- />
- </NFormItemGi>
- { (forms.mp3Type === 'MP3' || forms.musicSheetType === 'SINGLE') && (
- <NFormItemGi
- label="页面渲染声轨"
- path="audioType"
- rule={[
- {
- required: true,
- message: '请选择页面渲染声轨'
- }
- ]}
- >
- <CheckboxGroup>
- <NCheckbox value="长笛">长笛</NCheckbox>
- <NCheckbox value="竖笛">竖笛</NCheckbox>
- <NCheckbox value="葫芦丝">葫芦丝</NCheckbox>
- <NCheckbox value="萨克斯">萨克斯</NCheckbox>
- </CheckboxGroup>
- </NFormItemGi>
- )}
- {forms.mp3Type === 'MIDI' && forms.musicSheetType === 'CONCERT' && (
- <NFormItemGi
- label="用户可切换声轨"
- path="audioType"
- rule={[
- {
- required: true,
- message: '请选择用户可切换声轨'
- }
- ]}
- >
- <CheckboxGroup>
- <NCheckbox value="长笛">长笛</NCheckbox>
- <NCheckbox value="竖笛">竖笛</NCheckbox>
- <NCheckbox value="葫芦丝">葫芦丝</NCheckbox>
- <NCheckbox value="萨克斯">萨克斯</NCheckbox>
- </CheckboxGroup>
- </NFormItemGi>
- )}
- </NGrid>
- <NGrid cols={2}>
- <NFormItemGi label="是否播放节拍器" path="audioType"
- rule={[
- {
- required: true,
- message: '请选择是否播放节拍器'
- }
- ]}
- >
- <NRadioGroup
- v-model:value={forms.playMetronome}
- >
- <NRadio value={1}>是</NRadio>
- <NRadio value={0}>否</NRadio>
- </NRadioGroup>
- </NFormItemGi>
- {forms.playMetronome && (
- <NFormItemGi label="播放方式" path="audioType"
- rule={[
- {
- required: true,
- message: '请选择播放方式'
- }
- ]}
- >
- <NRadioGroup
- v-model:value={forms.playStyle}
- >
- <NRadio value="MP3">系统节拍器</NRadio>
- <NRadio value="MIDI">MP3节拍器</NRadio>
- </NRadioGroup>
- </NFormItemGi>
- )}
- </NGrid>
- {/* 只有播放类型为mp3时才会有原音 */}
- {forms.audioType === 'MP3' && (
- <>
- {forms.background.map((item: any, index: number) => (
- <>
- {item.track?.toLocaleUpperCase?.() != 'COMMON' && <NGrid class={styles.audioSection}>
- <NFormItemGi
- span={12}
- label="原音"
- path={`background[${index}].audioFileUrl`}
- rule={[
- {
- required: true,
- message: `请上传${
- item.track ? item.track + '的' : '第' + (index + 1) + '个'
- }原音`
- }
- ]}
- >
- <UploadFile
- size={10}
- v-model:fileList={item.audioFileUrl}
- tips="仅支持上传.mp3/.aac格式文件"
- listType="image"
- accept=".mp3,.aac"
- bucketName="cloud-coach"
- />
- </NFormItemGi>
- {state.partListNames.length > 1 && (
- <NFormItemGi
- span={12}
- label="所属轨道"
- path={`background[${index}].track`}
- rule={[
- {
- required: true,
- message: '请选择所属轨道'
- }
- ]}
- >
- <NSelect
- placeholder="请选择所属轨道"
- v-model:value={item.track}
- options={initPartsListStatus(item.track)}
- />
- </NFormItemGi>
- )}
- <NGi class={styles.btnRemove}>
- <NButton
- type="primary"
- text
- disabled={forms.background.length === 1}
- onClick={() => removeSys(index)}
- >
- 删除
- </NButton>
- </NGi>
- </NGrid>}
- </>
- ))}
- <NButton
- type="primary"
- dashed
- block
- disabled={state.partListNames.length <= forms.background.length}
- style={{
- marginBottom: '24px'
- }}
- onClick={createSys}
- >
- 添加原音
- </NButton>
- </>
- )}
- </NForm>
- {props.type !== 'preview' &&
- (
- <NSpace justify="end">
- <NButton type="default" onClick={() => emit('close')}>
- 取消
- </NButton>
- <NButton
- type="primary"
- onClick={() => onSubmit()}
- loading={btnLoading.value}
- disabled={btnLoading.value}
- >
- 确认
- </NButton>
- </NSpace>
- )}
- </div>
- )
- }
- })
|