import { defineComponent } from 'vue' import { Button, Field, Sticky, Form, Tag, Radio, RadioGroup, Popup, Icon, Empty, Picker, Toast, NoticeBar } from 'vant' import ColFieldGroup from '@/components/col-field-group' // import { MusicType } from 'src/teacher/music/list/item.d' import SubjectModel from '@/business-components/subject-list' import ColField from '@/components/col-field' import { teachercanEvaluateType, teacherChargeType, teachershowAudiType, teachershowFingeringType, teachershowHasBeatType, teacherNotationType, teacherStyleType } from '@/constant/music' import { getXmlInfo, FormatXMLInfo } from '@/helpers/music-xml' import Upload from './upload' import styles from './index.module.less' import SelectTag from '@/views/music/search/select-tag' import { browser } from '@/helpers/utils' import { postMessage } from '@/helpers/native-message' import { teacherState } from '@/teacher/teacher-cert/teacherState' import request from '@/helpers/request' import requestOrigin from 'umi-request' import UploadIcon from './upload.svg' import ColUpload from '@/components/col-upload' import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate' export type BackgroundMp3 = { url?: string track?: string } // 校验函数返回 true 表示校验通过,false 表示不通过 export const validator = val => { console.log(val) if (Number(val) <= 0) { return '收费金额必须大于0' } else { return true } } export default defineComponent({ name: 'MusicUpload', data() { return { reason: '', audioType: 'MP3', xmlFileUrl: '', xmlFileLoading: false, midiUrl: '', midiLoading: false, mp3Url: '', bgmp3Url: '', mp3Loading: false, bgmp3Loading: false, musicSheetName: '', composer: '', speed: '', hasBeat: 0, titleImg: '', accompanimentType: 'HOMEMODE', chargeType: 0, showFingering: 1, canEvaluate: 1, notation: 0, musicPrice: '', subJectIndex: 0, selectTagVisible: false, subJectVisible: false, tags: [] as string[], tagsNames: [] as Array<{ [id in string]: string }>, formated: {} as FormatXMLInfo, tagVisibility: false, subjectListres: [] as any[], subjectListNames: {} as any, selectedSubjectList: null as any, vlewSubjectList: null as any, submitLoading: false, showPicker: false, music_sheet_service_fee: 0, backgroundMp3s: [ { url: '', track: '' } ] as BackgroundMp3[] } }, watch: { formated() { this.mergeXmlData(this.formated) }, chargeType() { if (this.chargeType === 0) { this.musicPrice = '' } } }, computed: { choiceSubjectIds() { // 选择的科目编号 let ids = teacherState.teacherCert.subjectId ? teacherState.teacherCert.subjectId.split(',') : [] ids = ids.map((item: any) => Number(item)) return ids }, subjectList() { // 学科列表 const subjects: any = this.subjectListres || [] return subjects }, choiceSubject() { // 选择的科目 const tempArr: any[] = [] this.subjectList.forEach((parent: any) => { parent.subjects && parent.subjects.forEach((sub: any) => { if (this.choiceSubjectIds.includes(sub.id)) { tempArr.push(sub as never) } }) }) return tempArr } }, async mounted() { request .get('/api-teacher/sysConfig/queryByParamName', { params: { paramName: 'music_sheet_service_fee' } }) .then(res => (this.music_sheet_service_fee = res.data.paramValue)) // if (teacherState.subjectList.length <= 0) { await request.get('/api-teacher/subject/subjectSelect').then(res => { const list: any[] = [] for (const item of res.data || []) { const slist: any[] = (item as any).subjects || [] list.push(...slist) } this.subjectListres = list this.subjectListNames = this.getSubjectListNames(list) }) if (this.$route.params.id) { this.setDetail(this.$route.params.id as string) } // } }, methods: { async setDetail(id: string) { try { const res = await request.get('/api-teacher/music/sheet/detail/' + id) this.chargeType = res.data.chargeType === 'FREE' ? 0 : 2 this.showFingering = res.data.showFingering this.canEvaluate = res.data.canEvaluate if (this.chargeType) { this.musicPrice = res.data.musicPrice } this.composer = res.data.composer this.musicSheetName = res.data.musicSheetName this.audioType = res.data.audioType this.notation = res.data.notation this.selectedSubjectList = { label: res.data.musicSubject, value: res.data.subjectNames } this.vlewSubjectList = { label: res.data.musicSubject, value: res.data.subjectNames } this.subJectIndex = Object.keys(this.subjectListNames).findIndex( key => key === res.data.musicSubject ) const names = res.data.musicTagNames.split(',') this.tags = res.data.musicTag.split(',') this.tags = this.tags.filter((el: any) => { return el != '' }) for (let i = 0; i < names.length; i++) { this.tagsNames[this.tags[i]] = names[i] } this.xmlFileUrl = res.data.xmlFileUrl this.accompanimentType = res.data.accompanimentType this.titleImg = res.data.titleImg // this.audioType = res.data.mp3Type if (this.audioType === 'MP3') { this.hasBeat = (res.data.audioType === 'MP3' && res.data.mp3Type === 'MP3_METRONOME') || res.data.audioType === 'MIDI' ? 1 : 0 this.mp3Url = res.data.audioFileUrl || res.data.url //res.data.metronomeUrl || res.data.url } else { this.midiUrl = res.data.midiUrl } this.backgroundMp3s = (res.data.background || []).map((item, index) => { if (index === 0) { this.bgmp3Url = item.audioFileUrl } return { url: item.audioFileUrl, track: item.track } }) this.reason = res.data.reason console.log(this.bgmp3Url) } catch (error) { console.log(error) } }, createSubmitData() { const beatType = this.hasBeat ? 'MP3_METRONOME' : 'MP3' const mp3Type = this.audioType === 'MP3' ? beatType : 'MIDI' return { audioType: this.audioType, sourceType: 'TEACHER', mp3Type, hasBeat: Number(this.hasBeat), accompanimentType: this.accompanimentType, titleImg: this.titleImg, url: this.hasBeat ? '' : this.mp3Url, metronomeUrl: this.hasBeat ? this.mp3Url : '', audioFileUrl: this.mp3Url, showFingering: Number(this.showFingering), musicTag: this.tags.join(','), musicSubject: Number(this.selectedSubjectList?.label) || undefined, musicSheetName: this.musicSheetName, midiUrl: this.midiUrl, notation: Number(this.notation), xmlFileUrl: this.xmlFileUrl, canEvaluate: Number(this.canEvaluate), chargeType: this.chargeType === 0 ? 'FREE' : 'CHARGE', composer: this.composer, musicPrice: this.chargeType === 0 ? 0 : this.musicPrice, // 当选择免费时,重置金额为0 background: this.backgroundMp3s.map(item => ({ audioFileUrl: this.bgmp3Url, track: item.track // metronomeUrl: this.hasBeat ? this.bgmp3Url : '' })) } }, async submit(vals: any) { console.log(vals) this.submitLoading = true try { if (this.$route.params.id) { await request.post('/api-teacher/music/sheet/update', { data: { ...this.createSubmitData(), id: this.$route.params.id } }) } else { await request.post('/api-teacher/music/sheet/create', { data: this.createSubmitData() }) } } catch (error) {} this.submitLoading = false Toast('上传成功') setTimeout(() => { postMessage({ api: 'back' }) }, 800) console.log(vals) }, onFormatter(val: any) { return verifyNumberIntegerAndFloat(val) }, getSubjectListNames(list) { const data = {} for (const item of list) { data[item.id] = item.name if (item.subjects) { for (const sub of item.subjects) { data[sub.id] = sub.name } } } return data }, failed() { console.log('failed', this.backgroundMp3s) }, mergeXmlData(data: FormatXMLInfo) { this.formated = data // this.backgroundMp3s = data.partNames.map((partName: string) => ({ // track: partName // })) if (!this.musicSheetName) { this.musicSheetName = data.title } if (!this.composer) { this.composer = data.composer } // if (!this.speed && data.speed) { // this.speed = '' + data.speed // } }, readerFile(file: File) { const reader = new FileReader() reader.onload = () => { const xml = reader.result as string this.formated = getXmlInfo(xml) } reader.readAsText(file) }, onChoice(val: any) { this.subJectVisible = false this.selectedSubjectList = [val] }, onComfirm(tags: any, names: any) { this.tagsNames = names this.tagVisibility = false const data = Object.values(tags).flat().filter(Boolean) as string[] console.log(data) this.tags = data }, naiveXMLFile() { this.xmlFileLoading = true postMessage( { api: 'chooseFile', content: { type: 'xml', bucket: 'cloud-coach' } }, evt => { // @ts-ignore this.xmlFileUrl = evt?.fileUrl || this.xmlFileUrl || '' this.xmlFileLoading = false if (this.xmlFileUrl) { requestOrigin(this.xmlFileUrl).then( res => (this.formated = getXmlInfo(res)) ) } } ) }, naiveMidFile() { this.midiLoading = true postMessage( { api: 'chooseFile', content: { type: 'midi', bucket: 'cloud-coach' } }, evt => { // @ts-ignore this.midiUrl = evt?.fileUrl || this.midiUrl || '' this.midiLoading = false // this.midiUrl = path } ) }, naiveMp3File() { this.mp3Loading = true postMessage( { api: 'chooseFile', content: { type: 'mp3', bucket: 'cloud-coach' } }, evt => { // @ts-ignore this.mp3Url = evt?.fileUrl || this.mp3Url || '' this.mp3Loading = false // this.midiUrl = path } ) }, naiveBGMp3File() { this.bgmp3Loading = true postMessage( { api: 'chooseFile', content: { type: 'mp3', bucket: 'cloud-coach' } }, evt => { this.bgmp3Url // @ts-ignore this.bgmp3Url = evt?.fileUrl || this.bgmp3Url || '' this.bgmp3Loading = false // this.midiUrl = path } ) }, fileName(name = '') { return name.split('/').pop() }, removeBackground(index: number) { this.backgroundMp3s.splice(index, 1) }, onDetail(type: string) { let url = `${location.origin}/teacher/#/registerProtocol` if (type === 'question') { url = `${location.origin}/teacher/muic-standard/question.html` } else if (type === 'music') { url = `${location.origin}/teacher/muic-standard/index.html` } postMessage({ api: 'openWebView', content: { url, orientation: 1, isHideTitle: false } }) } }, render() { console.log(this.formated) const browserInfo = browser() return (
{this.reason && ( )}
注意事项:
1、必须是上传人自己参与制作的作品。
2、歌曲及歌曲信息中请勿涉及政治、宗教、广告、涉毒、犯罪、色情、低俗、暴力、血腥、消极等违规内容,违反者直接删除内容。多次违反将封号。
3、点击查看{' '} this.onDetail('protocol')}> 《用户注册协议》 ,如果您上传了文件,即认为您完全同意并遵守该协议的内容;
browserInfo.isApp ? ( ) : ( <> (this.xmlFileUrl = val)} accept=".xml" formatFile={this.readerFile} />
{this.fileName(this.xmlFileUrl)}
) }} />
曲谱审核标准:
1、文件大小不要超过5MB,不符合版面规范的乐谱,审核未通过的不予上架,详情参考 this.onDetail('music')}> 《曲谱排版规范》 ; 1、必须是上传人自己参与制作的作品。
2、XML与MIDI文件内容必须一致,推荐使用Sibelius打谱软件。导出设置:导出XML-未压缩(*.xml)/导出MIDI:音色-其他回放设备General MIDI、MIDI、MIDI文件类型-类型0、不要勾选“将弱拍小节导出为具有休止符的完整小节”。点击查看 this.onDetail('question')}> 《常见问题》
(this.audioType = val)} > {Object.keys(teachershowAudiType).map((item: string) => { const isActive = item === this.audioType const type = isActive ? 'primary' : 'default' return ( {teachershowAudiType[item]} ) })} {this.audioType === 'MP3' ? ( <> (this.hasBeat = val)} > {Object.keys(teachershowHasBeatType).map((item: string) => { const isActive = item === String(this.hasBeat) const type = isActive ? 'primary' : 'default' return ( {teachershowHasBeatType[item]} ) })} (this.accompanimentType = val)} > {Object.keys(teacherStyleType).map((item: string) => { const isActive = item === String(this.accompanimentType) const type = isActive ? 'primary' : 'default' return ( {teacherStyleType[item]} ) })} browserInfo.isApp ? ( ) : ( <> (this.mp3Url = val)} accept=".mp3" />
{this.fileName(this.mp3Url)}
) }} />
) : ( browserInfo.isApp ? ( ) : ( <> (this.midiUrl = val)} accept=".mid,.midi" />
{this.fileName(this.midiUrl)}
) }} />
)}
1、推荐上传自制伴奏,伴奏和谱面必须对齐。自制伴奏可以设置更高的收费标准。
2、普通伴奏如果涉及到版权纠纷,根据 this.onDetail('protocol')}> 《用户注册协议》 平台有权进行下架处理。
{this.audioType === 'MP3' && this.backgroundMp3s.map((item, index) => ( this.backgroundMp3s.length > 1 ? ( ) : null }} > browserInfo.isApp ? ( ) : ( <> (this.bgmp3Url = val)} accept=".mp3" />
{this.fileName(this.bgmp3Url)}
) }} />
))} (this.musicSheetName = val)} />
1、同一首曲目不可重复上传,如有不同版本统一用“()”补充。举例:人生的旋转木马(长笛二重奏版)。
2、曲目名后可添加曲目信息备注,包含但不限于曲目类型等。曲目名《xxxx》,举例:人生的旋转木马《哈尔的移动城堡》(长笛二重奏版)
3、其他信息不要写在曲目名里,如歌手、上传人员昵称等。
(this.composer = val)} /> {/* (this.speed = val)} class={styles['clear-px']} placeholder="请输入默认速度" /> */} (this.selectedSubjectList = )} onClick={() => (this.showPicker = true)} >
XML文件中,选择的曲目声部需要在总谱的置顶位置。
( ) }} > this.tags.length > 0 ? ( this.tags.map((item: any) => ( {this.tagsNames[item]} )) ) : ( ) }} /> (this.canEvaluate = val)} > {Object.keys(teachercanEvaluateType).map((item: string) => { const isActive = item === String(this.canEvaluate) const type = isActive ? 'primary' : 'default' return ( {teachercanEvaluateType[item]} ) })} (this.showFingering = val)} > {Object.keys(teachershowFingeringType).map((item: string) => { const isActive = item === String(this.showFingering) const type = isActive ? 'primary' : 'default' return ( {teachershowFingeringType[item]} ) })} { this.chargeType = Number(val) }} > {Object.keys(teacherChargeType).map((item: string) => { const isActive = item === String(this.chargeType) const type = isActive ? 'primary' : 'default' return ( {teacherChargeType[item]} ) })} { this.notation = Number(val) }} > {Object.keys(teacherNotationType).map((item: string) => { const isActive = item === String(this.notation) const type = isActive ? 'primary' : 'default' return ( {teacherNotationType[item]} ) })} {this.chargeType === 2 && ( '元' }} modelValue={this.musicPrice} rules={[ { required: true, validator, message: '请输入收费价格' } ]} onUpdate:modelValue={val => (this.musicPrice = val)} /> )} {this.chargeType === 2 && (

扣除手续费后该曲目预计收入为:

每人: {( ((parseFloat(this.musicPrice || '0') || 0) * (100 - this.music_sheet_service_fee)) / 100 ).toFixed(2)} 元/人

您的乐谱收入将在学员购买后结算到您的账户中

)}
(this.showPicker = val)} > ({ label: key, value }) )} onCancel={() => (this.showPicker = false)} onConfirm={val => { this.selectedSubjectList = val this.vlewSubjectList = val this.showPicker = false }} /> (this.subJectVisible = val)} > (this.tagVisibility = val)} > {}} rowSingle defaultValue={this.tags.join(',')} needAllButton={false} /> ) } })