基本信息
路由配置
首先是基础路由
{
path: '/content',
component: Layout,
redirect: '/content/list ',
name: '作品管理 ',
meta: { title: '作品管理 ', icon: 'el-icon-s-help ' },
children: [
{
path: 'list ',
name: '作品列表 ',
component: () => import( '@/views/video/content/list '),
meta: { title: '作品列表 ', icon: 'table ' }
},
{
path: 'info ',
name: '添加作品 ',
component: () => import( '@/views/video/content/info '),
meta: { title: '添加作品 ', icon: 'tree ' }
}
]
},
添加切换路由
{
path: 'info/:id ',
name: '添加作品 ',
component: () => import( '@/views/video/content/info '),
meta: {title: '添加作品 ', icon: 'tree '},
hidden: true
},
{
path: 'chapter/:id ',
name: '章节信息 ',
component: () => import( '@/views/video/content/chapter '),
meta: {title: '章节信息 ', icon: 'tree '},
hidden: true
},
{
path: 'send/:id ',
name: '最终发布 ',
component: () => import( '@/views/video/content/send '),
meta: {title: '最终发布 ', icon: 'tree '},
hidden: true
}
如上的路由的配置主要是用来进行组件之间切换的时候使用的,自行把对应的组件新建好,如下图
到了这里,可以自行的把前端跑起来看一下
编写添加界面
info,基础界面
<template>
<div class="app-container">
<h1>添加作品</h1>
<el-steps :active="1" finish-status="success">
<el-step title="填写作品基本信息"></el-step>
<el-step title="添加章节视频"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<el-form label-width="120px">
<el-form-item label-width="作品标题">
<el-input placeholder="请填写作品标题"/>
</el-form-item>
</el-form>
<el-button style="margin-top: 12px;" @click="next">下一步</el-button>
</div>
</template>
<script>
export default {
name: "info",
data() {
return {
active: 0
};
},
methods: {
next() {
// 路由跳转
this.$router.push({path: '/content/chapter/1'})
}
}
}
</script>
<style scoped>
</style>
chapter,基础界面
<template>
<div class="app-container">
<h1>章节信息</h1>
<el-steps :active="2" finish-status="success">
<el-step title="填写作品基本信息"></el-step>
<el-step title="添加章节视频"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<h1>章节信息</h1>
<el-button style="margin-top: 12px;" @click="pre">上一步</el-button>
<el-button style="margin-top: 12px;" @click="next">下一步</el-button>
</div>
</template>
<script>
export default {
name: "chapter",
data() {
return {
active: 0
};
},
methods: {
pre() {
this.$router.push({path: '/content/info/1'})
},
next() {
this.$router.push({path: '/content/send/1'})
}
}
}
</script>
<style scoped>
</style>
send,基础界面
<template>
<div class="app-container">
<h1>最终发布</h1>
<el-steps :active="3" finish-status="success">
<el-step title="填写作品基本信息"></el-step>
<el-step title="添加章节视频"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<el-button style="margin-top: 12px;" @click="pre">上一步</el-button>
<el-button style="margin-top: 12px;">发布</el-button>
</div>
</template>
<script>
export default {
name: "send",
data() {
return {
active: 0
};
},
methods: {
pre() {
this.$router.push({path: '/content/chapter/1'})
}
}
}
</script>
<style scoped>
</style>
组件之间的跳转
基本信息保存下一步,首先来新建一个编写 api 请求的文件如下
import request from '@/utils/request '
export default {
// 1.作者列表-分页查询
getAuthorList() {
return request({
url: `/service_video/author/getAuthorList`,
method: 'get '
})
},
// 2.添加作品信息
addContentInfo(contentVO) {
return request({
url: `/service_video/content/addContentInfo`,
method: 'post ',
data: contentVO
})
},
}
saveAndNext() {
content.addContentInfo(this.contentVO).then(res => {
this.$message({
type: 'success ',
message: res.message
});
//路由跳转
this.$router.push({path: '/content/chapter/ ' + res.data.contentId})
}).catch(error => {
this.$message.error("添加失败");
})
}
发布作品界面模型绑定
data() {
return {
active: 0,
contentVO: {
// 标题
title: '',
// 二级分类ID
categoryId: '',
// 一级分类ID
categoryParentId: '',
// 作者id
authorId: '',
// 总视频数
contentNum: '',
// 简介
description: '',
// 封面
cover: require('@/assets/images/cover.jpg'),
price: 0
},
// 作者列表
authorList: [],
// 一级分类列表
oneCategoryList: [],
// 二级分类列表
twoCategoryList: [],
// 请求API地址
BASE_API: process.env.VUE_APP_BASE_API
};
},
你也可以自己使用自己喜欢的素材图片,当然了我这里给出我文章中的素材占位图片下载地址:https://wws.lanzous.com/i5lrvnv53eb
界面绑定模型数据
<template>
<div class="app-container">
<h1>添加作品</h1>
<el-steps :active="1" finish-status="success">
<el-step title="填写作品基本信息"></el-step>
<el-step title="添加章节视频"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<el-form label-width="120px" style="margin-top: 10px">
<!--
标题
-->
<el-form-item label="作品标题">
<el-input placeholder="请填写作品标题" v-model="contentVO.title"/>
</el-form-item>
<!--
分类
-->
<el-form-item label="作品分类">
<el-select placeholder="一级分类" v-model="contentVO.categoryParentId" @change="oneCategoryChanged">
<el-option v-for="oneCategory in oneCategoryList"
:key="oneCategory.id"
:label="oneCategory.title"
:value="oneCategory.id"/>
</el-select>
<el-select placeholder="二级分类" v-model="contentVO.categoryId">
<el-option v-for="oneCategory in twoCategoryList"
:key="oneCategory.id"
:label="oneCategory.title"
:value="oneCategory.id"/>
</el-select>
</el-form-item>
<!--
作者
-->
<el-form-item label="作者">
<el-select placeholder="选择作者" v-model="contentVO.authorID">
<el-option v-for="author in authorList"
:key="author.id"
:label="author.name"
:value="author.id"
/>
</el-select>
</el-form-item>
<!--
总视频数
-->
<el-form-item label="总视频数">
<el-input :min="0" placeholder="总视频数" v-model="contentVO.contentNum"/>
</el-form-item>
<!--
简介
-->
<el-form-item label="作品简介">
<el-input :min="0" placeholder="简介" v-model="contentVO.description"/>
</el-form-item>
<!--
封面
-->
<el-form-item label="作品封面">
<el-upload
:show-file-list="false"
:action="BASE_API+'/service_upload/file/ossUploadFile'"
:on-success="uploadSuccess"
:limit="1"
name="file"
>
<img :src="contentVO.cover" style="width: 300px; height: 150px"/>
</el-upload>
</el-form-item>
<!--
价格
-->
<el-form-item label="价格">
<el-input :min="0" placeholder="价格" v-model="contentVO.price"/>
</el-form-item>
<el-button style="margin-top: 12px;" type="primary" @click="saveAndNext">基本信息保存下一步</el-button>
</el-form>
</div>
</template>
在点击按钮之前还需要处理一下后台(等待后续所有的准备都做好了在点击进行测试按照如下步骤走),作品ID不能让其自动生成 ,要自己手动设置需要设置成 input
紧接着修改一下作品表的实体类,对创建时间和更新时间,逻辑删除字段进行处理如下, 当然如上图中的作品简介的也是同理,后面所有生成的实体类当中都应该设置自动填充属性,至于逻辑删除我在一一强调即可
处理下拉列表数据,首先导入对应需要使用到的 api 文件如下
import content from "@/api/video/content/content";
import category from "@/api/video/category/category";
在这里我说明一个东西,那么就是关于 el-steps
组件的问题,我这里是单独的新建了三个不同的组件进行的是组件之间的跳转,并不是 el-steps
的跳转,所以每个页面的 el-steps
状态值都是固定写死了,例如下图中的状态
作者列表
// 加载所有的作者下拉列表
getAuthorList() {
content.getAuthorList().then(res => {
this.authorList = res.data.list;
}).catch(error => {
this.$message.error("系统繁忙!");
});
},
created() {
// 加载所有的作者下拉列表
this.getAuthorList();
}
修改一下后端加载所有创作者列表的接口,返回值不是我们需要的,因为是在写 ResponseResult 之前写的,所以没有改造为这种形式需要改造一下如下图
渲染创作者下拉列表
分类列表
// 加载所有的分类
getCategoryList() {
// 1.获取一级分类
category.getCategoryData().then(res => {
this.oneCategoryList = res.data.list;
}).catch(error => {
this.$message.error("系统繁忙!");
});
},
实现当一级分类发生改变时的监听事件
// 当一级分类改变时调用的方法,参数:当前选择的一级分类ID
oneCategoryChanged(value) {
for (let i = 0; i < this.oneCategoryList.length; i++) {
let category = this.oneCategoryList[i];
if (value === category.id) {
this.twoCategoryList = category.children;
// 清空已经选择的二级分类列表
this.contentVO.categoryId = ' ';
}
}
},
渲染分类数据方式如下
完善上传封面功能,OSS上传接口在之前就已经写好了调用一下即可
<!--
封面
-->
<el-form-item label="作品封面">
<el-upload
:show-file-list="false"
:action="BASE_API+'/service_upload/file/uploadOssFile'"
:on-success="uploadSuccess"
:limit="1"
name="file"
>
<img :src="contentVO.cover" style="width: 300px; height: 150px"/>
</el-upload>
</el-form-item>
uploadSuccess(res, file) {
this.contentVO.cover = res.data.url;
}
到这里添加的准备都已经完成了,还需要修改一下后端的添加接口,因为是 POST 请求,而参数又是一个对象我少加了一个 @RequestBody
注解如下图自行加上之后在测试添加即可,填写对应的信息
可以看到我这里已经成功了
基本信息修改
数据回显,首先在 data 中声明一个 contentId
属性如下
然后在 created
处于loading结束后,还做一些初始化,实现函数自执行(data 数据已经初始化,但是 DOM 结构渲染完成,组件没有加载)中做数据回显
created() {
// 思路为:判断路由当中有没有ID,如果有,就是修改,做数据回显
// 获取路由当中的ID
if (this.$route.params && this.$route.params.id) {
this.contentId = this.$route.params.id;
// 根据ID查询当前的作品,进行信息回显
this.getInfo();
} else {
// 加载所有的作者下拉列表
this.getAuthorList();
// 加载所有的分类
this.getCategoryList();
}
}
修改一下模型绑定的创作者 ID,将ID 改为 Id
根据ID查询当前的作品,进行信息回显, this.getInfo();
内容如下
// 根据id获取作品信息
getInfo() {
content.getContentWithInfoId(this.contentId).then(res => {
this.contentVO = res.data.contentVO;
// 1.获取一级分类
category.getCategoryData().then(res => {
this.oneCategoryList = res.data.list;
// 遍历每一个一级分类 , 判断当前的contentVO 是属于哪一个一级分类
for (let i = 0; i < this.oneCategoryList.length; i++) {
let oneCategory = this.oneCategoryList[i];
if (oneCategory.id === this.contentVO.categoryParentId) {
this.twoCategoryList = oneCategory.children;
}
}
});
//加载作者列表
this.getAuthorList();
});
},
紧接着修改 saveAndNext 主要就是判断有 ID 就是更新没有就是添加的操作
saveAndNext() {
// 判断当前是添加还是更新
if (this.contentId === ' ') {
content.addContentInfo(this.contentVO).then(res => {
this.$message({
type: 'success ',
message: res.message
});
//路由跳转
this.$router.push({path: '/content/chapter/ ' + res.data.contentId})
}).catch(error => {
this.$message.error("添加失败");
})
} else {
// 更新操作
content.updateContentInfo(this.contentVO).then(res => {
this.$message({
type: 'success ',
message: res.message
});
// 路由跳转跳转到章节
this.$router.push({path: '/content/chapter/ ' + this.contentId})
}).catch(error => {
this.$message.error("更新失败");
})
}
},
最终 info.vue
代码如下所示
<template>
<div class="app-container">
<h1>添加作品</h1>
<el-steps :active="1" finish-status="success">
<el-step title="填写作品基本信息"></el-step>
<el-step title="添加章节视频"></el-step>
<el-step title="完成"></el-step>
</el-steps>
<el-form label-width="120px" style="margin-top: 10px">
<!--
标题
-->
<el-form-item label="作品标题">
<el-input placeholder="请填写作品标题" v-model="contentVO.title"/>
</el-form-item>
<!--
分类
-->
<el-form-item label="作品分类">
<el-select placeholder="一级分类" v-model="contentVO.categoryParentId" @change="oneCategoryChanged">
<el-option v-for="oneCategory in oneCategoryList"
:key="oneCategory.id"
:label="oneCategory.title"
:value="oneCategory.id"/>
</el-select>
<el-select placeholder="二级分类" v-model="contentVO.categoryId">
<el-option v-for="oneCategory in twoCategoryList"
:key="oneCategory.id"
:label="oneCategory.title"
:value="oneCategory.id"/>
</el-select>
</el-form-item>
<!--
作者
-->
<el-form-item label="作者">
<el-select placeholder="选择作者" v-model="contentVO.authorId">
<el-option v-for="author in authorList"
:key="author.id"
:label="author.name"
:value="author.id"
/>
</el-select>
</el-form-item>
<!--
总视频数
-->
<el-form-item label="总视频数">
<el-input :min="0" placeholder="总视频数" v-model="contentVO.contentNum"/>
</el-form-item>
<!--
简介
-->
<el-form-item label="作品简介">
<el-input :min="0" placeholder="简介" v-model="contentVO.description"/>
</el-form-item>
<!--
封面
-->
<el-form-item label="作品封面">
<el-upload
:show-file-list="false"
:action="BASE_API+'/service_upload/file/uploadOssFile'"
:on-success="uploadSuccess"
:limit="1"
name="file"
>
<img :src="contentVO.cover" style="width: 300px; height: 150px"/>
</el-upload>
</el-form-item>
<!--
价格
-->
<el-form-item label="价格">
<el-input :min="0" placeholder="价格" v-model="contentVO.price"/>
</el-form-item>
<el-button style="margin-top: 12px;" type="primary" @click="saveAndNext">基本信息保存下一步</el-button>
</el-form>
</div>
</template>
<script>
import content from "@/api/video/content/content";
import category from "@/api/video/category/category";
export default {
name: "info",
data() {
return {
contentVO: {
// 标题
title: '',
// 二级分类ID
categoryId: '',
// 一级分类ID
categoryParentId: '',
// 作者id
authorId: '',
// 总视频数
contentNum: '',
// 简介
description: '',
// 封面
cover: require('@/assets/images/cover.jpg'),
price: 0
},
// 作品的ID
contentId: '',
// 作者列表
authorList: [],
// 一级分类列表
oneCategoryList: [],
// 二级分类列表
twoCategoryList: [],
// 请求API地址
BASE_API: process.env.VUE_APP_BASE_API
};
},
methods: {
saveAndNext() {
// 判断当前是添加还是更新
if (this.contentId === '') {
content.addContentInfo(this.contentVO).then(res => {
this.$message({
type: 'success',
message: res.message
});
//路由跳转
this.$router.push({path: '/content/chapter/' + res.data.contentId})
}).catch(error => {
this.$message.error("添加失败");
})
} else {
// 更新操作
content.updateContentInfo(this.contentVO).then(res => {
this.$message({
type: 'success',
message: res.message
});
// 路由跳转跳转到章节
this.$router.push({path: '/content/chapter/' + this.contentId})
}).catch(error => {
this.$message.error("更新失败");
})
}
},
// 加载所有的作者下拉列表
getAuthorList() {
content.getAuthorList().then(res => {
this.authorList = res.data.list;
}).catch(error => {
this.$message.error("系统繁忙!");
});
},
// 加载所有的分类
getCategoryList() {
// 1.获取一级分类
category.getCategoryData().then(res => {
this.oneCategoryList = res.data.list;
}).catch(error => {
this.$message.error("系统繁忙!");
});
},
// 当一级分类改变时调用的方法,参数:当前选择的一级分类ID
oneCategoryChanged(value) {
for (let i = 0; i < this.oneCategoryList.length; i++) {
let category = this.oneCategoryList[i];
if (value === category.id) {
this.twoCategoryList = category.children;
// 清空已经选择的二级分类列表
this.contentVO.categoryId = '';
}
}
},
uploadSuccess(res, file) {
this.contentVO.cover = res.data.url;
},
// 根据id获取作品信息
getInfo() {
content.getContentWithInfoId(this.contentId).then(res => {
this.contentVO = res.data.contentVO;
// 1.获取一级分类
category.getCategoryData().then(res => {
this.oneCategoryList = res.data.list;
// 遍历每一个一级分类 , 判断当前的contentVO 是属于哪一个一级分类
for (let i = 0; i < this.oneCategoryList.length; i++) {
let oneCategory = this.oneCategoryList[i];
if (oneCategory.id === this.contentVO.categoryParentId) {
this.twoCategoryList = oneCategory.children;
}
}
});
//加载作者列表
this.getAuthorList();
});
},
},
created() {
// 思路为:判断路由当中有没有ID,如果有,就是修改,做数据回显
// 获取路由当中的ID
if (this.$route.params && this.$route.params.id) {
this.contentId = this.$route.params.id;
// 根据ID查询当前的作品,进行信息回显
this.getInfo();
} else {
// 加载所有的作者下拉列表
this.getAuthorList();
// 加载所有的分类
this.getCategoryList();
}
}
}
</script>
<style scoped>
</style>
紧接着就是修改 content.js
把对应的 api 请求方法加上
// 3.根据id获取作品信息
getContentWithInfoId(id) {
return request({
url: `/service_video/content/getContentWithInfoId/${id}`,
method: 'get'
});
},
// 4.更新操作
updateContentInfo(contentVO) {
return request({
url: `/service_video/content/updateContentInfo`,
method: 'post',
data: contentVO
});
},
紧接着后端还需要完善一些信息,内容分别如下,首先是配置逻辑删除的自动填充配置如下,修改 common_base
模块中的 MyMetaObjectHandler
如下所示
在添加逻辑删除,添加数据的时候自动填充注解如下
紧接着就是把数据库里面的字段修正,单词打错了,执行如下 ALTER 语句即可修正,修改完成之后自行测试即可
ALTER TABLE `video_db`.`video_content`
CHANGE COLUMN `conntent_num` `content_num` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '作品个数' AFTER `price`;