This commit is contained in:
mubai
2023-12-23 22:32:52 +08:00
parent d85dbe915c
commit b48e53a637
151 changed files with 12451 additions and 1382 deletions

View File

@@ -0,0 +1,58 @@
<template>
<el-image-viewer
v-if="data.show"
:urlList="data.list"
:z-index="data.zIndex"
:initial-index="data.initialIndex"
:infinite="data.infinite"
:hideOnClickModal="data.hideOnClickModal"
@close="data.show = false"
></el-image-viewer>
</template>
<script setup lang="ts">
import {onMounted, reactive, watch} from "vue";
const props = defineProps({
options: {
type:Object,
default: {
list:Array,
currentLink: String,
show:Boolean,
}
},
remove: {
type:Function,
default: null,
}
})
const data = reactive({
show: false,
list: [{link:''}],
zIndex: 2000,
initialIndex: 0,
infinite: true,
hideOnClickModal: false,
})
onMounted(()=>{
data.list = props.options.list
data.list.forEach((item,index)=>{
if (item == props.options.currentLink) {
data.initialIndex = index
}
})
data.show = props.options?.show
})
watch([data],()=>{
!data.show && props.remove()
})
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,20 @@
import ImageViewer from "./ImageViewer.vue";
import {createApp} from "vue";
const Preview = (options:any) =>{
// 默认创建 ImageViewer 组件时为显示状态
options.show = true
// 创建节点用户挂载
const el = document.createElement("div")
document.body.appendChild(el)
const app = createApp(ImageViewer, {
options,
remove(){
app.unmount()
document.body.removeChild(el)
}
})
return app.mount(el)
}
export {Preview}

View File

@@ -3,7 +3,8 @@
<!-- 左侧logo以及搜索 -->
<div class="nav_left">
<!-- <img class="logo" src="/src/assets/logo.png">-->
<a href="/" class="site">GoFilm</a>
<!--<el-avatar class="logo" :size="45" :src="data.site.logo" alt="GoFilm"/>-->
<a href="/" class="site">{{ data.site.siteName }}</a>
<div class="search_group">
<input v-model="keyword" @keydown="(e)=>{e.keyCode == 13 && searchFilm()}" placeholder="搜索 动漫,剧集,电影 "
class="search"/>
@@ -13,10 +14,13 @@
<!--右侧顶级分类导航 -->
<div class="nav_right">
<a href="/">首页</a>
<a :href="`/filmClassify?Pid=${nav.film.id}`">电影</a>
<a :href="`/filmClassify?Pid=${nav.tv.id}`">剧集</a>
<a :href="`/filmClassify?Pid=${nav.cartoon.id}`">动漫</a>
<a :href="`/filmClassify?Pid=${nav.variety.id}`">综艺</a>
<!--<a :href="`/filmClassify?Pid=${nav.film.id}`">电影</a>-->
<!--<a :href="`/filmClassify?Pid=${nav.tv.id}`">剧集</a>-->
<!--<a :href="`/filmClassify?Pid=${nav.cartoon.id}`">动漫</a>-->
<!--<a :href="`/filmClassify?Pid=${nav.variety.id}`">综艺</a>-->
<template v-for="n in data.nav">
<a :href="`/filmClassify?Pid=${n.id}`">{{ n.name }}</a>
</template>
<div class="history-link hidden-md-and-down" v-on:mouseenter="handleHistory(true)"
v-on:mouseleave="handleHistory(false)">
@@ -65,6 +69,8 @@ const keyword = ref<string>('')
const data = reactive({
historyFlag: false,
historyList: [{}],
nav: Array,
site: Object,
})
// 加载观看历史记录信息
const handleHistory = (flag: boolean) => {
@@ -106,17 +112,30 @@ const nav = reactive({
tv: {},
variety: {},
})
// 获取站点信息
const getBasicInfo = ()=>{
ApiGet(`/manage/config/basic`).then((resp: any) => {
if (resp.code === 0) {
data.site = resp.data
} else {
ElMessage.error({message: resp.data.msg})
}
})
}
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
// nav.tv = resp.data.tv
// nav.film = resp.data.film
// nav.cartoon = resp.data.cartoon
// nav.variety = resp.data.variety
data.nav = resp.data
} else {
ElMessage.error({message: "请先输入影片名称关键字再进行搜索", duration: 1000})
}
})
getBasicInfo()
})
</script>

View File

@@ -30,7 +30,7 @@ defineProps({
margin-left: 10%;
transform: translate3d(-50%, -50%, 0);
background: rgba(0,0,0, 0.65);
z-index: 2002;
z-index: 5000;
overflow-y: hidden;
}
.loader-container {

View File

@@ -0,0 +1,239 @@
<template>
<div class="header_container">
<div class="left">
<a href="javascript:;" @click="collapse.changeCollapse"
:class="`iconfont ${ collapse.collapse.value ? 'icon-unfold': 'icon-fold'}`"></a>
<h3>后台管理中心</h3>
</div>
<div class="right">
<el-dropdown placement="bottom">
<div class="dropdown_user">
<el-avatar class="avatar" :size="35" :src="data.userInfo.avatar.toString()" alt="admin"/>
<span>{{ data.userInfo.nickName }}</span>
<el-icon class="el-icon--right">
<arrow-down/>
</el-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="a"><em class="iconfont icon-user-info"/>个人信息</el-dropdown-item>
<el-dropdown-item command="a" @click="dialogV.changePwd = true"><em class="iconfont icon-change-pwd2"/>修改密码
</el-dropdown-item>
<el-dropdown-item command="e" divided @click="logout"><em class="iconfont icon-logout"/>退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<!--密码修改弹窗-->
<el-dialog v-model="dialogV.changePwd" width="480px" title="用户密码修改">
<el-form :model="form.changePwd" :rules="rules" label-width="80px">
<el-form-item label="原始密码" prop="password">
<el-input v-model="form.changePwd.password" :type="form.type.password?'text':'password'"/>
<i :class="`cus-pwd iconfont ${form.type.password?'icon-eye2':'icon-eye'}`"
@click="form.type.password = !form.type.password"/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input v-model="form.changePwd.newPassword" :type="form.type.newPassword?'text':'password'"/>
<i :class="`cus-pwd iconfont ${form.type.newPassword?'icon-eye2':'icon-eye'}`"
@click="form.type.newPassword = !form.type.newPassword"/>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="form.changePwd.confirmPassword" :type="form.type.confirmPassword?'text':'password'"/>
<i :class="`cus-pwd iconfont ${form.type.confirmPassword?'icon-eye2':'icon-eye'}`"
@click="form.type.confirmPassword = !form.type.confirmPassword"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button color="#9b49e7" @click="changePassword">确认</el-button>
<el-button @click="cancelDialog">取消</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import {ArrowDown} from "@element-plus/icons-vue";
import {inject, onMounted, reactive} from "vue";
import {ApiGet, ApiPost} from "../../utils/request";
import {ElMessage} from "element-plus";
import {useRouter} from "vue-router";
import {clearAuthToken} from "../../utils/token";
const router = useRouter()
const data = reactive({
userInfo: {id: Number, userName: String, email: String, gender: Number, nickName: String, avatar: String, status: Number}
})
// 侧边菜单栏展开状态
const collapse = inject('collapse')
// 用户密码修改对话框
const dialogV = reactive({
changePwd: false,
})
// 用户密码修改表单
const form = reactive({
changePwd: {password: '', newPassword: '', confirmPassword: ''},
type: {password: false, newPassword: false, confirmPassword: false},
})
// 校验密码一致性
const regex = `^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$@$!%*?&])[A-Za-z\\d$@$!%*?&]{8,12}$`
const validateNewPwd = (rule: any, value: any, callback: any) => {
console.log(value)
if (value === '') {
callback(new Error('新密码不能为空'))
} else if (!value.match(regex)) {
// ruleFormRef.value.validateField('checkPass', () => null)
callback(new Error('密码必须为8-12位且包含大小写字母数字和特殊字符'))
}
callback()
}
const validateConfirmPwd = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('确认密码不能为空'))
} else if (form.changePwd.newPassword !== '' && form.changePwd.newPassword != form.changePwd.confirmPassword) {
callback(new Error('新密码与确认密码不一致'))
}
callback()
}
// 表单校验
const rules = reactive({
password: [{required: true, message: '原始密码信息不能为空', trigger: 'blur'}],
newPassword: [{required: true, validator: validateNewPwd, trigger: 'blur'}],
confirmPassword: [{required: true, validator: validateConfirmPwd, trigger: 'blur'}],
})
// 修改密码
const changePassword = ()=>{
ApiPost(`/changePassword`, {password: form.changePwd.password, newPassword: form.changePwd.newPassword}).then((resp: any) => {
if (resp.status === 'ok') {
// 退出登录成功则删除本地的token信息并返回到 登录 /login 界面
// clearAuthToken()
// router.push(`/login`)
ElMessage.success({message: resp.message})
} else {
ElMessage.error({message: resp.message})
}
})
}
// 对话框关闭
const cancelDialog = () => {
dialogV.changePwd = false
form.changePwd = {password: '', newPassword: '', confirmPassword: ''}
}
// 退出登录
const logout = () => {
// 发送请求使当前的token信息失效
ApiGet(`/logout`).then((resp: any) => {
if (resp.status === 'ok') {
// 退出登录成功则删除本地的token信息并返回到 登录 /login 界面
clearAuthToken()
router.push(`/login`)
} else {
ElMessage.error({message: resp.message})
}
})
}
// 获取用户信息
const getUserInfo = ()=>{
ApiGet(`/manage/user/info` ).then((resp: any) => {
if (resp.code === 0) {
resp.data.avatar = resp.data.avatar == 'empty'?'https://s2.loli.net/2023/12/05/O2SEiUcMx5aWlv4.jpg': resp.data.avatar
data.userInfo = resp.data
} else {
ElMessage.error({message: resp.msg})
}
})
}
onMounted(()=>{
// 获取用户信息, 初始化组件数据
getUserInfo()
})
</script>
<style scoped>
/*密码输入框后缀*/
.cus-pwd {
color: #b07ada;
position: absolute;
right: 8px;
cursor: pointer;
}
.header_container {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
}
.left {
display: flex;
justify-content: center;
align-items: center;
}
.left a {
font-size: 30px;
color: #9b49e7;
}
.left h3 {
color: #d9ecff;
}
.left a:hover {
color: #9b49e7b8;
}
.right {
display: flex;
justify-content: center;
align-items: center;
}
:deep(.el-dropdown) {
outline: none;
}
:deep(.el-dropdown-menu__item) {
padding: 8px 20px !important;
--el-dropdown-menuItem-hover-color: #8b40ff;
}
.dropdown_user {
outline: none;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.iconfont {
margin-right: 10px;
}
.avatar {
margin-right: 13px;
}
</style>

View File

@@ -0,0 +1,129 @@
<template>
<div>
<!--:collapse-transition="false" 关闭过渡动画-->
<el-menu default-active="2" class="side-nav" router :collapse="collapse.collapse.value" >
<el-menu-item index="" @click="toIndex" >
<el-avatar class="logo" :size="30" :src="data.site.logo.toString()" alt="GoFilm"/>
<template #title>
<b class="site_name">{{ data.site.siteName }}</b>
</template>
</el-menu-item>
<el-sub-menu index="/manage/index">
<template #title>
<el-icon><HomeFilled /></el-icon>
<span>网站管理</span>
</template>
<el-menu-item index="/manage/system/webSite">站点管理</el-menu-item>
<!--<el-menu-item index="">预留配置</el-menu-item>-->
</el-sub-menu>
<el-sub-menu index="/manage/collect">
<template #title>
<el-icon><MagicStick /></el-icon>
<span>采集管理</span>
</template>
<el-menu-item index="/manage/collect/index">影视采集</el-menu-item>
</el-sub-menu>
<el-sub-menu index="/manage/cron">
<template #title>
<el-icon><Timer /></el-icon>
<span>定时任务</span>
</template>
<el-menu-item index="/manage/cron/index">任务管理</el-menu-item>
</el-sub-menu>
<!--<el-menu-item index="/manage/category/index">-->
<!-- <el-icon><Menu /></el-icon>-->
<!-- <template #title>分类管理</template>-->
<!--</el-menu-item>-->
<el-sub-menu index="/manage/film">
<template #title>
<el-icon><Film /></el-icon>
<span>影片管理</span>
</template>
<el-menu-item index="/manage/film/class">影视分类</el-menu-item>
<el-menu-item index="/manage/film">影视信息</el-menu-item>
<el-menu-item index="/manage/film/add">影片添加</el-menu-item>
<el-menu-item index="/manage/film/detail">视频详情</el-menu-item>
</el-sub-menu>
<el-sub-menu index="/manage/file">
<template #title>
<el-icon><FolderOpened /></el-icon>
<span>文件管理</span>
</template>
<el-menu-item index="/manage/file/upload">文件上传</el-menu-item>
<el-menu-item index="/manage/file/gallery">图库管理</el-menu-item>
</el-sub-menu>
</el-menu>
</div>
</template>
<script setup lang="ts">
import {Menu , HomeFilled, MagicStick, Timer, Film, FolderOpened} from '@element-plus/icons-vue'
import {inject, onMounted, reactive} from "vue";
import {ApiGet} from "../../utils/request";
import {ElMessage} from "element-plus";
// 菜单栏展开状态
const collapse = inject('collapse')
const data = reactive({
site: {siteName: String, logo:String},
})
// 网站logo点击事件
const toIndex = ()=>{
window.open('/index')
}
// 初始化网站图标名称等组件数据
const getSiteInfo = ()=>{
ApiGet(`/manage/config/basic`).then((resp: any) => {
if (resp.code == 0) {
data.site = resp.data
} else {
ElMessage.error({message: resp.data.msg})
}
})
}
onMounted(()=>{
getSiteInfo()
})
</script>
<style scoped>
:deep(.el-menu){
--el-menu-bg-color: #191a23!important;
--el-menu-hover-bg-color: rgb(20, 21, 28);
--el-menu-level: 0;
--el-menu-text-color: #fff;
--el-menu-active-color: skyblue;
}
.side-nav {
padding: 20px 0;
height: 100vh;
border-right: none;
}
.side_head{
display: flex;
font-size: 16px;
}
.logo{
margin-right: 10px;
min-width: 30px;
}
.site_name {
color: transparent;
font-size: 20px;
font-style: italic;
-webkit-background-clip: text!important;
background-clip: text;
background: linear-gradient(118deg, #e91a90, #c965b3, #988cd7, #00acfd);
}
</style>

View File

@@ -24,7 +24,7 @@
<MoreFilled/>
</el-icon>
</a>
<CustomDialog />
<!--<CustomDialog />-->
</div>
</template>