123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- import { defineComponent, Directive, Ref, ref, Transition, Teleport, nextTick, computed, onMounted } from 'vue'
- import { Button, Cell, CellGroup, Dialog, Divider, Popover, Slider, Switch } from 'vant'
- import ButtonIcon from './icon'
- import runtime, * as RuntimeUtils from '/src/pages/detail/runtime'
- import Speed from '/src/pages/detail/speed'
- import detailState from '/src/pages/detail/state'
- import SettingState from '/src/pages/detail/setting-state'
- import appState from '/src/state'
- import FloatWraper from './float-wraper'
- import Evaluating, { evaluatStopPlay } from './evaluating'
- import iconTitle from '../popups/evaluating/icons/title.svg'
- import iconCancel from '../popups/evaluating/icons/cancel.svg'
- import iconConfirm from '../popups/evaluating/icons/confirm.svg'
- import { useClientType, useMenu, useOriginSearch, useReload } from '../uses'
- import { permissionPopup } from '../popups/permission/permission'
- import { open as openMusicList } from '../music-list'
- import { postMessage } from '/src/helpers/native-message'
- import Popups from '../popups'
- import Setting from '../popups/setting'
- import evastyles from '../popups/evaluating/index.module.less'
- import ModelWraper from './model-wraper'
- import Follow from '../popups/follow'
- import { switchProps } from '../popups/setting/evaluat'
- import iconBack from './icons/icon-back.png'
- import iconFollowEndBtn from '../popups/follow/icons/icon-followEndBtn.png'
- import iconEvaluatingEnd from './icons/icon-evaluatingEnd.png'
- import iconCameraOff from './icons/icon-camera-off.png'
- import iconCameraOn from './icons/icon-camera-on.png'
- import store from 'store'
- import styles from './index.module.less'
- import { sendBackRecordTotalTime } from '../App'
- import { unitTestData } from '../unitTest'
- export const confirmShow: Ref<boolean> = ref(false)
- /**评测开始按钮状态 */
- export const startButtonShow = ref(true)
- export const evaluatingRef: Ref<any> = ref({})
- export const settingPopup: Ref<any> = ref(null)
- export const suggestPopup: Ref<any> = ref(null)
- export const followRef = ref<any>(null)
- let openSuggestPopupFn = () => {}
- export const openSuggestPopup = () => {
- openSuggestPopupFn()
- }
- /**
- * 前置验证是否在APP中并且已经付费
- * @param cb 回调函数 {status} 验证状态
- * @returns
- */
- const beforeCheck = (cb: (status: boolean) => void) => {
- const search = useOriginSearch()
- const setting = (search.setting || {}) as any
- const chargeType = detailState.activeDetail?.paymentType
- const orderStatus = detailState.activeDetail?.orderStatus
- const play = detailState.activeDetail?.play
- const membershipDays = appState.user?.membershipDays || 0
- if (useClientType() === 'web' || play || setting.feeType === 'FREE') {
- return cb(true)
- }
- if (
- chargeType?.includes('VIP') &&
- chargeType?.includes('CHARGE') &&
- !(membershipDays > 0) &&
- orderStatus !== 'PAID'
- ) {
- permissionPopup.active = 'memberAndDemand'
- permissionPopup.show = true
- return cb(false)
- }
- if (chargeType === 'VIP' && !(membershipDays > 0)) {
- permissionPopup.active = 'member'
- permissionPopup.show = true
- return cb(false)
- }
- if (chargeType === 'CHARGE' && orderStatus !== 'PAID') {
- permissionPopup.active = 'demand'
- permissionPopup.show = true
- return cb(false)
- }
- cb(true)
- }
- const back: () => void = () => {
- sendBackRecordTotalTime()
- postMessage({
- api: 'back',
- })
- }
- export type IModelType = 'practice' | 'evaluation' | 'follow' | 'init'
- export const modelType = ref<IModelType>('init')
- export const onChangeModelType = (type: IModelType) => {
- if (type === modelType.value) return
- if (type === 'evaluation') {
- RuntimeUtils.changeSpeed(detailState.activeDetail?.originalSpeed, false)
- // 评测模式
- runtime.evaluatingStatus = true
- } else {
- const speeds = store.get('speeds') || {}
- const search = useOriginSearch()
- const speed = speeds[search.id as any]
- // 还原速度
- if (speed) {
- RuntimeUtils.changeSpeed(speeds[search.id as any])
- }
- }
- nextTick(() => {
- modelType.value = type
- })
- }
- export default defineComponent({
- name: 'Colexiu-Buttons',
- props: {
- onSetMusicScoreType: {
- type: Function,
- default: (n: any) => {},
- },
- },
- emits: ['setMusicScoreType'],
- setup(props, { emit }) {
- const search = useOriginSearch()
- const speedRef = ref()
- const [show] = useMenu()
- const camera = ref(false)
- //根据路由传参设置模式
- const useRouteSetModelType = () => {
- const modelType: IModelType = search.modelType as IModelType
- if (modelType && modelType != 'evaluation') {
- onChangeModelType(modelType)
- }
- }
- onMounted(() => {
- useRouteSetModelType()
- })
- // 固定调
- const musicTypeShow = ref(false)
- const musicAction = ref('')
- const onSelect = (action: any) => {
- musicAction.value = action.text
- confirmShow.value = true
- }
- const hanldeSelect = () => {
- if (musicAction.value === '五线谱') {
- // if (SettingState.sett.type == 'staff') return
- SettingState.sett.type = 'staff'
- } else if (musicAction.value === '简谱') {
- // if (SettingState.sett.type === 'jianpu' && !SettingState.sett.keySignature) return
- SettingState.sett.type = 'jianpu'
- SettingState.sett.keySignature = false
- } else if (musicAction.value === '固定调') {
- // if (SettingState.sett.type === 'jianpu' && SettingState.sett.keySignature) return
- SettingState.sett.type = 'jianpu'
- SettingState.sett.keySignature = true
- }
- sessionStorage.setItem('notation', SettingState.sett.type)
- }
- const musicType = (type: string) => {
- if (type === 'staff') {
- return SettingState.sett.type === type
- } else if (type === 'shoudiao') {
- return SettingState.sett.type === 'jianpu' && !SettingState.sett.keySignature
- } else if (type === 'guding') {
- return SettingState.sett.type === 'jianpu' && SettingState.sett.keySignature
- }
- }
- return () => {
- const changeModeIsDisabled =
- (detailState.activeDetail?.isAppPlay
- ? detailState.activeDetail?.midiUrl === ''
- : runtime.isFirstPlay || runtime.audiosInstance?.length == 1) ||
- runtime.evaluatingStatus ||
- (detailState.activeDetail?.isAppPlay && detailState.midiPlayIniting)
- return (
- <div
- onClick={(e: Event) => e.stopPropagation()}
- class={[styles.container, show.value ? '' : styles.outUp]}
- style={search.headerHeight ? { height: '1rem', paddingTop: '0.25rem' } : ''}
- >
- <div class={styles.leftButton}>
- {search?.modelType && !search.unitId ? null : <img class={styles.backbtn} src={iconBack} onClick={back} />}
- <div class={styles.titleWrap}>
- <div class={styles.title}>{detailState.activeDetail?.musicSheetName}</div>
- {search.albumName && <div class={styles.album}>{search.albumName}</div>}
- </div>
- </div>
- <div class={styles.centerButton}>
- <Transition name="finish">
- {!startButtonShow.value && (
- <Button
- class={[styles.button, styles.finish]}
- onClick={() => {
- evaluatingRef.value?.playerStop?.()
- }}
- >
- <img style={{width: '100%', display: 'block'}} src={iconEvaluatingEnd} />
- </Button>
- )}
- </Transition>
- <Transition name="finish">
- {followRef?.value?.data.start && (
- <Button
- class={[styles.button, styles.finish, styles.followEndBtn]}
- onClick={() => {
- followRef.value?.handleEnd?.()
- }}
- >
- <img style={{width: '100%', display: 'block'}} src={iconFollowEndBtn} />
- </Button>
- )}
- </Transition>
- </div>
- <div class={[styles.moreButton]} style={{ opacity: detailState.initRendered ? 1 : 0 }}>
- {!search?.modelType && modelType.value !== 'init' && !detailState.frozenMode && (
- <Button
- data-step="m0"
- class={[styles.button, styles.hasText]}
- disabled={(runtime.evaluatingStatus && !startButtonShow.value) || followRef.value?.data.start}
- onClick={() => {
- // 不是课后训练选段和单元测验选段,切换模式去除选段
- if (!unitTestData.isSelectMeasureMode && detailState.sectionStatus) {
- RuntimeUtils.clearSectionStatus()
- }
- if (modelType.value === 'practice') {
- // 当前为练习模式,需要停止播放
- RuntimeUtils.resetPlayStatus()
- RuntimeUtils.setCurrentTime(0)
- }
- if (modelType.value === 'evaluation') {
- runtime.evaluatingStatus = false
- evaluatStopPlay()
- }
- modelType.value = 'init'
- }}
- >
- <ButtonIcon
- key="modelType"
- name={
- ['init', 'practice'].includes(modelType.value)
- ? 'modelType'
- : ['follow'].includes(modelType.value)
- ? 'modelType1'
- : 'modelType2'
- }
- />
- <span>模式</span>
- </Button>
- )}
- {modelType.value === 'evaluation' && (
- <>
- <Popover
- v-model:show={camera.value}
- overlay={false}
- placement="bottom-end"
- class="cameraPopover"
- show-arrow={false}
- vSlots={{
- reference: () => (
- <div onClick={(e: Event) => {
- if (!startButtonShow.value) e.stopPropagation()
- }}>
- <Button class={[styles.button, styles.hasText]} disabled={!startButtonShow.value}>
- <img src={SettingState.sett.camera ? iconCameraOn : iconCameraOff} />
- <span>摄像头</span>
- </Button>
- </div>
- ),
- }}
- >
- <CellGroup border={false}>
- {
- <Cell center title="摄像头">
- <div style="display:flex;justify-content: flex-end;">
- <Switch v-model={SettingState.sett.camera} {...switchProps}>
- off
- </Switch>
- </div>
- </Cell>
- }
- {SettingState.sett.camera && (
- <Cell class="cameraOpacity" center title="透明度">
- <Slider
- style={{width: '90%'}}
- min={0}
- max={100}
- v-model:modelValue={SettingState.sett.opacity}
- v-slots={{
- button: () => <div class={styles.slider}>{SettingState.sett.opacity}</div>,
- }}
- ></Slider>
- </Cell>
- )}
- </CellGroup>
- </Popover>
- <Evaluating ref={evaluatingRef} />
- </>
- )}
- {modelType.value === 'practice' && (
- <>
- <Button
- data-step="m1"
- class={[styles.button, styles.hasText]}
- onClick={() => RuntimeUtils.changeMode(runtime.mode === 'background' ? 'music' : 'background')}
- disabled={changeModeIsDisabled}
- >
- <ButtonIcon key="music" name={runtime.mode === 'music' ? 'music' : 'accompaniment'} />
- <span>{runtime.mode === 'background' ? '伴奏' : '原声'}</span>
- </Button>
- {/* 如果为单元测试和课后训练 */}
- {unitTestData.isSelectMeasureMode ? null : (
- <Button
- data-step="m2"
- class={[styles.button, styles.hasText]}
- onClick={RuntimeUtils.sectionChange}
- disabled={runtime.evaluatingStatus || runtime.playState === 'play'}
- >
- <ButtonIcon
- key="section"
- name={
- 'section' +
- (detailState.section.length && detailState.section.length <= 2
- ? detailState.section.length
- : '')
- }
- />
- <span>选段</span>
- </Button>
- )}
- <Button
- data-step="m3"
- class={[styles.button, styles.hasText]}
- onClick={() => {
- SettingState.sett.fingering = !SettingState.sett.fingering
- RuntimeUtils.event.emit('settingFingeringChange')
- }}
- >
- <ButtonIcon key="music" name={SettingState.sett.fingering ? 'fingeringOn' : 'fingeringOff'} />
- <span>指法</span>
- </Button>
- </>
- )}
- {['practice', 'evaluation'].includes(modelType.value) && !search.lessonTrainingId && (
- <Popover
- trigger="manual"
- overlay={false}
- placement="bottom"
- class={styles.popover}
- show={show.value && runtime.speedShow && !(runtime.evaluatingStatus || runtime.playState === 'play')}
- // @ts-ignore
- onUpdate:show={(show: boolean) => (runtime.speedShow = show)}
- vSlots={{
- reference: () => (
- <Button
- data-step="m4"
- class={[styles.button, styles.hasText, styles.speedButton]}
- disabled={runtime.evaluatingStatus || runtime.playState === 'play'}
- onClick={() => {
- speedRef.value?.refUpdateSpeed(runtime.speed)
- runtime.speedShow = !runtime.speedShow
- }}
- >
- <ButtonIcon name="speed" />
- <span>速度</span>
- <span class={styles.label}>{runtime.speed}</span>
- </Button>
- ),
- }}
- >
- <Speed
- ref={speedRef}
- updateSpeed={(speed: number) => (runtime.speed = speed)}
- changed={RuntimeUtils.changeSpeed}
- mode={runtime.mode}
- changeMode={RuntimeUtils.changeMode}
- lib={{ speed: runtime.speed }}
- class={styles.speed}
- />
- </Popover>
- )}
- {detailState.activeDetail?.notation ? (
- <Popover
- class={styles.toggleMusicType}
- placement="bottom-end"
- show={musicTypeShow.value}
- // @ts-ignore
- onUpdate:show={(val: boolean) => {
- if (
- runtime.playState === 'play' ||
- (runtime.evaluatingStatus && !startButtonShow.value) ||
- followRef.value?.data.start
- ) {
- } else {
- musicTypeShow.value = val
- }
- }}
- >
- {{
- reference: () => (
- <Button
- disabled={
- runtime.playState === 'play' ||
- (runtime.evaluatingStatus && !startButtonShow.value) ||
- followRef.value?.data.start
- }
- class={[styles.button, styles.hasText, styles.speedButton]}
- >
- <ButtonIcon name="icon-zhuanpu" />
- <span>{musicType('staff') ? '转简谱' : '转五线谱'}</span>
- </Button>
- ),
- default: () => (
- <>
- <div role="menuitem" class="van-popover__action" onClick={() => onSelect({ text: '五线谱' })}>
- <ButtonIcon key="type" name={musicType('staff') ? 'icon-staff-active' : 'icon-staff'} />
- <div class={['action-text', musicType('staff') && 'action-active']}>五线谱</div>
- </div>
- <div role="menuitem" class="van-popover__action" onClick={() => onSelect({ text: '简谱' })}>
- <ButtonIcon key="type" name={musicType('shoudiao') ? 'shuodiao-active' : 'shuodiao'} />
- <div class={['action-text', musicType('shoudiao') && 'action-active']}>首调</div>
- </div>
- <div role="menuitem" class="van-popover__action" onClick={() => onSelect({ text: '固定调' })}>
- <ButtonIcon key="type" name={musicType('guding') ? 'guding-active' : 'guding'} />
- <div class={['action-text', musicType('guding') && 'action-active']}>固定调</div>
- </div>
- </>
- ),
- }}
- </Popover>
- ) : null}
- {detailState.initRendered && (
- <>
- <Button
- class={[styles.button, styles.hasText]}
- onClick={() => {
- settingPopup.value?.onShow()
- }}
- disabled={runtime.evaluatingStatus && !startButtonShow.value}
- >
- <ButtonIcon name="setting" />
- <span>设置</span>
- </Button>
- <Popups
- ref={settingPopup}
- style={{
- borderRadius: '8px',
- }}
- >
- <Setting active={modelType.value == 'practice' ? '2' : modelType.value == 'evaluation' ? '3' : '1'} />
- </Popups>
- </>
- )}
- </div>
- <FloatWraper />
- <Dialog.Component
- teleport="body"
- class={evastyles.confirm}
- style={{
- overflow: 'initial',
- }}
- vSlots={{
- title: () => <img class={evastyles.iconTitle} src={iconTitle} />,
- footer: () => (
- <div class={evastyles.footer}>
- <img src={iconCancel} onClick={() => (confirmShow.value = false)} />
- <img
- src={iconConfirm}
- onClick={() => {
- hanldeSelect()
- useReload()
- }}
- />
- </div>
- ),
- }}
- v-model:show={confirmShow.value}
- message={'设置成功,是否立即重新加载?'}
- />
- </div>
- )
- }
- },
- })
|