This commit is contained in:
lizhuang
2023-04-04 17:56:44 +08:00
parent 6a3fa25b7d
commit 46025f6011
93 changed files with 8110 additions and 0 deletions

49
client/src/App.vue Normal file
View File

@@ -0,0 +1,49 @@
<template>
<div class="main">
<router-view></router-view>
<Util/>
</div>
</template>
<script lang="ts" setup>
import Util from "./components/Util.vue";
</script>
<style>
html, body, #app {
width: 100%;
margin: 0;
padding: 0;
}
/*背景色切换*/
#app, .main {
max-width: 100%;
min-height: 100vh;
/*background: #222;*/
color: rgb(221, 221, 221);
background: rgb(34, 34, 34);
}
* {
box-sizing: border-box;
}
/*全局a标签默认样式去除*/
a {
color: #2d2e2f;
outline: none;
text-decoration: none;
color: #888888;
}
a:hover {
color: #888888;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1679472009425" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7100" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M 114.95 629.48 s -134.39 -197.75 80.63 -271.66 l 16.32 -9.6 s 8.64 -111.35 118.07 -142.07 c 0 0 68.16 -1.92 96.95 0 c 0 0 48.96 -99.83 174.71 -66.24 c 0 0 41.28 14.4 69.11 45.12 c 0 0 191.03 -62.4 181.43 124.79 c 0 0 -37.44 -173.75 -174.71 -79.67 c 0 0 -125.75 -135.35 -214.06 -19.2 l -20.16 32.64 s -171.83 -37.44 -207.35 137.27 c 0.01 -0.01 -203.49 31.67 -120.94 248.62 Z" fill="#1296db" p-id="7101"></path><path d="M 176.39 563.24 a 74.39 66.24 0 1 0 148.78 0 a 74.39 66.24 0 1 0 -148.78 0 Z" fill="#1296db" p-id="7102"></path><path d="M 383.25 414.93 m -69.12 0 a 69.12 69.12 0 1 0 138.24 0 a 69.12 69.12 0 1 0 -138.24 0 Z" fill="#1296db" p-id="7103"></path><path d="M 568.52 310.3 m -71.03 0 a 71.03 71.03 0 1 0 142.06 0 a 71.03 71.03 0 1 0 -142.06 0 Z" fill="#1296db" p-id="7104"></path><path d="M 673.15 329.5 a 61.44 70.07 0 1 0 122.88 0 a 61.44 70.07 0 1 0 -122.88 0 Z" fill="#1296db" p-id="7105"></path><path d="M 289.66 723.55 s 136.31 263.02 395.49 137.27 c 0 0 171.83 -88.31 136.31 -350.37 c 0 0 108.47 252.46 -122.87 383.97 c 0 0 -291.82 148.79 -408.93 -170.87 Z" fill="#1296db" p-id="7106"></path><path d="M 446.13 650.59 s 84.95 -139.67 239.02 -87.35 c 37.04 12.58 110.87 94.55 0 156.47 c -18.65 10.41 -43.2 7.68 -85.43 0 c 0 0 -29.76 63.36 -64.32 74.87 c 0 0 -134.39 57.6 -89.27 -143.99 Z" fill="#1296db" p-id="7107"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1679471653435" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="54082" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M799 832H224c-17.7 0-32 14.3-32 32s14.3 32 32 32h575c17.7 0 32-14.3 32-32s-14.3-32-32-32z m97-704H128c-35.3 0-64 28.7-64 64v511c0 35.3 28.7 64 64 64h768c35.3 0 64-28.7 64-64V192c0-35.3-28.7-64-64-64zM623.9 473.6l-197.6 98.5c-19.4 9.7-42.3-4.4-42.3-26v-197c0-21.6 22.9-35.7 42.3-26l197.6 98.5c21.5 10.7 21.5 41.3 0 52z" fill="#45C6FD" p-id="54083"></path></svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1679472049358" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8204" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M464 496H272c-17.6 0-32-14.4-32-32s14.4-32 32-32h192c17.6 0 32 14.4 32 32s-14.4 32-32 32z" fill="#1296db" p-id="8205"></path><path d="M368 720c-17.6 0-32-14.4-32-32V464c0-17.6 14.4-32 32-32s32 14.4 32 32v224c0 17.6-14.4 32-32 32zM656 720c-12.8 0-24-8-28.8-19.2l-96-224c-6.4-16 0-35.2 16-41.6 16-6.4 35.2 0 41.6 17.6L656 608l67.2-155.2c6.4-16 25.6-24 41.6-17.6 16 6.4 24 25.6 16 41.6l-96 224c-4.8 11.2-16 19.2-28.8 19.2z" fill="#1296db" p-id="8206"></path><path d="M848 928H176c-52.8 0-96-43.2-96-96V320c0-52.8 43.2-96 96-96h672c52.8 0 96 43.2 96 96v512c0 52.8-43.2 96-96 96zM176 288c-17.6 0-32 14.4-32 32v512c0 17.6 14.4 32 32 32h672c17.6 0 32-14.4 32-32V320c0-17.6-14.4-32-32-32H176z" fill="#1296db" p-id="8207"></path><path d="M448 288c-8 0-16-3.2-22.4-9.6l-160-160c-12.8-12.8-12.8-32 0-44.8 12.8-12.8 32-12.8 44.8 0l160 160c12.8 12.8 12.8 32 0 44.8-6.4 6.4-14.4 9.6-22.4 9.6zM576 288c-8 0-16-3.2-22.4-9.6-12.8-12.8-12.8-32 0-44.8l160-160c12.8-12.8 32-12.8 44.8 0 12.8 12.8 12.8 32 0 44.8l-160 160c-6.4 6.4-14.4 9.6-22.4 9.6z" fill="#1296db" p-id="8208"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,23 @@
<template>
<div class="custom-footer">
本站所有内容均来自互联网分享站点所提供的公开引用资源未提供资源上传存储服务
</div>
</template>
<script lang="ts" setup>
</script>
<style scoped>
.custom-footer {
text-align: center;
width: 100%;
height: 30px;
text-align: center;
line-height: 15px;
font-size: 15px;
color: #888888;
margin-top: 25px;
}
</style>

View File

@@ -0,0 +1,203 @@
<template>
<div class="header">
<!-- 左侧logo以及搜索 -->
<div class="nav_left">
<!-- <img class="logo" src="/src/assets/logo.png">-->
<a href="/" style="font-weight: 600;font-style: italic;font-size: 24px;margin-right: 5px">Boat</a>
<div class="search_group">
<input v-model="keyword" placeholder="搜索 动漫,剧集,电影 " class="search"/>
<el-button @click="searchFilm" :icon="Search"/>
</div>
</div>
<!--右侧顶级分类导航 -->
<div class="nav_right">
<el-link :underline="false" href="/">首页</el-link>
<el-link :underline="false" :href="`/categoryFilm?pid=${nav.film.id}`">电影</el-link>
<el-link :underline="false" :href="`/categoryFilm?pid=${nav.tv.id}`">剧集</el-link>
<el-link :underline="false" :href="`/categoryFilm?pid=${nav.cartoon.id}`">动漫</el-link>
<el-link :underline="false" :href="`/categoryFilm?pid=${nav.variety.id}`">综艺</el-link>
<!-- <span style="color:#777; font-weight: bold">|</span>-->
<el-link href="/search" class="hidden-md-and-up" :underline="false">
<el-icon style="font-size: 18px">
<Search/>
</el-icon>
</el-link>
</div>
</div>
</template>
<script lang="ts" setup>
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";
import {ApiGet} from "../utils/request";
//
const keyword = ref<string>('')
// 从父组件获取当前路由对象
const router = useRouter()
// 影片搜索
const searchFilm = () => {
if (keyword.value.length <= 0) {
ElMessage.error({message: "请先输入影片名称关键字再进行搜索", duration: 1500})
return
}
location.href = `/search?search=${keyword.value}`
// router.push({path: '/search', query:{search: keyword.value}, replace: true})
}
// 导航栏挂载完毕时发送一次请求拿到对应的分类id数据
const nav = reactive({
cartoon: {},
film: {},
tv: {},
variety: {},
})
onMounted(() => {
ApiGet('/navCategory').then((resp: any) => {
if (resp.status === 'ok') {
nav.tv = resp.data.tv
nav.film = resp.data.film
nav.cartoon = resp.data.cartoon
nav.variety = resp.data.variety
} else {
ElMessage.error({message: "请先输入影片名称关键字再进行搜索", duration: 1000})
}
})
})
</script>
<!--移动端适配-->
<style>
/*小尺寸时隐藏状态栏*/
@media (max-width: 650px) {
.nav_right {
display: flex;
justify-content: space-between;
/*display: none!important;*/
}
.nav_right a {
color: #ffffff;
flex-basis: calc(19% - 5px);
padding: 0 10px;
line-height: 40px;
/*border-radius: 5px;*/
/*border: 1px solid rebeccapurple;*/
}
.nav_right a:hover {
color: #ffffff;
/*background-color: transparent;*/
}
.header {
width: 100% !important;
height: 40px;
background: radial-gradient(circle, #d275cd, rgba(155, 73, 231, 0.72), #4ad1e5);
}
.nav_left {
display: none !important;
width: 90% !important;
margin: 0 auto;
}
}
</style>
<style scoped>
@media (min-width: 650px) {
.header {
width: 78%;
z-index: 0;
max-height: 40px;
line-height: 60px;
margin: 0 auto;
display: flex;
justify-content: space-between;
}
.nav_left {
display: flex;
}
/*搜索栏*/
.search_group {
width: 80%;
margin: 10px auto;
display: flex;
}
.search {
flex: 10;
background-color: #2e2e2e !important;
border: none !important;
height: 40px;
border-radius: 6px 0 0 6px;
padding-left: 20px;
color: #c9c4c4;
font-size: 15px;
font-weight: bold;
line-height: 60px;
}
.search::placeholder {
font-size: 15px;
color: #999999;
}
.search:focus {
outline: none;
}
.search_group button {
flex: 1;
margin: 0;
background-color: #2e2e2e;
color: rgb(171, 44, 68);
border: none !important;
height: 40px;
border-radius: 0 6px 6px 0;
font-size: 20px;
/*margin-bottom: 2px*/
}
.nav_right {
display: flex;
height: 60px;
flex-direction: row;
}
.nav_right a {
min-width: 60px;
height: 40px;
line-height: 60px;
margin: 10px 10px;
font-size: 15px;
text-align: center;
font-weight: bold;
}
.nav_right a:hover {
color: orange;
}
.logo {
height: 40px;
margin-top: 10px;
}
}
</style>

View File

@@ -0,0 +1,154 @@
<template>
<div class="container">
<h2 class="title">相关影片</h2>
<div class="content">
<div class="item" v-for="m in relateList">
<a :href="`/filmDetail?link=${m.id}`" class="cus_content_link"
:style="{backgroundImage: `url('${m.picture}')`}">
<span class="cus_tag hidden-md-and-down">{{ m.year }}</span>
<span class="cus_tag hidden-md-and-down">{{ m.cName }}</span>
<span class="cus_tag hidden-md-and-down">{{ m.area }}</span>
<span class="cus_remark hidden-md-and-up">{{ m.remarks }}</span>
</a>
<a :href="`/filmDetail?link=${m.id}`"
class=" content_text_tag">{{ m.name }}</a>
<span class="cus_remark hidden-md-and-down">{{ m.remarks }}</span>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
defineProps({
relateList : []
})
</script>
<style scoped>
@media (min-width: 650px) {
.container {
width: 100%;
margin-top: 36px;
}
.title {
padding: 0 0 5px 10px;
text-align: left;
border-bottom: 2px solid #777777;
}
.content {
width: 100%;
display: flex;
flex-direction: row;
flex-flow: wrap;
justify-content: space-between;
}
.container .item {
width: calc(14% - 18px);
margin-bottom: 18px;
}
.cus_content_link {
border-radius: 5px;
display: flex;
padding-top: 125%;
background-size: cover;
overflow: hidden;
}
.cus_tag {
text-align: center;
color: rgb(255, 255, 255);
padding: 0 3px;
margin: 0 0 10px 8px;
background: rgba(0, 0, 0, 0.55);
font-size: 12px;
border-radius: 5px;
}
.content_text_tag {
display: block;
font-size: 13px !important;
color: rgb(221, 221, 221);
padding: 2px 2px 2px 2px !important;
text-align: left;
max-width: 80%;
max-height: 20px;
white-space: nowrap; /* 禁止换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis; /* 使用省略号表示被截断的部分 */
}
.cus_remark {
display: block;
color: #888888;
width: 100%;
font-size: 12px;
text-align: left;
}
}
@media (max-width: 650px) {
.container {
width: 100%;
margin-top: 36px;
}
.title {
padding: 0 0 5px 10px;
text-align: left;
border-bottom: 2px solid #777777;
}
.content {
width: 100%;
display: flex;
flex-direction: row;
flex-flow: wrap;
justify-content: space-between;
}
.container .item {
width: calc(33% - 5px);
margin-bottom: 18px;
}
.cus_content_link {
border-radius: 5px;
display: flex;
padding-top: 125%;
background-size: cover;
overflow: hidden;
}
.cus_tag {
text-align: center;
color: rgb(255, 255, 255);
padding: 0 3px;
margin: 0 0 10px 8px;
background: rgba(0, 0, 0, 0.55);
font-size: 12px;
border-radius: 5px;
}
.content_text_tag {
font-size: 12px !important;
color: rgb(221, 221, 221);
width: 100%;
padding: 2px 0 2px 0 !important;
text-align: left;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.cus_remark {
display: block;
width: 100%;
font-size: 12px;
color: #c2c2c2;
text-align: center;
background: rgba(0,0,0,0.55);
border-radius: 0 0 5px 5px;
}
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<div class="util">
<el-collapse-transition>
<div v-show="control.show">
<a href="javascript:;" @click="changeStyle('top')">
<el-icon>
<ArrowUp />
</el-icon>
</a>
<a v-if="control.darkTheme" href="javascript:;" @click="changeStyle('light')">
<el-icon>
<Sunny/>
</el-icon>
</a>
<a v-if="!control.darkTheme" href="javascript:;" @click="changeStyle('dark')">
<el-icon>
<Moon/>
</el-icon>
</a>
</div>
</el-collapse-transition>
<a href="javascript:;" @click="changeStyle('more')" class="more">
<el-icon>
<MoreFilled/>
</el-icon>
</a>
</div>
</template>
<script setup lang="ts">
import {ArrowUp,Sunny, Moon, MoreFilled} from '@element-plus/icons-vue'
import {onBeforeMount, onMounted, onUnmounted, reactive} from "vue";
const control = reactive({
show: false,
darkTheme: true,
})
//
onMounted(()=>{
changeStyle(localStorage.getItem("theme")+'')
})
const changeStyle = (type:string)=>{
switch (type) {
case 'top':
let top = document.documentElement.scrollTop
if (top > 0) {
// 创建定时器,平滑滚动
const interval = setInterval(() => {
document.documentElement.scrollTop -= 10;
if (document.documentElement.scrollTop === 0) {
clearInterval(interval);
}
}, 5);
}
break
case 'light':
control.darkTheme = false
localStorage.setItem("theme", 'light')
document.getElementsByClassName('main')[0].style.background = `radial-gradient(circle, #C147E9, #810CA8, #2D033B)`
break
case 'dark':
control.darkTheme = true
localStorage.setItem("theme", 'dark')
document.getElementsByClassName('main')[0].style.background = `rgb(34,34,34)`
break
case 'more':
control.show = !control.show
break
}
}
</script>
<style scoped>
/*窗口工具栏设置*/
.util {
position: fixed;
right: 10px;
bottom: 15%;
width: 35px;
}
.util a {
display: block;
width: 100%;
margin-bottom: 3px;
height: 35px;
border-radius: 50%;
background: rgba(0,0,0,0.35);
}
.util a:hover{
background: #d329a4;
}
:deep(.el-icon) {
font-size: 18px;
height: 100%;
color: #ffffff;
}
.more {
background: rgb(238, 150, 0) !important;
}
</style>

20
client/src/main.ts Normal file
View File

@@ -0,0 +1,20 @@
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import { router} from "./router/router";
// 引入elementPlus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
// 引入路由
app.use(router)
app.mount('#app')

View File

@@ -0,0 +1,43 @@
import {
createRouter,
createWebHistory,
} from "vue-router";
// 1.定义路由组件
import IndexHome from "../views/IndexHome.vue";
import Home from "../views/index/Home.vue";
import FilmDetails from "../views/index/FilmDetails.vue";
import Play from "../views/index/Play.vue";
import SearchFilm from "../views/index/SearchFilm.vue";
import CategoryFilm from "../views/index/CategoryFilm.vue";
import NotFound from '../views/error/Error404.vue'
// 2. 定义一个路由
const routes = [
{
path: '/',
component: IndexHome,
redirect: '/index',
children: [
{path: 'index', component: Home},
{path: 'filmDetail', component: FilmDetails},
{path: 'play', component: Play},
{path: 'search', component: SearchFilm},
{path: 'CategoryFilm', component: CategoryFilm},
]
},
{path: `/:pathMatch(.*)*`, component: NotFound},
]
// 创建路由实例并传递 routes配置
const router = createRouter({
history: createWebHistory(),
routes
})
export {router}

80
client/src/style.css Normal file
View File

@@ -0,0 +1,80 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, system-ui, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@@ -0,0 +1,83 @@
import axios from "axios";
import {ElMessage, ElLoading } from "element-plus";
// 定义加载动画对象
let loading:any
const startLoading = ()=>{
loading = ElLoading.service({
lock: true,
text: `请求发送中...`,
background: `rgba(255,255,255,0.5)`,
// target: document.querySelector(`.content`)
})
}
const closeLoading = ()=>{
loading.close()
}
const http = (options: any) => {
return new Promise((resolve, reject) => {
// create an axios instance
const service = axios.create({
// baseURL: import.meta.env.VITE_URL_BASE, // api 的 base_url 注意 vue3
baseURL: `/api`, // api 的 base_url 注意 vue3
// baseURL: 'https://www.baidu.com/api', // 固定api
timeout: 80000, // request timeout
});
// request interceptor
service.interceptors.request.use(
(config: any) => {
// 开启loading动画
startLoading()
// let token: string = ""; //此处换成自己获取回来的token通常存在在cookie或者store里面
// if (token) {
// // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
// config.headers["X-Token"] = token;
//
// config.headers.Authorization = +token;
// }
return config;
},
(error) => {
// Do something with request error
Promise.reject(error);
}
);
// response interceptor
service.interceptors.response.use(
(response) => {
closeLoading()
return response.data;
},
(error) => {
closeLoading()
if (error.response.status == 403) {
ElMessage.error("请求异常: ", error)
} else {
ElMessage.error("服务器繁忙,请稍后再试");
}
return Promise.reject(error);
}
);
// 请求处理
service(options)
.then((res) => {
resolve(res);
})
.catch((error) => {
reject(error);
});
});
};
const ApiGet = (url:string, params?:any)=>{
return http({url, method:`get`, params,})
}
const ApiPost = (url:string, data:any) =>{
return http({url, method:`post`, data,})
}
export {http, ApiPost, ApiGet};

View File

@@ -0,0 +1,95 @@
<template>
<el-container>
<el-header>
<Header/>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
<el-footer>
<Footer/>
</el-footer>
</el-container>
</template>
<script setup lang="ts">
import Header from "../components/Header.vue";
import Footer from "../components/Footer.vue";
</script>
<style scoped>
:deep(.el-main) {
padding-top: 70px !important;
padding-bottom: 30px !important;
min-height: 85vh;
}
:deep(.el-header) {
padding: 0 !important;
position: fixed !important;
width: 100% !important;
min-height: 60px;
transform: translateZ(0);
z-index: 1000;
background-color: rgba(0, 0, 0, 0.85);
top: 0;
}
@media (min-width: 650px) {
.el-main {
margin: 0 auto;
padding: 100px 0;
/*padding-top: 100px!important;*/
}
}
@media (max-width: 650px) {
.el-main {
/*margin: 0 auto;*/
padding: 60px 0;
/*padding-top: 100px!important;*/
}
}
:deep(.el-menu--horizontal) {
border-bottom: 1px solid rgb(46, 46, 46);
}
/*@media (min-width: 768px){ //>=768的设备 }*/
/*@media (min-width: 992px){ //>=992的设备 }*/
/*@media (min-width: 1200){ //>=1200的设备 }*/
@media (min-width: 1024px) {
.el-main {
width: 1023px
}
}
@media (min-width: 990px) {
.el-main {
width: 970px
}
}
@media (min-width: 1200px) {
.el-main {
width: 1180px
}
}
@media (min-width: 1400px) {
.el-main {
width: 1400px
}
}
@media (min-width: 1560px) {
.el-main {
width: 1500px
}
}
</style>

View File

@@ -0,0 +1,21 @@
<template>
<div class="container">
<el-empty style="height: 100%; line-height: 100%; font-size: 20px" :image-size="400"
image="/src/assets/image/404.png">
<template #description>
<p style="font-size: 32px;width: 100%;color: #a574b7">你好像走错地方了哦!!!</p>
</template>
</el-empty>
</div>
</template>
<script>
</script>
<style scoped>
.container {
height: 100vh;
}
</style>

View File

@@ -0,0 +1,443 @@
<template>
<div class="container">
<div class="header">
<p>{{ d.category.name }}</p>
<div class="c_header">
<a :class="`nav ${d.list.length >0 && d.list[0].cid == c.id?'active':''}`" href="javascript:;" @click="changeCategory(c.id)"
v-for="c in d.category.children">{{ c.name }}</a>
</div>
</div>
<div class="c_content" >
<div class="item" v-for="item in d.list">
<a :href="`/filmDetail?link=${item.id}`" :style="{backgroundImage: `url('${item.picture}')`}">
<span class="cus_tag ">{{ item.year }}</span>
<span class="cus_tag ">{{ item.cName }}</span>
<span class="cus_tag ">{{ item.area }}</span>
<span class="cus_remark hidden-md-and-up">{{ item.remarks }}</span>
</a>
<a :href="`/filmDetail?link=${item.id}`" class="content_text_tag">{{ item.name.split("[")[0] }}</a>
<span class="cus_remark hidden-md-and-down">{{ item.remarks }}</span>
</div>
</div>
<div class="pagination_container ">
<el-pagination background layout="prev, pager, next"
v-model:current-page="d.page.current"
@current-change="changeCurrent"
:pager-count="5"
:background="true"
:page-size="d.page.pageSize"
:total="d.page.total"
:prev-icon="ArrowLeftBold"
:next-icon="ArrowRightBold"
hide-on-single-page
class="pagination"/>
</div>
</div>
</template>
<script setup lang="ts">
import {onMounted, reactive} from "vue";
import {useRouter} from "vue-router";
import {ApiGet} from "../../utils/request";
import {ElMessage} from "element-plus";
import {ArrowRightBold, ArrowLeftBold} from '@element-plus/icons-vue'
// 页面所需数据
const d = reactive({
category: {},
list: [],
page: {
current: 0,
},
})
// 获取路由参数查询对应数据
const router = useRouter()
// 点击分页按钮事件 current-change
const changeCurrent = (currentVal: number) => {
let query = router.currentRoute.value.query
// router.push({path: '/categoryFilm', query:{pid: query.pid, cid: query.cid, current: currentVal}})
if (query.cid && query.cid != "") {
location.href = `/categoryFilm?pid=${query.pid}&cid=${query.cid}&current=${currentVal}`
} else {
location.href = `/categoryFilm?pid=${query.pid}&&current=${currentVal}`
}
}
// 点击分类事件
const changeCategory = (cid: any) => {
let params = new URLSearchParams(location.search)
// location.href = `/categoryFilm?pid=${params.get('pid')}&cid=${cid}&current=${params.get('current')?params.get('current'):1}`
location.href = `/categoryFilm?pid=${params.get('pid')}&cid=${cid}&current=1`
}
const getFilmData = (param: any) => {
ApiGet('/filmCategory', {pid: param.pid, cid: param.cid, current: param.current}).then((resp: any) => {
if (resp.status === 'ok') {
d.category = resp.data.category
d.list = resp.data.list
d.page = resp.page
} else {
ElMessage.error({message: "请先输入影片名称关键字再进行搜索", duration: 1000})
}
})
}
onMounted(() => {
let query = router.currentRoute.value.query
getFilmData({pid: query.pid, cid: query.cid, current: query.current})
})
</script>
<!--移动端修改-->
<style scoped>
@media (max-width: 650px) {
.container {
padding: 0 10px;
}
/*顶部内容区域*/
.header {
width: 100%;
margin-bottom: 150px;
background: none!important;
}
.header p {
text-align: left;
font-weight: 600;
font-size: 24px;
color: #c9c4c4;
margin-top: 0;
padding-left: 10px;
height: 100%;
/*background: rgb(34, 34, 34);*/
}
.c_header {
max-width: 100%;
display: flex;
/*justify-content: start;*/
flex-wrap: nowrap;
overflow-x: scroll;
margin-bottom: 20px;
}
.c_header a {
white-space: nowrap;
margin-right: 10px;
color: #000;
font-weight: 400;
background: rgba(255, 255, 255, 0.94);
padding: 6px 10px;
border-radius: 10px;
}
.c_header a:hover {
color: orange;
}
.nav:before {
width: 36px;
height: 4px;
background: orange;
content: '';
position: absolute;
left: 35%;
bottom: 12px;
border-radius: 50px;
transform: scaleX(0);
transition: transform 0.5s ease-out;
}
.nav:hover:before {
width: 36px;
height: 4px;
background: orange;
content: '';
position: absolute;
left: 35%;
bottom: 12px;
border-radius: 50px;
transform: scaleX(1);
}
.active {
background: rgb(249 230 195)!important;
color: #e52424!important;
}
/*展示区域*/
.c_content {
width: 100%;
display: flex;
flex-flow: wrap;
justify-content: space-between;
}
.c_content .item {
flex-basis: calc(33% - 5px);
max-width: 33%;
margin-bottom: 20px;
/*width: 100px;*/
box-sizing: border-box;
overflow: hidden;
}
.item a {
border-radius: 5px;
display: flex;
padding-top: 125%;
background-size: cover;
width: 100%;
}
.cus_tag {
display: none;
}
.content_text_tag {
font-size: 12px !important;
color: rgb(221, 221, 221);
width: 100%;
padding: 2px 0 2px 0 !important;
text-align: left;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.cus_remark {
display: block;
width: 100%;
font-size: 12px;
color: #c2c2c2;
text-align: center;
background: rgba(0,0,0,0.55);
border-radius: 0 0 5px 5px;
}
}
</style>
<style scoped>
.container {
max-width: 100vw;
}
@media (min-width: 650px) {
/*顶部内容区域*/
.header {
width: 100%;
}
.header p {
text-align: left;
font-weight: 800;
font-size: 32px;
color: #c9c4c4;
margin-top: 0;
/*border-bottom: 2px solid #ffffff;*/
/*padding-bottom: 30px;*/
}
.c_header {
width: 100%;
display: flex;
justify-content: start;
/*justify-content: start;*/
margin-bottom: 20px;
}
.c_header a {
/*flex-basis: calc(14% - 16px);*/
margin-right: 20px;
color: #000;
font-weight: 800;
background: rgba(255, 255, 255, 0.94);
padding: 20px 40px;
border-radius: 10px;
position: relative;
}
.c_header a:hover {
color: orange;
}
.nav:before {
width: 36px;
height: 4px;
background: orange;
content: '';
position: absolute;
left: 35%;
bottom: 12px;
border-radius: 50px;
transform: scaleX(0);
transition: transform 0.5s ease-out;
}
.nav:hover:before {
width: 36px;
height: 4px;
background: orange;
content: '';
position: absolute;
left: 35%;
bottom: 12px;
border-radius: 50px;
transform: scaleX(1);
}
.active {
background: rgb(249 230 195)!important;
color: #e52424!important;
}
.c_content {
width: 100%;
display: flex;
flex-flow: wrap;
justify-content: space-between;
}
.c_content .item {
flex-basis: calc(14% - 16px);
max-width: 14%;
margin-bottom: 20px;
/*width: 100px;*/
box-sizing: border-box;
overflow: hidden;
}
.item a {
border-radius: 5px;
display: flex;
/*position: relative;*/
padding-top: 125%;
background-size: cover;
width: 100%;
}
.cus_tag {
text-align: center;
color: rgb(255, 255, 255);
padding: 0 3px;
margin: 0 0 10px 8px;
background: rgba(0, 0, 0, 0.55);
font-size: 12px;
border-radius: 5px;
}
.content_text_tag {
font-size: 14px !important;
color: rgb(221, 221, 221);
width: 96%;
padding: 2px 10px 2px 2px !important;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cus_remark {
display: block;
width: 100%;
padding-left: 3px;
font-size: 12px;
color: #999999;
text-align: left;
}
}
</style>
<style scoped>
/*分页插件区域*/
.pagination_container {
width: 100%;
margin-top: 30px;
/*background: deepskyblue;*/
text-align: center;
/*display: flex;*/
}
:deep(.el-pagination) {
width: 100% !important;
margin: 0 auto !important;
justify-content: center;
}
/*分页器样式修改*/
:deep(.number) {
font-weight: bold;
width: 45px;
height: 45px;
background: #2e2e2e !important;
color: #ffffff;
border-radius: 50%;
/*margin: 0 3px!important;*/
}
:deep(.number:hover) {
color: #67d9e8;
}
:deep(.btn-prev) {
font-weight: bold;
width: 45px;
height: 45px;
background: #2e2e2e !important;
color: #ffffff;
border-radius: 50%;
}
:deep(.btn-next) {
font-weight: bold;
width: 45px;
height: 45px;
background: #2e2e2e !important;
color: #ffffff;
border-radius: 50%;
}
:deep(.more) {
font-weight: bold;
width: 45px;
height: 45px;
background: #2e2e2e !important;
color: #ffffff;
border-radius: 50%;
}
:deep(.is-active) {
background: #67d9e8 !important;
}
/*移动端缩小*/
@media (max-width: 650px) {
:deep(.number){
width: 35px;
height: 35px;
}
:deep(.btn-prev) {
width: 35px;
height: 35px;
}
:deep(.btn-next) {
width: 35px;
height: 35px;
}
:deep(.more) {
width: 35px;
height: 35px;
}
}
</style>

View File

@@ -0,0 +1,383 @@
<template>
<div class="film">
<!-- hidden-sm-and-up 移动端title -->
<div class="hidden-sm-and-up">
<div class="title_mt ">
<a class="picture_mt" href="" :style="{backgroundImage: `url('${data.detail.picture}')`}"></a>
<div class="title_mt_right">
<h3>{{ data.detail.name }}</h3>
<ul class="tags">
<li style="margin: 2px 0">{{ `${data.detail.descriptor.classTag}`.replaceAll(",", " | ") }}</li>
</ul>
<p><span>导演:</span> {{ data.detail.descriptor.director }}</p>
<p><span>主演:</span>
{{ `${data.detail.descriptor.actor}`.split(",")[0] + " " + `${data.detail.descriptor.actor}`.split(",")[1] + " " + `${data.detail.descriptor.actor}`.split(",")[2] }}
</p>
<p><span>上映:</span> {{ data.detail.descriptor.releaseDate }}</p>
<p><span>地区:</span> {{ data.detail.descriptor.area }}</p>
<p v-if="data.detail.descriptor.remark"><span>连载:</span>{{ data.detail.descriptor.remark }}</p>
<p><span>评分:</span><b id="score">{{ data.detail.descriptor.dbScore }}</b></p>
</div>
</div>
<div class="mt_content">
<p v-html="`${data.detail.descriptor.content}`.replaceAll('  ', '')"></p>
</div>
</div>
<!-- pc端title-->
<div class="title hidden-sm-and-down ">
<a class="picture" href="" :style="{backgroundImage: `url('${data.detail.picture}')`}"></a>
<h2>{{ data.detail.name }}</h2>
<ul class="tags">
<li class="t_c">
<el-icon>
<Promotion/>
</el-icon>
{{ data.detail.descriptor.cName }}
</li>
<li>{{ `${data.detail.descriptor.classTag}`.replaceAll(",", "&emsp;") }}</li>
<li>{{ data.detail.descriptor.year }}</li>
<li>{{ data.detail.descriptor.area }}</li>
</ul>
<p><span>导演:</span> {{ data.detail.descriptor.director }}</p>
<p><span>主演:</span> {{ data.detail.descriptor.actor }}</p>
<p><span>上映:</span> {{ data.detail.descriptor.releaseDate }}</p>
<p v-if="data.detail.descriptor.remark"><span>连载:</span>{{ data.detail.descriptor.remark }}</p>
<p><span>评分:</span><b id="score">{{ data.detail.descriptor.dbScore }}</b></p>
<div class="cus_wap">
<p style="min-width: 40px"><span>剧情:</span></p>
<p ref="textContent" class="text_content">
<el-button v-if="`${data.detail.descriptor.content}`.length > 120" class="multi_text"
style="color:#a574b7;"
@click="showContent(multiBtn.state)" link>{{ multiBtn.text }}
</el-button>
<span class="cus_info" v-html="`${data.detail.descriptor.content}`.replaceAll('  ', '')"></span>
</p>
</div>
<p>
<el-button type="warning" class="player" size="large" @click="play({episode:0,source:0})" round>
<el-icon>
<CaretRight/>
</el-icon>
立即播放
</el-button>
</p>
</div>
<div class="play_list">
<h2 class="hidden-md-and-down">播放列表:(右侧切换播放源)</h2>
<el-tabs type="card" class="plya_tabs" tab-transition="fade" tab-animation="300" lazy>
<el-tab-pane v-for="(p,i) in data.detail.playList" :label="`播放地址${i+1}`" >
<div class="play_content">
<a v-for="(item,index) in p" href="javascript:;"
@click="play({source: i, episode: index})">{{ item.episode }}</a>
</div>
</el-tab-pane>
</el-tabs>
</div>
<div class="correlation">
<RelateList :relate-list="data.relate" />
</div>
</div>
</template>
<script setup lang="ts">
import {useRouter} from "vue-router";
import {onMounted, reactive, ref,} from "vue";
import {ApiGet} from "../../utils/request";
import {ElMessage} from 'element-plus'
import {Promotion, CaretRight} from "@element-plus/icons-vue";
import RelateList from "../../components/RelateList.vue";
const show = ref(false)
// 获取路由对象
const router = useRouter()
const data = reactive({
detail: {descriptor: {}},
relate: [],
})
onMounted(() => {
let link = router.currentRoute.value.query.link
ApiGet('/filmDetail', {id: link}).then((resp: any) => {
if (resp.status === "ok") {
data.detail = resp.data.detail
data.relate = resp.data.relate
} else {
ElMessage({
type: "error",
dangerouslyUseHTMLString: true,
message: resp.message,
})
}
})
})
// 选集播放点击事件
const play = (change: { source: number, episode: number }) => {
router.push({path: `/play`, query: {id: `${router.currentRoute.value.query.link}`, ...change}})
}
// 内容展开收起效果
const multiBtn = ref({state: false, text: '展开'})
const textContent = ref()
const showContent = (flag: boolean) => {
if (flag) {
multiBtn.value = {state: !flag, text: '展开'}
textContent.value.style.webkitLineClamp = 2
return
}
multiBtn.value = {state: !flag, text: '收起'}
textContent.value.style.webkitLineClamp = 8
}
</script>
<!--移动端适配-->
<style>
@media (max-width: 650px) {
.title_mt {
padding: 0 3px;
display: flex;
flex-direction: row;
flex-flow: nowrap;
overflow: hidden;
}
.picture_mt {
width: 100%;
height: 175px;
margin-right: 12px;
border-radius: 8px;
background-size: cover;
flex: 1;
}
.title_mt_right {
flex: 1.8;
text-align: left;
}
.title_mt_right h3 {
margin: 0 0 5px 0;
}
.title_mt_right p {
font-size: 12px;
margin: 3px 2px;
}
.mt_content {
margin-top: 5px;
border-top: 1px solid #777777;
border-bottom: 1px solid #777777;
width: 100%;
padding: 5px;
}
.mt_content p {
font-size: 12px;
text-align: left;
}
}
</style>
<style scoped>
.correlation {
width: 100%;
}
.film {
width: 100%;
padding: 0 1%;
}
/*影片播放列表信息展示*/
.play_list {
width: 100%;
border-radius: 10px;
background: #2e2e2e;
margin-top: 50px;
position: relative;
}
.play_content {
display: flex;
flex-flow: row wrap;
padding: 20px 10px 20px 18px;
}
.play_list > h2 {
position: absolute;
left: 10px;
top: -10px;
z-index: 50;
}
.play_content a {
font-size: 12px;
min-width: 65px;
padding: 6px 15px;
color: #ffffff;
border-radius: 6px;
margin: 8px 8px;
background: #888888;
}
.plya_tabs {
background: #2e2e2e;
}
:deep(.el-tabs__nav-scroll) {
display: flex;
justify-content: end;
}
:deep(.el-tabs__header) {
/*border-bottom: 1px solid #888888!important;*/
margin-bottom: 0;
border-bottom: none !important;
background: rgb(34, 34, 34);
height: 65px !important;
}
:deep(.el-tabs__nav) {
border: none !important;
}
:deep(.el-tabs__item.is-active) {
color: #ee9600;
}
:deep(.el-tabs__item:hover) {
color: orange;
background: #484646;
}
:deep(.el-tabs__item) {
height: 65px;
line-height: 65px;
margin-left: 2px;
border-radius: 8px 8px 0 0;
border: none !important;
color: #ffffff;
background: #2e2e2e;
}
:deep(.el-tab-pane) {
}
/*顶部影片信息显示区域*/
.title {
width: 100%;
border-radius: 10px;
background: #2e2e2e;
padding: 5px 30px 30px 30px;
position: relative;
}
.title > h2 {
text-align: left;
color: #888888;
}
.picture {
position: absolute;
width: 190px;
height: 270px;
right: 30px;
top: 30px;
border-radius: 8px;
background-size: cover;
}
.tags {
list-style-type: none;
display: flex;
justify-content: left;
margin: 0;
padding: 0;
}
.tags > li {
padding: 6px 10px;
border-radius: 5px;
background: rgba(66, 66, 66);
margin: 0 8px;
font-size: 12px;
color: #888888;
}
.tags > .t_c {
background: rgba(155, 73, 231, 0.72);
color: #c4c2c2;
margin-left: 0;
}
.title p {
text-align: left;
font-size: 14px;
margin: 20px 0;
max-width: 60%;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.title p span {
font-size: 15px;
font-weight: bold;
color: #888888;
margin-right: 5px;
}
#score {
color: #1cbeb9;
}
.cus_wap {
display: flex;
}
.title .text_content {
max-width: 70%;
line-height: 22.5px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
vertical-align: top;
margin-top: 5px;
}
.text_content::before {
content: '';
float: right;
width: 0; /*设置为0或者不设置宽度*/
height: calc(100% - 20px); /*先随便设置一个高度*/
}
.text_content .cus_info {
height: 100%;
margin: 0;
font-size: 15px !important;
font-weight: normal;
}
.multi_text {
float: right;
clear: both;
margin-right: 10px;
}
</style>

View File

@@ -0,0 +1,247 @@
<template>
<div class="container">
<div class="content_item" v-for="item in data.info.content">
<template v-if="item.nav.name !='综艺' & item.nav.name !='综艺片'">
<el-row class="row-bg cus_nav" justify="space-between">
<el-col :span="12" class="title">
<span :class="`iconfont ${item.nav.name.search('电影') != -1?'icon-film':item.nav.name.search('剧') != -1?'icon-tv':'icon-cartoon'}`"
style="color: #79bbff;font-size: 32px;margin-right: 10px; line-height: 130%" />
<a :href="`/categoryFilm?pid=${item.nav.id}`">{{ item.nav.name }}</a>
</el-col>
<el-col :span="12">
<ul class="nav_ul">
<li v-for="c in item.nav.children" class="nav_category hidden-md-and-down"><a
:href="`/categoryFilm?pid=${c.pid}&cid=${c.id}`">{{ c.name }}</a></li>
<li class=" hidden-md-and-down">更多 ></li>
</ul>
</el-col>
</el-row>
<el-row class="cus_content">
<el-col :md="24" :lg="20" :xl="20" class="cus_content">
<el-row style="max-width: 100%">
<template v-for=" (m,i) in item.movies">
<el-col :md="4" :sm="6" :xs="8" v-if="i <12" class="cus_content_item">
<a :href="`/filmDetail?link=${m.id}`" class="cus_content_link"
@error="handleImgError"
:style="{backgroundImage: `url('${m.picture}')`}">
<span class="cus_tag hidden-md-and-down">{{ m.year }}</span>
<span class="cus_tag hidden-md-and-down">{{ m.cName }}</span>
<span class="cus_tag hidden-md-and-down">{{ m.area }}</span>
</a>
<a :href="`/filmDetail?link=${m.id}`"
class="content_text content_text_tag">{{ m.name }}</a>
<span class="cus_remark">{{ m.remarks }}</span>
</el-col>
</template>
</el-row>
</el-col>
<el-col :md="0" :lg="4" :xl="4" class="hidden-md-and-down content_right">
<template v-for="(m,i) in item.movies">
<div class="content_right_item">
<a :href="`/filmDetail?link=${m.id}`"><b class="top_item">{{ i + 1 + '.' }}</b>
<span>{{ m.name }}</span></a>
</div>
</template>
</el-col>
</el-row>
</template>
</div>
</div>
</template>
<script lang="ts" setup>
import 'element-plus/theme-chalk/display.css'
import {onBeforeMount, onMounted, reactive} from "vue";
import {ApiGet} from "../../utils/request";
const data = reactive({
info: {}
})
onBeforeMount(() => {
ApiGet('/index').then((resp: any) => {
data.info = resp.data
})
})
</script>
<style scoped>
.container {
margin: 0 auto;
}
.content_item {
padding: 10px;
margin-bottom: 25px;
}
.title {
display: flex;
text-align: left;
height: 40px;
}
.title > a {
min-width: 40px;
color: rgb(221, 221, 221)
}
a {
color: #333;
padding-top: 10px;
text-decoration: none;
outline: none;
-webkit-tap-highlight-color: transparent;
}
.cus_nav {
border-bottom: 1px solid rgb(46, 46, 46);
height: 40px;
}
.nav_ul {
list-style-type: none;
display: flex;
flex-direction: row;
justify-content: end;
margin: 0;
}
.nav_category > a {
color: #999;
}
.nav_category > a:hover {
color: #1890ff;
}
.nav_ul > li {
min-width: 60px;
line-height: 40px;
text-align: center;
color: #999;
font-size: 14px;
font-weight: 400;
}
/*svg图标*/
embed {
width: 2rem;
height: 2rem;
margin-right: 8px;
margin-top: 5px;
}
/*影片简介区域*/
.cus_content {
display: flex;
padding-top: 15px;
}
.cus_content_link {
border-radius: 5px;
display: flex;
/*position: relative;*/
padding-top: 125%;
background-size: cover;
}
.cus_tag {
text-align: center;
color: rgb(255, 255, 255);
padding: 0 3px;
margin: 0 0 10px 8px;
background: rgba(0, 0, 0, 0.55);
font-size: 12px;
border-radius: 5px;
}
.content_text_tag {
font-size: 15px !important;
color: rgb(221, 221, 221);
padding: 2px 10px 2px 2px !important;
}
.cus_remark {
display: block;
width: 100%;
padding-left: 3px;
font-size: 12px;
color: #999999;
text-align: left;
}
.content_text {
display: block;
width: 100%;
padding: 2px 10px 10px 2px;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
white-space: nowrap;
text-align: left;
}
.content_right {
width: 100%;
}
.content_right_item {
display: flex;
padding-left: 10px;
border-bottom: 1px solid rgb(46, 46, 46);
}
.content_right_item > a {
padding: 10px 15px 10px 0;
color: hsla(0, 0%, 100%, .87);
display: block;
flex-grow: 1;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
white-space: nowrap;
}
:deep(.top_item) {
color: red;
/*font-style: oblique 10deg;*/
font-style: italic;
/*font-family: Inter;*/
margin-right: 6px;
}
.content_right_item a span:hover {
color: orange;
}
</style>
<!--移动端修改-->
<style scoped>
@media (min-width: 650px) {
.cus_content_item {
padding: 10px;
overflow: hidden;
/*margin-bottom: 10px;*/
}
}
@media (max-width: 650px) {
.cus_content_item {
padding: 0 6px 0 0;
margin-bottom: 10px;
overflow: hidden;
}
.nav_ul {
justify-content: end;
}
}
</style>

View File

@@ -0,0 +1,355 @@
<template>
<div class="player_area">
<!-- 视频播放区域-->
<div class="player_p">
<iframe ref="iframe" class="player" :src="data.current.link"
:name="data.detail.name"
marginheight="0"
marginwidth="0"
framespacing="0"
vspale="0"
frameborder="0" allowfullscreen="true" scolling="no"
sandbox="allow-scripts allow-same-origin allow-downloads">
</iframe>
</div>
<div class="current_play_info">
<div class="play_info_left">
<h3 class="current_play_title">{{ `${data.detail.name}&emsp;${data.current.episode}` }}</h3>
<div class="tags">
<b>
<el-icon>
<Promotion/>
</el-icon>
{{ data.detail.descriptor.cName }}</b>
<span>{{ data.detail.descriptor.classTag }}</span>
<span>{{ data.detail.descriptor.year }}</span>
<span>{{ data.detail.descriptor.area }}</span>
</div>
</div>
</div>
<!-- 播放选集 -->
<div class="play_list">
<h2 class="hidden-md-and-down">播放列表:(右侧切换播放源)</h2>
<el-tabs type="card" v-model="data.currentTabName" class="plya_tabs" @tab-change="changeSource">
<el-tab-pane v-for="(p,i) in data.detail.playList"
:name="`tab-${i}`"
:label="`播放地址${i+1}`">
<div class="play_content">
<a v-for="(item,index) in p" href="javascript:void(false)" @click="playChange(item)"
:class="data.current.link.search(item.link) !== -1?'play_active':''">{{ item.episode }}</a>
</div>
</el-tab-pane>
</el-tabs>
</div>
<div class="correlation">
<RelateList :relate-list="data.relate"/>
</div>
</div>
</template>
<script lang="ts" setup>
import {onMounted, reactive, ref, withDirectives} from "vue";
import {useRouter} from "vue-router";
import {ApiGet} from "../../utils/request";
import RelateList from "../../components/RelateList.vue";
import {Promotion} from "@element-plus/icons-vue";
// 播放页所需数据
const data = reactive({
detail: {descriptor: {}, playList: [[{episode: '', link: ''}]]},
current: {episode: '', link: ''},
currentTabName: '',
currentPlayFrom: 0,
currentEpisode: 0,
relate: [],
})
// 获取路由信息
const router = useRouter()
onMounted(() => {
let query = router.currentRoute.value.query
ApiGet(`/filmPlayInfo`, {id: query.id, playFrom: query.source, episode: query.episode}).then((resp: any) => {
if (resp.status === 'ok') {
data.detail = resp.data.detail
resp.data.current.link = converLink(resp.data.current.link)
data.current = resp.data.current
data.currentPlayFrom = resp.data.currentPlayFrom
data.currentEpisode = resp.data.currentEpisode
data.relate = resp.data.relate
// 设置当前选中的播放源
data.currentTabName = `tab-${query.source}`
}
})
})
// ===============================视频播放处理=======================================
// 视频解析接口地址, 默认使用第一个
const resolver = [
// m3u8使用此解析
'https://jx.jsonplayer.com/player/?url=',
// 'https://jx.m3u8.tv/jiexi/?url=',
// 'https://jx.jsonplayer.com/player/?url=',
// 'https://vip.bljiex.com/?url=',
// 'https://jx.bozrc.com:4433/player/?url=',
// html视频使用此解析
'http://www.82190555.com/index/qqvod.php?url=',
// 'https://jx.bozrc.com:4433/player/?url=',
// 'https://vip.bljiex.com/?url=',
// Google上随便找的
'https://vip.bljiex.com/?url=',
'https://jx.kingtail.xyz/?url=',
'http://www.82190555.com/index/qqvod.php?url=',
'https://www.nxflv.com/?url=',
'http://www.wmxz.wang/video.php?url=',
'https://www.feisuplayer.com/m3u8/?url=',
// tampermonkey 脚本使用的解析
'https://jx.bozrc.com:4433/player/?url=',
'https://z1.m1907.top/?jx=',
'https://jx.aidouer.net/?url=',
'https://www.gai4.com/?url=',
'https://okjx.cc/?url=',
'https://jx.rdhk.net/?v=',
'https://jx.blbo.cc:4433/?url=',
'https://jsap.attakids.com/?url=',
'https://jx.dj6u.com/?url=',
]
// 添加视频解析前缀
const converLink = (link: string): string => {
// 视频统一使用第三方解析
if (link.search("m3u8") != -1) {
return `${resolver[0] + link}`
}
// return `${resolver[1]+link}`
return `${link}`
}
// 点击播放对应影片
const playChange = (info: { link: string, episode: string }) => {
// 判断是否是m3u8播放器, 如果是则添加前缀
data.current.link = converLink(info.link)
data.current.episode = info.episode
}
// 点击播放源标签事件
const changeSource = (tabName: any) => {
data.currentTabName = tabName
data.detail.playList.find((item, index) => {
if (tabName.split("-")[1] - index == 0) {
item.find(i => {
if (i.episode == data.current.episode) {
data.current.link = converLink(i.link)
}
})
}
})
}
// 测试滑动音量调节
const iframe = ref()
// document.querySelector('#brightnessSlider').addEventListener('change', function() {
// // 获取滑块的值
// var brightness = this.value;
//
// // 设置亮度为滑块的值
// iframe.style.filter = 'brightness(' + brightness + '%)';
// })
</script>
<style scoped>
/*当前播放的影片信息展示*/
.current_play_info {
width: 100%;
padding: 15px 5px;
text-align: left;
}
.current_play_title {
font-weight: 500;
color: rgb(201, 196, 196);
margin: 0 0 5px 0;
}
/* 播放区域*/
.player_area {
width: 100%;
min-height: 100%;
/*height: 1400px;*/
/*background: red;*/
}
@media (min-width: 650px) {
.player_area {
padding: 10px 6%;
}
.tags b {
padding: 5px 10px;
background-color: rgba(155, 73, 231, 0.72);
font-size: 13px;
border-radius: 6px;
margin-right: 15px;
}
.tags span {
padding: 6px 12px;
background-color: #404042;
color: #b5b2b2;
border-radius: 5px;
margin: 0 8px;
font-size: 12px;
}
}
.player_p {
width: 100%;
min-height: 200px;
margin: 0;
padding-bottom: 56.25% !important;
/*padding-bottom: 42.25% !important;*/
position: relative;
background-image: url("/src/assets/image/play.png");
background-size: cover;
border-radius: 6px;
}
iframe {
border-radius: 6px;
left: 0;
width: 100%; /* 设置iframe元素的宽度为父容器的100% */
height: 100%; /* 设置iframe元素的高度为0以便自适应高度 */
/*padding-bottom: 56.25%; !* 使用padding-bottom属性计算iframe元素的高度这里假设视频的宽高比为16:9 *!*/
/*border: none; !* 去除iframe元素的边框 *!*/
/*transform: scale(1);*/
position: absolute;
}
/*右侧播放源选择区域*/
/*影片播放列表信息展示*/
/*影片播放列表信息展示*/
.play_list {
width: 100%;
border-radius: 10px;
background: #2e2e2e;
margin-top: 50px;
position: relative;
}
.play_content {
display: flex;
flex-flow: row wrap;
padding: 10px 10px 10px 18px;
}
.play_list > h2 {
position: absolute;
left: 10px;
top: -10px;
z-index: 50;
}
.play_content a {
font-size: 12px;
min-width: 65px;
padding: 6px 15px;
color: #ffffff;
border-radius: 6px;
margin: 8px 8px;
background: #888888;
}
/*集数选中效果*/
.play_active {
color: orange !important;
background: #424242 !important;
}
.play_content .play_tabs {
background: #2e2e2e;
}
:deep(.el-tabs__nav-scroll) {
display: flex;
justify-content: end;
}
:deep(.el-tabs__header) {
/*border-bottom: 1px solid #888888!important;*/
margin-bottom: 0;
border-bottom: none !important;
background: rgb(34, 34, 34);
height: 50px !important;
}
:deep(.el-tabs__nav) {
border: none !important;
}
:deep(.el-tabs__item.is-active) {
color: #ee9600;
}
:deep(.el-tabs__item:hover) {
color: orange;
background: #484646;
}
:deep(.el-tabs__item) {
height: 50px;
line-height: 50px;
margin-left: 2px;
border-radius: 8px 8px 0 0;
border: none !important;
color: #ffffff;
background: #2e2e2e;
}
/*推荐列表区域*/
.correlation {
width: 100%;
}
</style>
<!--移动端-->
<style scoped>
/*适应小尺寸*/
@media (max-width: 650px) {
.player_area {
padding: 5px 10px;
}
.tags b {
padding: 5px 10px;
background-color: rgba(155, 73, 231, 0.72);
font-size: 13px;
border-radius: 6px;
margin-right: 3px;
}
.tags span {
padding: 6px 10px;
background-color: #404042;
color: #b5b2b2;
border-radius: 5px;
margin: 0 3px;
font-size: 12px;
}
}
</style>

View File

@@ -0,0 +1,459 @@
<template>
<div class="container">
<div class="search_group">
<input v-model="data.search" placeholder="搜索 动漫,剧集,电影 " class="search"/>
<el-button @click="searchMovie" :icon="Search" style="" />
</div>
<div v-if="data.list.length > 0 " class="search_res">
<div class="title">
<h2>{{ data.oldSearch }}</h2>
<p>共找到{{ data.page.total }}部与"{{ data.oldSearch }}"相关的影视作品</p>
</div>
<div class="content">
<div class="film_item" v-for="m in data.list">
<a :href="`/filmDetail?link=${m.id}`" :style="{backgroundImage: `url('${m.picture}')`}"></a>
<div class="film_intro">
<h3>{{ m.name }}</h3>
<p class="tags">
<span class="tag_c">{{ m.cName }}</span>
<span>{{ m.year }}</span>
<span>{{ m.area }}</span>
</p>
<p><em>导演:</em>{{ m.director }}</p>
<p><em>主演:</em>{{ m.actor }}</p>
<p class="blurb"><em>剧情:</em>{{ m.blurb.replaceAll('  ', '') }}</p>
<el-button :icon="CaretRight" @click="play(m.id)">立即播放</el-button>
</div>
</div>
</div>
<div class="pagination_container">
<el-pagination background layout="prev, pager, next"
v-model:current-page="data.page.current"
@current-change="changeCurrent"
:pager-count="5"
:background="true"
:page-size="data.page.pageSize"
:total="data.page.total"
:prev-icon="ArrowLeftBold"
:next-icon="ArrowRightBold"
hide-on-single-page
class="pagination"/>
</div>
</div>
</div>
<el-empty v-if="data.list.length == 0 " description="输入影片名称进行搜索"/>
</template>
<script lang="ts" setup>
import {onMounted, reactive, watch} from "vue";
import {useRoute, useRouter} from "vue-router";
import {ApiGet} from "../../utils/request";
import {ArrowLeftBold, ArrowRightBold, CaretRight, Search} from '@element-plus/icons-vue'
import {ElMessage} from "element-plus";
const router = useRouter()
const route = useRoute()
const data = reactive({
list: [],
page: {
current: 0,
},
oldSearch: '',
search: '',
})
// 监听路由参数的变化
watch(
[route],
(oldRoute, newRoute) => {
refreshPage(router.currentRoute.value.query.search, router.currentRoute.value.query.current)
},
)
// 点击播放
const play = (id: string | number) => {
location.href = `/play?id=${id}&episode=0&source=0`
}
// 搜索按钮事件
const searchMovie = ()=>{
if (data.search.length <=0) {
ElMessage.error({message: '搜索信息不能为空', duration:1000})
return
}
location.href = location.href = `/search?search=${data.search}`
}
// 执行搜索请求
const refreshPage = (keyword: any, current: any) => {
ApiGet('/searchFilm', {keyword: keyword, current: current}).then((resp: any) => {
data.list = resp.data.list
data.page = resp.data.page
data.oldSearch = keyword
})
}
onMounted(() => {
if (router.currentRoute.value.query.search == null) {
return
}
refreshPage(router.currentRoute.value.query.search + '', router.currentRoute.value.query.current)
})
// 分页器
const changeCurrent = (currentVal: number) => {
let query = router.currentRoute.value.query
location.href = `/search?search=${query.search}&current=${currentVal}`
}
</script>
<!--移动端-->
<style scoped>
@media (max-width: 650px) {
.title h2 {
margin: 8px auto;
}
.film_item {
flex-basis: calc(100% - 20px);
margin: 0 10px;
display: flex;
background: #2e2e2e;
padding: 10px;
min-height: 180px;
max-height: 200px;
border-radius: 16px;
margin-bottom: 25px;
}
.film_item a {
flex: 2;
border-radius: 8px;
background-size: cover;
}
.film_intro {
max-width: 60%;
margin-left: 10px;
flex: 3;
text-align: left;
padding: 0 10px;
font-size: 15px;
position: relative;
}
.film_intro h3 {
font-size: 16px;
font-weight: bold;
}
.film_item h3, p, button {
margin: 2px 0 2px 0;
}
.film_item p {
max-width: 90%;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
font-size: 13px;
}
.film_item p em {
font-weight: bold;
margin-right: 8px;
}
.film_item button {
background-color: orange;
border-radius: 20px;
border: none !important;
color: #ffffff;
font-weight: bold;
position: absolute;
margin-bottom: 2px;
bottom: 0;
}
.blurb{
display: none!important;
}
.tags {
display: flex;
width: 90%;
justify-content: space-between;
}
.tags .tag_c{
background: rgba(155, 73, 231, 0.72);
}
.tags span {
border-radius: 5px;
padding: 3px 5px;
background: rgba(66, 66, 66);
color: #c9c4c4;
margin-right: 5px;
}
.search_group {
width: 80%;
margin: 0 auto;
display: flex;
}
.search {
flex: 10;
background-color: #2e2e2e!important;
border: none!important;
height: 40px;
border-radius: 6px 0 0 6px;
padding-left: 20px;
color: #c9c4c4;
font-size: 15px;
font-weight: bold;
}
.search::placeholder{
font-size: 15px;
color: #999999;
}
.search:focus {
outline: none;
}
.search_group button {
flex: 1;
margin: 0;
background-color: #2e2e2e;
color: rgba(155,73,231,0.72);
border: none!important;
height: 40px;
border-radius: 0 8px 8px 0;
font-size: 20px;
/*margin-bottom: 2px*/
}
}
</style>
<!--pc端-->
<style scoped>
.title {
margin-bottom: 20px;
}
.container {
width: 100%;
}
.content {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.search_res {
width: 100%;
}
@media (min-width: 650px) {
.film_item {
flex-basis: calc(50% - 18px);
max-width: 50%;
display: flex;
background: #2e2e2e;
padding: 16px;
min-height: 250px;
max-height: 280px;
border-radius: 16px;
margin-bottom: 25px;
}
.film_item a {
flex: 1;
border-radius: 8px;
background-size: cover;
}
.film_intro {
max-width: 75%;
margin-left: 10px;
flex: 3;
/*flex-grow: 4;*/
text-align: left;
padding: 0 10px;
font-size: 15px;
position: relative;
}
.film_item h3, p, button {
margin: 3px 0 12px 0;
}
.film_item p {
max-width: 90%;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
.film_item p em {
font-weight: bold;
margin-right: 8px;
}
.film_item button {
background-color: orange;
border-radius: 20px;
border: none !important;
color: #ffffff;
font-weight: bold;
position: absolute;
margin-bottom: 2px;
bottom: 0;
}
.tags {
display: flex;
width: 90%;
justify-content: space-between;
}
.tags .tag_c{
background: rgba(155, 73, 231, 0.72);
}
.tags span {
border-radius: 5px;
padding: 3px 5px;
background: rgba(66, 66, 66);
color: #c9c4c4;
margin-right: 10px;
}
.search_group {
width: 45%;
margin: 20px auto;
display: flex;
}
.search {
flex: 10;
background-color: #2e2e2e!important;
border: none!important;
height: 40px;
border-radius: 6px 0 0 6px;
padding-left: 20px;
color: #c9c4c4;
font-size: 15px;
font-weight: bold;
}
.search::placeholder{
font-size: 15px;
color: #999999;
}
.search:focus {
outline: none;
}
.search_group button {
flex: 1;
margin: 0;
background-color: #2e2e2e;
color: rgba(155,73,231,0.72);
border: none!important;
height: 40px;
border-radius: 0 6px 6px 0;
font-size: 20px;
}
}
</style>
<!--分页器-->
<style scoped>
/*分页插件区域*/
.pagination_container {
width: 100%;
margin-top: 30px;
/*background: deepskyblue;*/
text-align: center;
/*display: flex;*/
}
:deep(.el-pagination) {
width: 100% !important;
margin: 0 auto !important;
justify-content: center;
}
/*分页器样式修改*/
:deep(.number) {
font-weight: bold;
width: 45px;
height: 45px;
background: #2e2e2e !important;
color: #ffffff;
border-radius: 50%;
/*margin: 0 3px!important;*/
}
:deep(.number:hover) {
color: #67d9e8;
}
:deep(.btn-prev) {
font-weight: bold;
width: 45px;
height: 45px;
background: #2e2e2e !important;
color: #ffffff;
border-radius: 50%;
}
:deep(.btn-next) {
font-weight: bold;
width: 45px;
height: 45px;
background: #2e2e2e !important;
color: #ffffff;
border-radius: 50%;
}
:deep(.more) {
font-weight: bold;
width: 45px;
height: 45px;
background: #2e2e2e !important;
color: #ffffff;
border-radius: 50%;
}
:deep(.is-active) {
background: #67d9e8 !important;
}
/*移动端缩小*/
@media (max-width: 650px) {
:deep(.number) {
width: 35px;
height: 35px;
}
:deep(.btn-prev) {
width: 35px;
height: 35px;
}
:deep(.btn-next) {
width: 35px;
height: 35px;
}
:deep(.more) {
width: 35px;
height: 35px;
}
}
</style>

1
client/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />