index.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. import ColUpload from '@/components/col-upload'
  2. import request from '@/helpers/request'
  3. import { verifyNumberIntegerAndFloat } from '@/helpers/toolsValidate'
  4. import {
  5. ElButton,
  6. ElForm,
  7. ElFormItem,
  8. ElInput,
  9. ElOption,
  10. ElOptionGroup,
  11. ElRadioButton,
  12. ElRadioGroup,
  13. ElSelect,
  14. ElDialog,
  15. ElMessage
  16. } from 'element-plus'
  17. import { defineComponent } from 'vue'
  18. import styles from './index.module.less'
  19. export type BackgroundMp3 = {
  20. url?: string
  21. track?: string
  22. }
  23. export default defineComponent({
  24. name: 'music-operation',
  25. data() {
  26. const query = this.$route.query
  27. return {
  28. type: query.type || 'create',
  29. subjectList: [],
  30. tagList: [],
  31. submitLoading: false,
  32. reason: '',
  33. form: {
  34. audioType: 'MP3',
  35. xmlFileUrl: '',
  36. hasBeat: 0,
  37. mp3Url: '',
  38. bgmp3Url: '',
  39. midiUrl: '',
  40. musicSheetName: '',
  41. composer: '',
  42. musicSubject: null as any,
  43. tags: [] as string[],
  44. notation: 0,
  45. canEvaluate: 1,
  46. showFingering: 1,
  47. chargeType: 0,
  48. musicPrice: '',
  49. backgroundMp3s: [
  50. {
  51. url: '',
  52. track: ''
  53. }
  54. ] as BackgroundMp3[]
  55. },
  56. radioList: [], // 选中的人数
  57. tagStatus: false,
  58. music_sheet_service_fee: 0
  59. }
  60. },
  61. async mounted() {
  62. document.title = this.type === 'create' ? '新建曲谱' : '编辑曲谱'
  63. try {
  64. await request
  65. .get('/api-website/sysConfig/queryByParamName', {
  66. params: {
  67. paramName: 'music_sheet_service_fee'
  68. }
  69. })
  70. .then(res => (this.music_sheet_service_fee = res.data.paramValue))
  71. await request.get('/api-website/open/subject/subjectSelect').then(res => {
  72. this.subjectList = res.data || []
  73. })
  74. await request.get('/api-website/open/MusicTag/tree').then(res => {
  75. this.tagList = res.data || []
  76. })
  77. if (this.$route.query.id) {
  78. this.setDetail(this.$route.query.id as string)
  79. }
  80. } catch {}
  81. },
  82. methods: {
  83. async setDetail(id: string) {
  84. try {
  85. const res = await request.get(
  86. '/api-website/open/music/sheet/detail/' + id
  87. )
  88. this.form.chargeType = res.data.chargeType === 'FREE' ? 0 : 2
  89. this.form.showFingering = res.data.showFingering
  90. this.form.notation = res.data.notation
  91. this.form.canEvaluate = res.data.canEvaluate
  92. if (this.form.chargeType) {
  93. this.form.musicPrice = res.data.musicPrice
  94. }
  95. this.form.composer = res.data.composer
  96. this.form.musicSheetName = res.data.musicSheetName
  97. this.form.audioType = res.data.audioType
  98. this.form.musicSubject = Number(res.data.musicSubject)
  99. const musicTag = res.data.musicTag.split(',')
  100. this.form.tags = musicTag.map((item: any) => {
  101. return Number(item)
  102. })
  103. this.radioList = this.form.tags as any
  104. this.form.xmlFileUrl = res.data.xmlFileUrl
  105. this.form.audioType = res.data.mp3Type
  106. if (this.form.audioType === 'MP3') {
  107. this.form.hasBeat = res.data.hasBeat || 0
  108. this.form.mp3Url = res.data.metronomeUrl || res.data.url
  109. } else {
  110. this.form.midiUrl = res.data.midiUrl
  111. }
  112. this.form.backgroundMp3s = (res.data.background || []).map(
  113. (item: any, index: any) => {
  114. if (index === 0) {
  115. this.form.bgmp3Url = item.metronomeUrl || item.audioFileUrl
  116. }
  117. return {
  118. url: this.form.hasBeat ? item.metronomeUrl : item.audioFileUrl,
  119. track: item.track
  120. }
  121. }
  122. )
  123. this.reason = res.data.reason
  124. console.log(this.form.bgmp3Url)
  125. } catch (error) {
  126. console.log(error)
  127. }
  128. },
  129. createSubmitData() {
  130. const { form } = this
  131. const beatType = form.hasBeat ? 'MP3_METRONOME' : 'MP3'
  132. const mp3Type = form.audioType === 'MP3' ? beatType : 'MIDI'
  133. return {
  134. audioType: form.audioType,
  135. sourceType: 'TEACHER',
  136. mp3Type,
  137. hasBeat: form.hasBeat,
  138. url: form.hasBeat ? '' : form.mp3Url,
  139. metronomeUrl: form.hasBeat ? form.mp3Url : '',
  140. showFingering: Number(form.showFingering),
  141. notation: Number(form.notation),
  142. musicTag: form.tags.join(','),
  143. musicSubject: form.musicSubject || undefined,
  144. musicSheetName: form.musicSheetName,
  145. midiUrl: form.midiUrl,
  146. xmlFileUrl: form.xmlFileUrl,
  147. canEvaluate: Number(form.canEvaluate),
  148. chargeType: form.chargeType === 0 ? 'FREE' : 'CHARGE',
  149. composer: form.composer,
  150. musicPrice: form.musicPrice,
  151. background: form.backgroundMp3s.map(item => ({
  152. audioFileUrl: form.hasBeat ? '' : form.bgmp3Url,
  153. track: item.track,
  154. metronomeUrl: form.hasBeat ? form.bgmp3Url : ''
  155. }))
  156. }
  157. },
  158. onFormatter(e: any) {
  159. e.target.value = verifyNumberIntegerAndFloat(e.target.value)
  160. },
  161. onSubmit() {
  162. ;(this as any).$refs.form.validate(async (valid: any) => {
  163. if (valid) {
  164. this.submitLoading = true
  165. console.log(this.createSubmitData(), 'createSubmitData')
  166. try {
  167. if (this.$route.query.id) {
  168. await request.post('/api-website/music/sheet/update', {
  169. data: {
  170. ...this.createSubmitData(),
  171. id: this.$route.query.id
  172. }
  173. })
  174. } else {
  175. await request.post('/api-website/music/sheet/create', {
  176. data: this.createSubmitData()
  177. })
  178. }
  179. this.submitLoading = false
  180. ElMessage.success('上传成功')
  181. sessionStorage.setItem('musicActiveName', 'DOING')
  182. this.$router.back()
  183. } catch (error) {
  184. this.submitLoading = false
  185. }
  186. } else {
  187. this.$nextTick(() => {
  188. const isError = document.getElementsByClassName('is-error')
  189. isError[0].scrollIntoView({
  190. block: 'center',
  191. behavior: 'smooth'
  192. })
  193. })
  194. return false
  195. }
  196. })
  197. }
  198. },
  199. render() {
  200. return (
  201. <div class={styles.form}>
  202. <div class="text-2xl font-semibold text-black leading-none px-6 py-5 ">
  203. {this.type === 'create' ? '新建曲谱' : '编辑曲谱'}
  204. </div>
  205. <ElForm
  206. size="large"
  207. labelPosition="left"
  208. labelWidth={'150px'}
  209. model={this.form}
  210. ref="form"
  211. class="px-7 py-5"
  212. >
  213. <ElFormItem
  214. label="上传XML"
  215. prop="xmlFileUrl"
  216. rules={[{ required: true, message: '请选择MusicXML文件' }]}
  217. >
  218. <ColUpload
  219. v-model:modelValue={this.form.xmlFileUrl}
  220. bucket={'cloud-coach'}
  221. accept={'application/xml'}
  222. uploadType={'file'}
  223. extraTips="文件最大不能超过5MB"
  224. />
  225. </ElFormItem>
  226. <ElFormItem
  227. label="是否带节拍器"
  228. prop="hasBeat"
  229. rules={[{ required: true, message: '请选择是否带节拍器' }]}
  230. >
  231. <ElRadioGroup v-model={this.form.hasBeat}>
  232. <ElRadioButton label={0} class="mr-3 w-24">
  233. </ElRadioButton>
  234. <ElRadioButton label={1} class="w-24">
  235. </ElRadioButton>
  236. </ElRadioGroup>
  237. </ElFormItem>
  238. <ElFormItem label="伴奏文件" prop="mp3Url">
  239. <ColUpload
  240. v-model:modelValue={this.form.mp3Url}
  241. bucket={'cloud-coach'}
  242. accept={'.mp3'}
  243. uploadType={'file'}
  244. extraTips="文件最大不能超过5MB"
  245. />
  246. </ElFormItem>
  247. <ElFormItem
  248. label="原音文件"
  249. prop="bgmp3Url"
  250. rules={[{ required: true, message: '请选择原音文件' }]}
  251. >
  252. <ColUpload
  253. v-model:modelValue={this.form.bgmp3Url}
  254. bucket={'cloud-coach'}
  255. accept={'.mp3'}
  256. uploadType={'file'}
  257. extraTips="文件最大不能超过5MB"
  258. />
  259. </ElFormItem>
  260. <ElFormItem
  261. label="曲目名称"
  262. prop="musicSheetName"
  263. rules={[{ required: true, message: '请输入曲目名称' }]}
  264. >
  265. <ElInput
  266. v-model={this.form.musicSheetName}
  267. placeholder="请选择曲目名称"
  268. />
  269. </ElFormItem>
  270. <ElFormItem
  271. label="作曲人"
  272. prop="composer"
  273. rules={[{ required: true, message: '请输入作曲人' }]}
  274. >
  275. <ElInput v-model={this.form.composer} placeholder="请输入作曲人" />
  276. </ElFormItem>
  277. <ElFormItem
  278. label="曲目声部"
  279. prop="musicSubject"
  280. rules={[
  281. { required: true, message: '请选择曲目声部', trigger: 'change' }
  282. ]}
  283. >
  284. <ElSelect
  285. filterable
  286. v-model={this.form.musicSubject}
  287. placeholder="请选择曲目声部"
  288. class="w-full"
  289. >
  290. {this.subjectList.map((group: any) => (
  291. <ElOptionGroup key={group.id} label={group.name}>
  292. {group.subjects &&
  293. group.subjects.map((item: any) => (
  294. <ElOption
  295. key={item.id}
  296. value={item.id}
  297. label={item.name}
  298. />
  299. ))}
  300. </ElOptionGroup>
  301. ))}
  302. </ElSelect>
  303. </ElFormItem>
  304. <ElFormItem
  305. label="曲目标签"
  306. prop="tags"
  307. rules={[{ required: true, message: '请选择曲目标签' }]}
  308. >
  309. <div class="w-full relative">
  310. <div
  311. class=" w-full block h-[42px] absolute top-0 left-0 z-10"
  312. onClick={() => {
  313. console.log(111)
  314. this.tagStatus = true
  315. }}
  316. ></div>
  317. <ElSelect
  318. multiple
  319. v-model={this.form.tags}
  320. placeholder="请选择曲目标签"
  321. class="w-full"
  322. >
  323. {this.tagList.map((group: any) => (
  324. <ElOptionGroup key={group.id} label={group.name}>
  325. {group.children &&
  326. group.children.map((item: any) => (
  327. <ElOption
  328. key={item.id}
  329. value={item.id}
  330. label={item.name}
  331. />
  332. ))}
  333. </ElOptionGroup>
  334. ))}
  335. </ElSelect>
  336. </div>
  337. </ElFormItem>
  338. <ElFormItem
  339. label="是否可以转简谱"
  340. prop="notation"
  341. rules={[{ required: true, message: '请选择是否可以转简谱' }]}
  342. >
  343. <ElRadioGroup v-model={this.form.notation}>
  344. <ElRadioButton label={0} class="mr-3 w-24">
  345. </ElRadioButton>
  346. <ElRadioButton label={1} class="w-24">
  347. </ElRadioButton>
  348. </ElRadioGroup>
  349. </ElFormItem>
  350. <ElFormItem
  351. label="是否评测"
  352. prop="canEvaluate"
  353. rules={[{ required: true, message: '请选择是否评测' }]}
  354. >
  355. <ElRadioGroup v-model={this.form.canEvaluate}>
  356. <ElRadioButton label={0} class="mr-3 w-24">
  357. </ElRadioButton>
  358. <ElRadioButton label={1} class="w-24">
  359. </ElRadioButton>
  360. </ElRadioGroup>
  361. </ElFormItem>
  362. <ElFormItem
  363. label="指法展示"
  364. prop="showFingering"
  365. rules={[{ required: true, message: '请选择指法展示' }]}
  366. >
  367. <ElRadioGroup v-model={this.form.showFingering}>
  368. <ElRadioButton label={0} class="mr-3 w-24">
  369. </ElRadioButton>
  370. <ElRadioButton label={1} class="w-24">
  371. </ElRadioButton>
  372. </ElRadioGroup>
  373. </ElFormItem>
  374. <ElFormItem
  375. label="是否收费"
  376. prop="chargeType"
  377. rules={[{ required: true, message: '请选择是否收费' }]}
  378. >
  379. <ElRadioGroup v-model={this.form.chargeType}>
  380. <ElRadioButton label={0} class="mr-3 w-24">
  381. </ElRadioButton>
  382. <ElRadioButton label={2} class="w-24">
  383. </ElRadioButton>
  384. </ElRadioGroup>
  385. </ElFormItem>
  386. {this.form.chargeType === 2 && (
  387. <>
  388. <ElFormItem
  389. label="收费价格"
  390. prop="musicPrice"
  391. rules={[{ required: true, message: '请输入收费价格' }]}
  392. >
  393. <ElInput
  394. v-model={this.form.musicPrice}
  395. placeholder="请输入收费价格"
  396. // @ts-ignore
  397. maxlength={5}
  398. onKeyup={this.onFormatter}
  399. v-slots={{
  400. append: () => <span>元</span>
  401. }}
  402. />
  403. </ElFormItem>
  404. <ElFormItem>
  405. <div class={styles.rule}>
  406. <p>扣除手续费后该曲目预计收入为:</p>
  407. <p>
  408. 每人:
  409. <span>
  410. {((parseFloat(this.form.musicPrice || '0') || 0) *
  411. (100 - this.music_sheet_service_fee)) /
  412. 100}
  413. </span>
  414. 元/人
  415. </p>
  416. <p>您的乐谱收入将在学员购买后结算到您的账户中</p>
  417. </div>
  418. </ElFormItem>
  419. </>
  420. )}
  421. </ElForm>
  422. <div class="text-center pt-6 pb-7">
  423. <ElButton
  424. class="!w-44 !h-[48px]"
  425. round
  426. onClick={() => {
  427. this.$router.back()
  428. }}
  429. >
  430. 取消
  431. </ElButton>
  432. <ElButton
  433. type="primary"
  434. class="!w-44 !h-[48px]"
  435. round
  436. onClick={this.onSubmit}
  437. loading={this.submitLoading}
  438. >
  439. 提交审核
  440. </ElButton>
  441. </div>
  442. <ElDialog
  443. modelValue={this.tagStatus}
  444. onUpdate:modelValue={val => (this.tagStatus = val)}
  445. width="35%"
  446. title="全部标签"
  447. >
  448. {this.tagList.map((item: any, index: number) => (
  449. <div class={[styles.tags, 'py-2']}>
  450. <div class="text-sm pb-2">{item.name}</div>
  451. {item.children.map((child: any) => (
  452. <ElRadioGroup v-model={this.radioList[index]} class="pb-2">
  453. <ElRadioButton label={child.id} class="mr-3 w-24">
  454. {child.name}
  455. </ElRadioButton>
  456. </ElRadioGroup>
  457. ))}
  458. </div>
  459. ))}
  460. <div class="text-center pt-2">
  461. <ElButton
  462. class="!w-36 !h-[48px]"
  463. round
  464. size="large"
  465. onClick={() => {
  466. this.radioList = []
  467. }}
  468. >
  469. 重置
  470. </ElButton>
  471. <ElButton
  472. class="!w-36 !h-[48px]"
  473. round
  474. size="large"
  475. type="primary"
  476. onClick={() => {
  477. this.form.tags = this.radioList
  478. this.tagStatus = false
  479. ;(this as any).$refs.form.clearValidate('tags')
  480. }}
  481. >
  482. 确认
  483. </ElButton>
  484. </div>
  485. </ElDialog>
  486. </div>
  487. )
  488. }
  489. })