123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- import { defineComponent, toRefs, PropType, ref, Transition, onMounted, computed, nextTick } from 'vue'
- import { Button, Grid, GridItem, Popup } from 'vant'
- import qs from 'query-string'
- import { MusicSheelDetail, ShaeetStatusType } from '../../colexiu/index.d'
- import BackIcon from '../../colexiu/buttons/icons/icon-back.png'
- import { heardLevel } from '/src/constant/formats'
- import Note from '/src/pages/report/note'
- import PlayerPopup from '/src/pages/report/player'
- import styles from './index.module.less'
- import state from '/src/pages/state'
- import detailState from '/src/pages/detail/state'
- import AgainIcon from '../again.svg'
- import ExplainIcon from '../icon-explain.png'
- import ReplayIcon from '../icon-replay.png'
- import CloseIcon from './close.png'
- import detailStyles from './index.module.less'
- import { postMessage } from '/src/helpers/native-message'
- import { useOriginSearch } from '../../colexiu/uses'
- import { browser } from '/src/helpers/utils'
- import videobg from './videobg.png'
- import 'plyr/dist/plyr.css'
- import Plyr from 'plyr'
- export const active = ref<'pitch' | 'rhythm' | 'completion'>('pitch')
- const visible = ref(false)
- export default defineComponent({
- name: 'ColexiuReportHeader',
- props: {
- detail: {
- type: Object as PropType<MusicSheelDetail | null>,
- },
- record: {
- type: Object,
- },
- className: {
- type: String,
- },
- },
- emits: ['activeChange'],
- setup(props, { emit, expose }) {
- const browserInfo = browser()
- const search = useOriginSearch()
- const { detail, record } = toRefs(props)
- const videoShow = ref(false)
- const isInitPlyr = ref(false)
- const back = () => {
- postMessage({
- api: 'back',
- })
- }
- const _plrl = ref()
- const autoShow = () => {
- if (localStorage.getItem('explain-view') != '1') {
- visible.value = true
- setTimeout(() => {
- visible.value = false
- localStorage.setItem('explain-view', '1')
- }, 5000)
- }
- }
- const again = () => {
- if (search.source == 'evaluation') {
- back()
- return
- }
- const behaviorId = sessionStorage.getItem('behaviorId') || '' + new Date().valueOf()
- const url = qs.stringifyUrl({
- url: location.origin + '/orchestra-music-score',
- query: {
- id: detail.value?.id,
- behaviorId,
- client: browserInfo.isTeacher ? 'teacher' : 'student',
- },
- })
- postMessage({
- api: 'openAccompanyWebView',
- content: {
- url,
- orientation: 0,
- isHideTitle: true,
- statusBarTextColor: false,
- isOpenLight: true,
- },
- })
- }
- const mediaType = computed(() => {
- const subfix = (record.value?.videoFilePath || '').split('.').pop()
- if (subfix === 'wav' || subfix === 'mp3'|| subfix === 'm4a') {
- return 'audio'
- }
- return 'video'
- })
- const openAudioAndVideo = () => {
- videoShow.value = true
- if (isInitPlyr.value) return
- nextTick(() => {
- const id = mediaType.value === 'audio' ? '#audioSrc' : '#videoSrc'
- _plrl.value = new Plyr(id, {
- controls: ['play-large', 'play', 'progress'],
- fullscreen: { enabled: false },
- })
- isInitPlyr.value = true
- })
- }
- expose({
- autoShow,
- })
- return () => {
- return (
- <header class={[styles.header, styles.className]}>
- <div class={styles.info}>
- <div class={styles.leftButton}>
- <Button onClick={back} class={styles.back}>
- <img style={{ width: '30px', height: '30px', display: 'block' }} src={BackIcon} alt="back" />
- </Button>
- <span class={styles.musicName}>{record?.value?.sysMusicScoreName}</span>
- </div>
- {record.value && record.value?.heardLevel ? (
- <Grid class={styles.center} columnNum={5} style={{ alignItems: 'center', 'flex-wrap': 'nowrap' }}>
- <GridItem
- class="van-hairline--right"
- vSlots={{
- icon: () => <span>{(heardLevel as any)[record.value?.heardLevel]}</span>,
- text: () => <span>难度</span>,
- }}
- />
- <GridItem
- class="van-hairline--right"
- vSlots={{
- icon: () => <span>{record.value?.score}分</span>,
- text: () => <span>评测分数</span>,
- }}
- />
- {detailState.isPercussion ? null : (
- <GridItem
- class="van-hairline--right"
- onClick={() => {
- active.value = 'pitch'
- emit('activeChange', 'pitch')
- }}
- vSlots={{
- icon: () => (
- <span style={{ color: 'var(--van-primary-color)' }}>{record.value?.intonation}分</span>
- ),
- text: () => <span class={['switch', { active: active.value === 'pitch' }]}>音准</span>,
- }}
- />
- )}
- <GridItem
- class="van-hairline--right"
- onClick={() => {
- active.value = 'rhythm'
- emit('activeChange', 'rhythm')
- }}
- vSlots={{
- icon: () => <span style={{ color: 'var(--van-primary-color)' }}>{record.value?.cadence}分</span>,
- text: () => <span class={['switch', { active: active.value === 'rhythm' }]}>节奏</span>,
- }}
- />
- {detailState.isPercussion ? null : (
- <GridItem
- onClick={() => {
- active.value = 'completion'
- emit('activeChange', 'completion')
- }}
- vSlots={{
- icon: () => <span style={{ color: '#4EA1FF' }}>{record.value?.integrity}分</span>,
- text: () => <span class={['switch', { active: active.value === 'completion' }]}>完成度</span>,
- }}
- />
- )}
- </Grid>
- ) : null}
- <Grid class={styles.btns} style={{ alignItems: 'center', 'flex-wrap': 'nowrap' }}>
- {record.value?.videoFilePath ? (
- <GridItem
- onClick={openAudioAndVideo}
- vSlots={{
- icon: () => <img src={ReplayIcon} />,
- text: () => <span>回放</span>,
- }}
- />
- ) : null}
- <GridItem
- onClick={() => (visible.value = true)}
- vSlots={{
- icon: () => <img src={ExplainIcon} />,
- text: () => <span>释义</span>,
- }}
- />
- {browserInfo.isStudent ? (
- <GridItem
- onClick={again}
- vSlots={{
- icon: () => <img src={AgainIcon} />,
- text: () => <span>再来一遍</span>,
- }}
- />
- ) : null}
- </Grid>
- </div>
- {record.value ? (
- <div class={styles.demos}>
- {/* <div>
- <Note fill="#01C1B5" />
- <span>演奏正确</span>
- </div> */}
- {active.value === 'pitch' && (
- <>
- <div>
- <Note fill="#FFAB25" shadow x={-3} y={0} />
- <span>音高了</span>
- </div>
- <div>
- <Note fill="#FFAB25" shadow x={-1} y={-2} />
- <span>音低了</span>
- </div>
- </>
- )}
- {!state.isPercussion ? (
- <>
- {active.value === 'rhythm' && (
- <>
- <div>
- <Note fill="#FF4444" shadow x={0} />
- <span>节奏快了</span>
- </div>
- <div>
- <Note fill="#FF4444" shadow x={-3} y={-2} />
- <span>节奏慢了</span>
- </div>
- </>
- )}
- {active.value === 'completion' && (
- <div>
- <Note fill="#CC75FF" />
- <span>时值不足</span>
- </div>
- )}
- </>
- ) : null}
- {/* <div>
- <Note fill="#AEAEAE" />
- <span>未演奏</span>
- </div> */}
- </div>
- ) : null}
- <Popup
- teleport="body"
- show={visible.value}
- class={styles.pop}
- style={{ width: '60vw' }}
- onClickOverlay={() => (visible.value = false)}
- >
- <div class={styles.close} onClick={() => (visible.value = false)}>
- <img src={CloseIcon} />
- </div>
- <h2>图标释义</h2>
- <Grid columnNum={2} class={styles.btns} border={false}>
- <GridItem
- vSlots={{
- text: () => <span>绿色音符:演奏正确</span>,
- icon: () => <Note fill="#01C1B5" />,
- }}
- />
- <GridItem
- vSlots={{
- text: () => <span>红色音符:错音</span>,
- icon: () => <Note fill="#FF4444" />,
- }}
- />
- <GridItem
- vSlots={{
- text: () => <span>音符重影(红色在前):节奏过快</span>,
- icon: () => <Note fill="#FF4444" shadow x={0} />,
- }}
- />
- <GridItem
- vSlots={{
- text: () => <span>音符重影(红色在后):节奏慢了</span>,
- icon: () => <Note fill="#FF4444" shadow x={-3} y={-2} />,
- }}
- />
- <GridItem
- vSlots={{
- text: () => <span>音符重影(黄色在上):音高了</span>,
- icon: () => <Note fill="#FFAB25" shadow x={-3} y={0} />,
- }}
- />
- <GridItem
- vSlots={{
- text: () => <span>音符重影(黄色在下):音低了</span>,
- icon: () => <Note fill="#FFAB25" shadow x={-1} y={-2} />,
- }}
- />
- <GridItem
- vSlots={{
- text: () => <span>紫色音符:完整度不足</span>,
- icon: () => <Note fill="#CC75FF" />,
- }}
- />
- <GridItem
- vSlots={{
- text: () => <span>灰色音符:未演奏</span>,
- icon: () => <Note fill="#AEAEAE" />,
- }}
- />
- </Grid>
- </Popup>
- <Popup
- teleport="body"
- v-model:show={videoShow.value}
- class={styles.videoContent}
- onClose={() => {
- _plrl.value?.pause()
- }}
- >
- <div class={styles.close} onClick={() => (videoShow.value = false)}>
- <img src={CloseIcon} />
- </div>
- {mediaType.value === 'audio' && (
- <audio id="audioSrc" src={record.value?.videoFilePath} controls="false" preload="metadata" playsinline />
- )}
- {mediaType.value === 'video' && (
- <div class={styles.box}>
- <video
- id="videoSrc"
- class={styles.videoBox}
- src={record.value?.videoFilePath}
- data-poster={videobg}
- preload="metadata"
- playsinline
- />
- </div>
- )}
- </Popup>
- </header>
- )
- }
- },
- })
|