player optimize

This commit is contained in:
mubai
2026-04-04 21:58:07 +08:00
parent 8243d2171f
commit 557ee42ebb
11 changed files with 419 additions and 265 deletions

View File

@@ -1,7 +1,9 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {

View File

@@ -1,13 +1,15 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
CustomDialog: typeof import('./src/components/Popup/CustomDialog.vue')['default']
ElAside: typeof import('element-plus/es')['ElAside']

View File

@@ -10,22 +10,23 @@
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@videojs-player/vue": "^1.0.0",
"axios": "^1.3.4",
"element-plus": "^2.8.6",
"video.js": "^7.0.0",
"vue": "^3.4.31"
"@element-plus/icons-vue": "^2.3.2",
"axios": "^1.14.0",
"element-plus": "^2.13.6",
"vue": "^3.5.32",
"xgplayer": "^3.0.24",
"xgplayer-hls": "^3.0.24"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.1.0",
"express": "^4.18.2",
"typescript": "^4.9.3",
"unplugin-auto-import": "^0.15.1",
"unplugin-vue-components": "^0.24.1",
"vite": "^7.3.1",
"vite-plugin-ssr": "^0.4.85",
"vue-router": "^4.1.6",
"vue-tsc": "^1.2.0"
"@types/node": "^25.5.2",
"@vitejs/plugin-vue": "^6.0.5",
"express": "^5.2.1",
"typescript": "^6.0.2",
"unplugin-auto-import": "^21.0.0",
"unplugin-vue-components": "^32.0.0",
"vite": "^8.0.3",
"vite-plugin-ssr": "^0.4.142",
"vue-router": "^5.0.4",
"vue-tsc": "^3.2.6"
}
}

View File

@@ -45,7 +45,7 @@
<script setup lang="ts">
import {defineProps, inject, reactive, watchEffect} from 'vue'
import { inject, reactive, watchEffect} from 'vue'
import {Discount} from "@element-plus/icons-vue";
const props = defineProps({

View File

@@ -78,14 +78,12 @@ const router = createRouter({
})
// 添加全局前置守卫拦截未登录的跳转
router.beforeEach((to, from, next) =>{
router.beforeEach((to, from) =>{
// 如果访问的是 /manage 下的路由, 且 token信息为空 则跳转到登录界面
let matchPath = new RegExp(/^\/manage\//).test(to.path)
let token = getToken()
if ( matchPath && !token ) {
next('/login')
} else {
next()
return '/login'
}
})

View File

@@ -14,83 +14,78 @@
</div>
</div>
<!--播放器区域-->
<div class="player_area">
<video-player @mounted="playerMount" :src="data.options.src" :poster="posterImg" controls
:loop="false"
@keydown="handlePlay"
:bufferedPercent="30"
:volume="data.options.volume"
crossorigin="anonymous" playsinline class="video-player"
:playback-rates="[0.5, 1.0, 1.5, 2.0]"/>
</div>
<div ref="playerContainer" class="player_area" />
</div>
</template>
<script setup lang="ts">
import {Search} from "@element-plus/icons-vue";
import posterImg from "../../assets/image/play.png";
import {VideoPlayer} from "@videojs-player/vue";
import {reactive} from "vue";
// import {VideoPlayer} from "@videojs-player/vue";
import {onMounted, reactive, ref} from "vue";
import {ElMessage} from "element-plus";
import Player from "xgplayer"
import 'xgplayer/dist/index.min.css';
import HlsPlugin from 'xgplayer-hls'
const data = reactive({
link: '',
options: {
// width: '100%',
// height: '100%',
title: "", //视频名称
src: "", //视频源
url: "https://vip.dytt-tvs.com/20260401/15005_f4adf97f/index.m3u8", //视频源
volume: 0.6, // 音量
currentTime: 50,
autoplay: false,
},
})
// 播放器按钮功能处理
const handlePlay = (e: any) => {
e.preventDefault()
switch (e.keyCode) {
case 32:
if (e.target.paused) {
e.target.play()
} else {
e.target.pause()
}
break
case 37:
e.target.currentTime = e.target.currentTime - 5 < 0 ? 0 : e.target.currentTime - 5
break
case 39:
e.target.currentTime = e.target.currentTime + 5 > e.target.duration ? e.target.duration : e.target.currentTime + 5
break
case 38:
data.options.volume = data.options.volume + 0.05 > 1 ? 1 : data.options.volume + 0.05
break
case 40:
data.options.volume = data.options.volume - 0.05 < 0 ? 0 : data.options.volume - 0.05
break
}
}
// 主动触发快捷键
const triggerKeyMap = (keyCode: number) => {
let player = document.getElementsByTagName("video")[0]
player.focus()
const event = document.createEvent('HTMLEvents');
event.initEvent('keydown', true, false);
event.keyCode = keyCode; // 设置键码
player.dispatchEvent(event)
}
const handleBtn = (e: any) => {
let btns = document.getElementsByClassName('vjs-button')
for (let el of btns) {
el.addEventListener('keydown', function (t: any) {
t.preventDefault()
triggerKeyMap(t.keyCode)
})
}
}
// player 加载完成事件
const playerMount = (e:any) =>{
// 处理功能按钮相关事件
handleBtn(e)
}
// 获取播放器渲染节点
const playerContainer = ref<HTMLDivElement | undefined>(undefined)
// 视频组件实例化
const playerInstance = ref()
onMounted(() => {
playerInstance.value = new Player({
el: playerContainer.value,
url: data.options.url,
poster: posterImg,
width: "",
height: "",
autoplay: data.options.autoplay,
lang: 'zh-cn', // 设置语言为中文
volume: 0.7, // 初始音量
playbackRate:[3, 2, 1.5, 1, 0.75, 0.5],
playsinline: true,
plugins:[HlsPlugin],
hls: {
retryCount: 3, // 重试 3 次,默认值
retryDelay: 1000, // 每次重试间隔 1 秒,默认值
loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
fetchOptions: {mode: 'cors'},
targetLatency: 10, // 直播目标延迟,默认 10 秒
maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
disconnectTime: 0, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency
// preloadTime: 30 // 默认值
},
controls: {
autoHide: true
},
keyboard: { playbackRate: 3 },
mobile: {
controls: true,
rotateFullScreen: true,
playsinline: true,
hideDefaultControls: true,
pressRate: 3,//长按倍速
disablePress: false,
}
})
})
// 播放执行
const play = () => {
@@ -100,9 +95,11 @@ const play = () => {
return
}
// 同步 link 为 player src
data.options.src = data.link
data.options.url = data.link
playerInstance.value.src = data.link
playerInstance.value.load()
playerInstance.value.play()
// 执行播放器播放
document.getElementsByTagName("video")[0].play()
}
</script>
@@ -116,6 +113,10 @@ const play = () => {
height: 80%
}
/*========================================================================================================================*/
.player_header {
margin: 40px auto;
}
@@ -154,15 +155,16 @@ const play = () => {
/*播放器样式.*/
.player_area {
width: 100%;
/*height: 700px;*/
margin: 0;
padding-bottom: 56.25% !important;
width: 92%;
margin: 0 auto;
aspect-ratio: 16 / 9;
/* padding-bottom: 56.25% !important;*/
position: relative;
border-radius: 6px;
display: flex;
}
/*
.video-player {
width: 80% !important;
height: 80% !important;
@@ -184,7 +186,7 @@ const play = () => {
background: rgba(0, 0, 0, 0.32);
}
/*取消video被选中的白边*/
!*取消video被选中的白边*!
:deep(video:focus) {
border: none !important;
outline: none;
@@ -203,7 +205,7 @@ const play = () => {
border-radius: 6px;
}
/*进度条配色*/
!*进度条配色*!
:deep(.video-js .vjs-load-progress div) {
background: rgba(255, 255, 255, 0.55) !important;
}
@@ -215,5 +217,6 @@ const play = () => {
:deep(.video-js .vjs-slider) {
background-color: hsla(0, 0%, 100%, .2);
}
*/
</style>

View File

@@ -1,17 +1,6 @@
<template>
<div class="player_area" v-show="data.loading">
<div class="player_p">
<!--preload-->
<video-player @mounted="playerMount" :src="data.options.src" :poster="posterImg" controls
@ready="beforePlay"
@ended="isAutoPlay"
:loop="false"
@keydown="handlePlay"
:bufferedPercent="30"
:volume="data.options.volume"
crossorigin="anonymous" playsinline class="video-player"
:playback-rates="[0.5, 1.0, 1.5, 2.0]"/>
</div>
<div ref="playerContainer" class="player_p"/>
<div class="current_play_info">
<div class="play_info_left">
<h3 class="current_play_title"><a
@@ -74,29 +63,23 @@
<script lang="ts" setup>
import {
computed, inject,
onBeforeMount, onBeforeUpdate,
onBeforeMount,
reactive,
Ref,
ref,
watchEffect,
withDirectives
watch,
} from "vue";
import {onBeforeRouteUpdate, useRouter} from "vue-router";
import {ApiGet} from "../../utils/request";
import {useRouter} from "vue-router";
import {ApiGet} from "@/utils/request";
import {ElMessage} from "element-plus";
import RelateList from "../../components/index/RelateList.vue";
import {Promotion} from "@element-plus/icons-vue";
import posterImg from '../../assets/image/play.png'
import {cookieUtil, COOKIE_KEY_MAP} from '../../utils/cookie'
import {cookieUtil, COOKIE_KEY_MAP} from '@/utils/cookie'
// 引入视频播放器组件
import {VideoPlayer} from '@videojs-player/vue'
import 'video.js/dist/video-js.css'
import {fmt} from "../../utils/format";
// 播放源切换事件
const changeTab = (id: string) => {
data.currentTabId = id
}
import Player, {Events, Plugin} from "xgplayer"
import 'xgplayer/dist/index.min.css';
import HlsPlugin from 'xgplayer-hls'
import {fmt} from "@/utils/format";
// 播放页所需数据
const data = reactive({
@@ -110,7 +93,7 @@ const data = reactive({
picture: '',
playFrom: [],
DownFrom: '',
playList: [[]],
list: [[]],
downloadList: '',
subTitle: '',
cName: '',
@@ -133,7 +116,6 @@ const data = reactive({
dbScore: '',
hits: '',
content: '',
list: [],
},
current: {index: 0, episode: '', link: ''},
relate: [],
@@ -142,12 +124,18 @@ const data = reactive({
autoplay: true,
options: {
title: "", //视频名称
src: "", //视频源
url: "", //视频源
volume: 0.6, // 音量
currentTime: 50,
autoplay: false,
urls: [],
},
})
//
// 获取路由信息
const router = useRouter()
const global = inject<any>('global')
// 是否存在下一集
const hasNext = computed(() => {
let flag = false
data.detail.list.forEach((item: any) => {
@@ -157,111 +145,46 @@ const hasNext = computed(() => {
})
return flag
})
// 获取路由信息
const router = useRouter()
const global = inject<any>('global')
// 播放源切换事件
const changeTab = (id: string) => {
data.currentTabId = id
}
// 点击播集数播放对应影片
const playChange = (play: { sourceId: string, episodeIndex: number, target: any }) => {
data.detail.list.forEach((item: any) => {
if (item.id == play.sourceId) {
let currPlay = item.linkList[play.episodeIndex]
data.current = {index: play.episodeIndex, episode: currPlay.episode, link: currPlay.link}
data.options.src = currPlay.link
data.options.url = currPlay.link
data.options.title = data.detail.name + " " + currPlay.episode
data.currentTabId = play.sourceId
}
})
}
// player相关事件
const handlePlay = (e: any) => {
e.preventDefault()
switch (e.keyCode) {
case 32:
if (e.target.paused) {
e.target.play()
} else {
e.target.pause()
}
break
case 37:
e.target.currentTime = e.target.currentTime - 5 < 0 ? 0 : e.target.currentTime - 5
break
case 39:
e.target.currentTime = e.target.currentTime + 5 > e.target.duration ? e.target.duration : e.target.currentTime + 5
break
case 38:
data.options.volume = data.options.volume + 0.05 > 1 ? 1 : data.options.volume + 0.05
break
case 40:
data.options.volume = data.options.volume - 0.05 < 0 ? 0 : data.options.volume - 0.05
break
}
}
// 播放结束后是否自动播放下一集
const isAutoPlay = () => {
if (!data.autoplay) {
return
}
playNext()
}
// 点击下一集按钮
const playNext = () => {
// 如果不存在下一集信息则直接返回
if (!hasNext.value) {
return
}
playChange({sourceId: data.currentTabId, episodeIndex: data.current.index + 1, target: ''})
if (data.autoplay) {
setTimeout(() => {
document.getElementsByTagName("video")[0].play()
}, 1000)
playChange({sourceId: data.currentTabId, episodeIndex: data.current.index + 1, target: ''})
}, 100)
}
}
// 主动触发快捷键
const triggerKeyMap = (keyCode: number) => {
let player = document.getElementsByTagName("video")[0]
player.focus()
const event = document.createEvent('HTMLEvents');
event.initEvent('keydown', true, false);
event.keyCode = keyCode; // 设置键码
player.dispatchEvent(event)
}
const handleBtn = (e: any) => {
let btns = document.getElementsByClassName('vjs-button')
for (let el of btns) {
el.addEventListener('keydown', function (t: any) {
t.preventDefault()
triggerKeyMap(t.keyCode)
})
}
}
// player 加载完成事件
const playerMount = (e: any) => {
// 处理功能按钮相关事件
handleBtn(e)
}
// player 准备就绪事件
const beforePlay = (e: any) => {
// 从router参数中获取时间信息
let currentTime = router.currentRoute.value.query.currentTime
currentTime && e.target.player.currentTime(currentTime)
}
//影片信息加入本地的观看历史中, 先获取cookie,已存在则追加,否则添加
const saveFilmHisroy = () => {
if (data.options.src.length > 0) {
const saveFilmHistory = () => {
if (data.options.url.length > 0) {
// 处理播放历史要记录的影片相关信息
let player = document.getElementsByTagName("video")[0]
let history = cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY) ? JSON.parse(cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY)) : {}
let link = `/play?id=${data.detail.mid}&source=${data.currentTabId}&episode=${data.current.index}&currentTime=${player.currentTime}`
let link = `/play?id=${data.detail.mid}&source=${data.currentTabId}&episode=${data.current.index}&currentTime=${mPlayer.currentTime}`
// 处理播放时长
let timeStamp = new Date().getTime()
let time = fmt.dateFormat(timeStamp)
let progress = `${fmt.secondToTime(player.currentTime)} / ${fmt.secondToTime(player.duration)}`
let progress = `${fmt.secondToTime(mPlayer.currentTime)} / ${fmt.secondToTime(mPlayer.duration)}`
history[data.detail.mid] = {
id: data.detail.mid,
name: data.detail.name,
@@ -271,8 +194,8 @@ const saveFilmHisroy = () => {
timeStamp: timeStamp,
source: data.currentTabId,
link: link,
currentTime: player.currentTime,
duration: player.duration,
currentTime: mPlayer.currentTime,
duration: mPlayer.duration,
progress: progress,
devices: global.isMobile
}
@@ -282,7 +205,7 @@ const saveFilmHisroy = () => {
}
// 在浏览器关闭前或页面刷新前将当前影片的观看信息存入历史记录中
window.addEventListener('beforeunload', saveFilmHisroy)
window.addEventListener('beforeunload', saveFilmHistory)
// 初始化页面数据
@@ -294,73 +217,281 @@ onBeforeMount(() => {
data.current = {index: resp.data.currentEpisode, ...resp.data.current}
data.relate = resp.data.relate
// 设置当前的视频播放url
data.options.src = data.current.link
data.options.url = data.current.link
// 设置当前播放源ID信息
data.currentTabId = resp.data.currentPlayFrom
data.loading = true
data.detail.list.forEach((item: any) => {
if (resp.data.currentPlayFrom == item.id) {
data.options.urls = item.linkList.map((i: any) => {
return i.link
})
}
})
} else {
ElMessage.error({message: resp.msg})
}
}).then(() => {
// 拿到数据后初始化播放器
mPlayer = new Player({
el: playerContainer.value,
url: data.options.url,
poster: posterImg,
width: "",
height: "",
autoplay: data.options.autoplay,
lang: 'zh-cn', // 设置语言为中文
volume: 0.7, // 初始音量
playbackRate: [3, 2, 1.5, 1, 0.75, 0.5],
playnext: {
urlList: data.options.urls,
},
playsinline: true,
// "x5-playsinline": true, // 针对腾讯 X5 内核微信、QQ、部分安卓
// "x5-video-player-type": "h5-page", // 关键:使用同层播放器
// "webapp-role": "foo", // 某些特定环境下的 hack
"x5-video-orientation": "landscape",
"x5-video-player-fullscreen": "true",
plugins: [HlsPlugin, playListPlugin],
hls: {
retryCount: 3, // 重试 3 次,默认值
retryDelay: 1000, // 每次重试间隔 1 秒,默认值
loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
fetchOptions: {mode: 'cors'},
targetLatency: 10, // 直播目标延迟,默认 10 秒
maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
preloadTime: 100 ,// 默认值
disconnectTime: 0, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency
// preloadTime: 30 // 默认值
},
controls: {
autoHide: true
},
keyboard: {playbackRate: 3},
mobile: {
disableGesture: true,
// controls: true,
rotateFullScreen: true,
hideDefaultControls: true,
gestureX: true,
gestureYL: true,
disableVolume: true,
scopeR: 0.15,
pressRate: 3,//长按倍速
disablePress: false,
},
// controls: {
// autoHide: false,
// },
})
// 播放器初始化完成时设置播放时长参数
mPlayer.on(Events.READY, () => {
// 从router参数中获取时间信息
let currentTime = router.currentRoute.value.query.currentTime
if (currentTime) {
mPlayer.currentTime = currentTime
}
})
// 播放完成事件
mPlayer.on(Events.ENDED, () => {
data.autoplay && playNext()
})
// 下一集按钮点击事件
mPlayer.on(Events.PLAYNEXT, () => {
playNext()
})
})
})
// 获取playerContainer挂载节点
const playerContainer = ref<HTMLDivElement | undefined>(undefined)
let mPlayer: any = null
// 监测播放器数据信息变化
watch(data.options, (newVal) => {
if (mPlayer) {
mPlayer.pause();
mPlayer.currentTime = 0
mPlayer.src = newVal.url
// mPlayer.load()
mPlayer.play()
}
})
const {POSITIONS} = Plugin
class playListPlugin extends Plugin {
// 插件的名称将作为插件实例的唯一key值
static get pluginName() {
return 'customPlayList'
}
static get defaultConfig() {
return {
// 挂载在controls的右侧如果不指定则默认挂载在播放器根节点上
position: POSITIONS.CONTROLS_RIGHT
}
}
constructor(args: any) {
super(args)
}
// 定义属性类型
private listContainer: HTMLElement | null = null;
private currentIndex: number = 0;
private renderListItems() {
if (!this.listContainer) return;
// 清空现有内容
this.listContainer.innerHTML = '';
this.listContainer.className = 'playListContainer'
// 数据渲染
let l: any = data.detail.list.find((item: any) => {
if (item.id == data.currentTabId) {
return item
}
})
l.linkList.forEach((item: any, index: number) => {
const el = document.createElement('div')
el.className = 'playlist-item';
el.innerText = item.episode;
// 选中项高亮样式
const isActive = index === data.current.index
if (isActive) {
el.className = 'playlist-item active'
}
// 绑定点击切换视频事件
el.onclick = (e) => {
e.stopPropagation();
playChange({sourceId: data.currentTabId, episodeIndex: index, target: el})
// 重新渲染以更新高亮状态
this.renderListItems();
// 播放后自动收起列表(可选体验优化)
this.toggleList()
}
el.addEventListener('wheel', (e:any)=> {
e.preventDefault()
this.listContainer && (this.listContainer.scrollTop += e.deltaY+50)
});
this.listContainer && this.listContainer.appendChild(el);
})
}
// 播放器初始化完成后触发
afterPlayerInit() {
// TODO 播放器调用start初始化播放源之后的逻辑
this.bind('click', (e: any) => {
console.log('---------------------------------click')
e.stopPropagation(); // 阻止冒泡
this.toggleList();
})
// 点击播放器其他区域时关闭列表
if (this.player.root) {
this.player.root.addEventListener('click', () => {
if (this.listContainer) {
this.listContainer.style.display = 'none'
}
})
}
this.listContainer = document.querySelector('.playList-panel')
this.listContainer && this.listContainer.addEventListener('mouseout', (e) => {
// 手指离开时的操作
e.stopPropagation();
this.toggleList()
})
this.renderListItems()
}
// 切换列表显示状态
private toggleList() {
if (this.listContainer) {
const isHidden = (this.listContainer.style.display == 'none' || this.listContainer.style.display == '')
this.listContainer.style.display = isHidden ? 'block' : 'none'
let mobilePlugin = mPlayer.getPlugin('mobile');
if (isHidden) {
// console.log(mPlayer.plugins)
// console.log(mPlayer.getPlugin('cssfullscreen'))
// mPlayer.getPlugin('cssfullscreen').show();
// mobilePlugin.disable();
} else {
// mPlayer.getPlugin('cssfullscreen').hide();
// mobilePlugin.enable();
}
}
}
afterCreate() {
}
destroy() {
this.listContainer = null;
}
render() {
return `<xg-icon class="iconfont icon-dianying1" ><div class="playList-panel" /></xg-icon>`
}
}
</script>
<style>
.xg-right-grid .icon-dianying1 {
display: block;
color: #ffffff;
padding: 0;
font-size: 25px;
line-height: 40px;
}
.xgplayer-playlist-wrapper {
position: relative;
display: flex;
align-items: center;
height: 100%;
}
.playListContainer {
display: none;
position: absolute;
bottom: 100%;
right: 0;
width: 100px;
max-height: 180px;
overflow-y: auto;
background: rgba(0, 0, 0, 0.3);
border-radius: 4px;
margin-bottom: 6px;
}
.playListContainer .playlist-item {
font-size: 16px;
max-height: 30px;
line-height: 30px;
color: #fff;
cursor: pointer;
border-bottom: 1px solid #333;
background: transparent;
font-weight: normal;
}
.playListContainer .playlist-item:hover {
color: #9a5dd3cc;
}
.playListContainer .active {
background: #9a5dd3cc;
font-weight: bold;
}
</style>
<style scoped>
@import "/src/assets/css/film.css";
/*vue3-video-play 相关设置*/
/*//播放器控件区域大小*/
.video-player {
width: 100% !important;
height: 100% !important;
position: absolute;
border-radius: 6px;
}
:deep(.vjs-big-play-button) {
line-height: 2em;
height: 2em;
width: 2em;
border-radius: 50%;
border: none;
background: rgba(0, 0, 0, 0.65);
}
:deep(.vjs-control-bar) {
background: rgba(0, 0, 0, 0.32);
}
/*取消video被选中的白边*/
:deep(video:focus) {
border: none !important;
outline: none;
}
:deep(.data-vjs-player:focus) {
border: none !important;
outline: none;
}
:deep(.vjs-tech) {
border-radius: 6px;
}
:deep(img) {
border-radius: 6px;
}
/*进度条配色*/
:deep(.video-js .vjs-load-progress div) {
background: rgba(255, 255, 255, 0.55) !important;
}
:deep(.video-js .vjs-play-progress) {
background: #44c8cf;
}
:deep(.video-js .vjs-slider) {
background-color: hsla(0, 0%, 100%, .2);
}
/*当前播放的影片信息展示*/
@@ -459,9 +590,8 @@ onBeforeMount(() => {
.player_p {
width: 100%;
/*height: 700px;*/
aspect-ratio: 16 / 9;
margin: 0;
padding-bottom: 56.25% !important;
position: relative;
border-radius: 6px;
display: flex;

View File

@@ -3,7 +3,8 @@
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
// "moduleResolution": "Node",
"moduleResolution": "bundler",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
@@ -12,6 +13,7 @@
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true,
"types": ["node","vite/client"],
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]

View File

@@ -1,8 +1,15 @@
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import vue from "@vitejs/plugin-vue"
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {ElementPlusResolver} from "unplugin-vue-components/resolvers";
// @ts-ignore
import path from 'path'
import { fileURLToPath, URL } from 'node:url' // 1. 导入 node:url 模块
const __dirname = path.dirname(fileURLToPath(import.meta.url))
export default defineConfig({
// 本地测试环境
@@ -12,7 +19,7 @@ export default defineConfig({
proxy: {
"/api": {
target: `http://127.0.0.1:3601`,
// target: `http://113.44.5.201:3601`,
// target: `http://www.mubai.us.ci:3601`,
changeOrigin: true, // 允许跨域
rewrite: path => path.replace(/^\/api/, '')
}
@@ -48,7 +55,9 @@ export default defineConfig({
},
resolve: {
alias: {
}
// 2. 配置 @ 指向 src 目录的绝对路径
'@': path.resolve(__dirname, './src'),
},
},
})

View File

@@ -103,6 +103,7 @@ const (
// MysqlDsn mysql服务配置信息 root:root 设置mysql账户的用户名和密码
MysqlDsn = "root:root@(192.168.20.5:3601)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
//MysqlDsn = "root:MuBai0916$@(47.254.16.58:3610)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
/*
redis 配置信息
@@ -115,6 +116,10 @@ const (
RedisPassword = `root`
RedisDBNo = 0
//RedisAddr = `47.254.16.58:3620`
//RedisPassword = `MuBai0916$`
//RedisDBNo = 0
// MysqlDsn docker compose 环境下的链接信息 mysql:3306 为 docker compose 中 mysql服务对应的网络名称和端口
//MysqlDsn = "root:root@(mysql:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"

View File

@@ -177,6 +177,7 @@ func DelSlaveMovieInfos(id string) {
//}
}
// AddMovieDetailIndex 添加详情表索引
func AddMovieDetailIndex() {
var m MovieDetail
tableName := m.TableName()
@@ -184,14 +185,16 @@ func AddMovieDetailIndex() {
db.Mdb.Exec(fmt.Sprintf("CREATE UNIQUE INDEX idx_mid ON %s (mid)", tableName))
}
// AddSlaveMovieInfoIndex 添加附属站点信息表索引
func AddSlaveMovieInfoIndex() {
var s SlaveMovieInfo
tableName := s.TableName()
// 如果不存在索引则创建对应索引
if !db.Mdb.Migrator().HasIndex(&s, "idx_mid") {
// 添加索引
db.Mdb.Exec(fmt.Sprintf("CREATE INDEX idx_mid ON %s (mid)", tableName))
db.Mdb.Exec(fmt.Sprintf("CREATE INDEX idx_dbId ON %s (db_id)", tableName))
db.Mdb.Exec(fmt.Sprintf("CREATE INDEX idx_sid ON %s (sid DESC)", tableName))
db.Mdb.Exec(fmt.Sprintf("CREATE INDEX idx_mid ON %s (mid DESC)", tableName))
db.Mdb.Exec(fmt.Sprintf("CREATE INDEX idx_dbId ON %s (db_id DESC", tableName))
}
}
@@ -554,8 +557,7 @@ func GetDetailByMid(mid int64) MovieDetail {
if err != nil {
// 如果没有获取到对应值, 则去mysql中进行查找
if errors.Is(err, redis.Nil) {
if err := db.Mdb.Model(&MovieDetail{}).Select("id, mid, cid, pid, name, sub_title, c_name, state, picture, actor, director,"+
" content, remarks, area, year").Where("mid = ?", mid).Find(&m).Error; err != nil {
if err := db.Mdb.Where("mid = ?", mid).Find(&m).Error; err != nil {
log.Println("Find BasicInfo Failed: ", err)
return m
}
@@ -631,7 +633,7 @@ func GetBasicInfoByIds(ids []int64) []MovieBasicInfo {
// 如果存在nil值,则去mysql进行补全
if len(newIds) > 0 {
if err := db.Mdb.Model(&MovieDetail{}).Select("id, mid, cid, pid, name, sub_title, c_name, state, picture, actor, director,"+
" content, remarks, area, year").Where("mid IN (?)", ids).Find(&ml).Error; err != nil {
" content, remarks, area, year").Where("mid IN (?)", newIds).Find(&ml).Error; err != nil {
log.Println("BatchFind BasicInfo Failed: ", err)
return nil
}