123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- <script setup lang="ts">
- import { computed, onMounted, reactive, ref, watch } from 'vue'
- import { useRoute } from 'vue-router'
- import { useAudioManager } from '@/hook'
- import OpenApp from '@/components/OpenApp/index.vue'
- import { registerWxopenButton } from '@/utils/utils'
- import { getTrialAudioLXXRequest, getTrialAudioPTRequest } from '@/api/sectionAudition'
- import dayjs from 'dayjs'
- const logo = require('../assets/logo.png')
- const pause = require('../assets/pause.png')
- const play = require('../assets/play.png')
- const remindLogo = require('../assets/remind_logo.png')
- const close = require('../assets/close.png')
- const space = 32
- const defaultSection = {
- id: '',
- name: '',
- videoId: null,
- aiCourseSkuId: '',
- sort: 0,
- createTime: '',
- updateTime: '',
- readCount: 0,
- aiCourseItemChapterId: '',
- type: 1,
- payType: 0,
- freeTime: 0,
- audioId: '',
- imgCover: '',
- chapterName: '',
- chapterSort: 0,
- richText: null,
- splitList: null,
- readCountStr: '',
- audio: {
- id: null,
- audioUrl: '',
- parentId: null,
- createTime: null,
- updateTime: null,
- duration: ''
- }
- }
- const defaultCourse = {
- id: '',
- name: '',
- imgCover: '',
- imgCoverMini: '',
- createTime: '',
- updateTime: '',
- price: 0,
- markingPrice: 0,
- categoryId: '',
- courseCount: 0,
- aiCourseSpuId: '',
- suitAge: '',
- wxNumber: null,
- wxQrCode: null,
- wxName: null,
- wxHeadImg: null,
- description: '',
- mediaType: 1,
- simpleDescription: '',
- sort: 0,
- showChapter: 1,
- courseType: 0,
- subCategoryId: '',
- activityTag: null,
- courseTags: [],
- paidCount: 0,
- latestLearnedRecordId: null,
- latestLearnedRecord: null,
- abilityList: null,
- commentCount: null,
- shareCount: null,
- collectCount: null,
- isCollect: 0,
- hasPaid: 0,
- isComment: null,
- isShare: null,
- imgCoverWidth: null,
- imgCoverHeight: null,
- shareUrl: '',
- shareUrlQRCode: null,
- groupBuyActivity: null,
- groupBuyActivityId: null,
- groupBuyActivityUrl: null,
- totalLearnUser: 0,
- cashbackActivity: 0,
- cashbackActivityId: null,
- textbook: null,
- checkInExplain: null,
- chapterList: [
- {
- id: '',
- name: '',
- createTime: '',
- updateTime: null,
- aiCourseSkuId: '',
- sort: 0,
- coverImgUrl: '',
- itemList: [
- {
- id: '',
- name: '',
- videoId: null,
- aiCourseSkuId: '',
- sort: 0,
- createTime: '',
- updateTime: '',
- readCount: 0,
- aiCourseItemChapterId: '',
- type: 1,
- payType: 0,
- freeTime: 0,
- audioId: '',
- imgCover: '',
- chapterName: '',
- chapterSort: 0,
- richText: null,
- splitList: null,
- readCountStr: '',
- audio: {
- id: null,
- audioUrl: null,
- parentId: null,
- createTime: null,
- updateTime: null,
- duration: ''
- }
- }
- ]
- }
- ],
- spu: {
- id: '',
- name: '',
- imgCover: '',
- imgCoverMini: '',
- videoUrl: '',
- createTime: '',
- updateTime: '',
- price: 0,
- markingPrice: 0,
- categoryId: '',
- suitAge: '',
- parentNotice: null,
- courseCount: 0,
- description: null,
- outline: null,
- notice: null,
- simpleDescription: '',
- mediaType: 1,
- status: 1,
- isDelete: 0,
- sort: 0,
- showChapter: 1,
- isShow: true,
- showCoverImg: 1
- },
- showCoverImg: 1,
- position: 0,
- trialLearn: 0
- }
- const { id: ptId } = useRoute().query
- const { id: lxxId } = useRoute().params
- const [fco, atx] = useAudioManager({
- url: '',
- format: 'mm:ss'
- })
- const course = reactive(defaultCourse)
- const section = reactive(defaultSection)
- const percentage = ref(0)
- const playing = ref(false)
- const show = ref(false)
- const progressRef = ref()
- const progressBarWidth = ref(0)
- const isFirstClick = ref(false)
- // 剩余时间
- const remainingTime = computed(() => {
- // console.log(1 - percentage.value, '1 - percentage.value')
- return Math.round(section.freeTime - timeToSeconds(fco.updateTime))
- })
- const totalTime = computed(() => {
- // console.log(fco.duration, 'fco.durationfco.duration')
- if (!fco.duration) {
- return secondsToTime(section.audio.duration)
- }
- return fco.duration
- })
- const extinfo = computed(() => {
- return `home/wx/play?skuId=${course.id}&index=${course.position}`
- })
- const maxPercentage = computed(() => {
- if (section.freeTime && section.audio.duration) {
- return section.freeTime / Number(section.audio.duration)
- } else {
- return 1
- }
- })
- watch(
- () => fco.percentage,
- () => {
- setPercentage(fco.percentage || 0)
- if (fco.percentage >= maxPercentage.value) {
- fco.pause()
- playing.value = false
- show.value = true
- }
- }
- )
- watch(
- () => course.name,
- () => {
- console.log(section, 'duration')
- fco.src = section.audio.audioUrl
- document.title = course.name
- }
- )
- const setPercentage = (num: number) => {
- if (num > maxPercentage.value) {
- percentage.value = maxPercentage.value
- } else if (num < 0) {
- percentage.value = 0
- } else {
- percentage.value = num
- }
- }
- const timeToSeconds = (timeStr: string | number) => {
- if (typeof timeStr === 'string') {
- let [minutes, seconds] = timeStr.split(':').map(Number)
- // console.log(minutes, typeof seconds, 'minutes, seconds')
- if (minutes === undefined) minutes = 0
- if (seconds === undefined) seconds = 0
- return minutes * 60 + seconds
- } else {
- return 0
- }
- }
- function secondsToTime (seconds:string) {
- return dayjs(Math.round(Number(seconds) * 1000)).format('mm:ss')
- }
- function playAudio () {
- if (!isFirstClick.value) isFirstClick.value = true
- if (!playing.value) {
- if (fco.percentage >= maxPercentage.value) {
- fco.setPercentage(0)
- }
- fco.play()
- } else {
- fco.pause()
- }
- playing.value = !playing.value
- }
- async function getTrialAudio () {
- const id = ptId || lxxId
- const request = ptId ? getTrialAudioPTRequest : getTrialAudioLXXRequest
- const { data, status } = await request(id as string)
- if (status === 200) {
- Object.assign(course, data)
- if (data?.chapterList?.length > 0 && data.chapterList[0]?.itemList.length > 0) {
- Object.assign(section, data.chapterList[0].itemList[0])
- console.log(fco.duration, 'fco.duration')
- console.log(section.audio.duration, 'section.audio.duration')
- }
- }
- }
- function initAudio () {
- progressBarWidth.value = progressRef.value.clientWidth - space * 2
- progressRef.value.addEventListener('touchstart', () => {
- console.log('touchstart')
- if (!isFirstClick.value) {
- fco.play()
- playing.value = true
- isFirstClick.value = true
- }
- fco.pause()
- playing.value = false
- })
- progressRef.value.addEventListener('touchmove', (event: { touches: { pageX: number }[] }) => {
- console.log('touchmove')
- const { pageX } = event.touches[0]
- let num
- if (pageX <= space) {
- num = 0
- } else if ((pageX - space) / progressBarWidth.value > maxPercentage.value) {
- num = maxPercentage.value
- } else {
- num = (pageX - space) / progressBarWidth.value
- }
- fco.setPercentage(num)
- })
- progressRef.value.addEventListener('touchend', () => {
- console.log('touchend')
- fco.play()
- playing.value = true
- })
- }
- onMounted(async () => {
- initAudio()
- console.log(ptId, '拼图id')
- console.log(lxxId, '逻小熊id')
- await getTrialAudio()
- await registerWxopenButton()
- })
- </script>
- <template>
- <div class="section-audition sa">
- <div class="sa-cover" @click="playAudio">
- <img :src="section.imgCover" alt="" class="sa-cover-img" />
- <div class="sa-cover-tips">
- 试听剩余{{remainingTime}}s,体验完整版前往APP
- </div>
- <img :src="playing ? pause : play" alt="" class="sa-cover-btn" />
- </div>
- <div class="sa-process" :style="{opacity: course.imgCover ? 1: 0}">
- <div class="progress" ref="progressRef">
- <div
- class="progress-bar"
- :style="{ width: `calc(100vw - ${space * 2}px)` }"
- >
- <!--最大播放范围-->
- <div
- class="progress-bar-track"
- :style="{ width: `${maxPercentage * progressBarWidth}px` }"
- ></div>
- <!--最大播放位置小竖线-->
- <div
- v-if="maxPercentage < 1 && maxPercentage > 0"
- class="progress-bar-max"
- :style="{ left: `${maxPercentage * progressBarWidth}px` }"
- ></div>
- <!--当前位置圆点-->
- <div
- class="progress-bar-pivot"
- :style="{ left: `${percentage * progressBarWidth}px` }"
- ></div>
- <!--已经播放过的部分-->
- <div
- class="progress-bar-line"
- :style="{ width: `${percentage * progressBarWidth}px` }"
- ></div>
- </div>
- </div>
- <div class="sa-process-time">
- <div>{{ fco.updateTime || "00:00" }}</div>
- <div>{{ totalTime }}</div>
- </div>
- </div>
- <div class="sa-title">{{ course.name }}</div>
- <div class="sa-open">
- <img :src="logo" alt="logo" class="sa-open-logo" />
- <div class="sa-open-text">
- <h4>逻辑狗APP</h4>
- <div>少儿思维教育,就选逻辑狗</div>
- </div>
- <div class="sa-open-btn" @click="playAudio">
- <OpenApp :extinfo="extinfo">
- <div
- style="
- display: flex;
- justify-content: center;
- align-items: center;
- width: 74px;
- height: 32px;
- background: #0b47a4;
- border-radius: 16px;
- font-size: 14px;
- font-family: PingFang SC-Semibold, PingFang SC;
- font-weight: 600;
- color: #ffffff;
- "
- >
- 打开
- </div>
- </OpenApp>
- </div>
- </div>
- <VanOverlay :show="show" :z-index="3" @click="show = false">
- <div class="sa-remind" @click.stop>
- <img :src="remindLogo" alt="" class="sa-remind-logo" />
- <img
- :src="close"
- alt=""
- class="sa-remind-close"
- @click="show = false"
- />
- <div class="sa-remind-container">
- <div class="title">前往APP继续观看</div>
- <div class="desc">试听已结束,收听完整资源</div>
- <div class="desc mb12">请前往APP继续</div>
- <OpenApp :extinfo="extinfo">
- <div
- style="
- display: flex;
- justify-content: center;
- align-items: center;
- margin: 0 auto;
- width: 182px;
- height: 49px;
- background: #2654bf;
- border-radius: 25px 25px 25px 25px;
- font-size: 18px;
- font-family: PingFang SC-Medium, PingFang SC;
- font-weight: 500;
- color: #ffffff;
- "
- >
- 前往APP
- </div>
- </OpenApp>
- </div>
- </div>
- </VanOverlay>
- </div>
- </template>
- <style scoped lang="scss">
- .sa {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- min-height: 100vh;
- background-color: #fff;
- overflow: hidden;
- &-cover {
- position: relative;
- margin: 20px auto 0;
- width: 335px;
- border-radius: 20px;
- overflow: hidden;
- &-img {
- display: block;
- width: 100%;
- }
- &-tips {
- display: flex;
- justify-content: center;
- align-items: center;
- position: absolute;
- right: 16px;
- top: 16px;
- width: 226px;
- height: 24px;
- background: rgba(0, 0, 0, 0.5);
- border-radius: 40px;
- font-size: 13px;
- font-family: PingFang SC-Regular, PingFang SC;
- font-weight: 400;
- color: #ffffff;
- }
- &-btn {
- display: block;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 60px;
- height: 60px;
- }
- }
- &-process {
- width: 100%;
- .progress {
- padding: 27px 0;
- width: 100%;
- &-bar {
- position: relative;
- margin: 0 auto;
- width: calc(100vw - 40px);
- height: 3px;
- background-color: #ededed;
- border-radius: 2px;
- &-track {
- height: 3px;
- background-color: #b7d2ff;
- border-radius: 2px;
- }
- &-max {
- position: absolute;
- top: 50%;
- left: 0;
- transform: translate(-50%, -50%);
- width: 2px;
- height: 7px;
- background: #B7D2FF;
- border-radius: 1px;
- }
- &-pivot {
- position: absolute;
- top: 50%;
- left: 0;
- transform: translate(-50%, -50%);
- z-index: 3;
- width: 13px;
- height: 13px;
- background-color: #0643A2;
- border-radius: 50%;
- }
- &-line {
- position: absolute;
- top: 50%;
- left: 0;
- transform: translateY(-50%);
- z-index: 3;
- height: 3px;
- background-color: #0643A2;
- border-radius: 2px;
- }
- }
- }
- &-time {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-left: 31px;
- padding-right: 29px;
- position: relative;
- top: -13px;
- left: 0;
- z-index: 2;
- ont-size: 13px;
- font-family: PingFang SC-Regular, PingFang SC;
- font-weight: 400;
- color: #333333;
- box-sizing: border-box;
- }
- }
- &-title {
- margin-top: 18px;
- //height: 16px;
- font-size: 18px;
- font-family: PingFang SC-Semibold, PingFang SC;
- font-weight: 600;
- color: #333333;
- //line-height: 16px;
- text-align: center;
- }
- &-open {
- display: flex;
- align-items: center;
- position: fixed;
- bottom: 0;
- left: 0;
- width: 100vw;
- height: 67px;
- background: #f1f1f1;
- &-logo {
- display: block;
- margin-left: 14px;
- margin-right: 17px;
- width: 43px;
- height: 43px;
- }
- &-text {
- h4 {
- margin: 0 0 3px;
- height: 17px;
- font-size: 14px;
- font-family: PingFang SC-Semibold, PingFang SC;
- font-weight: 600;
- color: #333333;
- line-height: 17px;
- }
- div {
- height: 14px;
- font-size: 12px;
- font-family: PingFang SC-Regular, PingFang SC;
- font-weight: 400;
- color: #999999;
- line-height: 14px;
- }
- }
- &-btn {
- position: absolute;
- right: 13px;
- top: 50%;
- transform: translateY(-50%);
- }
- }
- &-remind {
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -65%);
- z-index: 5;
- img {
- display: block;
- }
- &-logo {
- position: relative;
- z-index: 2;
- width: 290px;
- height: 126px;
- }
- &-close {
- position: absolute;
- right: -6px;
- top: 107px;
- z-index: 3;
- width: 36px;
- height: 36px;
- }
- &-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-top: -14px;
- width: 290px;
- height: 186px;
- background: linear-gradient(180deg, #f4ffde 0%, #ffffff 100%);
- box-shadow: inset 0 0 5px 0 #a5a8a4;
- border-radius: 20px;
- overflow: hidden;
- .title {
- margin-top: 27px;
- margin-bottom: 14px;
- height: 24px;
- font-size: 20px;
- font-family: PingFang SC-Medium, PingFang SC;
- font-weight: 500;
- color: #093708;
- line-height: 24px;
- letter-spacing: 1px;
- }
- .desc {
- font-size: 16px;
- font-family: PingFang SC-Regular, PingFang SC;
- font-weight: 400;
- color: #759a6b;
- line-height: 20px;
- }
- .mb12 {
- margin-bottom: 12px;
- }
- }
- }
- }
- </style>
|