|
@@ -0,0 +1,536 @@
|
|
|
+import { computed, defineComponent, onMounted, reactive } from 'vue'
|
|
|
+import styles from './add-unit-item.module.less'
|
|
|
+import {
|
|
|
+ Button,
|
|
|
+ Cell,
|
|
|
+ CellGroup,
|
|
|
+ Dialog,
|
|
|
+ Field,
|
|
|
+ Icon,
|
|
|
+ Popup,
|
|
|
+ Radio,
|
|
|
+ RadioGroup,
|
|
|
+ showToast
|
|
|
+} from 'vant'
|
|
|
+import iconDelete from '../image/icon-delete2.png'
|
|
|
+import iconMusic from '../image/icon-music.png'
|
|
|
+import iconRadioDefault from '../image/icon-radio-default.png'
|
|
|
+import iconReadioChecked from '../image/icon-radio-checked.png'
|
|
|
+import OHeader from '@/components/o-header'
|
|
|
+import OPopup from '@/components/o-popup'
|
|
|
+import MusicList from './music-list'
|
|
|
+import { difficultyCoefficients } from '.'
|
|
|
+import OActionSheet from '@/components/o-action-sheet'
|
|
|
+import requestOrigin from 'umi-request'
|
|
|
+import OSticky from '@/components/o-sticky'
|
|
|
+import { useRoute, useRouter } from 'vue-router'
|
|
|
+import { unitData } from './data'
|
|
|
+import request from '@/helpers/request'
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'add-unit-item',
|
|
|
+ setup() {
|
|
|
+ const route = useRoute()
|
|
|
+ const router = useRouter()
|
|
|
+ const state = reactive({
|
|
|
+ musicStatus: false,
|
|
|
+ // selectMusic: {} as any,
|
|
|
+ level: route.query.level,
|
|
|
+ dialogShow: false,
|
|
|
+ activeIndex: 0, // 选择的索引
|
|
|
+ activeRow: {} as any,
|
|
|
+ musicLevelShow: false,
|
|
|
+ actions: [] as any,
|
|
|
+ partShow: false,
|
|
|
+ checkedPart: null as any, // 选择小节类型
|
|
|
+ startPart: null, // 开始小节
|
|
|
+ endPart: null // 结束小节
|
|
|
+ })
|
|
|
+
|
|
|
+ const isEdit = computed(() => {
|
|
|
+ return route.query.musicId ? true : false
|
|
|
+ })
|
|
|
+
|
|
|
+ difficultyCoefficients.forEach((item: any) => {
|
|
|
+ state.actions.push({
|
|
|
+ name: item.label,
|
|
|
+ value: item.value
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ // 格式化难度数据
|
|
|
+ const filterDifficulty = (difficulty: string) => {
|
|
|
+ let str = ''
|
|
|
+ state.actions.forEach((action: any) => {
|
|
|
+ if (action.value === difficulty) {
|
|
|
+ str = action.name
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return str
|
|
|
+ }
|
|
|
+
|
|
|
+ // 格式化小节数
|
|
|
+ const filterPart = (item: any) => {
|
|
|
+ if (item.partStart && item.partEnd) {
|
|
|
+ return item.partStart + '-' + item.partEnd + '小节'
|
|
|
+ } else {
|
|
|
+ return ''
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const onSubmit = async () => {
|
|
|
+ let pass = true
|
|
|
+ for (let i = 0; i < forms.length; i++) {
|
|
|
+ if (!forms[i].musicId) {
|
|
|
+ pass = false
|
|
|
+ showToast('请选择曲目')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if (!forms[i].partName) {
|
|
|
+ pass = false
|
|
|
+ showToast('请选择小节')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if (!forms[i].difficulty) {
|
|
|
+ pass = false
|
|
|
+ showToast('请选择难度')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if (!forms[i].musicScore) {
|
|
|
+ pass = false
|
|
|
+ showToast('请输入曲目分数')
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!forms[i].passScore) {
|
|
|
+ pass = false
|
|
|
+ showToast('请输入合格分数')
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!pass) return
|
|
|
+ console.log(true, '1212')
|
|
|
+
|
|
|
+ if (isEdit.value) {
|
|
|
+ const questionList = unitData['level' + state.level]?.questionList
|
|
|
+ for (let i = 0; i < questionList.length; i++) {
|
|
|
+ if (questionList[i].question.mediaUrls === route.query.musicId) {
|
|
|
+ questionList[i].question = {
|
|
|
+ name: forms[0].musicName,
|
|
|
+ mediaUrls: forms[0].musicId,
|
|
|
+ questionTypeCode: 'PLAY',
|
|
|
+ totalScore: forms[0].musicScore,
|
|
|
+ questionExtendsInfo: JSON.stringify({
|
|
|
+ musicName: forms[0].musicName,
|
|
|
+ musicSheetId: forms[0].musicId,
|
|
|
+ start: forms[0].partStart,
|
|
|
+ end: forms[0].partEnd,
|
|
|
+ score: forms[0].passScore,
|
|
|
+ difficulty: forms[0].difficulty
|
|
|
+ })
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const tempList: any = []
|
|
|
+ forms.forEach((item: any) => {
|
|
|
+ tempList.push({
|
|
|
+ unitExaminationId: null,
|
|
|
+ question: {
|
|
|
+ name: item.musicName,
|
|
|
+ mediaUrls: item.musicId,
|
|
|
+ questionTypeCode: 'PLAY',
|
|
|
+ totalScore: item.musicScore,
|
|
|
+ questionExtendsInfo: JSON.stringify({
|
|
|
+ musicName: item.musicName,
|
|
|
+ musicSheetId: item.musicId,
|
|
|
+ start: item.partStart,
|
|
|
+ end: item.partEnd,
|
|
|
+ score: item.passScore,
|
|
|
+ difficulty: item.difficulty
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ // 添加曲目
|
|
|
+ unitData['level' + state.level]?.questionList.push(...tempList)
|
|
|
+ }
|
|
|
+
|
|
|
+ router.back()
|
|
|
+ }
|
|
|
+
|
|
|
+ const _init = () => [
|
|
|
+ {
|
|
|
+ musicName: '',
|
|
|
+ musicId: null,
|
|
|
+ partName: '',
|
|
|
+ partLength: 0, // 总的小节数
|
|
|
+ partStart: null as any, // 开始小节数
|
|
|
+ partEnd: null as any, // 结束小节数
|
|
|
+ difficulty: null as any, // 难度
|
|
|
+ musicScore: null as any, // 曲目分数
|
|
|
+ passScore: null as any // 合格分数
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ const forms = reactive(_init())
|
|
|
+
|
|
|
+ const getXMLPart = async (xmlFileUrl: string) => {
|
|
|
+ let xmlStatus = 'init'
|
|
|
+ // 第一个声部小节
|
|
|
+ let firstMeasures: any = null
|
|
|
+ let partLength = 0
|
|
|
+ try {
|
|
|
+ // 获取文件
|
|
|
+ const res = await requestOrigin.get(xmlFileUrl, {
|
|
|
+ mode: 'cors'
|
|
|
+ })
|
|
|
+ const xmlParse = new DOMParser().parseFromString(res, 'text/xml')
|
|
|
+ const parts = xmlParse.getElementsByTagName('part')
|
|
|
+ firstMeasures = parts[0]?.getElementsByTagName('measure')
|
|
|
+ xmlStatus = 'success'
|
|
|
+ } catch (error) {
|
|
|
+ xmlStatus = 'error'
|
|
|
+ }
|
|
|
+ // 判断读取小节数
|
|
|
+ if (xmlStatus == 'success') {
|
|
|
+ partLength = firstMeasures.length
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ xmlStatus,
|
|
|
+ partLength
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ onMounted(async () => {
|
|
|
+ if (!route.query.musicId) return
|
|
|
+ const tData = unitData['level' + state.level]
|
|
|
+ let tempQ: any = null
|
|
|
+ tData?.questionList.forEach((item: any) => {
|
|
|
+ if (item.question.mediaUrls === route.query.musicId) {
|
|
|
+ tempQ = item
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (tempQ) {
|
|
|
+ const questionExtendsInfo = tempQ.question.questionExtendsInfo
|
|
|
+ ? JSON.parse(tempQ.question.questionExtendsInfo)
|
|
|
+ : {}
|
|
|
+
|
|
|
+ forms[0] = {
|
|
|
+ musicName: tempQ.question.name,
|
|
|
+ musicId: tempQ.question.mediaUrls,
|
|
|
+ partName: '',
|
|
|
+ partLength: 0, // 总的小节数
|
|
|
+ partStart: questionExtendsInfo.start || 0, // 开始小节数
|
|
|
+ partEnd: questionExtendsInfo.end || 0, // 结束小节数
|
|
|
+ difficulty: questionExtendsInfo.difficulty, // 难度
|
|
|
+ musicScore: tempQ.question.totalScore, // 曲目分数
|
|
|
+ passScore: questionExtendsInfo.score // 合格分数
|
|
|
+ }
|
|
|
+ // 初始化名称
|
|
|
+ forms[0].partName = filterPart(forms[0])
|
|
|
+
|
|
|
+ const { data } = await request.get('/api-teacher/musicSheet/detail/' + route.query.musicId)
|
|
|
+ const partData = await getXMLPart(data.xmlFileUrl)
|
|
|
+ forms[0].partLength = partData.partLength
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return () => (
|
|
|
+ <div class={styles.addUnitItem}>
|
|
|
+ <OHeader />
|
|
|
+ {forms.map((item: any, index: number) => (
|
|
|
+ <CellGroup class={styles.cellGroup}>
|
|
|
+ <Cell center>
|
|
|
+ {{
|
|
|
+ title: () => (
|
|
|
+ <div style={{ display: 'flex', 'align-items': 'center' }}>
|
|
|
+ <Icon name={iconMusic} class={styles.iconMusic} />
|
|
|
+ <div class={styles.title}>曲目{index + 1}</div>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ value: () =>
|
|
|
+ !isEdit.value && (
|
|
|
+ <Icon
|
|
|
+ name={iconDelete}
|
|
|
+ class={[styles.iconDelete, forms.length <= 1 ? styles.disabled : '']}
|
|
|
+ onClick={() => {
|
|
|
+ state.dialogShow = true
|
|
|
+ state.activeRow = {
|
|
|
+ ...item,
|
|
|
+ index
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </Cell>
|
|
|
+ <Field
|
|
|
+ isLink
|
|
|
+ clearable={false}
|
|
|
+ inputAlign="right"
|
|
|
+ label="练习内容"
|
|
|
+ placeholder="请选择曲目"
|
|
|
+ autocomplete="off"
|
|
|
+ readonly
|
|
|
+ modelValue={item.musicName}
|
|
|
+ onClick={() => {
|
|
|
+ state.activeIndex = index
|
|
|
+ state.musicStatus = true
|
|
|
+ state.activeRow = item
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ <Field
|
|
|
+ isLink
|
|
|
+ clearable={false}
|
|
|
+ inputAlign="right"
|
|
|
+ label="练习小节"
|
|
|
+ autocomplete="off"
|
|
|
+ readonly
|
|
|
+ modelValue={item.partName}
|
|
|
+ placeholder="请选择小节"
|
|
|
+ onClick={() => {
|
|
|
+ if (!item.musicId) {
|
|
|
+ return showToast('请选择曲目')
|
|
|
+ }
|
|
|
+ state.activeRow = item
|
|
|
+ state.partShow = true
|
|
|
+ state.startPart = item.partStart
|
|
|
+ state.endPart = item.partEnd
|
|
|
+ if (item.partEnd === item.partLength) {
|
|
|
+ state.checkedPart = '1'
|
|
|
+ } else {
|
|
|
+ state.checkedPart = '2'
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ <Field
|
|
|
+ isLink
|
|
|
+ clearable={false}
|
|
|
+ inputAlign="right"
|
|
|
+ label="练习难度"
|
|
|
+ autocomplete="off"
|
|
|
+ readonly
|
|
|
+ modelValue={filterDifficulty(item.difficulty)}
|
|
|
+ placeholder="请选择曲目难度"
|
|
|
+ onClick={() => {
|
|
|
+ state.musicLevelShow = true
|
|
|
+ state.activeRow = item
|
|
|
+ if (item.difficulty) {
|
|
|
+ state.actions.forEach((action: any) => {
|
|
|
+ if (action.value === item.difficulty) {
|
|
|
+ action.selected = true
|
|
|
+ } else {
|
|
|
+ action.selected = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ state.actions.forEach((action: any) => {
|
|
|
+ action.selected = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ <Field
|
|
|
+ inputAlign="right"
|
|
|
+ label="曲目分数"
|
|
|
+ type="number"
|
|
|
+ autocomplete="off"
|
|
|
+ maxlength={3}
|
|
|
+ class={styles.inputControl}
|
|
|
+ v-model={item.musicScore}
|
|
|
+ center
|
|
|
+ >
|
|
|
+ {{
|
|
|
+ extra: () => (
|
|
|
+ <div class={styles.loctionIconWrap}>
|
|
|
+ <span> 分</span>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </Field>
|
|
|
+ <Field
|
|
|
+ inputAlign="right"
|
|
|
+ label="合格分数"
|
|
|
+ type="number"
|
|
|
+ maxlength={3}
|
|
|
+ autocomplete="off"
|
|
|
+ class={styles.inputControl}
|
|
|
+ v-model={item.passScore}
|
|
|
+ center
|
|
|
+ >
|
|
|
+ {{
|
|
|
+ extra: () => (
|
|
|
+ <div class={styles.loctionIconWrap}>
|
|
|
+ <span> 分</span>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </Field>
|
|
|
+ </CellGroup>
|
|
|
+ ))}
|
|
|
+
|
|
|
+ {!isEdit.value && (
|
|
|
+ <Button
|
|
|
+ round
|
|
|
+ class={styles.addBtn}
|
|
|
+ onClick={() => {
|
|
|
+ forms.push(..._init())
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Icon name="plus" />
|
|
|
+ 添加测验曲目
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <OSticky position="bottom">
|
|
|
+ <div class={'btnGroup'}>
|
|
|
+ <Button type="primary" round block onClick={onSubmit}>
|
|
|
+ 确认
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </OSticky>
|
|
|
+
|
|
|
+ <OPopup v-model:modelValue={state.musicStatus} style={{ background: '#F8F8F8' }}>
|
|
|
+ <MusicList
|
|
|
+ onConfirm={async (row: any) => {
|
|
|
+ // 判断是否选择一样的曲目
|
|
|
+ if (row.id === state.activeRow.musicId) {
|
|
|
+ state.musicStatus = false
|
|
|
+ return
|
|
|
+ }
|
|
|
+ state.activeRow.musicId = row.id
|
|
|
+ state.activeRow.musicName = row.musicSheetName
|
|
|
+ state.musicStatus = false
|
|
|
+
|
|
|
+ const partData = await getXMLPart(row.xmlFileUrl)
|
|
|
+ if (partData.xmlStatus === 'success') {
|
|
|
+ state.activeRow.partLength = partData.partLength
|
|
|
+ state.activeRow.partStart = 1
|
|
|
+ state.activeRow.partEnd = partData.partLength
|
|
|
+ } else {
|
|
|
+ state.activeRow.partLength = 0
|
|
|
+ }
|
|
|
+
|
|
|
+ state.activeRow.partName = ''
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </OPopup>
|
|
|
+
|
|
|
+ <Dialog
|
|
|
+ v-model:show={state.dialogShow}
|
|
|
+ showCancelButton
|
|
|
+ message={`请确认是否删除曲目${state.activeRow.index + 1}?`}
|
|
|
+ confirmButtonText="删除"
|
|
|
+ onConfirm={() => {
|
|
|
+ forms.splice(state.activeRow.index, 1)
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {{ title: () => <div class={styles.dialogDelete}>删除题目</div> }}
|
|
|
+ </Dialog>
|
|
|
+
|
|
|
+ {/* 曲目难度 */}
|
|
|
+ <OActionSheet
|
|
|
+ v-model:show={state.musicLevelShow}
|
|
|
+ actions={state.actions}
|
|
|
+ onSelect={(val: any) => {
|
|
|
+ state.actions.forEach((child: any) => {
|
|
|
+ child.selected = false
|
|
|
+ })
|
|
|
+ val.selected = true
|
|
|
+ state.activeRow.difficulty = val.value
|
|
|
+ state.musicLevelShow = false
|
|
|
+ }}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 选择小节 */}
|
|
|
+ <Popup
|
|
|
+ round
|
|
|
+ position="bottom"
|
|
|
+ v-model:show={state.partShow}
|
|
|
+ closeable
|
|
|
+ class={styles.partPopup}
|
|
|
+ >
|
|
|
+ <div class={styles.partContainer}>
|
|
|
+ <div class={styles.partTitle}>请选择练习小节</div>
|
|
|
+
|
|
|
+ <RadioGroup v-model={state.checkedPart}>
|
|
|
+ <Cell
|
|
|
+ title={'全部小节'}
|
|
|
+ onClick={() => (state.checkedPart = '1')}
|
|
|
+ class={state.checkedPart == '1' && styles.activeCell}
|
|
|
+ >
|
|
|
+ {{
|
|
|
+ icon: () => (
|
|
|
+ <Radio name="1">
|
|
|
+ {{
|
|
|
+ icon: (props: any) => (
|
|
|
+ <img src={props.checked ? iconReadioChecked : iconRadioDefault} />
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </Radio>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </Cell>
|
|
|
+ <Cell
|
|
|
+ title={'部分小节'}
|
|
|
+ onClick={() => (state.checkedPart = '2')}
|
|
|
+ class={state.checkedPart == '2' && styles.activeCell}
|
|
|
+ >
|
|
|
+ {{
|
|
|
+ icon: () => (
|
|
|
+ <Radio name="2">
|
|
|
+ {{
|
|
|
+ icon: (props: any) => (
|
|
|
+ <img src={props.checked ? iconReadioChecked : iconRadioDefault} />
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </Radio>
|
|
|
+ ),
|
|
|
+ label: () => (
|
|
|
+ <div class={styles.partContent}>
|
|
|
+ <Field
|
|
|
+ type="number"
|
|
|
+ maxlength={3}
|
|
|
+ class={styles.partInput}
|
|
|
+ v-model={state.startPart}
|
|
|
+ />
|
|
|
+ <span>至</span>
|
|
|
+ <Field
|
|
|
+ type="number"
|
|
|
+ maxlength={3}
|
|
|
+ class={styles.partInput}
|
|
|
+ v-model={state.endPart}
|
|
|
+ />
|
|
|
+ <span>小节</span>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </Cell>
|
|
|
+ </RadioGroup>
|
|
|
+
|
|
|
+ <Button
|
|
|
+ round
|
|
|
+ block
|
|
|
+ type="primary"
|
|
|
+ class={styles.partBtn}
|
|
|
+ onClick={() => {
|
|
|
+ if (state.checkedPart === '1') {
|
|
|
+ state.activeRow.partStart = 1
|
|
|
+ state.activeRow.partEnd = state.activeRow.partLength
|
|
|
+ } else {
|
|
|
+ state.activeRow.partStart = state.startPart
|
|
|
+ state.activeRow.partEnd = state.endPart
|
|
|
+ }
|
|
|
+ state.activeRow.partName = filterPart(state.activeRow)
|
|
|
+ state.partShow = false
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 确认
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </Popup>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|