index.tsx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  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. } from '@/constant/music'
  29. import { getXmlInfo, FormatXMLInfo } from '@/helpers/music-xml'
  30. import Upload from './upload'
  31. import styles from './index.module.less'
  32. import SelectTag from '@/student/music/search/select-tag'
  33. import { browser } from '@/helpers/utils'
  34. import { postMessage } from '@/helpers/native-message'
  35. import { teacherState } from '@/teacher/teacher-cert/teacherState'
  36. import request from '@/helpers/request'
  37. import requestOrigin from 'umi-request'
  38. import UploadIcon from './upload.svg'
  39. export type BackgroundMp3 = {
  40. url?: string
  41. track?: string
  42. }
  43. export default defineComponent({
  44. name: 'MusicUpload',
  45. data() {
  46. return {
  47. reason: '',
  48. audioType: 'MP3',
  49. xmlFileUrl: '',
  50. xmlFileLoading: false,
  51. midiUrl: '',
  52. midiLoading: false,
  53. mp3Url: '',
  54. bgmp3Url: '',
  55. mp3Loading: false,
  56. bgmp3Loading: false,
  57. musicSheetName: '',
  58. composer: '',
  59. speed: '',
  60. hasBeat: 0,
  61. chargeType: 0,
  62. showFingering: 1,
  63. canEvaluate: 1,
  64. notation: 0,
  65. musicPrice: '',
  66. subJectIndex: 0,
  67. selectTagVisible: false,
  68. subJectVisible: false,
  69. tags: [] as string[],
  70. tagsNames: [] as Array<{ [id in string]: string }>,
  71. formated: {} as FormatXMLInfo,
  72. tagVisibility: false,
  73. subjectListres: [] as any[],
  74. subjectListNames: {} as any,
  75. selectedSubjectList: null as any,
  76. vlewSubjectList: null as any,
  77. submitLoading: false,
  78. showPicker: false,
  79. music_sheet_service_fee: 0,
  80. backgroundMp3s: [
  81. {
  82. url: '',
  83. track: ''
  84. }
  85. ] as BackgroundMp3[]
  86. }
  87. },
  88. watch: {
  89. formated() {
  90. this.mergeXmlData(this.formated)
  91. },
  92. chargeType() {
  93. if (this.chargeType === 0) {
  94. this.musicPrice = ''
  95. }
  96. }
  97. },
  98. computed: {
  99. choiceSubjectIds() {
  100. // 选择的科目编号
  101. let ids = teacherState.teacherCert.subjectId
  102. ? teacherState.teacherCert.subjectId.split(',')
  103. : []
  104. ids = ids.map((item: any) => Number(item))
  105. return ids
  106. },
  107. subjectList() {
  108. // 学科列表
  109. const subjects: any = this.subjectListres || []
  110. return subjects
  111. },
  112. choiceSubject() {
  113. // 选择的科目
  114. const tempArr: any[] = []
  115. this.subjectList.forEach((parent: any) => {
  116. parent.subjects &&
  117. parent.subjects.forEach((sub: any) => {
  118. if (this.choiceSubjectIds.includes(sub.id)) {
  119. tempArr.push(sub as never)
  120. }
  121. })
  122. })
  123. return tempArr
  124. }
  125. },
  126. async mounted() {
  127. request
  128. .get('/api-teacher/sysConfig/queryByParamName', {
  129. params: {
  130. paramName: 'music_sheet_service_fee'
  131. }
  132. })
  133. .then(res => (this.music_sheet_service_fee = res.data.paramValue))
  134. // if (teacherState.subjectList.length <= 0) {
  135. await request.get('/api-teacher/subject/subjectSelect').then(res => {
  136. const list: any[] = []
  137. for (const item of res.data || []) {
  138. const slist: any[] = (item as any).subjects || []
  139. list.push(...slist)
  140. }
  141. this.subjectListres = list
  142. this.subjectListNames = this.getSubjectListNames(list)
  143. })
  144. if (this.$route.params.id) {
  145. this.setDetail(this.$route.params.id as string)
  146. }
  147. // }
  148. },
  149. methods: {
  150. async setDetail(id: string) {
  151. try {
  152. const res = await request.get('/api-teacher/music/sheet/detail/' + id)
  153. this.chargeType = res.data.chargeType === 'FREE' ? 0 : 2
  154. this.showFingering = res.data.showFingering
  155. this.canEvaluate = res.data.canEvaluate
  156. if (this.chargeType) {
  157. this.musicPrice = res.data.musicPrice
  158. }
  159. this.composer = res.data.composer
  160. this.musicSheetName = res.data.musicSheetName
  161. this.audioType = res.data.audioType
  162. this.notation = res.data.notation
  163. this.selectedSubjectList = {
  164. label: res.data.musicSubject,
  165. value: res.data.subjectNames
  166. }
  167. this.vlewSubjectList = {
  168. label: res.data.musicSubject,
  169. value: res.data.subjectNames
  170. }
  171. this.subJectIndex = Object.keys(this.subjectListNames).findIndex(
  172. key => key === res.data.musicSubject
  173. )
  174. const names = res.data.musicTagNames.split(',')
  175. this.tags = res.data.musicTag.split(',')
  176. for (let i = 0; i < names.length; i++) {
  177. this.tagsNames[this.tags[i]] = names[i]
  178. }
  179. this.xmlFileUrl = res.data.xmlFileUrl
  180. // this.audioType = res.data.mp3Type
  181. if (this.audioType === 'MP3') {
  182. this.hasBeat =
  183. (res.data.audioType === 'MP3' &&
  184. res.data.mp3Type === 'MP3_METRONOME') ||
  185. res.data.audioType === 'MIDI'
  186. ? 1
  187. : 0
  188. this.mp3Url = res.data.audioFileUrl //res.data.metronomeUrl || res.data.url
  189. } else {
  190. this.midiUrl = res.data.midiUrl
  191. }
  192. this.backgroundMp3s = (res.data.background || []).map((item, index) => {
  193. if (index === 0) {
  194. this.bgmp3Url = item.audioFileUrl
  195. }
  196. return {
  197. url: item.audioFileUrl,
  198. track: item.track
  199. }
  200. })
  201. this.reason = res.data.reason
  202. console.log(this.bgmp3Url)
  203. } catch (error) {
  204. console.log(error)
  205. }
  206. },
  207. createSubmitData() {
  208. const beatType = this.hasBeat ? 'MP3_METRONOME' : 'MP3'
  209. const mp3Type = this.audioType === 'MP3' ? beatType : 'MIDI'
  210. return {
  211. audioType: this.audioType,
  212. sourceType: 'TEACHER',
  213. mp3Type,
  214. hasBeat: this.hasBeat,
  215. // url: this.hasBeat ? '' : this.mp3Url,
  216. // metronomeUrl: this.hasBeat ? this.mp3Url : '',
  217. audioFileUrl: this.mp3Url,
  218. showFingering: Number(this.showFingering),
  219. musicTag: this.tags.join(','),
  220. musicSubject: Number(this.selectedSubjectList?.label) || undefined,
  221. musicSheetName: this.musicSheetName,
  222. midiUrl: this.midiUrl,
  223. notation: Number(this.notation),
  224. xmlFileUrl: this.xmlFileUrl,
  225. canEvaluate: Number(this.canEvaluate),
  226. chargeType: this.chargeType === 0 ? 'FREE' : 'CHARGE',
  227. composer: this.composer,
  228. musicPrice: this.musicPrice,
  229. background: this.backgroundMp3s.map(item => ({
  230. audioFileUrl: this.bgmp3Url,
  231. track: item.track
  232. // metronomeUrl: this.hasBeat ? this.bgmp3Url : ''
  233. }))
  234. }
  235. },
  236. async submit(vals: any) {
  237. this.submitLoading = true
  238. try {
  239. if (this.$route.params.id) {
  240. await request.post('/api-teacher/music/sheet/update', {
  241. data: {
  242. ...this.createSubmitData(),
  243. id: this.$route.params.id
  244. }
  245. })
  246. } else {
  247. await request.post('/api-teacher/music/sheet/create', {
  248. data: this.createSubmitData()
  249. })
  250. }
  251. } catch (error) {}
  252. this.submitLoading = false
  253. Toast('上传成功')
  254. setTimeout(() => {
  255. postMessage({
  256. api: 'back'
  257. })
  258. }, 800)
  259. console.log(vals)
  260. },
  261. getSubjectListNames(list) {
  262. const data = {}
  263. for (const item of list) {
  264. data[item.id] = item.name
  265. if (item.subjects) {
  266. for (const sub of item.subjects) {
  267. data[sub.id] = sub.name
  268. }
  269. }
  270. }
  271. return data
  272. },
  273. failed() {
  274. console.log('failed', this.backgroundMp3s)
  275. },
  276. mergeXmlData(data: FormatXMLInfo) {
  277. this.formated = data
  278. // this.backgroundMp3s = data.partNames.map((partName: string) => ({
  279. // track: partName
  280. // }))
  281. if (!this.musicSheetName) {
  282. this.musicSheetName = data.title
  283. }
  284. if (!this.composer) {
  285. this.composer = data.composer
  286. }
  287. // if (!this.speed && data.speed) {
  288. // this.speed = '' + data.speed
  289. // }
  290. },
  291. readerFile(file: File) {
  292. const reader = new FileReader()
  293. reader.onload = () => {
  294. const xml = reader.result as string
  295. this.formated = getXmlInfo(xml)
  296. }
  297. reader.readAsText(file)
  298. },
  299. onChoice(val: any) {
  300. this.subJectVisible = false
  301. this.selectedSubjectList = [val]
  302. },
  303. onComfirm(tags: any, names: any) {
  304. this.tagsNames = names
  305. this.tagVisibility = false
  306. const data = Object.values(tags).flat().filter(Boolean) as string[]
  307. console.log(data)
  308. this.tags = data
  309. },
  310. naiveXMLFile() {
  311. this.xmlFileLoading = true
  312. postMessage(
  313. { api: 'chooseFile', content: { type: 'xml', bucket: 'cloud-coach' } },
  314. evt => {
  315. // @ts-ignore
  316. this.xmlFileUrl = evt?.fileUrl || this.xmlFileUrl || ''
  317. this.xmlFileLoading = false
  318. if (this.xmlFileUrl) {
  319. requestOrigin(this.xmlFileUrl).then(
  320. res => (this.formated = getXmlInfo(res))
  321. )
  322. }
  323. }
  324. )
  325. },
  326. naiveMidFile() {
  327. this.midiLoading = true
  328. postMessage(
  329. { api: 'chooseFile', content: { type: 'midi', bucket: 'cloud-coach' } },
  330. evt => {
  331. // @ts-ignore
  332. this.midiUrl = evt?.fileUrl || this.midiUrl || ''
  333. this.midiLoading = false
  334. // this.midiUrl = path
  335. }
  336. )
  337. },
  338. naiveMp3File() {
  339. this.mp3Loading = true
  340. postMessage(
  341. { api: 'chooseFile', content: { type: 'mp3', bucket: 'cloud-coach' } },
  342. evt => {
  343. // @ts-ignore
  344. this.mp3Url = evt?.fileUrl || this.mp3Url || ''
  345. this.mp3Loading = false
  346. // this.midiUrl = path
  347. }
  348. )
  349. },
  350. naiveBGMp3File() {
  351. this.bgmp3Loading = true
  352. postMessage(
  353. { api: 'chooseFile', content: { type: 'mp3', bucket: 'cloud-coach' } },
  354. evt => {
  355. this.bgmp3Url
  356. // @ts-ignore
  357. this.bgmp3Url = evt?.fileUrl || this.bgmp3Url || ''
  358. this.bgmp3Loading = false
  359. // this.midiUrl = path
  360. }
  361. )
  362. },
  363. fileName(name = '') {
  364. return name.split('/').pop()
  365. },
  366. removeBackground(index: number) {
  367. this.backgroundMp3s.splice(index, 1)
  368. }
  369. },
  370. render() {
  371. console.log(this.formated)
  372. const browserInfo = browser()
  373. return (
  374. <Form class={styles.form} onSubmit={this.submit} onFailed={this.failed}>
  375. {this.reason && (
  376. <NoticeBar wrapable scrollable={false} text={this.reason} />
  377. )}
  378. <div class={styles.container}>
  379. <ColFieldGroup class={styles.area}>
  380. <ColField border={false} required title="MusicXML文件">
  381. <Field
  382. name="xmlFileUrl"
  383. modelValue={this.xmlFileUrl}
  384. rules={[{ required: true, message: '请选择MusicXML文件' }]}
  385. // @ts-ignore
  386. vSlots={{
  387. input: () =>
  388. browserInfo.isApp ? (
  389. <Button
  390. icon={UploadIcon}
  391. class={styles.upbtn}
  392. onClick={this.naiveXMLFile}
  393. loading={this.xmlFileLoading}
  394. >
  395. {this.xmlFileUrl
  396. ? this.fileName(this.xmlFileUrl)
  397. : '上传文件'}
  398. </Button>
  399. ) : (
  400. <Upload
  401. onUpdate:modelValue={val => (this.xmlFileUrl = val)}
  402. accept=".xml"
  403. formatFile={this.readerFile}
  404. />
  405. )
  406. }}
  407. />
  408. </ColField>
  409. {/* <ColField required title="播放类型" border={false}>
  410. <RadioGroup
  411. class={styles['radio-group']}
  412. modelValue={this.audioType}
  413. onUpdate:modelValue={val => (this.audioType = val)}
  414. >
  415. {Object.keys(teachershowAudiType).map((item: string) => {
  416. const isActive = item === this.audioType
  417. const type = isActive ? 'primary' : 'default'
  418. return (
  419. <Radio class={styles.radio} name={item}>
  420. <Tag size="large" plain={isActive} type={type}>
  421. {teachershowAudiType[item]}
  422. </Tag>
  423. </Radio>
  424. )
  425. })}
  426. </RadioGroup>
  427. </ColField> */}
  428. {this.audioType === 'MP3' ? (
  429. <>
  430. <ColField required title="是否带节拍器" border={false}>
  431. <RadioGroup
  432. class={styles['radio-group']}
  433. modelValue={this.hasBeat}
  434. onUpdate:modelValue={val => (this.hasBeat = val)}
  435. >
  436. {Object.keys(teachershowHasBeatType).map((item: string) => {
  437. const isActive = item === String(this.hasBeat)
  438. const type = isActive ? 'primary' : 'default'
  439. return (
  440. <Radio class={styles.radio} name={item}>
  441. <Tag size="large" plain={isActive} type={type}>
  442. {teachershowHasBeatType[item]}
  443. </Tag>
  444. </Radio>
  445. )
  446. })}
  447. </RadioGroup>
  448. </ColField>
  449. <ColField border={false} title="伴奏文件">
  450. <Field
  451. name="mp3Url"
  452. modelValue={this.mp3Url}
  453. // @ts-ignore
  454. vSlots={{
  455. input: () =>
  456. browserInfo.isApp ? (
  457. <Button
  458. icon={UploadIcon}
  459. class={styles.upbtn}
  460. onClick={this.naiveMp3File}
  461. loading={this.mp3Loading}
  462. >
  463. {this.mp3Url
  464. ? this.fileName(this.mp3Url)
  465. : '上传文件'}
  466. </Button>
  467. ) : (
  468. <Upload
  469. onUpdate:modelValue={val => (this.mp3Url = val)}
  470. accept=".mp3"
  471. />
  472. )
  473. }}
  474. />
  475. </ColField>
  476. </>
  477. ) : (
  478. <ColField border={false} required title="MIDI文件">
  479. <Field
  480. name="midiUrl"
  481. modelValue={this.midiUrl}
  482. rules={[{ required: true, message: '请选择MIDI文件' }]}
  483. // @ts-ignore
  484. vSlots={{
  485. input: () =>
  486. browserInfo.isApp ? (
  487. <Button
  488. icon={UploadIcon}
  489. class={styles.upbtn}
  490. onClick={this.naiveMidFile}
  491. loading={this.midiLoading}
  492. >
  493. {this.midiUrl
  494. ? this.fileName(this.midiUrl)
  495. : '上传文件'}
  496. </Button>
  497. ) : (
  498. <Upload
  499. onUpdate:modelValue={val => (this.midiUrl = val)}
  500. accept=".mid"
  501. />
  502. )
  503. }}
  504. />
  505. </ColField>
  506. )}
  507. {this.backgroundMp3s.map((item, index) => (
  508. <ColField
  509. required
  510. border={false}
  511. title={(item.track || '') + '原音文件'}
  512. // @ts-ignore
  513. vSlots={{
  514. right: () =>
  515. this.backgroundMp3s.length > 1 ? (
  516. <Button
  517. onClick={() => this.removeBackground(index)}
  518. style={{ border: 'none' }}
  519. icon="cross"
  520. ></Button>
  521. ) : null
  522. }}
  523. >
  524. <Field
  525. name="url"
  526. modelValue={this.bgmp3Url}
  527. // @ts-ignore
  528. vSlots={{
  529. input: () =>
  530. browserInfo.isApp ? (
  531. <Button
  532. icon={UploadIcon}
  533. class={styles.upbtn}
  534. onClick={this.naiveBGMp3File}
  535. loading={this.bgmp3Loading}
  536. >
  537. {this.bgmp3Url
  538. ? this.fileName(this.bgmp3Url)
  539. : '上传文件'}
  540. </Button>
  541. ) : (
  542. <Upload
  543. onUpdate:modelValue={val => (this.bgmp3Url = val)}
  544. accept=".mp3"
  545. />
  546. )
  547. }}
  548. />
  549. </ColField>
  550. ))}
  551. </ColFieldGroup>
  552. <ColFieldGroup class={styles.area}>
  553. <ColField required title="曲目名称">
  554. <Field
  555. clearable
  556. name="musicSheetName"
  557. modelValue={this.musicSheetName}
  558. rules={[{ required: true, message: '请输入曲目名称' }]}
  559. class={styles['clear-px']}
  560. placeholder="请输入曲目名称"
  561. onUpdate:modelValue={val => (this.musicSheetName = val)}
  562. />
  563. </ColField>
  564. <ColField required title="作曲人">
  565. <Field
  566. clearable
  567. class={styles['clear-px']}
  568. placeholder="请输入作曲人姓名"
  569. name="composer"
  570. modelValue={this.composer}
  571. rules={[{ required: true, message: '请输入作曲人姓名' }]}
  572. onUpdate:modelValue={val => (this.composer = val)}
  573. />
  574. </ColField>
  575. {/* <ColField required title="默认速度">
  576. <Field
  577. clearable
  578. name="playSpeed"
  579. modelValue={this.speed}
  580. rules={[{ required: true, message: '请输入默认速度' }]}
  581. onUpdate:modelValue={val => (this.speed = val)}
  582. class={styles['clear-px']}
  583. placeholder="请输入默认速度"
  584. />
  585. </ColField> */}
  586. <ColField required title="曲目声部">
  587. <Field
  588. is-link
  589. readonly
  590. class={styles['clear-px']}
  591. placeholder="请选择曲目声部"
  592. name="vlewSubjectList"
  593. modelValue={this.vlewSubjectList?.value}
  594. rules={[{ required: true, message: '请选择曲目声部' }]}
  595. // onUpdate:modelValue={val => (this.selectedSubjectList = )}
  596. onClick={() => (this.showPicker = true)}
  597. ></Field>
  598. </ColField>
  599. </ColFieldGroup>
  600. <ColFieldGroup class={styles.area}>
  601. <ColField
  602. border={false}
  603. required
  604. title="曲目标签"
  605. v-slots={{
  606. right: () => (
  607. <Button
  608. class={styles.select}
  609. round
  610. type="primary"
  611. size="small"
  612. onClick={() => (this.tagVisibility = true)}
  613. >
  614. 选择
  615. </Button>
  616. )
  617. }}
  618. >
  619. <Field
  620. name="tags"
  621. modelValue={this.tags.length ? 1 : undefined}
  622. rules={[{ required: true, message: '请选择曲目标签' }]}
  623. // @ts-ignore
  624. vSlots={{
  625. input: () =>
  626. this.tags.length > 0 ? (
  627. this.tags.map((item: any) => (
  628. <Tag type="primary" size="large" class={styles.tags}>
  629. {this.tagsNames[item]}
  630. </Tag>
  631. ))
  632. ) : (
  633. <Empty
  634. style={{ width: '100%' }}
  635. description="请选择曲目标签"
  636. imageSize={0}
  637. />
  638. )
  639. }}
  640. />
  641. </ColField>
  642. </ColFieldGroup>
  643. <ColFieldGroup class={styles.area}>
  644. <ColField required title="是否评测" border={false}>
  645. <RadioGroup
  646. class={styles['radio-group']}
  647. modelValue={this.canEvaluate}
  648. onUpdate:modelValue={val => (this.canEvaluate = val)}
  649. >
  650. {Object.keys(teachercanEvaluateType).map((item: string) => {
  651. const isActive = item === String(this.canEvaluate)
  652. const type = isActive ? 'primary' : 'default'
  653. return (
  654. <Radio class={styles.radio} name={item}>
  655. <Tag size="large" plain={isActive} type={type}>
  656. {teachercanEvaluateType[item]}
  657. </Tag>
  658. </Radio>
  659. )
  660. })}
  661. </RadioGroup>
  662. </ColField>
  663. <ColField required title="指法展示" border={false}>
  664. <RadioGroup
  665. class={styles['radio-group']}
  666. modelValue={this.showFingering}
  667. onUpdate:modelValue={val => (this.showFingering = val)}
  668. >
  669. {Object.keys(teachershowFingeringType).map((item: string) => {
  670. const isActive = item === String(this.showFingering)
  671. const type = isActive ? 'primary' : 'default'
  672. return (
  673. <Radio class={styles.radio} name={item}>
  674. <Tag size="large" plain={isActive} type={type}>
  675. {teachershowFingeringType[item]}
  676. </Tag>
  677. </Radio>
  678. )
  679. })}
  680. </RadioGroup>
  681. </ColField>
  682. <ColField required title="是否收费" border={false}>
  683. <RadioGroup
  684. class={styles['radio-group']}
  685. modelValue={this.chargeType}
  686. onUpdate:modelValue={val => {
  687. this.chargeType = Number(val)
  688. }}
  689. >
  690. {Object.keys(teacherChargeType).map((item: string) => {
  691. const isActive = item === String(this.chargeType)
  692. const type = isActive ? 'primary' : 'default'
  693. return (
  694. <Radio class={styles.radio} name={item}>
  695. <Tag size="large" plain={isActive} type={type}>
  696. {teacherChargeType[item]}
  697. </Tag>
  698. </Radio>
  699. )
  700. })}
  701. </RadioGroup>
  702. </ColField>
  703. <ColField required title="支持简谱" border={false}>
  704. <RadioGroup
  705. class={styles['radio-group']}
  706. modelValue={this.notation}
  707. onUpdate:modelValue={val => {
  708. this.notation = Number(val)
  709. }}
  710. >
  711. {Object.keys(teacherNotationType).map((item: string) => {
  712. const isActive = item === String(this.notation)
  713. const type = isActive ? 'primary' : 'default'
  714. return (
  715. <Radio class={styles.radio} name={item}>
  716. <Tag size="large" plain={isActive} type={type}>
  717. {teacherNotationType[item]}
  718. </Tag>
  719. </Radio>
  720. )
  721. })}
  722. </RadioGroup>
  723. </ColField>
  724. {this.chargeType === 2 && (
  725. <ColField required title="收费价格">
  726. <Field
  727. clearable
  728. class={styles['clear-px']}
  729. placeholder="请输入收费价格"
  730. v-slots={{ button: () => '元' }}
  731. modelValue={this.musicPrice}
  732. rules={[{ required: true, message: '请输入收费价格' }]}
  733. onUpdate:modelValue={val => (this.musicPrice = val)}
  734. />
  735. </ColField>
  736. )}
  737. </ColFieldGroup>
  738. {this.chargeType === 2 && (
  739. <div class={styles.rule}>
  740. <p>扣除手续费后该曲目预计收入为:</p>
  741. <p>
  742. 每人:
  743. <span>
  744. {(
  745. ((parseFloat(this.musicPrice || '0') || 0) *
  746. (100 - this.music_sheet_service_fee)) /
  747. 100
  748. ).toFixed(2)}
  749. </span>
  750. 元/人
  751. </p>
  752. <p>您的乐谱收入将在学员购买后结算到您的账户中</p>
  753. </div>
  754. )}
  755. </div>
  756. <Sticky offsetBottom={0} position="bottom">
  757. <div class={styles['button-area']}>
  758. <Button
  759. type="primary"
  760. block
  761. round
  762. native-type="submit"
  763. loading={this.submitLoading}
  764. >
  765. 确认
  766. </Button>
  767. </div>
  768. </Sticky>
  769. <Popup
  770. show={this.showPicker}
  771. round
  772. position="bottom"
  773. teleport="body"
  774. onUpdate:show={val => (this.showPicker = val)}
  775. >
  776. <Picker
  777. defaultIndex={this.subJectIndex}
  778. columnsFieldNames={{
  779. text: 'value'
  780. }}
  781. columns={Object.entries(this.subjectListNames).map(
  782. ([key, value]) => ({ label: key, value })
  783. )}
  784. onCancel={() => (this.showPicker = false)}
  785. onConfirm={val => {
  786. this.selectedSubjectList = val
  787. this.vlewSubjectList = val
  788. this.showPicker = false
  789. }}
  790. />
  791. </Popup>
  792. <Popup
  793. show={this.subJectVisible}
  794. round
  795. closeable
  796. position="bottom"
  797. style={{ height: '60%' }}
  798. teleport="body"
  799. onUpdate:show={val => (this.subJectVisible = val)}
  800. >
  801. <SubjectModel
  802. subjectList={this.subjectList}
  803. choiceSubjectIds={this.choiceSubjectIds}
  804. onChoice={this.onChoice}
  805. selectType="Radio"
  806. />
  807. </Popup>
  808. <Popup
  809. show={this.tagVisibility}
  810. round
  811. closeable
  812. position="bottom"
  813. style={{ height: '60%' }}
  814. teleport="body"
  815. onUpdate:show={val => (this.tagVisibility = val)}
  816. >
  817. <SelectTag
  818. onConfirm={this.onComfirm}
  819. onCancel={() => {}}
  820. rowSingle
  821. defaultValue={this.tags.join(',')}
  822. needAllButton={false}
  823. />
  824. </Popup>
  825. </Form>
  826. )
  827. }
  828. })