update something

This commit is contained in:
mubai
2023-10-22 23:34:22 +08:00
parent 915897c21d
commit c4d9d8440a
16 changed files with 717 additions and 591 deletions

247
README.md
View File

@@ -2,7 +2,7 @@
一个基于 vue 和 gin 实现的在线观影网站
效果展示: <a href="http://119.23.231.91:3600" target="_blank"> 演示站点(国内访问速度快)</a> <a href="https://m.mubai.link/" target="_blank">正式站点</a>
效果展示: <a href="http://119.23.231.91:3600" target="_blank"> 演示站点(国内访问速度快)</a> <a href="https://m.mubai.link/" target="_blank">站点</a>
## 简介
@@ -14,102 +14,153 @@
## 目录结构
- client 客户端项目目录
- server 服务端接口项目目录
- film 项目部署相关配置目录
- client 客户端项目目录 [Client简介](./client/README.md)
- server 服务端接口项目目录 [Server简介](./client/README.md)
- film 项目部署相关配置目录 [film 项目安装](./film/README.md)
- 详细说明请查看具体目录中的README文件
```text
GoFilm
├─ client
│ ├─ public
│ │ └─ favicon.ico
│ ├─ src
│ │ ├─ assets
│ │ │ ├─ image
│ │ │ │ ├─ 404.png
│ │ │ │ ├─ cartoon.png
│ │ │ │ film.png
│ │ │ │ ├─ play.png
│ │ │ tv.png
│ │ │ └─ svg
│ │ │ ├─ cartoon.svg
│ │ │ ├─ film.svg
│ │ │ └─ tv.svg
│ │ ├─ components
│ │ │ ├─ Footer.vue
│ │ │ ├─ Header.vue
│ │ │ ├─ RelateList.vue
│ │ │ Util.vue
│ │ ├─ router
│ │ │ └─ router.ts
│ │ ├─ utils
│ │ │ └─ request.ts
│ │ ├─ views
│ │ │ error
│ │ │ │ └─ Error404.vue
│ │ │ ├─ index
│ │ │ │ CategoryFilm.vue
│ │ │ │ ├─ FilmDetails.vue
│ │ │ │ ├─ Home.vue
│ │ │ │ ├─ Play.vue
│ │ │ │ SearchFilm.vue
│ │ │ └─ IndexHome.vue
│ │ ├─ App.vue
│ │ ├─ main.ts
│ │ ├─ style.css
│ │ vite-env.d.ts
│ ├─ auto-imports.d.ts
│ ├─ components.d.ts
├─ index.html
│ ├─ package-lock.json
│ ├─ package.json
│ ├─ README.md
│ ├─ tsconfig.json
│ ├─ tsconfig.node.json
vite.config.ts
├─ film
data
│ │ ├─ nginx
│ │ ├─ html
│ │ │ └─ nginx.conf
│ │ └─ redis
│ │ └─ redis.conf
├─ docker-compose.yml
├─ Dockerfile
└─ README.md
├─ server
├─ config
│ │ └─ DataConfig.go
├─ controller
│ │ IndexController.go
│ │ └─ SpiderController.go
│ ├─ logic
│ │ IndexLogic.go
├─ model
│ │ ├─ Categories.go
│ │ ├─ LZJson.go
│ │ ├─ Movies.go
│ │ Search.go
├─ plugin
│ │ ├─ common
│ │ │ ├─ ProcessCategory.go
│ │ │ ProcessMovies.go
│ │ ├─ db
│ │ │ ├─ mysql.go
│ │ │ redis.go
│ │ └─ spider
│ │ ├─ Spider.go
│ │ ├─ SpiderCron.go
│ │ SpiderRequest.go
├─ router
│ │ └─ router.go
├─ go.mod
├─ go.sum
├─ main.go
└─ README.md
├─ LICENSE
└─ README.md
GoFilm-main
├─ client
│ ├─ public
│ │ └─ favicon.ico
│ ├─ src
│ │ ├─ assets
│ │ │ ├─ css
│ │ │ │ ├─ classify.css
│ │ │ │ ├─ film.css
│ │ │ │ pagination.css
│ │ │ └─ image
│ │ │ 404.png
│ │ │ └─ play.png
│ │ ├─ components
│ │ │ ├─ Loading
│ │ │ ├─ index.ts
│ │ │ │ └─ Loading.vue
│ │ │ ├─ FilmList.vue
│ │ │ ├─ Footer.vue
│ │ │ ├─ Header.vue
│ │ │ RelateList.vue
│ │ │ └─ Util.vue
│ │ ─ router
│ │ │ └─ router.ts
│ │ ├─ utils
│ │ │ ├─ cookie.ts
│ │ │ request.ts
│ │ ├─ views
│ │ │ ├─ error
│ │ │ │ Error404.vue
│ │ │ ├─ index
│ │ │ │ ├─ FilmClassify.vue
│ │ │ │ ├─ FilmClassifySearch.vue
│ │ │ │ FilmDetails.vue
│ │ │ │ ├─ Home.vue
│ │ │ │ ├─ Play.vue
│ │ │ │ └─ SearchFilm.vue
│ │ │ └─ IndexHome.vue
│ │ App.vue
│ ├─ main.ts
│ ├─ style.css
│ └─ vite-env.d.ts
│ ├─ auto-imports.d.ts
│ ├─ components.d.ts
│ ├─ index.html
│ ├─ package.json
│ ├─ README.md
tsconfig.json
│ ├─ tsconfig.node.json
vite.config.ts
├─ film
├─ data
│ │ ─ nginx
│ │ │ ├─ html
│ │ │ ├─ assets
│ │ │ │ ├─ 404-b813c94a.png
├─ index-984712d6.js
├─ index-de4c7ff5.css
│ │ │ └─ play-bb9c8990.png
├─ favicon.ico
│ │ │ │ └─ index.html
│ │ └─ nginx.conf
│ │ redis
│ │ └─ redis.conf
│ ├─ server
│ │ config
└─ DataConfig.go
│ │ ├─ controller
│ │ │ ├─ IndexController.go
│ │ │ └─ SpiderController.go
│ │ logic
├─ IndexLogic.go
│ │ │ └─ SpiderLogic.go
│ │ ├─ model
│ │ │ Categories.go
│ │ │ ├─ Movies.go
│ │ │ ├─ RequestParams.go
│ │ │ ResponseJson.go
│ │ │ └─ Search.go
│ │ ├─ plugin
│ │ ├─ common
│ │ │ │ dp
│ │ │ ├─ ProcessCategory.go
│ │ │ │ │ └─ ProcessMovies.go
│ │ │ └─ param
└─ SimpleParam.go
│ │ ├─ db
│ │ │ ├─ mysql.go
│ │ └─ redis.go
│ │ │ └─ spider
│ │ │ ├─ Spider.go
│ │ │ ├─ SpiderCron.go
│ │ │ └─ SpiderRequest.go
│ │ ├─ router
│ │ │ └─ router.go
│ │ ├─ go.mod
│ │ ├─ go.sum
│ │ ├─ main.go
│ │ └─ README.md
│ ├─ docker-compose.yml
│ ├─ Dockerfile
│ └─ README.md
├─ server
│ ├─ config
│ │ └─ DataConfig.go
│ ├─ controller
│ │ ├─ IndexController.go
│ │ └─ SpiderController.go
│ ├─ logic
│ │ ├─ IndexLogic.go
│ │ └─ SpiderLogic.go
│ ├─ model
│ │ ├─ Categories.go
│ │ ├─ Movies.go
│ │ ├─ RequestParams.go
│ │ ├─ ResponseJson.go
│ │ └─ Search.go
│ ├─ plugin
│ │ ├─ common
│ │ │ ├─ dp
│ │ │ │ ├─ ProcessCategory.go
│ │ │ │ └─ ProcessMovies.go
│ │ │ ├─ param
│ │ │ │ └─ SimpleParam.go
│ │ │ └─ util
│ │ │ ├─ FileDownload.go
│ │ │ └─ Request.go
│ │ ├─ db
│ │ │ ├─ mysql.go
│ │ │ └─ redis.go
│ │ └─ spider
│ │ ├─ Spider.go
│ │ └─ SpiderCron.go
│ ├─ router
│ │ └─ router.go
│ ├─ go.mod
│ ├─ go.sum
│ ├─ main.go
│ └─ README.md
├─ LICENSE
└─ README.md
```
@@ -128,11 +179,17 @@ GoFilm
## 更新迭代计划
- 目前用户界面的一些功能有待开发和完善, 大家也可以继续提供一些好的建议
- 目前pc端的历史记录写了一个简单的测试版, 后面有时间会同步完善pc和wrap端的历史记录和收藏功能
- 前台功能目前基本满足观看的需求, 后续考虑切入一些登录和账户以及管理后台的功能,慢慢完善这个项目.
## JetBrains 开源证书
感谢Jetbrains提供的免费开源许可, 项目开发中使用GoLangWebStam让编程变得更加的便捷高效.
感谢Jetbrains提供的免费开源许可, GoLangWebStorm 为编程开发带来了良好的体验.

View File

@@ -16,57 +16,55 @@
- router.ts 配置路由路径和组件之间的映射关系
```text
client
├─ anime_client
│ └─ README.md
├─ public
vite.svg
├─ src
│ ├─ assets
│ │ ├─ image
│ │ │ 404.png
│ │ │ ├─ cartoon.png
│ │ ├─ film.png
│ │ ─ play.png
│ │ └─ tv.png
│ │ svg
│ │ ├─ cartoon.svg
│ │ ├─ film.svg
│ │ └─ tv.svg
│ ├─ components
│ │ ├─ Footer.vue
│ │ ├─ Header.vue
│ │ RelateList.vue
│ └─ Util.vue
─ router
│ └─ router.ts
│ ├─ utils
│ │ └─ request.ts
│ ├─ views
│ │ ├─ error
│ │ │ └─ Error404.vue
│ │ ├─ index
│ │ │ ├─ CategoryFilm.vue
│ │ │ ├─ FilmDetails.vue
│ │ │ ├─ Home.vue
│ │ │ ├─ Play.vue
│ │ │ SearchFilm.vue
│ │ └─ IndexHome.vue
├─ App.vue
│ ├─ main.ts
│ ├─ style.css
vite-env.d.ts
├─ auto-imports.d.ts
├─ components.d.ts
├─ favicon.ico
├─ index.html
├─ package-lock.json
├─ package.json
├─ README.md
├─ tsconfig.json
tsconfig.node.json
└─ vite.config.ts
client
├─ public
│ └─ favicon.ico
├─ src
assets
│ │ ├─ css
│ │ │ ├─ classify.css
│ │ │ ├─ film.css
│ │ │ pagination.css
│ │ └─ image
│ │ ├─ 404.png
│ │ ─ play.png
├─ components
│ │ Loading
│ │ ├─ index.ts
│ │ └─ Loading.vue
│ │ ├─ FilmList.vue
│ ├─ Footer.vue
│ │ ├─ Header.vue
│ │ ├─ RelateList.vue
│ │ Util.vue
├─ router
│ └─ router.ts
├─ utils
│ ├─ cookie.ts
│ │ └─ request.ts
│ ├─ views
│ │ ├─ error
│ │ │ └─ Error404.vue
│ │ ├─ index
│ │ │ ├─ FilmClassify.vue
│ │ │ ├─ FilmClassifySearch.vue
│ │ │ ├─ FilmDetails.vue
│ │ │ ├─ Home.vue
│ │ │ Play.vue
│ │ │ └─ SearchFilm.vue
│ └─ IndexHome.vue
│ ├─ App.vue
│ ├─ main.ts
style.css
│ └─ vite-env.d.ts
├─ auto-imports.d.ts
├─ components.d.ts
├─ index.html
├─ package.json
├─ README.md
├─ tsconfig.json
├─ tsconfig.node.json
vite.config.ts
```
## 启动方式

View File

@@ -17,12 +17,9 @@ declare module '@vue/runtime-core' {
ElFooter: typeof import('element-plus/es')['ElFooter']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElLink: typeof import('element-plus/es')['ElLink']
ElMain: typeof import('element-plus/es')['ElMain']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRow: typeof import('element-plus/es')['ElRow']
ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
FilmList: typeof import('./src/components/FilmList.vue')['default']
Footer: typeof import('./src/components/Footer.vue')['default']
Header: typeof import('./src/components/Header.vue')['default']

View File

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

View File

@@ -1,52 +1,55 @@
<template>
<div class="header">
<!-- 左侧logo以及搜索 -->
<div class="nav_left">
<!-- <img class="logo" src="/src/assets/logo.png">-->
<a href="/" class="site" >GoFilm</a>
<div class="search_group">
<input v-model="keyword" @keydown="(e)=>{e.keyCode == 13 && searchFilm()}" placeholder="搜索 动漫,剧集,电影 " class="search"/>
<el-button @click="searchFilm" :icon="Search"/>
</div>
</div>
<!--右侧顶级分类导航 -->
<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>
<div class="header">
<!-- 左侧logo以及搜索 -->
<div class="nav_left">
<!-- <img class="logo" src="/src/assets/logo.png">-->
<a href="/" class="site">GoFilm</a>
<div class="search_group">
<input v-model="keyword" @keydown="(e)=>{e.keyCode == 13 && searchFilm()}" placeholder="搜索 动漫,剧集,电影 "
class="search"/>
<el-button @click="searchFilm" :icon="Search"/>
</div>
</div>
<!--右侧顶级分类导航 -->
<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>
<div class="history-link hidden-md-and-down" v-on:mouseenter="handleHistory(true)" v-on:mouseleave="handleHistory(false)">
<a :href="`/filmClassify?Pid=${nav.variety.id}`">
<b style="font-size: 22px;" class="iconfont icon-lishijilu"/>
</a>
<Transition name="fade-slide" duration="300">
<div v-if="data.historyFlag" class="dropdown-container">
<div class="history-h">
<b class="iconfont icon-lishijilu1 history-h-icon" />
<span class="history-h-title">历史观看记录</span>
</div>
<div class="history-c">
<a :href="h.link" class="history-c-item" v-for="h in data.historyList" >
<div class="history-link hidden-md-and-down" v-on:mouseenter="handleHistory(true)"
v-on:mouseleave="handleHistory(false)">
<a :href="`/filmClassify?Pid=${nav.variety.id}`">
<b style="font-size: 22px;" class="iconfont icon-history"/>
</a>
<Transition name="fade-slide" duration="300">
<div v-if="data.historyFlag" class="dropdown-container">
<div class="history-h">
<b class="iconfont icon-record history-h-icon"/>
<span class="history-h-title">历史观看记录</span>
<a v-if="data.historyList.length > 0" class="iconfont icon-clear1 history-del" @click="clearHistory"/>
</div>
<div v-if="data.historyList.length > 0" class="history-c">
<a :href="h.link" class="history-c-item" v-for="h in data.historyList">
<span class="history-c-item-t">{{ h.name }}
</span>
<span class="history-c-item-e">{{h.episode}}</span>
</a>
</div>
</div>
</Transition>
<span class="history-c-item-e">{{ h.episode }}</span>
</a>
</div>
<el-empty style="padding: 10px 0;" v-else description="暂无观看记录"/>
</div>
<!-- <span style="color:#777; font-weight: bold">|</span>-->
<a href="/search" class="hidden-md-and-up" :underline="false">
<el-icon style="font-size: 18px">
<Search/>
</el-icon>
</a>
</div>
<!--弹窗模块,显示按钮对应信息-->
</Transition>
</div>
<!-- <span style="color:#777; font-weight: bold">|</span>-->
<a href="/search" class="hidden-md-and-up">
<el-icon style="font-size: 18px">
<Search/>
</el-icon>
</a>
</div>
<!--弹窗模块,显示按钮对应信息-->
</div>
</template>
<script lang="ts" setup>
@@ -55,7 +58,7 @@ import {useRouter} from "vue-router";
import {Search, CircleClose} from '@element-plus/icons-vue'
import {ElMessage} from "element-plus";
import {ApiGet} from "../utils/request";
import {cookieUtil,COOKIE_KEY_MAP} from "../utils/cookie";
import {cookieUtil, COOKIE_KEY_MAP} from "../utils/cookie";
// 搜索关键字
const keyword = ref<string>('')
// 弹窗隐藏显示
@@ -64,324 +67,354 @@ const data = reactive({
historyList: [{}],
})
//
const handleHistory = (flag:boolean)=>{
const handleHistory = (flag: boolean) => {
data.historyFlag = flag
if (flag) {
// 获取cookie中的filmHistory
let historyMap = cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY)?JSON.parse(cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY)):null
let historyMap = cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY) ? JSON.parse(cookieUtil.getCookie(COOKIE_KEY_MAP.FILM_HISTORY)) : null
let arr = []
if (historyMap) {
for (let k in historyMap) {
arr.push(historyMap[k])
}
arr.sort((item1,item2)=> item2.timeStamp-item1.timeStamp)
arr.sort((item1, item2) => item2.timeStamp - item1.timeStamp)
}
data.historyList = arr
}
}
const clearHistory = () => {
cookieUtil.clearCookie(COOKIE_KEY_MAP.FILM_HISTORY)
data.historyList = []
}
// 从父组件获取当前路由对象
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})
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: {},
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})
}
})
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: 768px) {
.nav_right {
display: flex;
justify-content: space-between;
/*display: none!important;*/
}
.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 {
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;*/
}
.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);
}
.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;
}
.nav_left {
display: none !important;
width: 90% !important;
margin: 0 auto;
}
}
</style>
<style scoped>
.history-c {
max-height: 200px;
overflow-y: scroll;
padding-top: 12px;
padding-bottom: 12px;
}
.history-c .history-c-item{
display: flex;
justify-content:space-between;
margin: 0 auto;
line-height: 40px;
}
.history-c-item::before{
content: '';
height: 10px;
width: 10px;
display: inline-block;
position: absolute;
left: 22px;
border: 2px solid orangered;
background: #fff;
border-radius: 50%;
top: 15px;
}
.history-c-item::after{
content: '';
border-left: 1px solid #dbdee2;
position: absolute;
left: 28px;
top: 0;
height: 100%;
z-index: -1;
}
.history-c-item:hover:before{
content: '';
height: 10px;
width: 10px;
display: inline-block;
position: absolute;
left: 22px;
border: 2px solid orangered;
background: #DC3BB6FF;
border-radius: 50%;
top: 15px;
}
.history-c-item .history-c-item-t{
width: 100%;
text-align: left!important;
position: relative;
padding: 5px 5px 2px 55px;
margin: 0 10px;
flex: 2;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
}
.history-c-item-e{
flex: 1;
color: rgba(255,255,255,0.38);
padding: 5px 0 2px 0;
}
/*历史记录标题域*/
.history-h {
width: 100%;
display: flex;
justify-content: start;
border-bottom: 2px solid rgba(255,255,255,.15);
}
.history-h-icon{
flex: 1;
font-size: 24px;
color: orangered;
}
.history-h-icon::before{
margin-right: 6px;
}
.history-h-title{
flex: 4;
text-align: left;
font-size: 18px;
}
.nav_right a{
position: relative;
}
.dropdown-container{
position: absolute;
top: 100%;
left: 50%;
font-size: 14px;
color: rgba(255,255,255,0.38);
margin-top: 12px;
min-width: 300px;
max-width: 330px;
height: auto;
z-index: 1000;
border-radius: 8px;
overflow: hidden;
background: rgba(0,0,0,0.85);
transform: translate3d(-50%,0,0);
}
.history-link{
position: relative;
min-width: 60px;
height: 40px;
line-height: 40px;
margin: 10px 10px;
font-size: 15px;
text-align: center;
font-weight: bold;
}
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: all 0.8s linear;
}
.fade-slide-enter,
.fade-slide-leave-to {
opacity: 0;
transform: translate3d(-50%,-10px,0);
}
@media (min-width: 768px) {
.header {
width: 78%;
z-index: 0;
max-height: 40px;
line-height: 60px;
margin: 0 auto;
display: flex;
justify-content: space-between;
}
.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;
}
/*site标志样式*/
.site{
font-weight: 600;
font-style: italic;
font-size: 24px;
margin-right: 5px;
background: linear-gradient(118deg, #e91a90, #c965b3, #988cd7, #00acfd);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.nav_left {
display: flex;
}
/*搜索栏*/
.search_group {
width: 80%;
margin: 10px auto;
display: flex;
}
/*site标志样式*/
.site {
font-weight: 600;
font-style: italic;
font-size: 24px;
margin-right: 5px;
background: linear-gradient(118deg, #e91a90, #c965b3, #988cd7, #00acfd);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.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_group {
width: 80%;
margin: 10px auto;
display: flex;
}
.search::placeholder {
font-size: 15px;
color: #999999;
}
.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:focus {
outline: none;
}
.search::placeholder {
font-size: 15px;
color: #999999;
}
.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*/
}
.search:focus {
outline: none;
}
.nav_right {
display: flex;
height: 60px;
flex-direction: row;
}
.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> a {
min-width: 60px;
height: 40px;
line-height: 40px;
margin: 10px 10px;
font-size: 15px;
text-align: center;
font-weight: bold;
}
.nav_right {
display: flex;
height: 60px;
flex-direction: row;
}
.nav_right a:hover {
color: orange;
}
.nav_right > a {
min-width: 60px;
height: 40px;
line-height: 40px;
margin: 10px 10px;
font-size: 15px;
text-align: center;
font-weight: bold;
}
.logo {
height: 40px;
margin-top: 10px;
}
.nav_right a:hover {
color: orange;
}
.logo {
height: 40px;
margin-top: 10px;
}
/*history preview*/
/*element-plus empty state color style*/
:deep(.el-empty) {
--el-empty-fill-color-1: rgba(155, 73, 231, 0.72);
--el-empty-fill-color-2: #67d9e891;
--el-empty-fill-color-3: rgb(106 19 187 / 72%);
--el-empty-fill-color-4: #67d9e8;
--el-empty-fill-color-5: #5abcc9;
--el-empty-fill-color-6: #9fb2d9;
--el-empty-fill-color-7: #61989f;
--el-empty-fill-color-8: #697dc5;
--el-empty-fill-color-9: rgb(43 51 63 / 44%);
}
.history-c {
max-height: 200px;
overflow-y: scroll;
margin-top: 12px;
margin-bottom: 12px;
}
.history-c .history-c-item {
display: flex;
justify-content: space-between;
margin: 0 auto;
line-height: 40px;
}
.history-c-item::before {
content: '';
height: 10px;
width: 10px;
display: inline-block;
position: absolute;
left: 22px;
border: 2px solid orangered;
background: #fff;
border-radius: 50%;
top: 15px;
}
.history-c-item::after {
content: '';
border-left: 1px solid #dbdee2;
position: absolute;
left: 28px;
top: 0;
height: 100%;
z-index: -1;
}
.history-c-item:hover:before {
content: '';
height: 10px;
width: 10px;
display: inline-block;
position: absolute;
left: 22px;
border: 2px solid orangered;
background: rgba(220, 59, 182, 0.97);
border-radius: 50%;
top: 15px;
}
.history-c-item .history-c-item-t {
width: 100%;
text-align: left !important;
position: relative;
padding: 5px 5px 2px 55px;
margin: 0 10px;
flex: 2;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
}
.history-c-item-e {
flex: 1;
color: rgba(255, 255, 255, 0.38);
padding: 5px 0 2px 0;
}
/*历史记录标题域*/
.history-h {
width: 100%;
display: flex;
justify-content: start;
border-bottom: 2px solid rgba(255, 255, 255, .15);
}
.history-h-icon {
flex: 1;
font-size: 24px;
color: orangered;
}
.history-h-icon::before {
margin-right: 6px;
}
.history-h-title {
flex: 3;
text-align: left;
font-size: 18px;
}
.history-del {
flex: 1;
font-size: 25px;
color: #99999991;
}
.history-del:hover {
color: orangered;
}
.nav_right a {
position: relative;
}
.dropdown-container {
position: absolute;
top: 100%;
left: 50%;
font-size: 14px;
color: rgba(255, 255, 255, 0.38);
margin-top: 12px;
min-width: 300px;
max-width: 330px;
height: auto;
z-index: 1000;
border-radius: 8px;
overflow: hidden;
background: rgba(0, 0, 0, 0.85);
transform: translate3d(-50%, 0, 0);
}
.history-link {
position: relative;
min-width: 60px;
height: 40px;
line-height: 40px;
margin: 10px 10px;
font-size: 15px;
text-align: center;
font-weight: bold;
}
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: all 0.8s linear;
}
.fade-slide-enter,
.fade-slide-leave-to {
opacity: 0;
transform: translate3d(-50%, -10px, 0);
}
}
</style>

View File

@@ -12,6 +12,8 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {

View File

@@ -27,7 +27,6 @@ const cookieUtil =
let [k,v] = cookies[i].split("=")
if (k == name) {
return decodeURIComponent(v)
}
}
return "";

View File

@@ -1,13 +1,13 @@
import axios from "axios";
import {ElMessage } from "element-plus";
import {ElMessage} from "element-plus";
// 自定义loading加载动画
import {load} from "../components/Loading";
let loadingCount: number = 0;
const http = (options: any) => {
// 开启loading动画
load.start('')
return new Promise((resolve, reject) => {
// create an axios instance
const service = axios.create({
@@ -17,9 +17,10 @@ const http = (options: any) => {
});
// request interceptor
service.interceptors.request.use(
(config: any) => {
service.interceptors.request.use((config: any) => {
// 开启loading动画
loadingCount++
load.start('')
// let token: string = ""; //此处换成自己获取回来的token通常存在在cookie或者store里面
// if (token) {
// // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
@@ -28,20 +29,19 @@ const http = (options: any) => {
// config.headers.Authorization = +token;
// }
return config;
},
(error) => {
}, (error) => {
// Do something with request error
Promise.reject(error);
return Promise.reject(error);
}
);
// response interceptor
service.interceptors.response.use(
(response) => {
load.close()
service.interceptors.response.use((response) => {
// 关闭loading动画
loadingCount--
loadingCount == 0 && load.close()
return response.data;
},
(error) => {
}, (error) => {
if (error.response.status == 403) {
ElMessage.error("请求异常: ", error)
} else {
@@ -49,7 +49,6 @@ const http = (options: any) => {
}
return Promise.reject(error);
}
);
// 请求处理
service(options)
@@ -63,11 +62,11 @@ const http = (options: any) => {
});
};
const ApiGet = (url:string, params?:any)=>{
return http({url, method:`get`, params,})
const ApiGet = (url: string, params?: any) => {
return http({url, method: `get`, params,})
}
const ApiPost = (url:string, data:any) =>{
return http({url, method:`post`, data,})
const ApiPost = (url: string, data: any) => {
return http({url, method: `post`, data,})
}
export {http, ApiPost, ApiGet};

View File

@@ -16,11 +16,11 @@
<h3 class="current_play_title"><a
:href="`/filmDetail?link=${data.detail.id}`">{{ data.detail.name }}</a>{{ data.current.episode }}</h3>
<div class="tags">
<b>
<a :href="`/filmClassifySearch?Pid=${data.detail.pid}&Category=${data.detail.cid}`" >
<el-icon>
<Promotion/>
</el-icon>
{{ data.detail.descriptor.cName }}</b>
{{ data.detail.descriptor.cName }}</a>
<span>{{
data.detail.descriptor.classTag ? data.detail.descriptor.classTag.replaceAll(',', '/') : '未知'
}}</span>
@@ -359,9 +359,10 @@ watchEffect(()=>{
padding: 10px 6%;
}
.tags b {
.tags a {
padding: 5px 10px;
background-color: rgba(155, 73, 231, 0.72);
color: #c4c2c2;
font-size: 13px;
border-radius: 6px;
margin-right: 15px;
@@ -447,8 +448,9 @@ watchEffect(()=>{
padding: 5px 10px;
}
.tags b {
.tags a {
padding: 5px 10px;
color: #c4c2c2;
background-color: rgba(155, 73, 231, 0.72);
font-size: 13px;
border-radius: 6px;

View File

@@ -7,31 +7,31 @@ import {ElementPlusResolver} from "unplugin-vue-components/resolvers";
// https://vitejs.dev/config/
export default defineConfig({
// 本地测试环境
server: {
host: '0.0.0.0',
port: 3600,
proxy: {
"/api": {
target: `http://127.0.0.1:3601`,
changeOrigin: true, // 允许跨域
rewrite: path => path.replace(/^\/api/,'')
}
},
},
// nginx发布构建时使用此配置
// server: {
// host: 'localhost',
// host: '0.0.0.0',
// port: 3600,
// proxy: {
// "/api": {
// target: `http://localhost`,
// target: `http://127.0.0.1:3601`,
// changeOrigin: true, // 允许跨域
// rewrite: path => path.replace(/^\/api/,'')
// }
// },
// },
// nginx发布构建时使用此配置
server: {
host: 'localhost',
port: 3600,
proxy: {
"/api": {
target: `http://localhost`,
changeOrigin: true, // 允许跨域
rewrite: path => path.replace(/^\/api/,'')
}
},
},
plugins: [
vue(),
AutoImport({

View File

@@ -8,50 +8,71 @@
- nginx.conf 配置后端接口的代理和端口等相关信息
- redis
- redis.conf 配置redis的远程访问和密码等信息
- server 服务端资源文件, Dockerfile 生成镜像时所需
- docker-compose.yml docker 服务配置启动文件
- Dockerfile go程序镜像构建文件
```text
film
├─ data
│ ├─ nginx
│ │ ├─ html
│ │ └─ nginx.conf
│ └─ redis
│ └─ redis.conf
├─ docker-compose.yml
├─ Dockerfile
README.md
film
├─ data
│ ├─ nginx
│ │ ├─ html
│ │ └─ nginx.conf
│ └─ redis
│ └─ redis.conf
├─ server
├─ docker-compose.yml
Dockerfile
└─ README.md
```
>此目录下的client内容和server内容并不一定与client同步 (小更新可能不会实时同步到运行服务器上)
>
>可自行将根目录下的server和client内容与此目录下的对应文件进行替换
## 2. 程序构建运行
### 1. 环境准备
1. Linux 服务器
2. 安装 docker, docker compose 服务
2. 安装 docker, docker compose 服务
- Centos 安装 Docker Engine [官方文档链接](https://docs.docker.com/engine/install/centos/)
- Ubuntu 安装 Docker Engine [官方文档链接](https://docs.docker.com/engine/install/ubuntu/)
```shell
# Centos 系统安装Docker Engine示例
# 1. 卸载旧版本Docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
#2. 设置存储库
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
#3. 安装最新版本Docker
$ sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
#启动 Docker 服务
sudo systemctl start docker
```
### 2. 启动流程
> 如果使用默认配置信息,则执行如下流程
- 将本项目中的 film 文件夹完整的上传到服务器的 ` /opt/` 目录下
- 将本项目中的 film 文件夹完整的上传到服务器的 ` /opt/` 目录下 (放在其他目录下时需同步修改 `Dockerfile` 以及 `docker-compose.yml` 文件中的相关路径)
- 进入服务器中的 `/opt/film/` 目录并执行 `docker compose build` 构建相关docker镜像
-`/opt/film/` 目录下执行命令 `docker compose up -d` (后台运行服务)
- 使用 `docker ps` 命令查看相关服务是否成功启动
- 等待后端程序初始化工作和数据爬取, 大概8分钟左右
- 等待后端程序初始化工作和数据爬取, 大概3~8分钟左右
- 停止服务 `docker compose down`
- 查看服务容器运行状态 `docker ps`
- 浏览器中访问nginx服务地址查看效果, 例: [http://xxx.xxx.xxx:3600](http://xxx.xxx.xxx:3600)
### 3.服务配置信息修改
- film 后端接口服务配置, `film/server` 下存放了程序的构建文件, 修改后重新构建镜像即可
@@ -64,6 +85,7 @@ film
- mysql 和 redis 服务配置修改后需要同步修改 `/film/server/config/DataConfig.go` 中的连接地址和账户名信息
```go
## 配置使用的用户名密码信息需和ocker-compose.yml文件中设置的一致
const (
// mysql服务配置信息修改
mysqlDsn = "用户名:密码$@(服务名:服务端口)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
@@ -78,4 +100,18 @@ const (
RedisPassword = `密码`
RedisDBNo = 0
)
## docker-compose.yml (设置服务的启动端口和服务名以及账户密码信息)
mysql:
container_name: film_mysql
image: mysql
ports:
- 3610:3306
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: FilmSite
redis:
container_name: film_redis
image: redis
ports:
- 3620:6379
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -10,9 +10,9 @@
<meta name="referrer" content="never">
<meta charset="UTF-8"/>
<title>(╥﹏╥)</title>
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_3992367_xtm26gd5aa.css">
<script type="module" crossorigin src="/assets/index-984712d6.js"></script>
<link rel="stylesheet" href="/assets/index-de4c7ff5.css">
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_3992367_r0ou59wsgum.css">
<script type="module" crossorigin src="/assets/index-b4ec3039.js"></script>
<link rel="stylesheet" href="/assets/index-e57331be.css">
</head>
<body>
<div id="app"></div>

View File

@@ -39,37 +39,43 @@ gorm.io/driver/mysql v1.4.7
- spider gocolly配置, 执行逻辑, 数据前置处理等
```text
server
├─ config
│ └─ DataConfig.go
├─ controller
│ ├─ IndexController.go
│ └─ SpiderController.go
├─ logic
─ IndexLogic.go
├─ model
│ ├─ Categories.go
│ ├─ LZJson.go
│ ├─ Movies.go
Search.go
├─ plugin
common
├─ JsonUtils.go
│ ├─ ProcessCategory.go
│ │ ProcessMovies.go
├─ db
│ │ ├─ mysql.go
│ │ redis.go
└─ spider
Spider.go
│ ├─ SpiderCron.go
│ └─ SpiderRequest.go
├─ router
└─ router.go
├─ go.mod
├─ go.sum
├─ main.go
└─ README.md
server
├─ config
│ └─ DataConfig.go
├─ controller
│ ├─ IndexController.go
│ └─ SpiderController.go
├─ logic
─ IndexLogic.go
│ └─ SpiderLogic.go
├─ model
│ ├─ Categories.go
│ ├─ Movies.go
RequestParams.go
│ ├─ ResponseJson.go
Search.go
├─ plugin
│ ├─ common
│ │ dp
│ │ ├─ ProcessCategory.go
│ │ │ └─ ProcessMovies.go
│ │ param
│ │ └─ SimpleParam.go
util
│ ├─ FileDownload.go
│ └─ Request.go
│ ├─ db
│ ├─ mysql.go
│ │ └─ redis.go
│ └─ spider
│ ├─ Spider.go
│ └─ SpiderCron.go
├─ router
│ └─ router.go
├─ go.mod
├─ go.sum
├─ main.go
└─ README.md
```
@@ -133,8 +139,6 @@ server
#### 2.Redis
> 连接信息(以docker compose部署为例) :