import TheSticky from '@/components/the-sticky' import styles from './new-index.module.less' import { useEventListener, useWindowScroll } from '@vueuse/core' import { postMessage } from '@/helpers/native-message' import iconShare from '../../images/icon-share.png' import oStart from '../album-detail/icon-hart.png' import iStart from '../album-detail/icon-hart-active.png' import iconDownload from './images/icon-download.png' import iconMemberSmall from './images/icon-member-small.png' import { computed, defineComponent, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue' import umiRequest from 'umi-request' import { useRoute, useRouter } from 'vue-router' import request from '@/helpers/request' import ColHeader from '@/components/col-header' import { Button, Cell, CellGroup, Dialog, Icon, Image, Popup, RadioGroup, Radio, Toast, Picker } from 'vant' import { useRect } from '@vant/use' import { Vue3Lottie } from 'vue3-lottie' import { getRandomKey, musicBuy } from '../music' import { getOssUploadUrl, state } from '@/state' import { browser, moneyFormat } from '@/helpers/utils' import { orderStatus } from '@/views/order-detail/orderStatus' import AstronautJSON from './animate/bigLoad.json' import ColShare from '@/components/col-share' import iconListen from './images/icon_listen.png' import iconTeacher from '@common/images/icon_teacher.png' import emtpy from './images/emtpy.png' import activeButtonIcon from './images/icon_checkbox.png' import inactiveButtonIcon from './images/icon_checkbox_default.png' import staffDetafult from './images/staff-default.png' import firstDefault from './images/first-default.png' import fixedDefault from './images/fixed-default.png' import Plyr from 'plyr' import 'plyr/dist/plyr.css' import Download from './download' import { getInstrumentName } from '@/constant/instruments' export default defineComponent({ name: 'new-index', setup() { localStorage.setItem('behaviorId', getRandomKey()) const router = useRouter() const route = useRoute() const loading = ref(false) const background = ref('rgba(55, 205, 177, 0)') const color = ref('#fff') const aId = Number(route.query.activityId) || 0 const studentActivityId = ref(aId) const isError = ref(false) const headers = ref(null) const footers = ref(null) const heightInfo = ref('0') const musicDetail = ref(null) const audioFileUrl = ref('') let showImg = [] as any const firstList = ref>([]) const fixedList = ref>([]) const staffList = ref>([]) const accompanyUrl = ref('') const downloadStatus = ref(false) const staff = reactive({ status: false, radio: 'staff' // staff first fixed }) const colors: any = { FREE: { color: '#88D5AC', text: '免费' }, VIP: { color: '#FFFA6B', text: '会员' }, CHARGE: { color: '#AEFAFF', text: '点播' } } // 更改预览状态 const onChangeStaff = (type: string) => { staff.radio = type staff.status = false } watch( () => staff.radio, (val: string) => { if (val == 'first') { showImg = firstList.value } else if (val == 'fixed') { showImg = fixedList.value } else { showImg = staffList.value } } ) const FetchList = async (id?: any) => { if (loading.value) { return } loading.value = true isError.value = false try { const res = await request.get(`/music/sheet/detail/${route.query.id}`, { prefix: state.platformType === 'TEACHER' ? '/api-teacher' : '/api-student' }) musicDetail.value = res.data // 取原音,如果有多个则默认第一个 const background = res.data.background audioFileUrl.value = background && background.length > 0 ? background[0].audioFileUrl : '' // const arrImgs = res.data.musicImg ? res.data.musicImg.split(',') : [] showImg = res.data.musicImg ? res.data.musicImg.split(',') : [] firstList.value = res.data.firstTone ? res.data.firstTone.split(',') : [] fixedList.value = res.data.fixedTone ? res.data.fixedTone.split(',') : [] staffList.value = res.data.musicImg ? res.data.musicImg.split(',') : [] // if (!showImg.value) { // setAccompanyUrl() // window.addEventListener( // 'message', // async e => { // // 给图片设置背景色 // const tempCanvas = await imgToCanvas(e.data) // const img = convasToImg(tempCanvas) // // 开始上传图片 // uploadFunction(img) // }, // false // ) // } nextTick(() => { renderStaff() }) } catch (error) { isError.value = true } if (musicDetail.value?.musicSheetType !== 'CONCERT') { loading.value = false } } const base64ToBlob = data => { const arr = data.split(','), mime = arr[0].match(/:(.*?);/)[1] const bstr = atob(arr[1]) let n = bstr.length const u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new Blob([u8arr], { type: mime }) } const uploadFunction = async file => { try { const formData = new FormData() const fileName = new Date().getTime() + Math.ceil(Math.random() * 1000) + '.png' const keyTime = new Date().getTime() + fileName const obj = { filename: fileName, bucketName: 'cloud-coach', postData: { filename: fileName, acl: 'public-read', key: keyTime } } const res = await request.post(state.platformApi + '/getUploadSign', { data: obj }) Toast.loading({ message: '加载中...', forbidClick: true, loadingType: 'spinner', duration: 0 }) const dataObj = { policy: res.data.policy, signature: res.data.signature, key: keyTime, KSSAccessKeyId: res.data.kssAccessKeyId, acl: 'public-read', name: fileName } for (const key in dataObj) { formData.append(key, dataObj[key]) } const files = base64ToBlob(file) formData.append('file', files, fileName) const ossUploadUrl = getOssUploadUrl('cloud-coach') await umiRequest(ossUploadUrl, { method: 'POST', data: formData }) Toast.clear() const imgurl = getOssUploadUrl('cloud-coach') + keyTime await request.post(state.platformApi + '/open/music/sheet/img', { data: { musicSheetId: musicDetail.value.id, musicImg: imgurl } }) // showImg.value = imgurl } catch (e) { console.log(e) } } const setAccompanyUrl = () => { let url = location.origin if ( location.host.includes('dev.colexiu') || location.host.includes('192.168') || location.host.includes('localhost') ) { url = 'https://dev.colexiu.com' } const music = musicDetail.value let subjectId = '' if (music.background && music.background.length > 0) { subjectId = music.background[0].id } accompanyUrl.value = url + `/accompany/colxiu-website.html?id=${music.id}&part-index=${subjectId}` } const player = ref(null) const audio = ref(null) const freeRate = ref(0) const initAudio = async () => { const controls = [ // 'play-large', 'play', 'progress', 'captions', // 'fullscreen', 'duration' ] player.value = new Plyr(audio.value, { controls: controls }) const config = await request.get( '/api-student/sysConfig/queryByParamNameList', { params: { paramNames: 'music_sheet_free_rate' } } ) freeRate.value = config.data[0]?.paramValue || 0 player.value.on('timeupdate', () => { // 允许播放时间 const players = player.value const playTime = (players.duration * freeRate.value) / 100 || 0 // 时间,不能播放 if (players.currentTime >= playTime && !buyState.value.play) { players.stop() // players.pause() } }) } const showLoading = (e: any) => { console.log(e) if (e.data?.api === 'musicStaffRender') { loading.value = e.data.loading } } onMounted(async () => { await FetchList() const { height } = useRect(headers as any) const footer = useRect(footers as any) heightInfo.value = height + footer.height // 初始化音频 if (audioFileUrl.value) { initAudio() } window.addEventListener('message', showLoading) // useEventListener(document, 'scroll', evt => { // const { y } = useWindowScroll() // if (y.value > 20) { // background.value = `rgba(255, 255, 255)` // color.value = 'black' // postMessage({ // api: 'backIconChange', // content: { iconStyle: 'black' } // }) // } else { // background.value = 'transparent' // color.value = '#fff' // postMessage({ // api: 'backIconChange', // content: { iconStyle: 'white' } // }) // } // }) }) onUnmounted(() => { window.removeEventListener('message', showLoading) }) const toggleFavorite = async () => { try { await request.post('/music/sheet/favorite/' + musicDetail.value?.id, { prefix: state.platformType === 'TEACHER' ? '/api-teacher' : '/api-student' }) musicDetail.value.favorite = musicDetail.value?.favorite ? 0 : 1 musicDetail.value.favoriteCount = musicDetail.value?.favorite ? musicDetail.value.favoriteCount + 1 : musicDetail.value.favoriteCount - 1 < 0 ? 0 : musicDetail.value.favoriteCount - 1 setTimeout(() => { Toast(musicDetail.value?.favorite ? '收藏成功' : '取消收藏成功') }, 100) } catch (error) { // } } const onAddCourse = async () => { try { const res = await request.post('/api-teacher/courseCourseware/submit', { data: { musicSheetId: musicDetail.value.id, clientType: 'TEACHER', userId: state.user.data?.userId } }) console.log(res) setTimeout(() => { musicDetail.value.coursewareId = res.data.id || '' Toast('添加成功') musicDetail.value.coursewareStatus = 1 }, 100) } catch { // } } const removeCourse = async () => { Dialog.confirm({ title: '提示', message: '您是否确定移除课件', confirmButtonColor: '#269a93', cancelButtonText: '取消', confirmButtonText: '确定' }).then(async () => { try { await request.post( '/api-teacher/courseCourseware/remove/' + musicDetail.value.coursewareId, { data: {} } ) setTimeout(() => { Toast('移除成功') musicDetail.value.coursewareStatus = 0 }, 100) } catch { // } }) } const onBuy = async () => { const music = musicDetail.value orderStatus.orderObject.orderType = 'MUSIC' orderStatus.orderObject.orderName = music.musicSheetName orderStatus.orderObject.orderDesc = music.musicSheetName orderStatus.orderObject.actualPrice = music.musicPrice orderStatus.orderObject.recomUserId = route.query.recomUserId || 0 orderStatus.orderObject.activityId = route.query.activityId || 0 orderStatus.orderObject.orderNo = '' orderStatus.orderObject.orderList = [ { orderType: 'MUSIC', goodsName: music.musicSheetName, actualPrice: music.musicPrice, ...music } ] const res = await request.post('/api-student/userOrder/getPendingOrder', { data: { goodType: 'MUSIC', bizId: music.id } }) const result = res.data if (result) { Dialog.confirm({ title: '提示', message: '您有一个未支付的订单,是否继续支付?', theme: 'round-button', className: 'confirm-button-group', cancelButtonText: '取消订单', confirmButtonText: '继续支付' }) .then(async () => { orderStatus.orderObject.orderNo = result.orderNo orderStatus.orderObject.actualPrice = result.actualPrice orderStatus.orderObject.discountPrice = result.discountPrice orderStatus.orderObject.paymentConfig = { ...result.paymentConfig, paymentVendor: result.paymentVendor, paymentVersion: result.paymentVersion } routerTo() }) .catch(() => { Dialog.close() // 只用取消订单,不用做其它处理 cancelPayment(result.orderNo) }) } else { routerTo() } } const routerTo = () => { const music = musicDetail.value router.push({ path: '/orderDetail', query: { orderType: 'MUSIC', musicId: music.id } }) } const cancelPayment = async (orderNo: string) => { try { await request.post('/api-student/userOrder/orderCancel', { data: { orderNo } }) } catch { // } } const paymentType = computed(() => { let paymentType = musicDetail.value?.paymentType if (typeof paymentType === 'string') { paymentType = paymentType.split(',') return paymentType } return [] }) const buyState = computed(() => { const music = musicDetail.value return { play: music.play ? true : false, // 是否可以播放 free: music?.paymentType.includes('FREE'), charge: music?.paymentType.includes('CHARGE'), vip: music?.paymentType.includes('VIP'), buy: music?.orderStatus === 'PAID' // 是否已买 } }) const shareStatus = ref(false) const shareUrl = ref('') const shareDiscount = ref(0) // console.log(data) const onShare = async () => { try { const res = await request.post('/api-teacher/open/musicShareProfit', { data: { bizId: musicDetail.value?.id, userId: state.user.data?.userId } }) let url = location.origin + `/teacher/#/shareMusic?id=${musicDetail.value?.id}&recomUserId=${state.user.data?.userId}&userType=${state.platformType}` // 判断是否有活动 if (res.data.discount === 1) { url += `&activityId=${res.data.activityId}` } shareDiscount.value = res.data.discount || 0 console.log(url) shareUrl.value = url shareStatus.value = true return } catch { // } } const staffData = reactive({ open: false, iframeSrc: '', musicXml: '', instrumentName: '', iframeRef: null as any, partIndex: 0, partList: [] as any[] }) /** 渲染五线谱 */ const renderStaff = () => { staffData.iframeSrc = `${location.origin}${location.pathname}osmd/index.html` staffData.musicXml = musicDetail.value?.xmlFileUrl || '' staffData.partList = musicDetail.value?.background || [] staffData.instrumentName = getInstrumentName( staffData.partList[staffData.partIndex]?.track ) } const musicIframeLoad = () => { const iframeRef: any = document.getElementById('staffIframeRef') if (iframeRef && iframeRef.contentWindow.renderXml) { iframeRef.contentWindow.renderXml( staffData.musicXml, staffData.partIndex ) } } const resetRender = () => { const iframeRef: any = document.getElementById('staffIframeRef') if (iframeRef && iframeRef.contentWindow.renderXml) { iframeRef.contentWindow.resetRender(staffData.partIndex) staffData.instrumentName = getInstrumentName( staffData.partList[staffData.partIndex]?.track ) } } const partColumns = computed(() => { return staffData.partList.map((item: any, index: number) => { const instrumentName = getInstrumentName(item.track) return { text: item.track + (instrumentName ? `(${instrumentName})` : ''), value: index } }) }) return () => (
{/* {albumDetail.value?.paymentType === 'CHARGE' && ( 付费 )} */}
{musicDetail.value?.musicSheetName}
{!musicDetail.value?.composer ? `上传者:${musicDetail.value?.addName || ''}` : `作曲:${musicDetail.value?.composer || ''}`}
{musicDetail.value?.id && ( <> {musicDetail.value?.musicTagNames && musicDetail.value?.musicTagNames.split(',').map(name => ( {name} ))} )}
分享
{ if (showImg.length > 0) { downloadStatus.value = true } else { Toast('暂无图片') } }} > 下载
toggleFavorite()} > {musicDetail.value?.favoriteCount}
{musicDetail.value?.id && !buyState.value.play && (
{buyState.value.charge && buyState.value.vip ? ( <> 开通会员或点播单曲,即可自由练习该曲谱 ¥{moneyFormat(musicDetail.value?.musicPrice)} ) : buyState.value.vip ? ( 此曲谱为会员专享,开通会员即可自由练习该曲谱 ) : buyState.value.charge ? ( <> 此曲谱为点播曲谱,点播即可自由练习该曲谱 ¥{moneyFormat(musicDetail.value?.musicPrice)} ) : ( '' )}
)}
{musicDetail.value?.notation ? ( { staff.status = true }} > 转谱 ) : null}

{(musicDetail.value?.musicSheetName ? musicDetail.value?.musicSheetName : '') + (staffData.instrumentName ? `(${staffData.instrumentName})` : '')}

{musicDetail.value?.musicSheetType === 'CONCERT' ? ( <> {loading.value && ( <>

加载中...

)} ) : ( <> {showImg.length > 0 ? ( ) : loading.value ? ( <>

加载中...

) : (

暂无乐谱预览图

)} )}
{musicDetail.value?.id && (
{audioFileUrl.value && ( <> {!buyState.value.play && freeRate.value != 100 && freeRate.value != 0 && (
每首曲目可试听{freeRate.value}%
)}
)}
{/* 判断是否是免费的,或者已经购买过 */} {buyState.value.play ? ( ) : (
{/* 判断是否是需要收费的 */} {buyState.value.charge && ( )} {/* 判断是否有会员的 */} {buyState.value.vip && ( )}
)}
)}
{shareDiscount.value === 1 && (
专属优惠
)}

{musicDetail.value?.musicSheetName}

作曲人:{musicDetail.value?.composer}

选择转换曲谱
onChangeStaff('staff')} > {{ icon: () => ( ), title: () => 五线谱, value: () => ( {{ icon: (props: any) => ( ) }} ) }} onChangeStaff('first')} > {{ icon: () => ( ), title: () => 简谱-首调, value: () => ( {{ icon: (props: any) => ( ) }} ) }} onChangeStaff('fixed')} > {{ icon: () => ( ), title: () => 简谱-固定调, value: () => ( {{ icon: (props: any) => ( ) }} ) }}
{ staffData.open = false staffData.partIndex = value.value nextTick(() => { resetRender() }) }} onCancel={() => (staffData.open = false)} />
) } })