import { defineComponent, onMounted, Ref, ref, Transition, watch } from 'vue' import request from '/src/helpers/request' import originRequest from 'umi-request' import MusicSheet from '/src/music-sheet' import runtime from '/src/pages/detail/runtime' import { formatXML, onlyVisible, getAllNodes, getCustomInfo, getBoundingBoxByverticalNote, getParentNote, } from '/src/pages/detail/helpers' import SettingState from '/src/pages/detail/setting-state' import detailState from '/src/pages/detail/state' import { useOriginSearch } from '../colexiu/uses' import styles from '../colexiu/index.module.less' import detailStyles from './index.module.less' import { OpenSheetMusicDisplay } from '/osmd-extended/src' import { MusicSheelDetail, ShaeetStatusType } from '../colexiu/index.d' import Header, { active } from './header' import { colorsClass } from '/src/pages/report' import { getLeveByScoreMeasure } from '/src/pages/detail/evaluating/helper' import { Button, Skeleton } from 'vant' import Empty from '/src/components/empty' import { useSpecialShapedScreen } from '../colexiu/uses/use-app' import { postMessage, promisefiyPostMessage } from '/src/helpers/native-message' const search = useOriginSearch() const useXml = async (url: string, detail: MusicSheelDetail) => { const xml = await originRequest(url) let score = ref('') const parseXmlInfo = getCustomInfo(xml) score.value = formatXML(parseXmlInfo.parsedXML, { title: detail.musicSheetName, }) const partIndex = Number(search['part-index']) || 0 score.value = onlyVisible(score.value, partIndex) return score } const useDetail = (id: number | string): [Ref, Ref, Ref] => { const status = ref('loading') const data = ref({}) const record = ref({}) onMounted(async () => { // 没有token const token = sessionStorage.getItem('Authorization') console.log('第一次请求', token) if (!token) { // 获取token const res = await promisefiyPostMessage({ api: 'getToken' }) if (res?.content?.accessToken) { sessionStorage.setItem('Authorization', res.content.tokenType + ' ' + res.content.accessToken) } } status.value = 'loading' try { const recordRes = await request.get('/musicPracticeRecord/getLastEvaluationMusicalNotesPlayStats', { params: { recordId: search.id, }, }) if (!recordRes.data) { status.value = 'error' return } record.value = recordRes.data const res = await request.get(`/musicSheet/detail/${record.value?.musicalNotesPlayStats.examSongId}`) data.value = res.data detailState.partIndex = recordRes.data.partIndex || 0 detailState.isPercussion = res.data?.background?.[detailState.partIndex]?.musicSubject == 1 status.value = 'success' } catch (error) { status.value = 'error' console.log(error) } }) return [status, data, record] } export default defineComponent({ name: 'Colexiu', setup() { const headerRef = ref() const renderLoading = ref(true) const renderError = ref(false) const score = ref('') const useedid = ref([]) const allNote = ref([]) const [detailStatus, detail, record] = useDetail(search.id as string) watch(detailStatus, async () => { if (detailStatus.value === 'success' && detail.value.xmlFileUrl) { const xml = await useXml(detail.value.xmlFileUrl, detail.value) // console.log(runtime.songs, detailState.partListNames) score.value = xml.value } }) // useUser() useSpecialShapedScreen() const getOffsetPosition = (type: keyof typeof colorsClass): string => { switch (type) { case 'CADENCE_FAST': return 'translateX(2px)' case 'CADENCE_SLOW': return 'translateX(-2px)' case 'INTONATION_HIGH': return 'translateY(-2px)' case 'INTONATION_LOW': return 'translateY(2px)' default: return '' } } const filterNotes = () => { const include = ['RIGHT', 'WRONG', 'CADENCE_WRONG'] console.log(active.value) if (active.value === 'pitch') { include.push(...['CADENCE_FAST', 'CADENCE_SLOW']) } else if (active.value === 'rhythm') { include.push(...['INTONATION_HIGH', 'INTONATION_LOW']) } else if (active.value === 'completion') { include.push(...['INTEGRITY_WRONG']) } return record.value.musicalNotesPlayStats.notesData.filter((item: any) => include.includes(item.musicalErrorType)) } const setViewColor = () => { clearViewColor() for (const note of filterNotes()) { const active = allNote.value[note.musicalNotesIndex] setTimeout(() => { if (useedid.value.includes(active.id)) { return } useedid.value.push(active.id) const svgEl = document.getElementById('vf-' + active.id) const stemEl = document.getElementById('vf-' + active.id + '-stem') const errType = note.musicalErrorType as keyof typeof colorsClass const isNeedCopyElement = ['INTONATION_HIGH', 'INTONATION_LOW', 'CADENCE_FAST', 'CADENCE_SLOW'].includes( errType ) stemEl?.classList.add(colorsClass[errType]) svgEl?.classList.add(colorsClass[errType]) if (svgEl && isNeedCopyElement) { stemEl?.classList.remove(colorsClass[errType]) stemEl?.classList.add(colorsClass.RIGHT) svgEl?.classList.remove(colorsClass[errType]) svgEl?.classList.add(colorsClass.RIGHT) const copySvg = svgEl.querySelector('.vf-notehead')!.cloneNode(true) as SVGSVGElement copySvg.style.transform = getOffsetPosition(errType) svgEl.style.opacity = '.7' if (stemEl) { stemEl.style.opacity = '.7' } copySvg.id = 'vf-' + active.id + '-copy' copySvg?.classList.add(colorsClass[errType]) // stemEl?.classList.add(colorsClass.RIGHT) // @ts-ignore osmd?.container.querySelector('svg')!.insertAdjacentElement('afterbegin', copySvg) // svgEl?.parentElement?.appendChild(copySvg) } }, 300) } } const removeClass = (el?: HTMLElement | null) => { if (!el) return const classList = el.classList.values() for (const val of classList) { if (val?.indexOf('vf-') !== 0) { el.classList.remove(val) } } } const clearViewColor = () => { for (const id of useedid.value) { removeClass(document.getElementById('vf-' + id)) removeClass(document.getElementById('vf-' + id + '-stem')) const qid = 'vf-' + id + '-copy' const copyEl = document.getElementById(qid) if (copyEl) { copyEl.remove() } } useedid.value = [] } const onRerender = (osmd: OpenSheetMusicDisplay) => { renderLoading.value = false headerRef.value?.autoShow() setTimeout(() => { for (const item of Array.from(document.querySelectorAll('.vf-beam'))) { ;(item as SVGAElement).querySelector('path')?.setAttribute('fill', '#aeaeae') } }) runtime.osmd = osmd allNote.value = getAllNodes(runtime.osmd) setViewColor() const setEvaluatings = (note: any, data: any, dontTransition = true) => { const startNote = getBoundingBoxByverticalNote(note) detailState.evaluatings = { ...detailState.evaluatings, [startNote.measureIndex]: { ...startNote, ...getLeveByScoreMeasure(data.score), score: data.score, dontTransition, }, } } if (record.value.userMeasureScore) { for (const key in record.value.userMeasureScore) { if (Object.prototype.hasOwnProperty.call(record.value.userMeasureScore, key)) { const data = record.value.userMeasureScore[key] for (const time of allNote.value) { if (data.measureRenderIndex == time.noteElement.sourceMeasure.MeasureNumberXML - 1) { if (!time.noteElement.tie) { setEvaluatings(time, data) } else { for (const item of time.noteElement.tie.notes) { const note = getParentNote(item) if (!note) continue setEvaluatings(note, data, item !== time.noteElement.tie.StartNote) } } } } } } } // console.log(detailState.evaluatings, record.value.userMeasureScore) // detailState.activeDetail.originalSpeed = osmd.Sheet.userStartTempoInBPM // RuntimeUtils.setAudioInit() } const onRenderError = () => { renderError.value = true renderLoading.value = false } return () => { const loading = renderLoading.value || detailStatus.value === 'loading' const error = renderError.value || detailStatus.value === 'error' // console.log('ColexiuRender', detail.value.musicSubject, score.value) return (
{!renderLoading.value && (
setViewColor()} /> )}
{loading && !error && } {error && } {score.value && ( <>
{detail.value.musicSheetName}
)}
) } }, })