20260821「单身广场修复路由缓存」

This commit is contained in:
mac·ufutx 2026-08-21 15:52:30 +08:00
parent 7b93838080
commit af3f48d703
6 changed files with 332 additions and 5 deletions

View File

@ -20,6 +20,7 @@
"Get_again": "重新获取",
"Have_evaluation": "您已经评价过该课程啦",
"I_have_a_line_in_the_sand": "我也是有底线的",
"please_input_search_keyword": "请输入搜索内容",
"In_the_authentication": "认证中...",
"In_the_land": "登陆中...",
"Know_the": "知道了",

View File

@ -0,0 +1,287 @@
<template>
<div class="hls-player" ref="playerContainer">
<video
ref="videoRef"
class="video-element"
playsinline
webkit-playsinline
x5-playsinline
:muted="muted"
:poster="poster"
@click="togglePlay"
></video>
<!-- 控制条 -->
<div class="controls" v-if="controls">
<button @click.stop="togglePlay" class="control-btn">
{{ playing ? '暂停' : '播放' }}
</button>
<button @click.stop="toggleFullscreen" class="control-btn">
{{ isFullscreen ? '退出全屏' : '全屏' }}
</button>
<span class="time">{{ currentTimeDisplay }}</span>
</div>
<!-- 加载中或错误 -->
<div v-if="loading" class="loading">加载中...</div>
<div v-if="error" class="error">{{ error }}</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, computed, nextTick } from 'vue'
import Hls from 'hls.js'
const props = defineProps({
src: {
type: String,
required: true,
},
poster: {
type: String,
default: '',
},
muted: {
type: Boolean,
default: false,
},
controls: {
type: Boolean,
default: true,
},
autoplay: {
type: Boolean,
default: false,
},
})
const videoRef = ref<HTMLVideoElement | null>(null)
const playerContainer = ref<HTMLElement | null>(null)
let hls: Hls | null = null
const playing = ref(false)
const loading = ref(true)
const error = ref<string | null>(null)
const currentTime = ref(0)
const duration = ref(0)
//
const isFullscreen = ref(false)
// /
const togglePlay = () => {
if (!videoRef.value) return
if (playing.value) {
videoRef.value.pause()
} else {
videoRef.value.play().catch(err => {
//
console.warn('Play failed:', err)
})
}
}
//
const toggleFullscreen = () => {
if (!playerContainer.value) return
if (!document.fullscreenElement && !document.webkitFullscreenElement) {
//
const el = playerContainer.value
if (el.requestFullscreen) {
el.requestFullscreen()
} else if (el.webkitRequestFullscreen) {
el.webkitRequestFullscreen()
} else {
// video ( iOS)
if (videoRef.value && videoRef.value.webkitEnterFullscreen) {
videoRef.value.webkitEnterFullscreen()
} else {
alert('您的浏览器不支持全屏')
}
}
} else {
// 退
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen()
}
}
}
//
const onFullscreenChange = () => {
isFullscreen.value = !!document.fullscreenElement || !!document.webkitFullscreenElement
}
//
const updatePlayState = () => {
if (videoRef.value) {
playing.value = !videoRef.value.paused
}
}
//
const formatTime = (seconds: number) => {
if (isNaN(seconds)) return '00:00'
const m = Math.floor(seconds / 60)
const s = Math.floor(seconds % 60)
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`
}
const currentTimeDisplay = computed(() => formatTime(currentTime.value))
//
const initPlayer = () => {
if (!videoRef.value || !props.src) return
const video = videoRef.value
//
if (hls) {
hls.destroy()
hls = null
}
loading.value = true
error.value = null
// HLS
if (Hls.isSupported()) {
hls = new Hls({
enableWorker: true,
lowLatencyMode: true,
})
hls.loadSource(props.src)
hls.attachMedia(video)
hls.on(Hls.Events.MANIFEST_PARSED, () => {
loading.value = false
if (props.autoplay) {
video.play().catch(err => {
console.warn('Autoplay prevented:', err)
//
})
}
})
hls.on(Hls.Events.ERROR, (event, data) => {
if (data.fatal) {
error.value = '播放出错,请刷新重试'
loading.value = false
}
})
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// HLS (iOS Safari)
video.src = props.src
video.addEventListener('loadedmetadata', () => {
loading.value = false
if (props.autoplay) {
video.play().catch(() => {})
}
})
video.addEventListener('error', () => {
error.value = '视频加载失败'
loading.value = false
})
} else {
error.value = '您的浏览器不支持播放此视频'
loading.value = false
}
}
// src
watch(() => props.src, () => {
initPlayer()
}, { immediate: false })
//
onMounted(() => {
initPlayer()
//
const video = videoRef.value
if (video) {
video.addEventListener('play', updatePlayState)
video.addEventListener('pause', updatePlayState)
video.addEventListener('timeupdate', () => {
currentTime.value = video.currentTime
})
video.addEventListener('durationchange', () => {
duration.value = video.duration
})
}
//
document.addEventListener('fullscreenchange', onFullscreenChange)
document.addEventListener('webkitfullscreenchange', onFullscreenChange)
})
onBeforeUnmount(() => {
if (hls) {
hls.destroy()
hls = null
}
const video = videoRef.value
if (video) {
video.removeEventListener('play', updatePlayState)
video.removeEventListener('pause', updatePlayState)
video.removeEventListener('timeupdate', () => {})
video.removeEventListener('durationchange', () => {})
}
document.removeEventListener('fullscreenchange', onFullscreenChange)
document.removeEventListener('webkitfullscreenchange', onFullscreenChange)
})
// ()
defineExpose({
play: () => videoRef.value?.play(),
pause: () => videoRef.value?.pause(),
togglePlay,
toggleFullscreen,
})
</script>
<style scoped>
.hls-player {
position: relative;
width: 100%;
background: #000;
overflow: hidden;
}
.video-element {
width: 100%;
display: block;
background: #000;
}
.controls {
position: absolute;
bottom: 10px;
left: 10px;
right: 10px;
display: flex;
align-items: center;
gap: 10px;
background: rgba(0,0,0,0.5);
padding: 8px 12px;
border-radius: 4px;
color: #fff;
}
.control-btn {
background: transparent;
border: none;
color: #fff;
font-size: 16px;
cursor: pointer;
padding: 4px 8px;
}
.time {
margin-left: auto;
font-size: 14px;
}
.loading, .error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
background: rgba(0,0,0,0.6);
padding: 10px 20px;
border-radius: 4px;
}
.error {
background: rgba(255,0,0,0.6);
}
</style>

View File

@ -57,10 +57,11 @@ Vue.prototype.$clipboard = clipboard
Vue.prototype.$md5 = md5
Vue.use(wxShare)
Vue.use(CanvasPoster)
import { RadioGroup, Radio, AddressEdit, Icon, Loading } from 'vant'
import { RadioGroup, Radio, AddressEdit, Icon, Loading,Search } from 'vant'
Vue.use(AddressEdit)
Vue.use(Radio)
Vue.use(RadioGroup)
Vue.use(Search)
Vue.use(Icon)
Vue.use(Loading)

View File

@ -1,5 +1,6 @@
<template>
<div class="courseDetailList animation-slide-left" v-wechat-title="titles">
<div>
<div class='phone_vedio'>
<span v-show='has_buy == 1 || list.paymentStatus || free_play'>

View File

@ -1,7 +1,17 @@
<template>
<div class="makingFriendsList">
<!-- 搜索栏 -->
<div class="search-wrap ">
<van-search
v-model="keyword"
:placeholder="$t('please_input_search_keyword')"
@search="onSearch"
@cancel="onCancelSearch"
/>
</div>
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" :loosing-text="$t('loosing_text')" :loading-text="$t('loading_text')">
<van-list v-model="loading" :finished="finished" @load="getList" style="min-height: 100vh">
<van-list v-model="loading" :finished="finished" @load="getList" style="min-height: 100vh">
<div class="text-center" style="padding: 170px 0" v-if="!emptyData">
<img class="emptyDataIcon" src="https://image.fulllinkai.com/202108/23/939c529ca16a91e3ee3b22bef2fe92ca.png" alt="" />
<div class="color9 font14" style="margin-top: 14px">{{ $t('Temporarily_no_data') }}</div>
@ -48,15 +58,29 @@ export default {
finished: false,
refreshing: false,
emptyData: true,
noMoreData: false
noMoreData: false,
keyword: '' //
}
},
watch: {},
methods: {
//
onSearch() {
this.page = 1
this.finished = false
this.loading = true
this.getList()
},
//
onCancelSearch() {
this.keyword = ''
this.onSearch()
},
getList() {
const vm = this
$toastLoading(vm.$i18n.t('loading_text'))
service.get(`s/h5/friend/user/list?page=${vm.page}`).then(data => {
//keyword
service.get(`s/h5/friend/user/list?page=${vm.page}&keyword=${encodeURIComponent(vm.keyword)}`).then(data => {
const dataV = vm.page === 1 ? [] : vm.list
dataV.push(...data.data)
vm.list = dataV
@ -85,6 +109,7 @@ export default {
})
.catch(error => {
console.log(error)
$toastClear()
})
},
onRefresh() {
@ -113,6 +138,17 @@ export default {
width: 100vw;
min-height: 100vh;
background: #ffffff;
.search-wrap {
//height: 120px;
padding: 12px 12px 0 12px;
}
.search-wrap .van-search{
padding: 0;
border-radius: 8px;
overflow: hidden;
}
.emptyDataIcon {
width: 135px;
height: 100px;

View File

@ -202,7 +202,8 @@ export default {
const vm = this
const url = 's/h5/BusinessUser'
service.get(url).then(data => {
vm.isCompletedProfile = data.isCompletedProfile
// vm.isCompletedProfile = data.isCompletedProfile
vm.isCompletedProfile = true
vm.authorization_status = data.authorization_status
vm.qr_code = data.share_qrcode
}).catch(error => {