mirror of
https://github.com/ProudMuBai/GoFilm.git
synced 2026-04-10 00:17:30 +08:00
add new features
This commit is contained in:
1
client/components.d.ts
vendored
1
client/components.d.ts
vendored
@@ -21,6 +21,7 @@ declare module '@vue/runtime-core' {
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@videojs-player/vue": "^1.0.0",
|
||||
"axios": "^1.3.4",
|
||||
"element-plus": "^2.4.4",
|
||||
"element-plus": "^2.8.6",
|
||||
"video.js": "^8.0.4",
|
||||
"vue": "^3.4.31"
|
||||
},
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<span>网站管理</span>
|
||||
</template>
|
||||
<el-menu-item index="/manage/system/webSite">站点管理</el-menu-item>
|
||||
<!--<el-menu-item index="">预留配置</el-menu-item>-->
|
||||
<el-menu-item index="/manage/system/banners">海报管理</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-sub-menu index="/manage/collect">
|
||||
<template #title>
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
</a>
|
||||
|
||||
<!-- wrap 导航-->
|
||||
<a @click="visible = true" class="iconfont icon-caidan" style="font-size: 26px"/>
|
||||
<el-drawer v-model="visible" append-to-body :show-close="false" size="45%" class="warp-drawer">
|
||||
<a @click="handleDrawer" class="iconfont icon-caidan" style="font-size: 26px"/>
|
||||
<el-drawer v-model="visible" append-to-body :show-close="false" size="45%" class="warp-drawer" @closed="handleDrawer" >
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<h2 class="menu-title" :id="titleId" :class="titleClass">{{ data.site.siteName }}</h2>
|
||||
</template>
|
||||
@@ -62,7 +62,6 @@
|
||||
</template>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
</div>
|
||||
<!--弹窗模块,显示按钮对应信息-->
|
||||
</div>
|
||||
@@ -78,6 +77,15 @@ import {cookieUtil, COOKIE_KEY_MAP} from "../../utils/cookie";
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
const handleDrawer = ()=>{
|
||||
visible.value = !visible.value
|
||||
if(visible.value) {
|
||||
document.body.style.position = 'fixed'
|
||||
} else {
|
||||
document.body.style.position = ''
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 搜索关键字
|
||||
const keyword = ref<string>('')
|
||||
@@ -160,9 +168,12 @@ onMounted(() => {
|
||||
.el-drawer__body {
|
||||
--el-drawer-padding-primary: 20px;
|
||||
}
|
||||
|
||||
.el-overlay {
|
||||
max-height: 100vh;
|
||||
}
|
||||
.warp-drawer {
|
||||
background-color: var(--bg-dark) !important;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.el-drawer__header {
|
||||
|
||||
@@ -25,6 +25,7 @@ import Film from "../views/manage/film/Film.vue";
|
||||
import FileUpload from "../views/manage/file/FileUpload.vue";
|
||||
import FilmAdd from "../views/manage/film/FilmAdd.vue";
|
||||
import CustomPlay from "../views/index/CustomPlay.vue";
|
||||
import Banners from "../views/manage/system/Banners.vue";
|
||||
|
||||
|
||||
// 2. 定义一个路由
|
||||
@@ -52,6 +53,7 @@ const routes = [
|
||||
{path: 'index', component: ManageIndex},
|
||||
{path: 'collect/index', component: CollectManage},
|
||||
{path: 'system/webSite', component: SiteConfig},
|
||||
{path: 'system/banners', component: Banners},
|
||||
{path: 'cron/index', component: CronManage},
|
||||
{path: 'file/upload', component: FileUpload},
|
||||
{path: 'file/gallery', component: Temp},
|
||||
|
||||
@@ -1,169 +1,181 @@
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, system-ui, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
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;
|
||||
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%;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
|
||||
/*公共颜色*/
|
||||
--bg-light: #ffffff;
|
||||
/*公共颜色*/
|
||||
--bg-light: #ffffff;
|
||||
|
||||
|
||||
--bg-dark: #21252b;
|
||||
--bg-dark: #21252b;
|
||||
|
||||
|
||||
--text-color-primary: #ffffff;
|
||||
--content-text-color: #888888;
|
||||
--text-color-primary: #ffffff;
|
||||
--content-text-color: #888888;
|
||||
|
||||
--paging-parmary-color: #9b49e7d6;
|
||||
--paging-parmary-color-hover: #a574b7;
|
||||
--paging-parmary-color: #9b49e7d6;
|
||||
--paging-parmary-color-hover: #a574b7;
|
||||
|
||||
--border-gray-color: #0000001f;
|
||||
--border-gray-color: #0000001f;
|
||||
|
||||
--btn-primary-color: #8636cc;
|
||||
--btn-pink-color: #d942bf;
|
||||
--btn-bg-linght: #fff;
|
||||
--btn-primary-color: #8636cc;
|
||||
--btn-pink-color: #d942bf;
|
||||
--btn-bg-linght: #fff;
|
||||
|
||||
--bg-fill-light: #8d00fb1a;
|
||||
--bg-fill-light: #8d00fb1a;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
outline: none;
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
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;
|
||||
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;
|
||||
border-color: #646cff;
|
||||
}
|
||||
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
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;
|
||||
}
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
/*通用样式*/
|
||||
.primary{
|
||||
color: #99DBF5;
|
||||
.primary {
|
||||
color: #99DBF5;
|
||||
}
|
||||
|
||||
.dark {
|
||||
color: rgba(0,0,0,0.68);
|
||||
color: rgba(0, 0, 0, 0.68);
|
||||
}
|
||||
|
||||
.light {
|
||||
color: rgba(255,255,255,0.68);
|
||||
color: rgba(255, 255, 255, 0.68);
|
||||
}
|
||||
.silver{
|
||||
color: rgba(255,255,255,0.75);
|
||||
|
||||
.silver {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
|
||||
/*滚动条样式*/
|
||||
@media (min-width: 768px) {
|
||||
::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 5px;
|
||||
height: 10px;
|
||||
background: rgba(255,255,255,0.25);
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 5px;
|
||||
height: 10px;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
/*el-dialog样式*/
|
||||
@media (min-width: 768px){
|
||||
.el-dialog__body{
|
||||
padding: 45px!important;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.el-dialog__body {
|
||||
padding: 45px !important;
|
||||
}
|
||||
|
||||
}
|
||||
@media (max-width: 768px){
|
||||
.el-dialog__body{
|
||||
padding: 20px!important;
|
||||
}
|
||||
.el-dialog__header{
|
||||
padding: 12px 0 8px 0 !important;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.el-dialog__body {
|
||||
padding: 20px !important;
|
||||
}
|
||||
|
||||
.el-dialog__header {
|
||||
padding: 12px 0 8px 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog{
|
||||
border-radius: 8px !important;
|
||||
background-image: linear-gradient( 135deg, #81FFEF 10%, #F067B4 100%) !important;
|
||||
}
|
||||
.el-dialog__header{
|
||||
border-bottom: 1px solid rgba(0,0,0,0.1);
|
||||
margin-right: 0!important;
|
||||
.el-dialog {
|
||||
border-radius: 8px !important;
|
||||
background-image: linear-gradient(135deg, #81FFEF 10%, #F067B4 100%) !important;
|
||||
--el-text-color-primary: #5e1e99b8;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn{
|
||||
outline: none!important;
|
||||
border: none;
|
||||
.el-dialog__header {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
outline: none !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/*分页插件颜色*/
|
||||
.el-popper {
|
||||
--el-color-primary: var(--paging-parmary-color);
|
||||
--el-color-primary: var(--paging-parmary-color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<el-container>
|
||||
<el-header>
|
||||
<!--<Header/>-->
|
||||
<Header />
|
||||
<NewHeader />
|
||||
</el-header>
|
||||
<el-main>
|
||||
<router-view></router-view>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div v-if="false" class="hidden-sm-and-up banner_wrap" @touchstart="touchS" @touchend="touchE" >
|
||||
<div v-if="true" class="hidden-sm-and-up banner_wrap" @touchstart="touchS" @touchend="touchE" >
|
||||
<el-carousel v-model="data.banner.current" ref="wrap" :pause-on-hover="false" :interval="5000" trigger="hover" height="200px" arrow="never" >
|
||||
<el-carousel-item v-for="item in banners" :key="item" >
|
||||
<el-image style="width: 100%; height: 100%;" :src="item.picture" fit="fill"/>
|
||||
@@ -8,7 +8,7 @@
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
<div v-if="false" class="banner hidden-sm-and-down"
|
||||
<div v-if="true" class="banner hidden-sm-and-down"
|
||||
:style="{background:`url(${data.banner.current.picture})`, backgroundRepeat: 'no-repeat', backgroundSize: 'cover'}">
|
||||
<div class="preview">
|
||||
<el-carousel @change="carousel" :interval="5000" height="240px" arrow="always">
|
||||
|
||||
@@ -67,7 +67,8 @@
|
||||
<div class="cus_util">
|
||||
<el-button color="#9b49e7" :icon="CirclePlus" @click="dialogV.addV = true">添加采集站</el-button>
|
||||
<el-button color="#d942bf" @click="openBatchCollect" :icon="Promotion">一键采集</el-button>
|
||||
<el-button type="danger" :icon="BellFilled">ReZero</el-button>
|
||||
<el-button type="danger" :icon="DeleteFilled" @click="dialogV.clear = true" >RemoveAll</el-button>
|
||||
<el-button type="primary" :icon="BellFilled" @click="dialogV.reCollect = true" >AutoCollect</el-button>
|
||||
</div>
|
||||
<!--站点添加弹窗-->
|
||||
<el-dialog v-model="dialogV.addV" title="添加采集站点">
|
||||
@@ -191,6 +192,36 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!--影片删除提示弹窗-->
|
||||
<el-dialog v-model="dialogV.clear" title="是否清除所有影视数据 ?" width="500">
|
||||
<el-form :model="form">
|
||||
<el-form-item label="确认密码" >
|
||||
<el-input v-model="data.password" type="password" placeholder="请输入账户密码并开确认执行" autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogV.clear = false">取消</el-button>
|
||||
<el-button type="primary" @click="clearFilms">确认执行</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!--Re0 从零开始的自动全量采集-->
|
||||
<el-dialog v-model="dialogV.reCollect" title="是否清除影片数据并重新采集 ?" width="500">
|
||||
<el-form :model="form">
|
||||
<el-form-item label="确认密码" >
|
||||
<el-input v-model="data.password" type="password" placeholder="请输入账户密码并开确认执行" autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogV.reCollect = false">取消</el-button>
|
||||
<el-button type="primary" @click="reCollectFilm">确认执行</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -200,7 +231,7 @@
|
||||
import {onMounted, reactive} from "vue";
|
||||
import {ApiGet, ApiPost} from "../../../utils/request";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {Delete, Edit, SwitchButton, CirclePlus, Promotion, BellFilled} from "@element-plus/icons-vue";
|
||||
import {Delete, Edit, SwitchButton, CirclePlus, Promotion, BellFilled, DeleteFilled} from "@element-plus/icons-vue";
|
||||
|
||||
const data = reactive({
|
||||
siteList: [],
|
||||
@@ -208,13 +239,16 @@ const data = reactive({
|
||||
{time: 24, label: '采集今日'},
|
||||
{time: 24 * 7, label: '采集本周'},
|
||||
{time: -1, label: '采集全部'},
|
||||
]
|
||||
],
|
||||
password: '',
|
||||
})
|
||||
|
||||
const dialogV = reactive({
|
||||
addV: false,
|
||||
editV: false,
|
||||
batchV:false,
|
||||
clear: false,
|
||||
reCollect: false,
|
||||
})
|
||||
|
||||
interface FilmSource {
|
||||
@@ -271,9 +305,6 @@ const startTask = (row:any)=>{
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 弹窗图片同步开关限制
|
||||
const restrict = (t:number)=>{
|
||||
// t 弹窗类型 0 - add | 1 - edit
|
||||
@@ -327,7 +358,6 @@ const openEditDialog = (id:string)=>{
|
||||
dialogV.editV = true
|
||||
}
|
||||
|
||||
|
||||
// switch 开关
|
||||
const changeSourceState = (s:any)=>{
|
||||
ApiPost(`/manage/collect/change`, {id:s.id, state: s.state, syncPictures: s.syncPictures}).then((resp: any) => {
|
||||
@@ -373,6 +403,7 @@ const cancelDialog = ()=>{
|
||||
form.add = {name: '', uri: '', resultModel: 0, grade: 1, collectType: 0, syncPictures: false, state: false, interval: 0}
|
||||
}
|
||||
|
||||
// 获取采集列表信息
|
||||
const getCollectList = ()=>{
|
||||
ApiGet(`/manage/collect/list`).then((resp: any) => {
|
||||
if (resp.code === 0) {
|
||||
@@ -402,6 +433,41 @@ const getCollectList = ()=>{
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 清除影片信息
|
||||
const clearFilms = ()=>{
|
||||
if (data.password.length <= 0) {
|
||||
ElMessage.error({message: '操作失败, 密钥信息缺失'})
|
||||
return
|
||||
}
|
||||
ApiGet(`/manage/spider/clear`, {password: data.password}).then((resp:any)=>{
|
||||
if (resp.code === 0) {
|
||||
ElMessage.success({message: resp.msg})
|
||||
} else {
|
||||
ElMessage.error({message: resp.msg})
|
||||
}
|
||||
dialogV.clear = false
|
||||
data.password = ''
|
||||
})
|
||||
}
|
||||
|
||||
// 从零开始重新采集
|
||||
const reCollectFilm = ()=>{
|
||||
if (data.password.length <= 0) {
|
||||
ElMessage.error({message: '操作失败, 密钥信息缺失'})
|
||||
return
|
||||
}
|
||||
ApiGet(`/manage/spider/zero`, {password: data.password}).then((resp:any)=>{
|
||||
if (resp.code === 0) {
|
||||
ElMessage.success({message: resp.msg})
|
||||
} else {
|
||||
ElMessage.error({message: resp.msg})
|
||||
}
|
||||
dialogV.reCollect = false
|
||||
data.password = ''
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getCollectList()
|
||||
})
|
||||
@@ -409,8 +475,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
}
|
||||
|
||||
|
||||
.cus_util {
|
||||
display: flex;
|
||||
|
||||
79
client/src/views/manage/system/Banners.vue
Normal file
79
client/src/views/manage/system/Banners.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<h2 style="color: #8e48b4">首页横幅管理界面</h2>
|
||||
<el-table
|
||||
:data="data.banners" style="width: 100%" border size="default"
|
||||
:row-class-name="'cus-tr'" table-layout="auto">
|
||||
<el-table-column prop="name" label="影片名称"/>
|
||||
<el-table-column prop="collectType" align="center" label="影片类型">
|
||||
<template #default="scope">
|
||||
<el-tag type="warning">{{ scope.row.cName }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="collectType" align="center" label="上映年份">
|
||||
<template #default="scope">
|
||||
<el-tag type="warning">{{ scope.row.year }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="collectType" align="center" label="影片海报">
|
||||
<template #default="scope">
|
||||
<el-image style="width: 120px; height: 80px" :src="scope.row.poster" :preview-src-list="[scope.row.poster]" preview-teleported fit="contain" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="collectType" align="center" label="影片封面">
|
||||
<template #default="scope">
|
||||
<el-image style="width: 60px; height: 80px" :src="scope.row.picture" :preview-src-list="[scope.row.picture]" preview-teleported fit="cover" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="collectType" align="center" label="排序">
|
||||
<template #default="scope">
|
||||
<el-tag disable-transitions>{{ scope.row.sort }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="resultModel" align="center" label="连载状态">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="(scope.row.remarks+'').search('更新') == -1" type="success" >{{ scope.row.remark}}</el-tag>
|
||||
<el-tag v-else type="primary" >{{ scope.row.remark}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button type="success" :icon="SwitchButton" plain circle @click="" />
|
||||
<el-button type="primary" :icon="Edit" plain circle @click="" />
|
||||
<el-button type="danger" :icon="Delete" plain circle @click="" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {Delete, Edit, SwitchButton} from "@element-plus/icons-vue";
|
||||
import {onMounted, reactive} from "vue";
|
||||
import {ApiGet} from "../../../utils/request";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const data = reactive({
|
||||
banners: [],
|
||||
|
||||
})
|
||||
|
||||
const getBanners = ()=>{
|
||||
ApiGet(`/manage/banner/list`).then((resp:any)=>{
|
||||
if(resp.code === 0){
|
||||
data.banners = resp.data
|
||||
} else {
|
||||
ElMessage.error({message: resp.msg})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
// 初始化banners数据
|
||||
getBanners()
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -23,8 +23,8 @@ const (
|
||||
// -------------------------redis key-----------------------------------
|
||||
const (
|
||||
// CategoryTreeKey 分类树 key
|
||||
CategoryTreeKey = "CategoryTree"
|
||||
CategoryTreeExpired = time.Hour * 24 * 90
|
||||
CategoryTreeKey = "CategoryTree"
|
||||
FilmExpired = time.Hour * 24 * 365 * 10
|
||||
// MovieListInfoKey movies分类列表 key
|
||||
MovieListInfoKey = "MovieList:Cid%d"
|
||||
|
||||
@@ -62,6 +62,8 @@ const (
|
||||
ManageConfigExpired = time.Hour * 24 * 365 * 10
|
||||
// SiteConfigBasic 网站参数配置
|
||||
SiteConfigBasic = "SystemConfig:SiteConfig:Basic"
|
||||
// BannersKey 轮播组件key 你
|
||||
BannersKey = "SystemConfig:Banners"
|
||||
|
||||
// FilmCrontabKey 定时任务列表信息
|
||||
FilmCrontabKey = "Cron:Task:Film"
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"server/plugin/SystemInit"
|
||||
"server/plugin/common/util"
|
||||
"server/plugin/spider"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func ManageIndex(c *gin.Context) {
|
||||
@@ -235,6 +236,99 @@ func ResetSiteBasic(c *gin.Context) {
|
||||
system.SuccessOnlyMsg("配置信息重置成功", c)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------ 轮播数据配置 ------------------------------------------------------
|
||||
|
||||
// BannerList 获取轮播图数据
|
||||
func BannerList(c *gin.Context) {
|
||||
bl := logic.ML.GetBanners()
|
||||
system.Success(bl, "配置信息重置成功", c)
|
||||
}
|
||||
|
||||
// BannerFind 返回ID对应的横幅信息
|
||||
func BannerFind(c *gin.Context) {
|
||||
idStr := c.Query("id")
|
||||
if idStr == "" {
|
||||
system.Failed("Banner信息获取失败, ID信息异常", c)
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
system.Failed("删除失败,参数类型异常", c)
|
||||
return
|
||||
}
|
||||
bl := logic.ML.GetBanners()
|
||||
for _, b := range bl {
|
||||
if b.Id == id {
|
||||
system.Success(b, "Banner信息获取成功", c)
|
||||
return
|
||||
}
|
||||
}
|
||||
system.Failed("Banner信息获取失败", c)
|
||||
}
|
||||
|
||||
// BannerAdd 添加海报数据
|
||||
func BannerAdd(c *gin.Context) {
|
||||
var b system.Banner
|
||||
if err := c.ShouldBindJSON(&b); err != nil {
|
||||
system.Failed("Banner参数提交异常", c)
|
||||
return
|
||||
}
|
||||
bl := logic.ML.GetBanners()
|
||||
bl = append(bl, b)
|
||||
if err := logic.ML.SaveBanners(bl); err != nil {
|
||||
system.Failed(fmt.Sprintln("Banners信息添加失败,", err), c)
|
||||
return
|
||||
}
|
||||
system.SuccessOnlyMsg("海报信息添加成功", c)
|
||||
}
|
||||
|
||||
// BannerUpdate 更新海报数据
|
||||
func BannerUpdate(c *gin.Context) {
|
||||
var banner system.Banner
|
||||
if err := c.ShouldBindJSON(&banner); err != nil {
|
||||
system.Failed("Banner参数提交异常", c)
|
||||
return
|
||||
}
|
||||
bl := logic.ML.GetBanners()
|
||||
for i, b := range bl {
|
||||
if b.Id == banner.Id {
|
||||
bl[i] = banner
|
||||
if err := logic.ML.SaveBanners(bl); err != nil {
|
||||
system.Failed("海报信息更新失败", c)
|
||||
} else {
|
||||
system.SuccessOnlyMsg("海报信息更新成功", c)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
system.Failed("海报信息更新失败, 未匹配对应Banner信息", c)
|
||||
}
|
||||
|
||||
// BannerDel 删除海报数据
|
||||
func BannerDel(c *gin.Context) {
|
||||
idStr := c.Query("id")
|
||||
if idStr == "" {
|
||||
system.Failed("Banner信息获取失败, ID信息异常", c)
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
system.Failed("删除失败,参数类型异常", c)
|
||||
return
|
||||
}
|
||||
bl := logic.ML.GetBanners()
|
||||
for i, b := range bl {
|
||||
if b.Id == id {
|
||||
bl = append(bl[:i], bl[i+1:]...)
|
||||
_ = logic.ML.SaveBanners(bl)
|
||||
system.SuccessOnlyMsg("海报信息删除成功", c)
|
||||
return
|
||||
}
|
||||
}
|
||||
system.Failed("海报信息删除失败", c)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------ 参数校验 ------------------------------------------------------
|
||||
func validFilmSource(fs system.FilmSource) error {
|
||||
// 资源名称不能为空 且长度不能超过20
|
||||
|
||||
@@ -3,6 +3,7 @@ package controller
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"server/config"
|
||||
"server/logic"
|
||||
"server/model/system"
|
||||
"strconv"
|
||||
@@ -64,13 +65,27 @@ func StarSpider(c *gin.Context) {
|
||||
system.SuccessOnlyMsg("采集任务已成功开启!!!", c)
|
||||
}
|
||||
|
||||
// ClearAllFilm 删除所有film信息
|
||||
func ClearAllFilm(c *gin.Context) {
|
||||
// 清空采集数据进行重新采集前校验输入的密码是否正确
|
||||
pwd := c.DefaultQuery("password", "")
|
||||
// 如密码错误则不执行后续操作
|
||||
if !verifyPassword(c, pwd) {
|
||||
system.Failed("重置失败, 密钥校验失败!!!", c)
|
||||
return
|
||||
}
|
||||
// 删除已采集的所有影片信息
|
||||
logic.SL.ClearFilms()
|
||||
system.SuccessOnlyMsg("影视数据已删除!!!", c)
|
||||
}
|
||||
|
||||
// SpiderReset 重置影视数据, 清空库存, 从零开始
|
||||
func SpiderReset(c *gin.Context) {
|
||||
// 清空采集数据进行重新采集前校验输入的密码是否正确
|
||||
key := c.DefaultQuery("accessKey", "")
|
||||
pwd := c.DefaultQuery("password", "")
|
||||
// 如密码错误则不执行后续操作
|
||||
if len(key) <= 0 || key != "Re0" {
|
||||
system.Failed("重置失败, 密钥校验失败!!!", c)
|
||||
if !verifyPassword(c, pwd) {
|
||||
system.Failed("重置失败, 密码校验失败!!!", c)
|
||||
return
|
||||
}
|
||||
// 前置校验通过则清空采集数据并对已启用站点进行 全量采集
|
||||
@@ -87,3 +102,17 @@ func CoverFilmClass(c *gin.Context) {
|
||||
}
|
||||
system.SuccessOnlyMsg("影视分类信息重置成功, 请稍等片刻后刷新页面", c)
|
||||
}
|
||||
|
||||
// 校验密码有效性
|
||||
func verifyPassword(c *gin.Context, password string) bool {
|
||||
// 获取已登录的用户信息
|
||||
v, ok := c.Get(config.AuthUserClaims)
|
||||
if !ok {
|
||||
system.Failed("操作失败,登录信息异常!!!", c)
|
||||
return false
|
||||
}
|
||||
// 从context中获取用户的登录信息
|
||||
uc := v.(*system.UserClaims)
|
||||
// 校验密码
|
||||
return logic.UL.VerifyUserPassword(uc.UserID, password)
|
||||
}
|
||||
|
||||
@@ -16,18 +16,22 @@ func (ml *ManageLogic) GetFilmSourceList() []system.FilmSource {
|
||||
return system.GetCollectSourceList()
|
||||
}
|
||||
|
||||
// GetFilmSource 获取ID对应的采集源信息
|
||||
func (ml *ManageLogic) GetFilmSource(id string) *system.FilmSource {
|
||||
return system.FindCollectSourceById(id)
|
||||
}
|
||||
|
||||
// UpdateFilmSource 更新采集源信息
|
||||
func (ml *ManageLogic) UpdateFilmSource(s system.FilmSource) error {
|
||||
return system.UpdateCollectSource(s)
|
||||
}
|
||||
|
||||
// SaveFilmSource 保存采集源信息
|
||||
func (ml *ManageLogic) SaveFilmSource(s system.FilmSource) error {
|
||||
return system.AddCollectSource(s)
|
||||
}
|
||||
|
||||
// DelFilmSource 删除采集源信息
|
||||
func (ml *ManageLogic) DelFilmSource(id string) error {
|
||||
// 先查找是否存在对应ID的站点信息
|
||||
s := system.FindCollectSourceById(id)
|
||||
@@ -51,3 +55,13 @@ func (ml *ManageLogic) GetSiteBasicConfig() system.BasicConfig {
|
||||
func (ml *ManageLogic) UpdateSiteBasic(c system.BasicConfig) error {
|
||||
return system.SaveSiteBasic(c)
|
||||
}
|
||||
|
||||
// GetBanners 获取轮播组件信息
|
||||
func (ml *ManageLogic) GetBanners() system.Banners {
|
||||
return system.GetBanners()
|
||||
}
|
||||
|
||||
// SaveBanners 保存轮播信息
|
||||
func (ml *ManageLogic) SaveBanners(bl system.Banners) error {
|
||||
return system.SaveBanners(bl)
|
||||
}
|
||||
|
||||
@@ -38,6 +38,11 @@ func (sl *SpiderLogic) AutoCollect(time int) {
|
||||
go spider.AutoCollect(time)
|
||||
}
|
||||
|
||||
// ClearFilms 删除采集的数据信息
|
||||
func (sl *SpiderLogic) ClearFilms() {
|
||||
go spider.ClearSpider()
|
||||
}
|
||||
|
||||
// ZeroCollect 数据清除从零开始采集
|
||||
func (sl *SpiderLogic) ZeroCollect(time int) {
|
||||
go spider.StarZero(time)
|
||||
|
||||
@@ -57,6 +57,7 @@ func (ul *UserLogic) ChangePassword(account, password, newPassword string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserInfo 获取用户基本信息
|
||||
func (ul *UserLogic) GetUserInfo(id uint) system.UserInfoVo {
|
||||
// 通过用户ID查询对应的用户信息
|
||||
u := system.GetUserById(id)
|
||||
@@ -64,3 +65,11 @@ func (ul *UserLogic) GetUserInfo(id uint) system.UserInfoVo {
|
||||
var vo = system.UserInfoVo{Id: u.ID, UserName: u.UserName, Email: u.Email, Gender: u.Gender, NickName: u.NickName, Avatar: u.Avatar, Status: u.Status}
|
||||
return vo
|
||||
}
|
||||
|
||||
// VerifyUserPassword 校验密码
|
||||
func (ul *UserLogic) VerifyUserPassword(id uint, password string) bool {
|
||||
// 获取当前登录的用户全部信息
|
||||
u := system.GetUserById(id)
|
||||
// 校验密码是否正确
|
||||
return util.PasswordEncrypt(password, u.Salt) == u.Password
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ func start() {
|
||||
|
||||
// 启动前先执行数据库内容的初始化工作
|
||||
DefaultDataInit()
|
||||
SystemInit.BannersInit()
|
||||
// 开启路由监听
|
||||
r := router.SetupRouter()
|
||||
_ = r.Run(fmt.Sprintf(":%s", config.ListenerPort))
|
||||
|
||||
@@ -26,7 +26,7 @@ type CategoryTree struct {
|
||||
// SaveCategoryTree 保存影片分类信息
|
||||
func SaveCategoryTree(tree *CategoryTree) error {
|
||||
data, _ := json.Marshal(tree)
|
||||
return db.Rdb.Set(db.Cxt, config.CategoryTreeKey, data, config.CategoryTreeExpired).Err()
|
||||
return db.Rdb.Set(db.Cxt, config.CategoryTreeKey, data, config.FilmExpired).Err()
|
||||
}
|
||||
|
||||
// GetCategoryTree 获取影片分类信息
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"log"
|
||||
"server/config"
|
||||
"server/plugin/db"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// BasicConfig 网站基本信息
|
||||
type BasicConfig struct {
|
||||
SiteName string `json:"siteName"` // 网站名称
|
||||
Domain string `json:"domain"` // 网站域名
|
||||
@@ -17,6 +19,30 @@ type BasicConfig struct {
|
||||
Hint string `json:"hint"` // 网站关闭提示
|
||||
}
|
||||
|
||||
// Banner 首页横幅信息
|
||||
type Banner struct {
|
||||
Id int64 `json:"id"` // 绑定所属影片Id
|
||||
Name string `json:"name"` // 影片名称
|
||||
Year int64 `json:"year"` // 上映年份
|
||||
CName string `json:"cName"` // 分类名称
|
||||
Poster string `json:"poster"` // 海报图片链接
|
||||
Picture string `json:"picture"` // 横幅大图链接
|
||||
Remark string `json:"remark"` // 更新状态描述信息
|
||||
Sort int64 `json:"sort"` // 排序分值
|
||||
}
|
||||
|
||||
type Banners []Banner
|
||||
|
||||
func (bl Banners) Len() int {
|
||||
return len(bl)
|
||||
}
|
||||
func (bl Banners) Less(i, j int) bool {
|
||||
return bl[i].Sort < bl[j].Sort
|
||||
}
|
||||
func (bl Banners) Swap(i, j int) {
|
||||
bl[i], bl[j] = bl[j], bl[i]
|
||||
}
|
||||
|
||||
// ------------------------------------------------------ Redis ------------------------------------------------------
|
||||
|
||||
// SaveSiteBasic 保存网站基本配置信息
|
||||
@@ -34,3 +60,21 @@ func GetSiteBasic() BasicConfig {
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// GetBanners 获取轮播配置信息
|
||||
func GetBanners() Banners {
|
||||
var bl Banners
|
||||
data := db.Rdb.Get(db.Cxt, config.BannersKey).Val()
|
||||
if err := json.Unmarshal([]byte(data), &bl); err != nil {
|
||||
log.Println("Get Banners Error", err)
|
||||
}
|
||||
// 通过 sort 对banners进行排序
|
||||
sort.Sort(bl)
|
||||
return bl
|
||||
}
|
||||
|
||||
// SaveBanners 保存轮播配置信息
|
||||
func SaveBanners(bl Banners) error {
|
||||
data, _ := json.Marshal(bl)
|
||||
return db.Rdb.Set(db.Cxt, config.BannersKey, data, config.ManageConfigExpired).Err()
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func SaveDetails(list []MovieDetail) (err error) {
|
||||
data, _ := json.Marshal(detail)
|
||||
// 1. 原使用Zset存储, 但是不便于单个检索 db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Cid%d", config.MovieDetailKey, detail.Cid), redis.Z{Score: float64(detail.Id), Member: member}).Err()
|
||||
// 改为普通 k v 存储, k-> id关键字, v json序列化的结果
|
||||
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
|
||||
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
|
||||
// 2. 同步保存简略信息到redis中
|
||||
SaveMovieBasicInfo(detail)
|
||||
// 3. 保存 Search tag redis中
|
||||
@@ -121,7 +121,7 @@ func SaveDetail(detail MovieDetail) (err error) {
|
||||
// 序列化影片详情信息
|
||||
data, _ := json.Marshal(detail)
|
||||
// 保存影片信息到Redis
|
||||
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
|
||||
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func SaveMovieBasicInfo(detail MovieDetail) {
|
||||
Year: detail.Year,
|
||||
}
|
||||
data, _ := json.Marshal(basicInfo)
|
||||
_ = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
|
||||
_ = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
|
||||
}
|
||||
|
||||
// SaveSitePlayList 仅保存播放url列表信息到当前站点
|
||||
|
||||
@@ -20,3 +20,13 @@ func BasicConfigInit() {
|
||||
}
|
||||
_ = system.SaveSiteBasic(bc)
|
||||
}
|
||||
|
||||
func BannersInit() {
|
||||
var bl = system.Banners{
|
||||
system.Banner{Name: "樱花庄的宠物女孩", Year: 2020, CName: "日韩动漫", Poster: "https://s2.loli.net/2024/02/21/Wt1QDhabdEI7HcL.jpg", Picture: "https://img.bfzypic.com/upload/vod/20230424-43/06e79232a4650aea00f7476356a49847.jpg", Remark: "已完结"},
|
||||
system.Banner{Name: "从零开始的异世界生活", Year: 2020, CName: "日韩动漫", Poster: "https://s2.loli.net/2024/02/21/Wt1QDhabdEI7HcL.jpg", Picture: "https://img.bfzypic.com/upload/vod/20230424-43/06e79232a4650aea00f7476356a49847.jpg", Remark: "已完结"},
|
||||
system.Banner{Name: "五等分的花嫁", Year: 2020, CName: "日韩动漫", Poster: "https://s2.loli.net/2024/02/21/Wt1QDhabdEI7HcL.jpg", Picture: "https://img.bfzypic.com/upload/vod/20230424-43/06e79232a4650aea00f7476356a49847.jpg", Remark: "已完结"},
|
||||
system.Banner{Name: "我的青春恋爱物语果然有问题", Year: 2020, CName: "日韩动漫", Poster: "https://s2.loli.net/2024/02/21/Wt1QDhabdEI7HcL.jpg", Picture: "https://img.bfzypic.com/upload/vod/20230424-43/06e79232a4650aea00f7476356a49847.jpg", Remark: "已完结"},
|
||||
}
|
||||
_ = system.SaveBanners(bl)
|
||||
}
|
||||
|
||||
@@ -229,6 +229,11 @@ func AutoCollect(h int) {
|
||||
}
|
||||
}
|
||||
|
||||
// ClearSpider 删除已采集的影片信息
|
||||
func ClearSpider() {
|
||||
system.FilmZero()
|
||||
}
|
||||
|
||||
// StarZero 情况站点内所有影片信息
|
||||
func StarZero(h int) {
|
||||
// 首先清除影视信息
|
||||
|
||||
@@ -34,14 +34,26 @@ func SetupRouter() *gin.Engine {
|
||||
manageRoute.Use(middleware.AuthToken())
|
||||
{
|
||||
manageRoute.GET(`/index`, controller.ManageIndex)
|
||||
|
||||
// 系统相关
|
||||
sysConfig := manageRoute.Group(`/config`)
|
||||
{
|
||||
sysConfig.GET("/basic", controller.SiteBasicConfig)
|
||||
sysConfig.POST("/basic/update", controller.UpdateSiteBasic)
|
||||
sysConfig.GET("/basic/reset", controller.ResetSiteBasic)
|
||||
sysConfig.GET(`/basic`, controller.SiteBasicConfig)
|
||||
sysConfig.POST(`/basic/update`, controller.UpdateSiteBasic)
|
||||
sysConfig.GET(`/basic/reset`, controller.ResetSiteBasic)
|
||||
}
|
||||
|
||||
// 轮播相关
|
||||
banner := manageRoute.Group(`banner`)
|
||||
{
|
||||
banner.GET(`/list`, controller.BannerList)
|
||||
banner.GET(`/find`, controller.BannerFind)
|
||||
banner.POST(`/add`, controller.BannerAdd)
|
||||
banner.GET(`/update`, controller.BannerUpdate)
|
||||
banner.GET(`/del`, controller.BannerDel)
|
||||
}
|
||||
|
||||
// 用户相关
|
||||
userRoute := manageRoute.Group(`/user`)
|
||||
{
|
||||
userRoute.GET(`/info`, controller.UserInfo)
|
||||
@@ -77,6 +89,7 @@ func SetupRouter() *gin.Engine {
|
||||
{
|
||||
spiderRoute.POST(`/start`, controller.StarSpider)
|
||||
spiderRoute.GET(`/zero`, controller.SpiderReset)
|
||||
spiderRoute.GET(`/clear`, controller.ClearAllFilm)
|
||||
spiderRoute.GET(`/class/cover`, controller.CoverFilmClass)
|
||||
}
|
||||
// filmManage 影视管理
|
||||
|
||||
Reference in New Issue
Block a user