add film delete and recover function

This commit is contained in:
mubai
2024-07-11 05:35:22 +08:00
parent 3da0366237
commit 862aca9f4c
24 changed files with 519 additions and 144 deletions

View File

@@ -40,7 +40,14 @@
>新增内容: >新增内容:
> >
>- 新增详细部署说明文档, 以及 `1Panel部署方式` , [点击查看](https://blog.mubai.link/2024/04/21/Docs/gofilm/) >- 新增详细部署说明文档, 以及 `1Panel部署方式` , [点击查看](https://blog.mubai.link/2024/04/21/Docs/gofilm/)
>- 修复`docker-compose.yml` 文件中的书写格式问题, 添加容器重启策略 >- 影片分类界面 `二级分类` 展示开关修改为 屏蔽 和 恢复 效果, 点击可对所属分类影片进行屏蔽和恢复
>- 影片信息界面删除按钮功能生效, 可删除单一影片信息 (删除后暂无设置恢复效果)
>- 修复部分按钮点击后褐色边框问题
>
>后续计划:
>
>- 优先针对手机端主页以及导航做修改
>- 同步手机端历史记录功能
## 目录结构 ## 目录结构

View File

@@ -21,7 +21,6 @@ declare module '@vue/runtime-core' {
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown'] ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']

View File

@@ -15,7 +15,7 @@
"axios": "^1.3.4", "axios": "^1.3.4",
"element-plus": "^2.4.4", "element-plus": "^2.4.4",
"video.js": "^8.0.4", "video.js": "^8.0.4",
"vue": "^3.2.47" "vue": "^3.4.31"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.1.0", "@vitejs/plugin-vue": "^4.1.0",

View File

@@ -77,7 +77,7 @@ button:hover {
} }
button:focus, button:focus,
button:focus-visible { button:focus-visible {
outline: 4px auto -webkit-focus-ring-color; outline: none;
} }
.card { .card {

View File

@@ -2,7 +2,7 @@
<el-container> <el-container>
<el-header> <el-header>
<!--<Header/>--> <!--<Header/>-->
<NewHeader /> <Header />
</el-header> </el-header>
<el-main> <el-main>
<router-view></router-view> <router-view></router-view>

View File

@@ -90,7 +90,8 @@
</el-table-column> </el-table-column>
<el-table-column prop="remarks" align="center" label="更新状态"> <el-table-column prop="remarks" align="center" label="更新状态">
<template #default="scope"> <template #default="scope">
<el-tag :type="scope.row.remarks == '已完结' ?'success':''" disable-transitions>{{scope.row.remarks }}</el-tag> <el-tag v-if="scope.row.remarks == '已完结'" type="success" disable-transitions>{{scope.row.remarks }}</el-tag>
<el-tag v-else disable-transitions>{{scope.row.remarks }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column sortable prop="updateStamp" align="center" label="更新时间"> <el-table-column sortable prop="updateStamp" align="center" label="更新时间">
@@ -102,7 +103,7 @@
<template #default="scope"> <template #default="scope">
<el-button type="success" :icon="Aim" @click="" plain circle/> <el-button type="success" :icon="Aim" @click="" plain circle/>
<el-button type="primary" :icon="Edit" @click="" plain circle/> <el-button type="primary" :icon="Edit" @click="" plain circle/>
<el-button type="danger" :icon="Delete" @click="" plain circle/> <el-button type="danger" :icon="Delete" @click="delFilm(scope.row.ID)" plain circle/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -178,11 +179,12 @@ const changeClass = (value: any) => {
} }
} }
// 生成序列号
const serialNum = (index: number) => { const serialNum = (index: number) => {
return (data.page.current - 1) * data.page.pageSize + index + 1 return (data.page.current - 1) * data.page.pageSize + index + 1
} }
// 搜索满足条件的影片
const searchFilm = ()=>{ const searchFilm = ()=>{
let p = data.params let p = data.params
// 时间选择器参数处理 如果 dateGroup 不为空 则追加时间范围参数 // 时间选择器参数处理 如果 dateGroup 不为空 则追加时间范围参数
@@ -197,6 +199,7 @@ const searchFilm = ()=>{
} }
// 获取影片分页信息
const getFilmPage = () => { const getFilmPage = () => {
let {current, pageSize} = data.page let {current, pageSize} = data.page
let params = data.params let params = data.params
@@ -228,6 +231,19 @@ onMounted(() => {
getFilmPage() getFilmPage()
}) })
// 删除影片信息
const delFilm = (id:number) =>{
console.log(id)
ApiGet( `/manage/film/search/del`, {id: id}).then((resp: any) => {
if (resp.code === 0) {
ElMessage.success({message: resp.msg})
getFilmPage()
} else {
ElMessage.error({message: resp.msg})
}
})
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -12,7 +12,8 @@
</el-table-column> </el-table-column>
<el-table-column prop="show" align="center" label="是否展示"> <el-table-column prop="show" align="center" label="是否展示">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row.show" inline-prompt active-text="展示" @change="changeClassState(scope.row.id, scope.row.show)" inactive-text="隐藏"/> <el-switch v-if="scope.row.pid == 0" v-model="scope.row.show" inline-prompt active-text="展示" inactive-text="隐藏" @change="changeClassState(scope.row.id, scope.row.show)" />
<el-switch v-else v-model="scope.row.show" inline-prompt active-text="屏蔽" inactive-text="恢复" @change="changeClassState(scope.row.id, scope.row.show)" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
@@ -54,16 +55,25 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Delete, Edit,RefreshLeft,} from "@element-plus/icons-vue"; import {Delete, Edit, RefreshLeft, Warning,} from "@element-plus/icons-vue";
import {onMounted, reactive} from "vue"; import {onMounted, reactive} from "vue";
import {ApiGet, ApiPost} from "../../../utils/request"; import {ApiGet, ApiPost} from "../../../utils/request";
import {ElMessage} from "element-plus"; import {ElMessage,ElMessageBox} from "element-plus";
// table数据 // table数据
const data = reactive({ const data = reactive({
classTree: [] classTree: []
}) })
//
const confirmShield = (id:any)=>{
ElMessageBox.confirm('Are you sure to close this dialog?').then(() => {
console.log(id)
}).catch(() => {
// catch error
})
}
// dialog 弹窗数据 // dialog 弹窗数据
const dialog = reactive({ const dialog = reactive({
editV: false, editV: false,
@@ -120,6 +130,7 @@ const changeClassState = (id:string, show: number)=>{
} }
}) })
} }
// 对话框关闭时重置表单数据为初始值 // 对话框关闭时重置表单数据为初始值
const cancelDialog = ()=>{ const cancelDialog = ()=>{
dialog.editForm = {id: -99, pid: -99, name:'', show: true, children:[]} dialog.editForm = {id: -99, pid: -99, name:'', show: true, children:[]}

View File

@@ -12,9 +12,8 @@ export default defineConfig({
// port: 3600, // port: 3600,
// proxy: { // proxy: {
// "/api": { // "/api": {
// // target: `http://127.0.0.1:3601`, // target: `http://127.0.0.1:3601`,
// target: `http://1.94.30.26:3601`, // // target: `http://1.94.30.26:3601`,
// // target: `http://[2408:8266:701:2271:5fc5:85b8:ac18:562e]:3601`,
// changeOrigin: true, // 允许跨域 // changeOrigin: true, // 允许跨域
// rewrite: path => path.replace(/^\/api/, '') // rewrite: path => path.replace(/^\/api/, '')
// } // }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -11,8 +11,8 @@
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<title>(╥﹏╥)</title> <title>(╥﹏╥)</title>
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_3992367_cesmizkmqka.css"> <link rel="stylesheet" href="//at.alicdn.com/t/c/font_3992367_cesmizkmqka.css">
<script type="module" crossorigin src="/assets/index-58818233.js"></script> <script type="module" crossorigin src="/assets/index-73b7bf54.js"></script>
<link rel="stylesheet" href="/assets/index-ac85c556.css"> <link rel="stylesheet" href="/assets/index-c3496d45.css">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@@ -55,9 +55,9 @@ services:
command: [ command: [
'mysqld', 'mysqld',
'--default-storage-engine=INNODB', '--default-storage-engine=INNODB',
'--innodb-buffer-pool-size=128M', # '--innodb-buffer-pool-size=128M',
'--character-set-server=utf8mb4', # '--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci', # '--collation-server=utf8mb4_unicode_ci',
'--default-time-zone=+8:00', '--default-time-zone=+8:00',
'--lower-case-table-names=1' '--lower-case-table-names=1'
] ]

View File

@@ -104,6 +104,27 @@ func FilmAdd(c *gin.Context) {
system.SuccessOnlyMsg("影片信息添加成功", c) system.SuccessOnlyMsg("影片信息添加成功", c)
} }
// FilmDelete 删除影片检索信息, 逻辑删除
func FilmDelete(c *gin.Context) {
// 获取影片ID
idStr := c.DefaultQuery("id", "")
if idStr == "" {
system.Failed("删除失败,缺少ID参数", c)
return
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
system.Failed("删除失败,参数类型异常", c)
return
}
// 通过ID删除对应影片检索信息
if err = logic.FL.DelFilm(id); err != nil {
system.Failed(fmt.Sprintln("删除失败: ", err.Error()), c)
return
}
system.SuccessOnlyMsg("影片删除成功", c)
}
//----------------------------------------------------影片分类处理---------------------------------------------------- //----------------------------------------------------影片分类处理----------------------------------------------------
// FilmClassTree 影片分类树数据 // FilmClassTree 影片分类树数据

View File

@@ -19,10 +19,10 @@ var FL *FilmLogic
//----------------------------------------------------影片管理业务逻辑---------------------------------------------------- //----------------------------------------------------影片管理业务逻辑----------------------------------------------------
// GetFilmPage 获取影片检索信息分页数据
func (fl *FilmLogic) GetFilmPage(s system.SearchVo) []system.SearchInfo { func (fl *FilmLogic) GetFilmPage(s system.SearchVo) []system.SearchInfo {
// 获取影片检索信息分页数据 // 获取影片检索信息分页数据
sl := system.GetSearchPage(s) sl := system.GetSearchPage(s)
//
return sl return sl
} }
@@ -70,6 +70,17 @@ func (fl *FilmLogic) SaveFilmDetail(fd system.FilmDetailVo) error {
return system.SaveDetail(detail) return system.SaveDetail(detail)
} }
// DelFilm 删除分类影片
func (fl *FilmLogic) DelFilm(id int64) error {
// 通过id查询对应影片信息是否存在
s := system.GetSearchInfoById(id)
if s == nil {
return errors.New("影片信息不存在")
}
// 通过id删除对应影片信息
return system.DelFilmSearch(id)
}
//----------------------------------------------------影片分类业务逻辑---------------------------------------------------- //----------------------------------------------------影片分类业务逻辑----------------------------------------------------
// GetFilmClassTree 获取影片分类信息 // GetFilmClassTree 获取影片分类信息
@@ -78,6 +89,7 @@ func (fl *FilmLogic) GetFilmClassTree() system.CategoryTree {
return system.GetCategoryTree() return system.GetCategoryTree()
} }
// GetFilmClassById 通过ID获取影片分类信息
func (fl *FilmLogic) GetFilmClassById(id int64) *system.CategoryTree { func (fl *FilmLogic) GetFilmClassById(id int64) *system.CategoryTree {
tree := system.GetCategoryTree() tree := system.GetCategoryTree()
for _, c := range tree.Children { for _, c := range tree.Children {
@@ -120,6 +132,18 @@ func (fl *FilmLogic) UpdateClass(class system.CategoryTree) error {
if class.Name != "" { if class.Name != "" {
subC.Name = class.Name subC.Name = class.Name
} }
// 如果所属分类为二级分类且show属性进行了变化
if class.Show {
// 如果show为ture则将其分类下的影片信息进行恢复
if err := system.RecoverFilmSearch(class.Id); err != nil {
return err
}
} else {
// 如果show为false则将其分类下的影片信息进行屏蔽
if err := system.ShieldFilmSearch(class.Id); err != nil {
return err
}
}
subC.Show = class.Show subC.Show = class.Show
if err := system.SaveCategoryTree(&tree); err != nil { if err := system.SaveCategoryTree(&tree); err != nil {
return fmt.Errorf("影片分类信息更新失败: %s", err.Error()) return fmt.Errorf("影片分类信息更新失败: %s", err.Error())

View File

@@ -18,7 +18,7 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
// 初始化mysql00000 // 初始化mysql
err = db.InitMysql() err = db.InitMysql()
if err != nil { if err != nil {
panic(err) panic(err)

View File

@@ -694,6 +694,7 @@ func GetMovieListBySort(t int, pid int64, page *Page) []MovieBasicInfo {
// ================================= Manage 管理后台 ================================= // ================================= Manage 管理后台 =================================
// GetSearchPage 获取影片检索分页数据
func GetSearchPage(s SearchVo) []SearchInfo { func GetSearchPage(s SearchVo) []SearchInfo {
// 构建 query查询条件 // 构建 query查询条件
query := db.Mdb.Model(&SearchInfo{}) query := db.Mdb.Model(&SearchInfo{})
@@ -762,6 +763,46 @@ func GetSearchOptions(pid int64) map[string]interface{} {
return tagMap return tagMap
} }
// GetSearchInfoById 查询id对应的检索信息
func GetSearchInfoById(id int64) *SearchInfo {
s := SearchInfo{}
if err := db.Mdb.First(&s, id).Error; err != nil {
log.Println(err)
return nil
}
return &s
}
// DelFilmSearch 删除影片检索信息, (不影响后续更新, 逻辑删除)
func DelFilmSearch(id int64) error {
// 通过检索id对影片检索信息进行删除
if err := db.Mdb.Delete(&SearchInfo{}, id).Error; err != nil {
log.Println(err)
return err
}
return nil
}
// ShieldFilmSearch 删除所属分类下的所有影片检索信息
func ShieldFilmSearch(cid int64) error {
// 通过检索id对影片检索信息进行删除
if err := db.Mdb.Where("cid = ?", cid).Delete(&SearchInfo{}).Error; err != nil {
log.Println(err)
return err
}
return nil
}
// RecoverFilmSearch 恢复所属分类下的影片检索信息状态
func RecoverFilmSearch(cid int64) error {
// 通过检索id对影片检索信息进行删除
if err := db.Mdb.Model(&SearchInfo{}).Unscoped().Where("cid = ?", cid).Update("deleted_at", nil).Error; err != nil {
log.Println(err)
return err
}
return nil
}
// ================================= 接口数据缓存 ================================= // ================================= 接口数据缓存 =================================
// DataCache API请求 数据缓存 // DataCache API请求 数据缓存

View File

@@ -84,6 +84,7 @@ func SetupRouter() *gin.Engine {
{ {
filmRoute.POST(`/add`, controller.FilmAdd) filmRoute.POST(`/add`, controller.FilmAdd)
filmRoute.GET(`/search/list`, controller.FilmSearchPage) filmRoute.GET(`/search/list`, controller.FilmSearchPage)
filmRoute.GET(`/search/del`, controller.FilmDelete)
filmRoute.GET(`/class/tree`, controller.FilmClassTree) filmRoute.GET(`/class/tree`, controller.FilmClassTree)
filmRoute.GET(`/class/find`, controller.FindFilmClass) filmRoute.GET(`/class/find`, controller.FindFilmClass)

View File

@@ -87,7 +87,7 @@ const (
//mysql服务配置信息 root:root 设置mysql账户的用户名和密码 //mysql服务配置信息 root:root 设置mysql账户的用户名和密码
MysqlDsn = "root:root@(192.168.20.7:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local" MysqlDsn = "root:root@(192.168.20.5:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
// MysqlDsn docker compose 环境下的链接信息 mysql:3306 为 docker compose 中 mysql服务对应的网络名称和端口 // MysqlDsn docker compose 环境下的链接信息 mysql:3306 为 docker compose 中 mysql服务对应的网络名称和端口
//MysqlDsn = "root:root@(mysql:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local" //MysqlDsn = "root:root@(mysql:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
@@ -98,7 +98,7 @@ const (
RedisPassword redis访问密码 RedisPassword redis访问密码
RedisDBNo 使用第几号库 RedisDBNo 使用第几号库
*/ */
RedisAddr = `192.168.20.7:6379` RedisAddr = `192.168.20.5:6379`
RedisPassword = `root` RedisPassword = `root`
RedisDBNo = 0 RedisDBNo = 0

View File

@@ -104,6 +104,27 @@ func FilmAdd(c *gin.Context) {
system.SuccessOnlyMsg("影片信息添加成功", c) system.SuccessOnlyMsg("影片信息添加成功", c)
} }
// FilmDelete 删除影片检索信息, 逻辑删除
func FilmDelete(c *gin.Context) {
// 获取影片ID
idStr := c.DefaultQuery("id", "")
if idStr == "" {
system.Failed("删除失败,缺少ID参数", c)
return
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
system.Failed("删除失败,参数类型异常", c)
return
}
// 通过ID删除对应影片检索信息
if err = logic.FL.DelFilm(id); err != nil {
system.Failed(fmt.Sprintln("删除失败: ", err.Error()), c)
return
}
system.SuccessOnlyMsg("影片删除成功", c)
}
//----------------------------------------------------影片分类处理---------------------------------------------------- //----------------------------------------------------影片分类处理----------------------------------------------------
// FilmClassTree 影片分类树数据 // FilmClassTree 影片分类树数据

View File

@@ -19,10 +19,10 @@ var FL *FilmLogic
//----------------------------------------------------影片管理业务逻辑---------------------------------------------------- //----------------------------------------------------影片管理业务逻辑----------------------------------------------------
// GetFilmPage 获取影片检索信息分页数据
func (fl *FilmLogic) GetFilmPage(s system.SearchVo) []system.SearchInfo { func (fl *FilmLogic) GetFilmPage(s system.SearchVo) []system.SearchInfo {
// 获取影片检索信息分页数据 // 获取影片检索信息分页数据
sl := system.GetSearchPage(s) sl := system.GetSearchPage(s)
//
return sl return sl
} }
@@ -70,6 +70,17 @@ func (fl *FilmLogic) SaveFilmDetail(fd system.FilmDetailVo) error {
return system.SaveDetail(detail) return system.SaveDetail(detail)
} }
// DelFilm 删除分类影片
func (fl *FilmLogic) DelFilm(id int64) error {
// 通过id查询对应影片信息是否存在
s := system.GetSearchInfoById(id)
if s == nil {
return errors.New("影片信息不存在")
}
// 通过id删除对应影片信息
return system.DelFilmSearch(id)
}
//----------------------------------------------------影片分类业务逻辑---------------------------------------------------- //----------------------------------------------------影片分类业务逻辑----------------------------------------------------
// GetFilmClassTree 获取影片分类信息 // GetFilmClassTree 获取影片分类信息
@@ -78,6 +89,7 @@ func (fl *FilmLogic) GetFilmClassTree() system.CategoryTree {
return system.GetCategoryTree() return system.GetCategoryTree()
} }
// GetFilmClassById 通过ID获取影片分类信息
func (fl *FilmLogic) GetFilmClassById(id int64) *system.CategoryTree { func (fl *FilmLogic) GetFilmClassById(id int64) *system.CategoryTree {
tree := system.GetCategoryTree() tree := system.GetCategoryTree()
for _, c := range tree.Children { for _, c := range tree.Children {
@@ -120,6 +132,18 @@ func (fl *FilmLogic) UpdateClass(class system.CategoryTree) error {
if class.Name != "" { if class.Name != "" {
subC.Name = class.Name subC.Name = class.Name
} }
// 如果所属分类为二级分类且show属性进行了变化
if class.Show {
// 如果show为ture则将其分类下的影片信息进行恢复
if err := system.RecoverFilmSearch(class.Id); err != nil {
return err
}
} else {
// 如果show为false则将其分类下的影片信息进行屏蔽
if err := system.ShieldFilmSearch(class.Id); err != nil {
return err
}
}
subC.Show = class.Show subC.Show = class.Show
if err := system.SaveCategoryTree(&tree); err != nil { if err := system.SaveCategoryTree(&tree); err != nil {
return fmt.Errorf("影片分类信息更新失败: %s", err.Error()) return fmt.Errorf("影片分类信息更新失败: %s", err.Error())

View File

@@ -694,6 +694,7 @@ func GetMovieListBySort(t int, pid int64, page *Page) []MovieBasicInfo {
// ================================= Manage 管理后台 ================================= // ================================= Manage 管理后台 =================================
// GetSearchPage 获取影片检索分页数据
func GetSearchPage(s SearchVo) []SearchInfo { func GetSearchPage(s SearchVo) []SearchInfo {
// 构建 query查询条件 // 构建 query查询条件
query := db.Mdb.Model(&SearchInfo{}) query := db.Mdb.Model(&SearchInfo{})
@@ -762,6 +763,46 @@ func GetSearchOptions(pid int64) map[string]interface{} {
return tagMap return tagMap
} }
// GetSearchInfoById 查询id对应的检索信息
func GetSearchInfoById(id int64) *SearchInfo {
s := SearchInfo{}
if err := db.Mdb.First(&s, id).Error; err != nil {
log.Println(err)
return nil
}
return &s
}
// DelFilmSearch 删除影片检索信息, (不影响后续更新, 逻辑删除)
func DelFilmSearch(id int64) error {
// 通过检索id对影片检索信息进行删除
if err := db.Mdb.Delete(&SearchInfo{}, id).Error; err != nil {
log.Println(err)
return err
}
return nil
}
// ShieldFilmSearch 删除所属分类下的所有影片检索信息
func ShieldFilmSearch(cid int64) error {
// 通过检索id对影片检索信息进行删除
if err := db.Mdb.Where("cid = ?", cid).Delete(&SearchInfo{}).Error; err != nil {
log.Println(err)
return err
}
return nil
}
// RecoverFilmSearch 恢复所属分类下的影片检索信息状态
func RecoverFilmSearch(cid int64) error {
// 通过检索id对影片检索信息进行删除
if err := db.Mdb.Model(&SearchInfo{}).Unscoped().Where("cid = ?", cid).Update("deleted_at", nil).Error; err != nil {
log.Println(err)
return err
}
return nil
}
// ================================= 接口数据缓存 ================================= // ================================= 接口数据缓存 =================================
// DataCache API请求 数据缓存 // DataCache API请求 数据缓存

View File

@@ -84,6 +84,7 @@ func SetupRouter() *gin.Engine {
{ {
filmRoute.POST(`/add`, controller.FilmAdd) filmRoute.POST(`/add`, controller.FilmAdd)
filmRoute.GET(`/search/list`, controller.FilmSearchPage) filmRoute.GET(`/search/list`, controller.FilmSearchPage)
filmRoute.GET(`/search/del`, controller.FilmDelete)
filmRoute.GET(`/class/tree`, controller.FilmClassTree) filmRoute.GET(`/class/tree`, controller.FilmClassTree)
filmRoute.GET(`/class/find`, controller.FindFilmClass) filmRoute.GET(`/class/find`, controller.FindFilmClass)