20260821「单身广场修复路由缓存」
This commit is contained in:
parent
7b93838080
commit
af3f48d703
@ -20,6 +20,7 @@
|
|||||||
"Get_again": "重新获取",
|
"Get_again": "重新获取",
|
||||||
"Have_evaluation": "您已经评价过该课程啦",
|
"Have_evaluation": "您已经评价过该课程啦",
|
||||||
"I_have_a_line_in_the_sand": "我也是有底线的",
|
"I_have_a_line_in_the_sand": "我也是有底线的",
|
||||||
|
"please_input_search_keyword": "请输入搜索内容",
|
||||||
"In_the_authentication": "认证中...",
|
"In_the_authentication": "认证中...",
|
||||||
"In_the_land": "登陆中...",
|
"In_the_land": "登陆中...",
|
||||||
"Know_the": "知道了",
|
"Know_the": "知道了",
|
||||||
|
|||||||
287
src/components/HlsPlayer.vue
Normal file
287
src/components/HlsPlayer.vue
Normal 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>
|
||||||
@ -57,10 +57,11 @@ Vue.prototype.$clipboard = clipboard
|
|||||||
Vue.prototype.$md5 = md5
|
Vue.prototype.$md5 = md5
|
||||||
Vue.use(wxShare)
|
Vue.use(wxShare)
|
||||||
Vue.use(CanvasPoster)
|
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(AddressEdit)
|
||||||
Vue.use(Radio)
|
Vue.use(Radio)
|
||||||
Vue.use(RadioGroup)
|
Vue.use(RadioGroup)
|
||||||
|
Vue.use(Search)
|
||||||
|
|
||||||
Vue.use(Icon)
|
Vue.use(Icon)
|
||||||
Vue.use(Loading)
|
Vue.use(Loading)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="courseDetailList animation-slide-left" v-wechat-title="titles">
|
<div class="courseDetailList animation-slide-left" v-wechat-title="titles">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class='phone_vedio'>
|
<div class='phone_vedio'>
|
||||||
<span v-show='has_buy == 1 || list.paymentStatus || free_play'>
|
<span v-show='has_buy == 1 || list.paymentStatus || free_play'>
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="makingFriendsList">
|
<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-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">
|
<div class="text-center" style="padding: 170px 0" v-if="!emptyData">
|
||||||
<img class="emptyDataIcon" src="https://image.fulllinkai.com/202108/23/939c529ca16a91e3ee3b22bef2fe92ca.png" alt="" />
|
<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>
|
<div class="color9 font14" style="margin-top: 14px">{{ $t('Temporarily_no_data') }}</div>
|
||||||
@ -48,15 +58,29 @@ export default {
|
|||||||
finished: false,
|
finished: false,
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
emptyData: true,
|
emptyData: true,
|
||||||
noMoreData: false
|
noMoreData: false,
|
||||||
|
keyword: '' //搜索关键词
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {},
|
watch: {},
|
||||||
methods: {
|
methods: {
|
||||||
|
//触发搜索
|
||||||
|
onSearch() {
|
||||||
|
this.page = 1
|
||||||
|
this.finished = false
|
||||||
|
this.loading = true
|
||||||
|
this.getList()
|
||||||
|
},
|
||||||
|
//取消搜索清空
|
||||||
|
onCancelSearch() {
|
||||||
|
this.keyword = ''
|
||||||
|
this.onSearch()
|
||||||
|
},
|
||||||
getList() {
|
getList() {
|
||||||
const vm = this
|
const vm = this
|
||||||
$toastLoading(vm.$i18n.t('loading_text'))
|
$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
|
const dataV = vm.page === 1 ? [] : vm.list
|
||||||
dataV.push(...data.data)
|
dataV.push(...data.data)
|
||||||
vm.list = dataV
|
vm.list = dataV
|
||||||
@ -85,6 +109,7 @@ export default {
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
$toastClear()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onRefresh() {
|
onRefresh() {
|
||||||
@ -113,6 +138,17 @@ export default {
|
|||||||
width: 100vw;
|
width: 100vw;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
|
||||||
|
.search-wrap {
|
||||||
|
//height: 120px;
|
||||||
|
padding: 12px 12px 0 12px;
|
||||||
|
}
|
||||||
|
.search-wrap .van-search{
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.emptyDataIcon {
|
.emptyDataIcon {
|
||||||
width: 135px;
|
width: 135px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
|
|||||||
@ -202,7 +202,8 @@ export default {
|
|||||||
const vm = this
|
const vm = this
|
||||||
const url = 's/h5/BusinessUser'
|
const url = 's/h5/BusinessUser'
|
||||||
service.get(url).then(data => {
|
service.get(url).then(data => {
|
||||||
vm.isCompletedProfile = data.isCompletedProfile
|
// vm.isCompletedProfile = data.isCompletedProfile。暂时不判断用户的资料完善度
|
||||||
|
vm.isCompletedProfile = true
|
||||||
vm.authorization_status = data.authorization_status
|
vm.authorization_status = data.authorization_status
|
||||||
vm.qr_code = data.share_qrcode
|
vm.qr_code = data.share_qrcode
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user