add mobile history view

This commit is contained in:
mubai
2025-02-09 23:33:08 +08:00
parent e90614ab97
commit ab5a737fa3
9 changed files with 169 additions and 77 deletions

View File

@@ -10,7 +10,7 @@
<meta name="referrer" content="never">
<meta charset="UTF-8"/>
<title>(╥﹏╥)</title>
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_3992367_mevt6vhsvc.css">
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_3992367_s6heb1d6gt.css">
</head>
<body>
<div id="app"></div>

View File

@@ -18,7 +18,7 @@
<div v-if="!global.isMobile" class="film-card-inner">
<div class="film-card-front">
<a :href="`/filmDetail?link=${item.id}`" class="default_image link_content">
<a :href="`/filmDetail?link=${item.id}`" class="link_content">
<div class="tag_group">
<span class="cus_tag ">{{ item.year ? item.year.slice(0, 4) : '未知' }}</span>
<span class="cus_tag ">{{ item.cName }}</span>
@@ -28,17 +28,15 @@
<img :src="item.picture" :alt="item.name?.split('[')[0]" @error="handleImg">
</a>
</div>
<div class="film-card-back">
<div class="film-card-back" @click="toDetail(item.id)">
<p class="card-title" >{{item.name}}</p>
<p v-show="item.blurb != ''" class="card-blurb">{{ item.blurb }}</p>
<p v-show="item.blurb == ''" class="card-blurb"> 暂无简介 </p>
<el-button class="card-detail" :icon="Discount" color="#626aef" plain round onclick="goDetail(item.id)" >详情</el-button>
<el-button class="card-detail" :icon="Discount" color="#626aef" plain round @click="toDetail(item.id)" >详情</el-button>
</div>
</div>
<a v-if="!global.isMobile" :href="`/filmDetail?link=${item.id}`" class="content_text_tag hidden-sm-and-down">{{ item.name.split("[")[0] }}</a>
</div>
</template>
<el-empty v-if="d.list.length <= 0" style="padding: 10px 0;margin: 0 auto" description="暂无相关数据"/>
@@ -46,6 +44,7 @@
</template>
<script setup lang="ts">
import {defineProps, inject, reactive, watchEffect} from 'vue'
import {Discount} from "@element-plus/icons-vue";
@@ -65,7 +64,7 @@ const handleImg = (e: Event) => {
e.target.style.display = "none"
}
const goDetail = (id:number) =>{
const toDetail = (id:any) =>{
location.href = `/filmDetail?link=${id}`
}
@@ -94,7 +93,7 @@ watchEffect(() => {
<style scoped>
.default_image {
background: url("/src/assets/image/404.png");
background: url("/src/assets/image/404.png") no-repeat;
background-size: cover;
}
@@ -261,7 +260,7 @@ watchEffect(() => {
</style>
<style>
<style scoped>
.film-card {
background-color: transparent;
@@ -270,7 +269,6 @@ watchEffect(() => {
font-family: sans-serif;
}
.film-card-inner {
padding-top: 125%;
@@ -296,18 +294,26 @@ watchEffect(() => {
height: 100%;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
background: linear-gradient(#fff2, transparent);
border: 1px solid rgba(255, 255, 255, 0.1);
/* border: 1px solid rgba(255, 255, 255, 0.1);*/
box-shadow: 0 25px 25px rgba(0, 0, 0, 0.25);
}
.film-card-back {
transform: rotateY(180deg);
.film-card-front {
border: none;
background: url("/src/assets/image/404.png") no-repeat;
background-size: cover;
}
.film-card-back {
cursor: pointer;
transform: rotateY(180deg);
padding: 0 5px;
background: linear-gradient(#fff2, transparent);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.card-title, .card-actor {
.card-title {
max-width: 70%;
margin: 0 auto;
font-size: 14px ;
overflow: hidden;

View File

@@ -13,12 +13,12 @@
</div>
<!--右侧顶级分类导航 -->
<div class="nav_right">
<div class="nav_link">
<a href="/">首页</a>
<template v-for="n in data.nav">
<a :href="`/filmClassify?Pid=${n.id}`">{{ n.name }}</a>
</template>
</div>
<div class="nav_link">
<a href="/">首页</a>
<template v-for="n in data.nav">
<a :href="`/filmClassify?Pid=${n.id}`">{{ n.name }}</a>
</template>
</div>
<div class="history-link hidden-md-and-down" v-on:mouseenter="handleHistory(true)"
v-on:mouseleave="handleHistory(false)">
<a :href="`/filmClassify?Pid=${nav.variety.id}`">
@@ -33,8 +33,7 @@
</div>
<div v-if="data.historyList.length > 0" class="history-c">
<a :href="h.link" class="history-c-item" v-for="h in data.historyList">
<span class="history-c-item-t">{{ h.name }}
</span>
<span class="history-c-item-t">{{ h.name }}</span>
<span class="history-c-item-e">{{ h.episode }}</span>
</a>
</div>
@@ -56,7 +55,7 @@
</template>
<script lang="ts" setup>
import { onMounted, reactive, ref, watch} from "vue";
import {onMounted, reactive, ref, watch} from "vue";
import {useRouter} from "vue-router";
import {Search, CircleClose} from '@element-plus/icons-vue'
import {ElMessage} from "element-plus";
@@ -116,7 +115,7 @@ const nav = reactive({
})
// 获取站点信息
const getBasicInfo = ()=>{
const getBasicInfo = () => {
ApiGet(`/config/basic`).then((resp: any) => {
if (resp.code === 0) {
data.site = resp.data
@@ -137,12 +136,9 @@ onMounted(() => {
})
</script>
<!--移动端适配-->
<style>
/*小尺寸时隐藏状态栏*/
@@ -153,6 +149,7 @@ onMounted(() => {
justify-content: space-between;
height: 40px;
}
.nav_link {
display: flex;
justify-content: space-between;
@@ -160,6 +157,7 @@ onMounted(() => {
width: 90%;
overflow-y: scroll;
}
.nav_link a {
white-space: nowrap;
color: #ffffff;
@@ -168,6 +166,7 @@ onMounted(() => {
line-height: 40px;
}
.nav_right .hidden-md-and-up {
color: #ffffff;
flex-basis: calc(19% - 5px);
@@ -269,7 +268,7 @@ onMounted(() => {
flex-direction: row;
}
.nav_right a {
.nav_right a {
min-width: 60px;
height: 40px;
line-height: 40px;
@@ -289,7 +288,6 @@ onMounted(() => {
}
/*history preview*/
/*element-plus empty state color style*/
:deep(.el-empty) {
--el-empty-fill-color-1: rgba(155, 73, 231, 0.72);

View File

@@ -13,9 +13,17 @@
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
/*公共颜色*/
--bg-light: #ffffff;
/*字体样式*/
--text-font-title: 16px;
--text-font-title-md: 15px;
--text-font-content: 12px;
--text-title-color: #FBA518;
--text-content-color: #9499a0;
/*公共颜色*/
--bg-light: #ffffff;
--bg-dark: #21252b;

View File

@@ -1,9 +1,7 @@
const fmt = {
dateFormat( dateStamp:number, format:string='YYYY-mm-dd HH:MM:SS') {
dateFormat(dateStamp: number, format: string = 'YYYY-mm-dd HH:MM:SS') {
// 根据时间戳生成当前时间, 单位 毫秒
let date = new Date(dateStamp*1000)
let date = new Date(dateStamp)
const opt = {
"Y+": date.getFullYear().toString(), // 年
"m+": (date.getMonth() + 1).toString(), // 月
@@ -18,10 +16,20 @@ const fmt = {
let r = new RegExp("(" + k + ")").exec(format);
// 如果有匹配成功项
if (r) {
format = format.replace(r[1], (r[1].length == 1) ? (opt[k as keyof typeof opt]) : (opt[k as keyof typeof opt].padStart(r[1].length, "0")))
format = format.replace(r[1], (r[1].length == 1) ? (opt[k as keyof typeof opt]) : (opt[k as keyof typeof opt].padStart(r[1].length, "0")))
}
}
return format;
},
secondToTime(seconds: number) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = Math.floor(seconds % 60);
let timeStr = ''
timeStr = hours < 10 ? `0${hours}` : `${hours}`;
timeStr += minutes < 10 ? `:0${minutes}` : `:${minutes}`;
timeStr += remainingSeconds < 10 ? `:0${remainingSeconds}` : `:${remainingSeconds}`;
return timeStr;
}
}

View File

@@ -1,25 +1,31 @@
<template>
<div class="container">
<div class="card" v-for="item in data.historyList">
<div v-if="global.isMobile" class="container">
<div class="card" v-for="h in data.historyList">
<div class="card-left">
<a :href="item.link"></a>
<a class="card-link" :href="h.link" :style="{backgroundImage: `url(${h.picture})`}"></a>
</div>
<div class="card-right">
<h5>{{item.name}}</h5>
<span>{{item.episode}}</span>
<h5 class="card-title"> {{ h.name }}</h5>
<div class="card-content">
<p class="card-episode">{{ `已观看: ${h.progress}` }}</p>
<p class="card-time "><b :class="`iconfont ${h.devices?'icon-mobile':'icon-pc1'}`" />{{ h.time }}</p>
<p class="card-episode">{{ h.episode }}</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {onMounted, reactive} from "vue";
import {inject, onMounted, reactive} from "vue";
import {COOKIE_KEY_MAP, cookieUtil} from "../../utils/cookie";
const data = reactive({
historyList: [{}]
})
const global = inject<any>('global')
onMounted(() => {
// 获取cookie中的filmHistory
let historyMap = cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY) ? JSON.parse(cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY)) : null
@@ -39,19 +45,66 @@ onMounted(() => {
<style scoped>
.card {
border: 1px solid red;
width: 100%;
max-height: 250px;
display: flex;
padding: 5px 5px;
flex-direction: row;
background: linear-gradient(#fff2, transparent);
border: 1px solid rgba(255, 255, 255, 0.1);
/* border-bottom: 1px solid rgba(255, 255, 255, 0.1);*/
margin: 5px auto;
border-radius: 5px;
}
.card-left {
flex-basis: 20%;
border: 1px solid greenyellow;
flex-basis: 27%;
display: flex;
}
.card-right {
flex-basis: 80%;
border: 1px solid deepskyblue;
flex-basis: 73%;
max-width: 73%;
display: flex;
flex-direction: column;
justify-content: space-between;
text-align: left;
padding-left: 5%;
font-size: var(--text-font-content);
}
.card-link {
width: 100%;
padding-top: 125%;
flex-grow: 1;
border-radius: 3px;
background-repeat: no-repeat;
background-size: cover;
}
.card-title {
max-width: 80%;
margin-top: 10px;
color: var(--text-title-color);
font-size: var(--text-font-title-md);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-content p {
margin-top: 3px;
margin-bottom: 0;
color: var(--text-content-color);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.iconfont {
vertical-align: bottom;
margin-right: 10px;
}
</style>

View File

@@ -17,7 +17,7 @@
<h3 class="current_play_title"><a
:href="`/filmDetail?link=${data.detail.id}`">{{ data.detail.name }}</a>{{ data.current.episode }}</h3>
<div class="tags">
<a :href="`/filmClassifySearch?Pid=${data.detail.pid}&Category=${data.detail.cid}`" >
<a :href="`/filmClassifySearch?Pid=${data.detail.pid}&Category=${data.detail.cid}`">
<el-icon>
<Promotion/>
</el-icon>
@@ -49,8 +49,10 @@
<div class="play-list">
<div class="play-list-item" v-show="data.currentTabId == item.id" v-for="item in data.detail.list">
<a :class="`play-link ${v.link == data.current.link?'play-link-active':''}`" v-for="(v,i) in item.linkList"
href="javascript:;" @click="playChange({sourceId: item.id, episodeIndex: i, target: this})">{{ v.episode }}
<div class="loading-wave" v-if="v.link == data.current.link" >
href="javascript:;" @click="playChange({sourceId: item.id, episodeIndex: i, target: this})">{{
v.episode
}}
<div class="loading-wave" v-if="v.link == data.current.link">
<div class="loading-bar"></div>
<div class="loading-bar"></div>
<div class="loading-bar"></div>
@@ -71,7 +73,7 @@
<script lang="ts" setup>
import {
computed,
computed, inject,
onBeforeMount, onBeforeUpdate,
reactive,
Ref,
@@ -85,10 +87,11 @@ 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) => {
@@ -134,7 +137,6 @@ const data = reactive({
list: [],
},
current: {index: 0, episode: '', link: ''},
currentEpisode: 0,
relate: [],
currentTabId: '', // 当前播放源ID
// @videojs-player 播放属性设置
@@ -149,7 +151,7 @@ const data = reactive({
//
const hasNext = computed(() => {
let flag = false
data.detail.list.forEach((item:any)=>{
data.detail.list.forEach((item: any) => {
if (data.currentTabId == item.id) {
flag = data.current.index != item.linkList.length - 1
}
@@ -159,18 +161,17 @@ const hasNext = computed(() => {
// 获取路由信息
const router = useRouter()
const global = inject<any>('global')
// 点击播集数播放对应影片
const playChange = (play: { sourceId: string, episodeIndex: number, target: any }) => {
data.detail.list.forEach((item:any)=>{
data.detail.list.forEach((item: any) => {
if (item.id == play.sourceId) {
let currPlay =item.linkList[play.episodeIndex]
let currPlay = item.linkList[play.episodeIndex]
data.current = {index: play.episodeIndex, episode: currPlay.episode, link: currPlay.link}
data.currentEpisode = play.episodeIndex
data.options.src = currPlay.link
data.options.title = data.detail.name + " " + currPlay.episode
data.currentTabId = play.sourceId
data.currentTabId = play.sourceId
}
})
}
@@ -205,7 +206,7 @@ const isAutoPlay = () => {
if (!data.autoplay) {
return
}
playNext()
playNext()
}
// 点击下一集按钮
const playNext = () => {
@@ -239,31 +240,51 @@ const handleBtn = (e: any) => {
}
}
// player 加载完成事件
const playerMount = (e:any) =>{
const playerMount = (e: any) => {
// 处理功能按钮相关事件
handleBtn(e)
}
// player 准备就绪事件
const beforePlay = (e:any)=>{
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 saveFilmHisroy = () => {
if (data.options.src.length > 0) {
// 处理播放历史要记录的影片相关信息
let player = document.getElementsByTagName("video")[0]
let historys = cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY)? JSON.parse(cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY)) : {}
let link = `/play?id=${data.detail.id}&source=${data.currentPlayFrom}&episode=${data.currentEpisode}&currentTime=${player.currentTime}`
historys[data.detail.id] = { name:data.detail.name, link: link, episode: data.current.episode, timeStamp: (new Date()).valueOf()}
let historys = cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY) ? JSON.parse(cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY)) : {}
let link = `/play?id=${data.detail.id}&source=${data.currentTabId}&episode=${data.current.index}&currentTime=${player.currentTime}`
let timeStamp = new Date().getTime()
let time = fmt.dateFormat(timeStamp)
let progress = `${fmt.secondToTime(player.currentTime)} / ${fmt.secondToTime(player.duration)}`
historys[data.detail.id] = {
id: data.detail.id,
name: data.detail.name,
picture: data.detail.picture,
episode: data.current.episode,
time: time,
timeStamp: timeStamp,
source: data.currentTabId,
link: link,
currentTime: player.currentTime,
duration: player.duration,
progress: progress,
devices: global.isMobile
}
// 处理播放时长
cookieUtil.setCookie(COOKIE_KEY_MAP.FILM_HISTORY, JSON.stringify(historys))
}}
}
}
// 在浏览器关闭前或页面刷新前将当前影片的观看信息存入历史记录中
window.addEventListener('beforeunload',saveFilmHisroy)
window.addEventListener('beforeunload', saveFilmHisroy)
// 初始化页面数据
@@ -273,8 +294,6 @@ onBeforeMount(() => {
if (resp.code === 0) {
data.detail = resp.data.detail
data.current = {index: resp.data.currentEpisode, ...resp.data.current}
// data.currentPlayFrom = resp.data.currentPlayFrom
data.currentEpisode = resp.data.currentEpisode
data.relate = resp.data.relate
// 设置当前的视频播放url
data.options.src = data.current.link
@@ -388,7 +407,6 @@ onBeforeMount(() => {
}
/* 播放区域*/
.player_area {
width: 100%;
@@ -541,6 +559,7 @@ onBeforeMount(() => {
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 12px;
}
.play_info_right a:active {
color: #FFBB5C;
background: rgb(0, 0, 0, 0.2);

View File

@@ -96,7 +96,7 @@
</el-table-column>
<el-table-column sortable prop="updateStamp" align="center" label="更新时间">
<template #default="scope">
<el-tag type="success" disable-transitions>{{fmt.dateFormat(scope.row.updateStamp) }}</el-tag>
<el-tag type="success" disable-transitions>{{fmt.dateFormat(scope.row.updateStamp*1000)}}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="100px">