index copy.tsx 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  1. import { defineComponent } from 'vue'
  2. import {
  3. Button,
  4. Field,
  5. Sticky,
  6. Form,
  7. Tag,
  8. Radio,
  9. RadioGroup,
  10. Popup,
  11. Icon,
  12. Empty,
  13. Picker,
  14. Toast,
  15. NoticeBar
  16. } from 'vant'
  17. import ColFieldGroup from '@/components/col-field-group'
  18. // import { MusicType } from 'src/teacher/music/list/item.d'
  19. import SubjectModel from '@/business-components/subject-list'
  20. import ColField from '@/components/col-field'
  21. import {
  22. teachercanEvaluateType,
  23. teacherChargeType,
  24. teachershowAudiType,
  25. teachershowFingeringType,
  26. teachershowHasBeatType,
  27. teacherNotationType,
  28. teacherStyleType,
  29. teacherExquisiteType
  30. } from '@/constant/music'
  31. import { getXmlInfo, FormatXMLInfo } from '@/helpers/music-xml'
  32. import Upload from './upload'
  33. import styles from './index.module.less'
  34. import SelectTag from '@/views/music/search/select-tag'
  35. import { browser } from '@/helpers/utils'
  36. import { postMessage } from '@/helpers/native-message'
  37. import { teacherState } from '@/teacher/teacher-cert/teacherState'
  38. import request from '@/helpers/request'
  39. import requestOrigin from 'umi-request'
  40. import UploadIcon from './upload.svg'
  41. import ColUpload from '@/components/col-upload'
  42. import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate'
  43. import { state } from '@/state'
  44. import ColHeader from '@/components/col-header'
  45. export type BackgroundMp3 = {
  46. url?: string
  47. track?: string
  48. }
  49. // 校验函数返回 true 表示校验通过,false 表示不通过
  50. export const validator = val => {
  51. console.log(val)
  52. if (Number(val) <= 0) {
  53. return '收费金额必须大于0'
  54. } else {
  55. return true
  56. }
  57. }
  58. export default defineComponent({
  59. name: 'MusicUpload',
  60. data() {
  61. return {
  62. reason: '',
  63. audioType: 'MP3',
  64. xmlFileUrl: '',
  65. xmlFileLoading: false,
  66. midiUrl: '',
  67. midiLoading: false,
  68. mp3Url: '',
  69. bgmp3Url: '',
  70. mp3Loading: false,
  71. bgmp3Loading: false,
  72. musicSheetName: '',
  73. composer: '',
  74. speed: '',
  75. hasBeat: 0,
  76. titleImg: '',
  77. accompanimentType: 'HOMEMODE',
  78. chargeType: 0,
  79. paymentType: '',
  80. showFingering: 1,
  81. canEvaluate: 1,
  82. notation: 1,
  83. musicPrice: '',
  84. subJectIndex: 0,
  85. selectTagVisible: false,
  86. subJectVisible: false,
  87. tags: [] as string[],
  88. tagsNames: [] as Array<{ [id in string]: string }>,
  89. formated: {} as FormatXMLInfo,
  90. tagVisibility: false,
  91. subjectListres: [] as any[],
  92. subjectListNames: {} as any,
  93. selectedSubjectList: null as any,
  94. vlewSubjectList: null as any,
  95. submitLoading: false,
  96. showPicker: false,
  97. music_sheet_service_fee: 0,
  98. music_account_period: 0,
  99. exquisiteFlag: 0,
  100. backgroundMp3s: [
  101. {
  102. url: '',
  103. track: ''
  104. }
  105. ] as BackgroundMp3[],
  106. checked: false
  107. }
  108. },
  109. watch: {
  110. formated() {
  111. this.mergeXmlData(this.formated)
  112. },
  113. chargeType() {
  114. if (this.chargeType === 0) {
  115. this.musicPrice = ''
  116. this.paymentType = ''
  117. }
  118. }
  119. },
  120. computed: {
  121. choiceSubjectIds() {
  122. // 选择的科目编号
  123. let ids = teacherState.teacherCert.subjectId
  124. ? teacherState.teacherCert.subjectId.split(',')
  125. : []
  126. ids = ids.map((item: any) => Number(item))
  127. return ids
  128. },
  129. subjectList() {
  130. // 学科列表
  131. const subjects: any = this.subjectListres || []
  132. return subjects
  133. },
  134. choiceSubject() {
  135. // 选择的科目
  136. const tempArr: any[] = []
  137. this.subjectList.forEach((parent: any) => {
  138. parent.subjects &&
  139. parent.subjects.forEach((sub: any) => {
  140. if (this.choiceSubjectIds.includes(sub.id)) {
  141. tempArr.push(sub as never)
  142. }
  143. })
  144. })
  145. return tempArr
  146. }
  147. },
  148. async mounted() {
  149. // 获取基础数据
  150. request
  151. .get('/api-teacher/sysConfig/queryByParamNameList', {
  152. params: {
  153. paramNames: 'music_sheet_service_fee,music_account_period'
  154. }
  155. })
  156. .then((res: any) => {
  157. console.log(res, 'res')
  158. const data = res.data || []
  159. data.forEach((item: any) => {
  160. if (item.paramName === 'music_sheet_service_fee') {
  161. this.music_sheet_service_fee = item.paramValue
  162. } else if (item.paramName === 'music_account_period') {
  163. this.music_account_period = item.paramValue
  164. }
  165. })
  166. })
  167. // console.log(config, 'config')
  168. // request
  169. // .get('/api-teacher/sysConfig/queryByParamName', {
  170. // params: {
  171. // paramName: 'music_sheet_service_fee'
  172. // }
  173. // })
  174. // .then(res => (this.music_sheet_service_fee = res.data.paramValue))
  175. // if (teacherState.subjectList.length <= 0) {
  176. await request.get('/api-teacher/subject/subjectSelect').then(res => {
  177. const list: any[] = []
  178. for (const item of res.data || []) {
  179. const slist: any[] = (item as any).subjects || []
  180. list.push(...slist)
  181. }
  182. this.subjectListres = list
  183. this.subjectListNames = this.getSubjectListNames(list)
  184. })
  185. if (this.$route.params.id) {
  186. this.setDetail(this.$route.params.id as string)
  187. }
  188. const resVersion = await request.post('/api-teacher/open/appVersion', {
  189. data: {
  190. platform:
  191. state.platformType === 'STUDENT' ? 'ios-student' : 'ios-teacher',
  192. version: state.version
  193. }
  194. })
  195. this.checked = resVersion.data.check ? true : false
  196. // 审核版本金额默认为0
  197. if (this.checked) {
  198. this.chargeType = 0
  199. }
  200. // }
  201. },
  202. methods: {
  203. async setDetail(id: string) {
  204. try {
  205. const res = await request.get('/api-teacher/music/sheet/detail/' + id)
  206. this.chargeType = res.data.paymentType === 'FREE' ? 0 : 2
  207. this.paymentType = res.data.paymentType
  208. this.showFingering = res.data.showFingering
  209. this.canEvaluate = res.data.canEvaluate
  210. if (this.chargeType) {
  211. this.musicPrice = res.data.musicPrice
  212. }
  213. this.composer = res.data.composer
  214. this.musicSheetName = res.data.musicSheetName
  215. this.audioType = res.data.audioType
  216. this.notation = res.data.notation
  217. this.selectedSubjectList = {
  218. label: res.data.musicSubject,
  219. value: res.data.subjectNames
  220. }
  221. this.vlewSubjectList = {
  222. label: res.data.musicSubject,
  223. value: res.data.subjectNames
  224. }
  225. this.subJectIndex = Object.keys(this.subjectListNames).findIndex(
  226. key => key === res.data.musicSubject
  227. )
  228. const names = res.data.musicTagNames.split(',')
  229. this.tags = res.data.musicTag.split(',')
  230. this.tags = this.tags.filter((el: any) => {
  231. return el != ''
  232. })
  233. for (let i = 0; i < names.length; i++) {
  234. this.tagsNames[this.tags[i]] = names[i]
  235. }
  236. this.exquisiteFlag = res.data.exquisiteFlag
  237. this.xmlFileUrl = res.data.xmlFileUrl
  238. this.accompanimentType = res.data.accompanimentType
  239. this.titleImg = res.data.titleImg
  240. // this.audioType = res.data.mp3Type
  241. if (this.audioType === 'MP3') {
  242. this.hasBeat =
  243. (res.data.audioType === 'MP3' &&
  244. res.data.mp3Type === 'MP3_METRONOME') ||
  245. res.data.audioType === 'MIDI'
  246. ? 1
  247. : 0
  248. this.mp3Url = res.data.audioFileUrl || res.data.url //res.data.metronomeUrl || res.data.url
  249. } else {
  250. this.midiUrl = res.data.midiUrl
  251. }
  252. this.backgroundMp3s = (res.data.background || []).map((item, index) => {
  253. if (index === 0) {
  254. this.bgmp3Url = item.audioFileUrl
  255. }
  256. return {
  257. url: item.audioFileUrl,
  258. track: item.track
  259. }
  260. })
  261. this.reason = res.data.reason
  262. console.log(this.bgmp3Url)
  263. } catch (error) {
  264. console.log(error)
  265. }
  266. },
  267. createSubmitData() {
  268. const beatType = this.hasBeat ? 'MP3_METRONOME' : 'MP3'
  269. const mp3Type = this.audioType === 'MP3' ? beatType : 'MIDI'
  270. return {
  271. audioType: this.audioType,
  272. sourceType: 'TEACHER',
  273. mp3Type,
  274. hasBeat: Number(this.hasBeat),
  275. accompanimentType: this.accompanimentType,
  276. titleImg: this.titleImg,
  277. url: this.hasBeat ? '' : this.mp3Url,
  278. metronomeUrl: this.hasBeat ? this.mp3Url : '',
  279. audioFileUrl: this.mp3Url,
  280. showFingering: Number(this.showFingering),
  281. musicTag: this.tags.join(','),
  282. musicSubject: Number(this.selectedSubjectList?.label) || undefined,
  283. musicSheetName: this.musicSheetName,
  284. midiUrl: this.midiUrl,
  285. notation: Number(this.notation),
  286. xmlFileUrl: this.xmlFileUrl,
  287. canEvaluate: Number(this.canEvaluate),
  288. chargeType: this.chargeType === 0 ? 'FREE' : 'CHARGE',
  289. paymentType: this.chargeType === 0 ? 'FREE' : 'CHARGE',
  290. exquisiteFlag: this.exquisiteFlag,
  291. composer: this.composer,
  292. musicPrice: this.chargeType === 0 ? 0 : this.musicPrice, // 当选择免费时,重置金额为0
  293. background: this.backgroundMp3s.map(item => ({
  294. audioFileUrl: this.bgmp3Url,
  295. track: item.track
  296. // metronomeUrl: this.hasBeat ? this.bgmp3Url : ''
  297. }))
  298. }
  299. },
  300. async submit(vals: any) {
  301. console.log(vals)
  302. this.submitLoading = true
  303. try {
  304. if (this.$route.params.id) {
  305. await request.post('/api-teacher/music/sheet/update', {
  306. data: {
  307. ...this.createSubmitData(),
  308. id: this.$route.params.id
  309. }
  310. })
  311. } else {
  312. await request.post('/api-teacher/music/sheet/create', {
  313. data: this.createSubmitData()
  314. })
  315. }
  316. } catch (error) {}
  317. Toast('上传成功')
  318. setTimeout(() => {
  319. postMessage({
  320. api: 'back'
  321. })
  322. this.submitLoading = false
  323. }, 800)
  324. console.log(vals)
  325. },
  326. onFormatter(val: any) {
  327. return verifyNumberIntegerAndFloat(val)
  328. },
  329. getSubjectListNames(list) {
  330. const data = {}
  331. for (const item of list) {
  332. data[item.id] = item.name
  333. if (item.subjects) {
  334. for (const sub of item.subjects) {
  335. data[sub.id] = sub.name
  336. }
  337. }
  338. }
  339. return data
  340. },
  341. failed() {
  342. console.log('failed', this.backgroundMp3s)
  343. },
  344. mergeXmlData(data: FormatXMLInfo) {
  345. this.formated = data
  346. // this.backgroundMp3s = data.partNames.map((partName: string) => ({
  347. // track: partName
  348. // }))
  349. if (!this.musicSheetName) {
  350. this.musicSheetName = data.title
  351. }
  352. if (!this.composer) {
  353. this.composer = data.composer
  354. }
  355. // if (!this.speed && data.speed) {
  356. // this.speed = '' + data.speed
  357. // }
  358. },
  359. readerFile(file: File) {
  360. const reader = new FileReader()
  361. reader.onload = () => {
  362. const xml = reader.result as string
  363. this.formated = getXmlInfo(xml)
  364. }
  365. reader.readAsText(file)
  366. },
  367. onChoice(val: any) {
  368. this.subJectVisible = false
  369. this.selectedSubjectList = [val]
  370. },
  371. onComfirm(tags: any, names: any) {
  372. this.tagsNames = names
  373. this.tagVisibility = false
  374. const data = Object.values(tags).flat().filter(Boolean) as string[]
  375. console.log(data)
  376. this.tags = data
  377. },
  378. naiveXMLFile() {
  379. this.xmlFileLoading = true
  380. postMessage(
  381. { api: 'chooseFile', content: { type: 'xml', bucket: 'cloud-coach' } },
  382. evt => {
  383. // @ts-ignore
  384. this.xmlFileUrl = evt?.fileUrl || this.xmlFileUrl || ''
  385. this.xmlFileLoading = false
  386. if (this.xmlFileUrl) {
  387. requestOrigin(this.xmlFileUrl).then(
  388. res => (this.formated = getXmlInfo(res))
  389. )
  390. }
  391. }
  392. )
  393. },
  394. naiveMidFile() {
  395. this.midiLoading = true
  396. postMessage(
  397. { api: 'chooseFile', content: { type: 'midi', bucket: 'cloud-coach' } },
  398. evt => {
  399. // @ts-ignore
  400. this.midiUrl = evt?.fileUrl || this.midiUrl || ''
  401. this.midiLoading = false
  402. // this.midiUrl = path
  403. }
  404. )
  405. },
  406. naiveMp3File() {
  407. this.mp3Loading = true
  408. postMessage(
  409. { api: 'chooseFile', content: { type: 'mp3', bucket: 'cloud-coach' } },
  410. evt => {
  411. // @ts-ignore
  412. this.mp3Url = evt?.fileUrl || this.mp3Url || ''
  413. this.mp3Loading = false
  414. // this.midiUrl = path
  415. }
  416. )
  417. },
  418. naiveBGMp3File() {
  419. this.bgmp3Loading = true
  420. postMessage(
  421. { api: 'chooseFile', content: { type: 'mp3', bucket: 'cloud-coach' } },
  422. evt => {
  423. this.bgmp3Url
  424. // @ts-ignore
  425. this.bgmp3Url = evt?.fileUrl || this.bgmp3Url || ''
  426. this.bgmp3Loading = false
  427. // this.midiUrl = path
  428. }
  429. )
  430. },
  431. fileName(name = '') {
  432. return name.split('/').pop()
  433. },
  434. removeBackground(index: number) {
  435. this.backgroundMp3s.splice(index, 1)
  436. },
  437. onDetail(type: string) {
  438. let url = `${location.origin}/teacher/#/registerProtocol`
  439. if (type === 'question') {
  440. url = `${location.origin}/teacher/muic-standard/question.html`
  441. } else if (type === 'music') {
  442. url = `${location.origin}/teacher/muic-standard/index.html`
  443. }
  444. postMessage({
  445. api: 'openWebView',
  446. content: {
  447. url,
  448. orientation: 1,
  449. isHideTitle: false
  450. }
  451. })
  452. }
  453. },
  454. render() {
  455. console.log(this.formated)
  456. const browserInfo = browser()
  457. return (
  458. <Form class={styles.form} onSubmit={this.submit} onFailed={this.failed}>
  459. <ColHeader />
  460. {this.reason && (
  461. <NoticeBar wrapable scrollable={false} text={this.reason} />
  462. )}
  463. <div class={styles.container}>
  464. <div class={styles.tips}>
  465. <div class={styles.tipsTitle}>注意事项:</div>
  466. <div class={styles.tipsContent}>
  467. 1、必须是上传人自己参与制作的作品。
  468. <br />
  469. 2、歌曲及歌曲信息中请勿涉及政治、宗教、广告、涉毒、犯罪、色情、低俗、暴力、血腥、消极等违规内容,违反者直接删除内容。多次违反将封号。
  470. <br />
  471. 3、点击查看
  472. <span onClick={() => this.onDetail('protocol')}>
  473. 《用户注册协议》
  474. </span>
  475. ,如果您上传了文件,即认为您完全同意并遵守该协议的内容;
  476. </div>
  477. </div>
  478. <ColFieldGroup class={styles.area}>
  479. <ColField border={false} required title="MusicXML文件">
  480. <Field
  481. name="xmlFileUrl"
  482. modelValue={this.xmlFileUrl}
  483. rules={[{ required: true, message: '请选择MusicXML文件' }]}
  484. // @ts-ignore
  485. vSlots={{
  486. input: () =>
  487. browserInfo.isApp ? (
  488. <Button
  489. icon={UploadIcon}
  490. class={styles.upbtn}
  491. onClick={this.naiveXMLFile}
  492. loading={this.xmlFileLoading}
  493. >
  494. {this.xmlFileUrl
  495. ? this.fileName(this.xmlFileUrl)
  496. : '上传文件'}
  497. </Button>
  498. ) : (
  499. <>
  500. <Upload
  501. onUpdate:modelValue={val => (this.xmlFileUrl = val)}
  502. accept=".xml"
  503. formatFile={this.readerFile}
  504. />
  505. <div style={{ marginLeft: '8px' }}>
  506. {this.fileName(this.xmlFileUrl)}
  507. </div>
  508. </>
  509. )
  510. }}
  511. />
  512. </ColField>
  513. </ColFieldGroup>
  514. <div class={styles.tips}>
  515. <div class={styles.tipsTitle}>曲谱审核标准:</div>
  516. <div class={styles.tipsContent}>
  517. 1、文件大小不要超过5MB,不符合版面规范的乐谱,审核未通过的不予上架,详情参考
  518. <span onClick={() => this.onDetail('music')}>
  519. 《曲谱排版规范》
  520. </span>
  521. ; 1、必须是上传人自己参与制作的作品。
  522. <br />
  523. 2、XML与MIDI文件内容必须一致,推荐使用Sibelius打谱软件。导出设置:导出XML-未压缩(*.xml)/导出MIDI:音色-其他回放设备General
  524. MIDI、MIDI、MIDI文件类型-类型0、不要勾选“将弱拍小节导出为具有休止符的完整小节”。点击查看
  525. <span onClick={() => this.onDetail('question')}>
  526. 《常见问题》
  527. </span>
  528. </div>
  529. </div>
  530. <ColFieldGroup class={styles.area}>
  531. <ColField required title="播放类型" border={false}>
  532. <RadioGroup
  533. class={styles['radio-group']}
  534. modelValue={this.audioType}
  535. onUpdate:modelValue={val => (this.audioType = val)}
  536. >
  537. {Object.keys(teachershowAudiType).map((item: string) => {
  538. const isActive = item === this.audioType
  539. const type = isActive ? 'primary' : 'default'
  540. return (
  541. <Radio class={styles.radio} name={item}>
  542. <Tag size="large" plain={isActive} type={type}>
  543. {teachershowAudiType[item]}
  544. </Tag>
  545. </Radio>
  546. )
  547. })}
  548. </RadioGroup>
  549. </ColField>
  550. {this.audioType === 'MP3' ? (
  551. <>
  552. {/* <ColField required title="是否带节拍器" border={false}>
  553. <RadioGroup
  554. class={styles['radio-group']}
  555. modelValue={this.hasBeat}
  556. onUpdate:modelValue={val => (this.hasBeat = val)}
  557. >
  558. {Object.keys(teachershowHasBeatType).map((item: string) => {
  559. const isActive = item === String(this.hasBeat)
  560. const type = isActive ? 'primary' : 'default'
  561. return (
  562. <Radio class={styles.radio} name={item}>
  563. <Tag size="large" plain={isActive} type={type}>
  564. {teachershowHasBeatType[item]}
  565. </Tag>
  566. </Radio>
  567. )
  568. })}
  569. </RadioGroup>
  570. </ColField> */}
  571. <ColField required title="伴奏类型" border={false}>
  572. <RadioGroup
  573. class={styles['radio-group']}
  574. modelValue={this.accompanimentType}
  575. onUpdate:modelValue={val => (this.accompanimentType = val)}
  576. >
  577. {Object.keys(teacherStyleType).map((item: string) => {
  578. const isActive = item === String(this.accompanimentType)
  579. const type = isActive ? 'primary' : 'default'
  580. return (
  581. <Radio class={styles.radio} name={item}>
  582. <Tag size="large" plain={isActive} type={type}>
  583. {teacherStyleType[item]}
  584. </Tag>
  585. </Radio>
  586. )
  587. })}
  588. </RadioGroup>
  589. </ColField>
  590. <ColField border={false} title="伴奏文件">
  591. <Field
  592. name="mp3Url"
  593. modelValue={this.mp3Url}
  594. // @ts-ignore
  595. vSlots={{
  596. input: () =>
  597. browserInfo.isApp ? (
  598. <Button
  599. icon={UploadIcon}
  600. class={styles.upbtn}
  601. onClick={this.naiveMp3File}
  602. loading={this.mp3Loading}
  603. >
  604. {this.mp3Url
  605. ? this.fileName(this.mp3Url)
  606. : '上传文件'}
  607. </Button>
  608. ) : (
  609. <>
  610. <Upload
  611. onUpdate:modelValue={val => (this.mp3Url = val)}
  612. accept=".mp3"
  613. />
  614. <div style={{ marginLeft: '8px' }}>
  615. {this.fileName(this.mp3Url)}
  616. </div>
  617. </>
  618. )
  619. }}
  620. />
  621. </ColField>
  622. </>
  623. ) : (
  624. <ColField border={false} required title="MIDI文件">
  625. <Field
  626. name="midiUrl"
  627. modelValue={this.midiUrl}
  628. rules={[{ required: true, message: '请选择MIDI文件' }]}
  629. // @ts-ignore
  630. vSlots={{
  631. input: () =>
  632. browserInfo.isApp ? (
  633. <Button
  634. icon={UploadIcon}
  635. class={styles.upbtn}
  636. onClick={this.naiveMidFile}
  637. loading={this.midiLoading}
  638. >
  639. {this.midiUrl
  640. ? this.fileName(this.midiUrl)
  641. : '上传文件'}
  642. </Button>
  643. ) : (
  644. <>
  645. <Upload
  646. onUpdate:modelValue={val => (this.midiUrl = val)}
  647. accept=".mid,.midi"
  648. />
  649. <div style={{ marginLeft: '8px' }}>
  650. {this.fileName(this.midiUrl)}
  651. </div>
  652. </>
  653. )
  654. }}
  655. />
  656. </ColField>
  657. )}
  658. </ColFieldGroup>
  659. <div class={styles.tips}>
  660. <div class={styles.tipsContent}>
  661. 1、推荐上传自制伴奏,伴奏和谱面必须对齐。自制伴奏可以设置更高的收费标准。
  662. <br />
  663. 2、普通伴奏如果涉及到版权纠纷,根据
  664. <span onClick={() => this.onDetail('protocol')}>
  665. 《用户注册协议》
  666. </span>
  667. 平台有权进行下架处理。
  668. </div>
  669. </div>
  670. <ColFieldGroup class={styles.area}>
  671. {this.audioType === 'MP3' &&
  672. this.backgroundMp3s.map((item, index) => (
  673. <ColField
  674. required
  675. border={false}
  676. title={(item.track || '') + '原音文件'}
  677. // @ts-ignore
  678. vSlots={{
  679. right: () =>
  680. this.backgroundMp3s.length > 1 ? (
  681. <Button
  682. onClick={() => this.removeBackground(index)}
  683. style={{ border: 'none' }}
  684. icon="cross"
  685. ></Button>
  686. ) : null
  687. }}
  688. >
  689. <Field
  690. name="url"
  691. modelValue={this.bgmp3Url}
  692. // @ts-ignore
  693. vSlots={{
  694. input: () =>
  695. browserInfo.isApp ? (
  696. <Button
  697. icon={UploadIcon}
  698. class={styles.upbtn}
  699. onClick={this.naiveBGMp3File}
  700. loading={this.bgmp3Loading}
  701. >
  702. {this.bgmp3Url
  703. ? this.fileName(this.bgmp3Url)
  704. : '上传文件'}
  705. </Button>
  706. ) : (
  707. <>
  708. <Upload
  709. onUpdate:modelValue={val => (this.bgmp3Url = val)}
  710. accept=".mp3"
  711. />
  712. <div style={{ marginLeft: '8px' }}>
  713. {this.fileName(this.bgmp3Url)}
  714. </div>
  715. </>
  716. )
  717. }}
  718. />
  719. </ColField>
  720. ))}
  721. <ColField required title="曲目名称">
  722. <Field
  723. clearable
  724. name="musicSheetName"
  725. modelValue={this.musicSheetName}
  726. rules={[{ required: true, message: '请输入曲目名称' }]}
  727. class={styles['clear-px']}
  728. placeholder="请输入曲目名称"
  729. onUpdate:modelValue={val => (this.musicSheetName = val)}
  730. />
  731. </ColField>
  732. </ColFieldGroup>
  733. <div class={styles.tips}>
  734. <div class={styles.tipsContent}>
  735. 1、同一首曲目不可重复上传,如有不同版本统一用“()”补充。举例:人生的旋转木马(长笛二重奏版)。
  736. <br />
  737. 2、曲目名后可添加曲目信息备注,包含但不限于曲目类型等。曲目名《xxxx》,举例:人生的旋转木马《哈尔的移动城堡》(长笛二重奏版)
  738. <br />
  739. 3、其他信息不要写在曲目名里,如歌手、上传人员昵称等。
  740. </div>
  741. </div>
  742. <ColFieldGroup class={styles.area}>
  743. <ColField border={false} required title="曲谱封面">
  744. <ColUpload
  745. cropper
  746. bucket="cloud-coach"
  747. options={{
  748. autoCropWidth: 600,
  749. autoCropHeight: 600
  750. }}
  751. v-model={this.titleImg}
  752. class={styles.imgContainer}
  753. />
  754. </ColField>
  755. </ColFieldGroup>
  756. <ColFieldGroup class={styles.area}>
  757. <ColField required title="艺术家">
  758. <Field
  759. clearable
  760. class={styles['clear-px']}
  761. placeholder="请输入艺术家姓名"
  762. name="composer"
  763. modelValue={this.composer}
  764. rules={[{ required: true, message: '请输入艺术家姓名' }]}
  765. onUpdate:modelValue={val => (this.composer = val)}
  766. />
  767. </ColField>
  768. {/* <ColField required title="默认速度">
  769. <Field
  770. clearable
  771. name="playSpeed"
  772. modelValue={this.speed}
  773. rules={[{ required: true, message: '请输入默认速度' }]}
  774. onUpdate:modelValue={val => (this.speed = val)}
  775. class={styles['clear-px']}
  776. placeholder="请输入默认速度"
  777. />
  778. </ColField> */}
  779. <ColField required title="曲目声部">
  780. <Field
  781. is-link
  782. readonly
  783. class={styles['clear-px']}
  784. placeholder="请选择曲目声部"
  785. name="vlewSubjectList"
  786. modelValue={this.vlewSubjectList?.value}
  787. rules={[{ required: true, message: '请选择曲目声部' }]}
  788. // onUpdate:modelValue={val => (this.selectedSubjectList = )}
  789. onClick={() => (this.showPicker = true)}
  790. ></Field>
  791. </ColField>
  792. </ColFieldGroup>
  793. <div class={styles.tips}>
  794. <div class={styles.tipsContent}>
  795. XML文件中,选择的曲目声部需要在总谱的置顶位置。
  796. </div>
  797. </div>
  798. <ColFieldGroup class={styles.area}>
  799. <ColField
  800. border={false}
  801. required
  802. title="曲目标签"
  803. v-slots={{
  804. right: () => (
  805. <Button
  806. class={styles.select}
  807. round
  808. type="primary"
  809. size="small"
  810. onClick={() => (this.tagVisibility = true)}
  811. >
  812. 选择
  813. </Button>
  814. )
  815. }}
  816. >
  817. <Field
  818. name="tags"
  819. modelValue={this.tags.length ? 1 : undefined}
  820. rules={[{ required: true, message: '请选择曲目标签' }]}
  821. // @ts-ignore
  822. vSlots={{
  823. input: () =>
  824. this.tags.length > 0 ? (
  825. this.tags.map((item: any) => (
  826. <Tag type="primary" size="large" class={styles.tags}>
  827. {this.tagsNames[item]}
  828. </Tag>
  829. ))
  830. ) : (
  831. <Empty
  832. style={{ width: '100%' }}
  833. description="请选择曲目标签"
  834. imageSize={0}
  835. />
  836. )
  837. }}
  838. />
  839. </ColField>
  840. </ColFieldGroup>
  841. <ColFieldGroup class={styles.area}>
  842. {/* <ColField required title="是否评测" border={false}>
  843. <RadioGroup
  844. class={styles['radio-group']}
  845. modelValue={this.canEvaluate}
  846. onUpdate:modelValue={val => (this.canEvaluate = val)}
  847. >
  848. {Object.keys(teachercanEvaluateType).map((item: string) => {
  849. const isActive = item === String(this.canEvaluate)
  850. const type = isActive ? 'primary' : 'default'
  851. return (
  852. <Radio class={styles.radio} name={item}>
  853. <Tag size="large" plain={isActive} type={type}>
  854. {teachercanEvaluateType[item]}
  855. </Tag>
  856. </Radio>
  857. )
  858. })}
  859. </RadioGroup>
  860. </ColField> */}
  861. {/* <ColField required title="指法展示" border={false}>
  862. <RadioGroup
  863. class={styles['radio-group']}
  864. modelValue={this.showFingering}
  865. onUpdate:modelValue={val => (this.showFingering = val)}
  866. >
  867. {Object.keys(teachershowFingeringType).map((item: string) => {
  868. const isActive = item === String(this.showFingering)
  869. const type = isActive ? 'primary' : 'default'
  870. return (
  871. <Radio class={styles.radio} name={item}>
  872. <Tag size="large" plain={isActive} type={type}>
  873. {teachershowFingeringType[item]}
  874. </Tag>
  875. </Radio>
  876. )
  877. })}
  878. </RadioGroup>
  879. </ColField> */}
  880. {/* 判断是否是审核版本 */}
  881. {!this.checked && (
  882. <ColField required title="是否收费" border={false}>
  883. <RadioGroup
  884. class={styles['radio-group']}
  885. modelValue={this.chargeType}
  886. onUpdate:modelValue={val => {
  887. this.chargeType = Number(val)
  888. }}
  889. >
  890. {Object.keys(teacherChargeType).map((item: string) => {
  891. const isActive = item === String(this.chargeType)
  892. const type = isActive ? 'primary' : 'default'
  893. return (
  894. <Radio class={styles.radio} name={item}>
  895. <Tag size="large" plain={isActive} type={type}>
  896. {teacherChargeType[item]}
  897. </Tag>
  898. </Radio>
  899. )
  900. })}
  901. </RadioGroup>
  902. </ColField>
  903. )}
  904. {this.chargeType === 2 && (
  905. <ColField required title="收费价格">
  906. <Field
  907. clearable
  908. class={styles['clear-px']}
  909. placeholder="请输入收费价格"
  910. formatter={this.onFormatter}
  911. v-slots={{ button: () => '元' }}
  912. modelValue={this.musicPrice}
  913. rules={[
  914. { required: true, validator, message: '请输入收费价格' }
  915. ]}
  916. onUpdate:modelValue={val => (this.musicPrice = val)}
  917. />
  918. </ColField>
  919. )}
  920. {/* <ColField required title="支持简谱" border={false}>
  921. <RadioGroup
  922. class={styles['radio-group']}
  923. modelValue={this.notation}
  924. onUpdate:modelValue={val => {
  925. this.notation = Number(val)
  926. }}
  927. >
  928. {Object.keys(teacherNotationType).map((item: string) => {
  929. const isActive = item === String(this.notation)
  930. const type = isActive ? 'primary' : 'default'
  931. return (
  932. <Radio class={styles.radio} name={item}>
  933. <Tag size="large" plain={isActive} type={type}>
  934. {teacherNotationType[item]}
  935. </Tag>
  936. </Radio>
  937. )
  938. })}
  939. </RadioGroup>
  940. </ColField> */}
  941. <ColField required title="是否精品乐谱" border={false}>
  942. <RadioGroup
  943. class={styles['radio-group']}
  944. modelValue={this.exquisiteFlag}
  945. onUpdate:modelValue={val => {
  946. this.exquisiteFlag = Number(val)
  947. }}
  948. >
  949. {Object.keys(teacherExquisiteType).map((item: string) => {
  950. const isActive = item === String(this.exquisiteFlag)
  951. const type = isActive ? 'primary' : 'default'
  952. return (
  953. <Radio class={styles.radio} name={item}>
  954. <Tag size="large" plain={isActive} type={type}>
  955. {teacherExquisiteType[item]}
  956. </Tag>
  957. </Radio>
  958. )
  959. })}
  960. </RadioGroup>
  961. </ColField>
  962. </ColFieldGroup>
  963. {this.chargeType === 2 && (
  964. <div class={styles.rule}>
  965. <p>扣除手续费后该曲目预计收入为:</p>
  966. <p>
  967. 每人:
  968. <span>
  969. {(
  970. ((parseFloat(this.musicPrice || '0') || 0) *
  971. (100 - this.music_sheet_service_fee)) /
  972. 100
  973. ).toFixed(2)}
  974. </span>
  975. 元/人
  976. </p>
  977. <p>
  978. 您的乐谱收入在学员购买后{this.music_account_period}
  979. 天结算到您的账户中
  980. </p>
  981. </div>
  982. )}
  983. </div>
  984. <Sticky offsetBottom={0} position="bottom">
  985. <div class={styles['button-area']}>
  986. <Button
  987. type="primary"
  988. block
  989. round
  990. native-type="submit"
  991. loading={this.submitLoading}
  992. >
  993. 确认
  994. </Button>
  995. </div>
  996. </Sticky>
  997. <Popup
  998. show={this.showPicker}
  999. round
  1000. position="bottom"
  1001. teleport="body"
  1002. onUpdate:show={val => (this.showPicker = val)}
  1003. >
  1004. <Picker
  1005. defaultIndex={this.subJectIndex}
  1006. columnsFieldNames={{
  1007. text: 'value'
  1008. }}
  1009. columns={Object.entries(this.subjectListNames).map(
  1010. ([key, value]) => ({ label: key, value })
  1011. )}
  1012. onCancel={() => (this.showPicker = false)}
  1013. onConfirm={val => {
  1014. this.selectedSubjectList = val
  1015. this.vlewSubjectList = val
  1016. this.showPicker = false
  1017. }}
  1018. />
  1019. </Popup>
  1020. <Popup
  1021. show={this.subJectVisible}
  1022. round
  1023. closeable
  1024. position="bottom"
  1025. style={{ height: '60%' }}
  1026. teleport="body"
  1027. onUpdate:show={val => (this.subJectVisible = val)}
  1028. >
  1029. <SubjectModel
  1030. subjectList={this.subjectList}
  1031. choiceSubjectIds={this.choiceSubjectIds}
  1032. onChoice={this.onChoice}
  1033. selectType="Radio"
  1034. />
  1035. </Popup>
  1036. <Popup
  1037. show={this.tagVisibility}
  1038. round
  1039. closeable
  1040. position="bottom"
  1041. style={{ height: '60%' }}
  1042. teleport="body"
  1043. onUpdate:show={val => (this.tagVisibility = val)}
  1044. >
  1045. <SelectTag
  1046. onConfirm={this.onComfirm}
  1047. onCancel={() => {}}
  1048. rowSingle
  1049. defaultValue={this.tags.join(',')}
  1050. needAllButton={false}
  1051. />
  1052. </Popup>
  1053. </Form>
  1054. )
  1055. }
  1056. })