前后端项目接口联调
一般在前后端分离的项目中,后端人员需要提供后台接口,而前端人员提供前端项目(如Vue项目的Vue脚手架项目)
进行联调,都按照接口文档来进行操作项目
对于Vue脚手架来说,版本有时决定地址,就如你设置自动打开浏览器时,地址是0.0.0.0:8080,可以如下操作进行修改
npm uninstall -g vue-cli
npm install -g @vue/cli@4.5.15
这样可能就会自动打开了,所以说,有时没有错,但符合的结果不对,可能是版本问题
js进行文件上传:
当使用js时,前台没有表单以及表单提交,或者不想用这个表单,只要js传递数据时,可以使用FormData对象,简称表单数据
FormData的主要用途有两个
将form表单元素的name与value进行组合,实现表单数据的序列化,从而减少表单元素的拼接
提高工作效率
异步上传文件
创建FormData对象
var formdata=new FormData();
formdata.append("name","laotie");
console.log(formdata.get("name"));
formdata.set("name","laoliu");
console.log(formdata.get("name"));
this.params = new FormData();
onchange(file) {
if (file != null) {
this.params.append("file", file.raw, file.name);
}
}
前面我说过键值对不是数据,因为键值对使用[]是根据key来的,即使用[]只能获得对应key的值
前端的断点,需要加上debugger,类似于java断点,执行到这里,那么浏览器就会到检查的源代码的地方,进行调试
具体可以自己实验,实际上是浏览器的功能,即是浏览器发现这个代码而进行调试的,所以如果浏览器不进行断点调试
那么这个代码就无作用,与java是有所不同的,前者受浏览器限制,后者只在于自己,打了就可调试
最后使用脚手架时,由于基本上是js的文件的操作,所以查看源代码,基本上没有对应dom元素,只有在检查元素里才可查看的到
对应的参数数据,如data的值可以有多种,实际上前面说过的最后变成键值对中数组不会变成键值对(一般都是这样,数组怎么变成呢)
注意:使用Vue脚手架,若发现没有错误,结果也不对,那么手写一下,可能就会对
前端做的回显,一般是在一个组件里操作第一次回显的数据的
一般后台进行操作equals时,最好忽略大小写,因为前端的传参,可能与你测试的某些参数不一样
如application/json;charset=utf-8,后面的utf-8前端传递的可能是大写的
组件之间是替换的
从这里开始,进行联调(对应的初始项目地址在最后给出了)
下面的组件还没有在对应环境中操作,所以复制是运行不了的,最后会给出对应代码地址(需要一定的功底部署)
测试的组件:
<template>
<!-- v-model的值,为当前框的值 label选项的标签名-->
<el-select v-model="status" placeholder="请选择">
<el-option v-for="item in Object.keys(statusMapping)"
:key="item"
:label="statusMapping[item]"
:value="item"></el-option>
<!-- Object.keys(statusMapping)获取键值对的key数组
其中value必须在,否则不会显示,而我们绑定的v-model的值
其中找到对应的value的值的对应的那条数据可以是key或者直接的数据
若是key则将他的这条数据的其中label的对应值默认的显示出来
若是直接值,则value自己显示,但label若也是直接值,则显示label的值
所以若没有对应的value参数自然就是显示label,否则使用对应的参数
而for循环的则也是给value然后他给label来显示,与上面是一样的
所以value不可以不写
当没有对应的:label时,则value默认从循环后的数据里面操作key或者是直接的数据然后显示出来,而不是让label来显示了
-->
</el-select>
</template>
<script>
export default {
data() {
return {
options: [{
value: '选项1',
label: '黄金糕'
}],
statusMapping : {
0: "已隐藏",
1: "待更新",
2: "已更新"
},
status:"" //当成参数给label值对应,然后给出显示,若参数没有循环中存在,那么直接显示该参数
}
}
}
</script>
<style scoped>
</style>
<template>
<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick">
<!--
slot-scope:作用域插槽
node:当前节点对象,这个可以不写,但写了就要写对应属性,否则就是空值,这是当然的
即设置显示的值的
data:当前数据对象,键值对存放
每个树都有对应
-->
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ data.section_name || data.theme}}</span><!-- 取出对应数据-->
<!-- <span>级别:{{ node.level }}</span> -->
<!-- node.level是级别等级,从1开始-->
</span>
</el-tree>
</template>
<script>
/*
data:展示数据
props:配置属性结构
defaultProps: {
children: 'children',
//指定生成子树的属性名称,即value的值决定是否有下一级,当children: 'children1'
//加一个1,如果没有children1,那么没有下一级,其中key是固定的,不可改变,改变就不起作用了
label: 'label'
//指定当前树,不是子树
}
*/
export default {
data() {
return {
data: [
{
id: 5,
course_id: 10,
section_name: "麻式太极",
description: "麻式太极拳,你动我试试",
orderNum: 0,
status: 2,
create_time: "2019-07-11 10:55:10.0",
update_time: "2019-10-09 12:43:01.0",
isDel: 0,
lessonList: [
{
id: 32,
course_id: 10,
section_id: 5,
theme: "第一讲:如何给自己洗脑",
duration: 10,
is_free: 1,
order_num: 1,
status: 2,
create_time: "2019-01-23 20:37:02.0",
update_time: "2020-02-24 18:37:34.0",
isDel: 0
},
{
id: 33,
course_id: 10,
section_id: 5,
theme: "第二讲:如何给别人洗脑",
duration: 10,
is_free: 1,
order_num: 1,
status: 2,
create_time: "2019-01-23 20:37:02.0",
update_time: "2020-02-24 18:37:34.0",
isDel: 0
}
]
}
],
defaultProps: {
children:"lessonList",
label: item => {
//第一个参数是节点的data键值对数值,因为data是节点的值
//所以下面的item在不同的节点,是不同的,所以可以item.theme
//每个树都是对应data和对应node
return item.section_name || item.theme
}
}
};
},
methods: {
handleNodeClick(data) {
console.log(data);
}
}
};
</script>
<style>
</style>
主组件:
<template>
<section class="courses">
<!-- 表单部分 -->
<el-form class="actions" :inline="true" :model="filter">
<el-form-item class="input-title" label="课程名称">
<el-input v-model="filter.course_name" type="search" placeholder="课程名称"/>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="filter.status" placeholder="课程状态">
<el-option label="全部" value></el-option>
<el-option label="已发布" value="1"></el-option>
<el-option label="草稿" value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="filterQuery">查询</el-button>
</el-form-item>
<el-form-item class="btn-add">
<el-button type="primary" icon="el-icon-plus" @click="addCourse">新建课程</el-button>
</el-form-item>
</el-form>
<!-- 表格部分 -->
<el-table :data="courses" v-loading="loading" element-loading-text="数据加载中...">
<el-table-column prop="id" label="ID" width="100"></el-table-column>
<el-table-column prop="course_name" label="课程名称" width="200"></el-table-column>
<el-table-column
prop="price"
label="价格"
align="center"
width="120"
:formatter="priceFormatter"
></el-table-column>
<el-table-column prop="sort_num" label="排序" align="center" width="120"></el-table-column>
<!-- 状态展示 -->
<el-table-column prop="status" label="状态" align="center" width="120">
<template slot-scope="scope">
<i
class="status status-success"
title="已发布"
v-if="scope.row.status == '1'"
@click="updateStatus(scope.row)"
></i>
<i class="status status-warning" title="草稿" v-else-if="scope.row.status == '0'"></i>
</template>
</el-table-column>
<!-- 操作部分 -->
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<!-- 状态按钮 -->
<el-button
size="mini"
:type="scope.row.status == '1' ? 'danger' : 'success'"
@click="updateStatus(scope.row)"
>{{ scope.row.status == "1" ? "下架" : "发布" }}</el-button>
<!-- 营销信息按钮 -->
<el-button size="mini" @click="handleNavigate('CourseItem', scope.row.id)">营销信息</el-
button>
<!-- 内容管理按钮 -->
<el-button size="mini" @click="handleNavigate('CourseTasks', scope.row.id)">内容管理</el-
button>
</template>
</el-table-column>
</el-table>
</section>
</template>
<script>
// JS部分代码
//引入axios
import { axios } from "../utils";
export default {
name: "Courses",
title: "课程管理",
//定义数据部分
data() {
return {
filter: { course_name: "", status: "" }, //查询对象
courses: [], //课程信息集合
loading: false //是否弹出加载
};
},
//钩子函数
created() {
this.loadCourses();
},
methods: {
//方法1: 获取课程列表
loadCourses() {
this.loading = true;
//请求后台查询课程列表接口
return axios.get("/course",{
params:{
methodName: "findCourseList"
}
}).then(resp => { //可以不写括号
console.log(resp);
this.courses = resp.data
this.loading = false;
}).catch(error => {
this.$message.error("数据获取失败")
})
},
//方法2: 条件查询课程信息
filterQuery() {
this.loading = true;
//请求后台条件查询接口
return axios.get("/course",{
//准备参数
params:{
methodName: "findByCourseNameAndStatus",
course_name:this.filter.course_name,
status:this.filter.status
}
}).then(resp => { //可以不写括号
console.log(resp);
this.courses = resp.data
this.loading = false;
}).catch(error => {
this.$message.error("数据获取失败")
// this.loading = false;加不加无所谓,返回有提示,看着加载也行,但如果你要严谨的话,就可以加
})
},
//方法3: 添加课程跳转方法
addCourse() {
//跳转到CourseItem.vue
this.$router.push({name:"CourseItem",params:{courseId:"new"}})
//有键值对的地方,一定在{}里面,而由于对应路由需要一个id
//那么就必须是键值对,且对应id只识别params的数据
//不传递id的话,如果没有对应判断,那么url不会显示,但还是会过去
},
//方法4: 修改课程状态
updateStatus(item) {
//item表示选中的数据 =Course对象
axios.get("/course",{
params:{
methodName:"updateCourseStatus",
id:item.id
}
}).then(resp => {
// console.log(resp)
// console.log(item)
//将返回的状态字段数据,封装到item对象中,就是将对应的状态信息进行覆盖,并不是重新赋值
Object.assign(item,resp.data);
//item就是上面的scope.row,代表当前行,这个行中有对应字段,获得对应数据的
//获取对应根据prop来获得内容,所以在状态信息进行覆盖时,也就是改变,但并不会进行设置
//因为没有双向绑定,即需要重新刷新页面进行显示
//重新加载页面,可以理解为刷新,因为不刷新的话,可能看不到最新消息,虽然可以不写
// console.log(resp)
// console.log(item)
window.location.reload;
//一般使用window.location.reload();,虽然可能是一样的,但最好使用后面的有()的,因为可能不会起作用
}).catch(error => {
this.$message.error("状态修改失败")
})
},
//方法5: 根据路由名称, 导航到对应组件
handleNavigate(name, id) {
//根据参数 使用对应的路由 导航到指定的组件
this.$router.push({name,params:{ //前面也说过,变量在键值对的,会默认为有对应值,即也变成键值对
courseId:id
}})
},
//价格前面添加¥ 方法
priceFormatter(row, column, value, index) {
return `¥${value}`;
}
}
};
</script>
<style lang="scss">
.courses {
.actions {
display: flex;
align-items: flex-end;
}
.input-title {
.el-form-item__content {
min-width: 22vw;
}
}
.btn-add {
margin-left: auto;
}
}
</style>
<template>
<section class="course-item">
<!-- 头部 -->
<div class="header">
<!-- 返回上一页 back()方法就是返回上一个路由的操作,简称为返回上一页 -->
<el-page-header
@back="() => this.$router.back()"
:content="course.title"
/>
<el-button type="primary" @click="handleSave">保存</el-button>
</div>
<!-- 表单开始 -->
<el-form ref="form" :model="course" :rules="rules" label-width="120px">
<el-card
shadow="never"
v-loading="loading"
element-loading-text="数据加载中..."
>
<header slot="header">基本信息</header>
<el-form-item label="名称" prop="course_name">
<el-input
v-model="course.course_name"
type="text"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="简介" prop="brief">
<el-input
v-model="course.brief"
type="text"
maxlength="100"
show-word-limit
/>
</el-form-item>
<el-form-item label="讲师姓名" prop="teacher_name">
<el-input
v-model="course.teacher_name"
type="text"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="讲师简介" prop="teacher_title">
<el-input
v-model="course.teacher_info"
type="text"
maxlength="100"
show-word-limit
/>
</el-form-item>
<el-form-item
label="课程概述"
prop="preview_first_field"
class="form-control-summary"
>
<el-input
v-model="course.preview_first_field"
type="text"
maxlength="20"
show-word-limit
/>
<el-input
v-model="course.preview_second_field"
type="text"
maxlength="20"
show-word-limit
/>
</el-form-item>
</el-card>
<el-card
shadow="never"
v-loading="loading"
element-loading-text="数据加载中..."
>
<header slot="header">销售信息</header>
<el-form-item label="售卖价格" prop="discounts">
<el-input v-model="course.discounts" type="number">
<template slot="append">元</template>
</el-input>
</el-form-item>
<el-form-item label="商品原价">
<el-input v-model="course.price" type="number">
<template slot="append">元</template>
</el-input>
</el-form-item>
<el-form-item label="活动标签">
<el-input
v-model="course.price_tag"
type="text"
maxlength="4"
show-word-limit
/>
</el-form-item>
</el-card>
<el-card
shadow="never"
v-loading="loading"
element-loading-text="数据加载中..."
>
<header slot="header">分享信息</header>
<!-- 上传图片部分 -->
<el-form-item label="分享小图" prop="share_image_title">
<el-input v-model="course.share_image_title" type="text">
<!-- :auto-upload="false",取消自动上传, :on-change="onchange" 调用onchange进行处理 -->
<el-upload
slot="prepend"
:auto-upload="false"
:on-change="onchange"
action
:limit="1"
>
<!-- false,不立即上传,也就是不发送到后台-->
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-input>
</el-form-item>
<el-form-item label="分享标题" prop="share_title">
<el-input
v-model="course.share_title"
type="text"
maxlength="40"
show-word-limit
/>
</el-form-item>
<el-form-item label="分享简介" prop="share_description">
<el-input
v-model="course.share_description"
type="text"
maxlength="60"
show-word-limit
/>
</el-form-item>
</el-card>
<el-card
shadow="never"
v-loading="loading"
element-loading-text="数据加载中..."
>
<header slot="course_description">课程详情</header>
<editor v-model="course.course_description" />
</el-card>
<div class="actions">
<el-button type="primary" @click="handleSave">保存</el-button>
</div>
</el-form>
<!-- 表单结束 -->
</section>
</template>
<script>
//引入富文本编辑器 和 axios
import Editor from "@/components/Editor.vue";
import { axios } from "../utils";
export default {
name: "CourseItem",
title: "营销信息",
components: { Editor },
data() {
//表单校验规则
const rules = {
course_name: [
{ required: true, message: "请输入课程名称", trigger: "blur" },
{ min: 2, max: 50, message: "长度在 2 到 50 个字符", trigger: "blur" }
],
brief: [
{ required: true, message: "请输入课程简介", trigger: "change" },
{ min: 5, max: 100, message: "长度在 5 到 100 个字符", trigger: "blur" }
],
teacher_name: [
{ required: true, message: "请输入讲师姓名", trigger: "change" },
{ min: 2, max: 50, message: "长度在 2 到 50 个字符", trigger: "blur" }
],
teacher_info: [
{ required: true, message: "请输入讲师简介", trigger: "change" },
{ min: 5, max: 50, message: "长度在 5 到 50 个字符", trigger: "blur" }
],
preview_first_field: [
{ required: true, message: "请输入课程概述", trigger: "change" },
{ min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" }
],
preview_second_field: [
{ required: true, message: "请输入课程概述", trigger: "change" },
{ min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" }
],
price: [{ required: true, message: "请输入课程售价", trigger: "change" }],
share_img: [
{ required: true, message: "请上传分享图片", trigger: "change" }
],
share_title: [
{ required: true, message: "请输入分享标题", trigger: "change" }
],
share_description: [
{ required: true, message: "请输入分享简介", trigger: "change" }
]
};
//数据部分
return {
rules, //规则
course: {}, //课程
loading: false,
params: {}
};
},
//钩子函数
created() {
//1.显示当前页面在网站中的位置
this.$breadcrumbs = [
{ name: "Courses", text: "课程管理" },
{ text: "营销信息" }
];
//获取路由传递的参数
//获取当前路由,所以可以通过push来改变位置,改变浏览器自动就会访问,这是浏览器的作用,与这无关
const id = this.$route.params.courseId;
//注意:这里是route,不是router,前面是获得值,后面是使用方法,不要搞混,他们有不同的对应值存放
//判断id是否有值,没有值 就跳转到错误页面
if(!id) return this.redirectToError()
//数字取否,为false,小于等于0的判断也为false,其余基本为true
//只有一条语句,也可以与java一样省略括号
//若没有值,那么就要判断是去新建还是修改,即是new还是有对应id值
if(id === "new"){
//new 代表新增操作
this.course.title="新增课程"
}else{
//否则就是修改操作
//alert("修改操作的ID是:" + id)
//根据ID查询课程信息,进行回显
this.loadCourse(id)
}
//创建FormData对象,将图片和表单其他内容 一同上传
this.params = new FormData();
},
methods: {
//方法1: 保存和修改课程信息
handleSave() {
//校验表单是否正确,$refs表示当前的dom,即获得对应表单,然后执行对应校验方法
this.$refs.form.validate(valid => { //valid是校验后的boolean值
//判断校验是否通过
if(!valid) return false
//设置多部件上传,Content-type属性
let config = {
headers:{
"Content-type":"multipart/form-data"
}
}
//获取到表单数据,保存到params中(params就是FormData对象)
for(let key in this.course){
//若in右边是数组,则一一获得,若是键值对,则一一获得key,不获得value
//debugger //前端的debug调试方式,与java断点是类似的
console.log(key + " = " + this.course[key])
this.params.append(key,this.course[key]) //记得文件的那个去掉,防止被后端继续操作,使得数据出现问题
}
//请求后台接口 保存课程信息
//可以是多个{}
axios.post("/courseSalesInfo",this.params,config).then(resp =>{
if(resp.data.status == 0){
this.$router.back()
} else if(resp.data.status == 1){
this.$message.error(this.data.msg)
}
}).catch(error => {
this.$message.error("保存课程失败!")
})
//这些$message是element-ui的js
})
},
//文件上传
onchange(file) {
if(file != null){
//将文件信息,保存到FormData对象中
//参数1:表示文件上传项,对应名称最好要正确 参数2:文件上传的二进制数据,参数3:文件名
//之所以可以得到二进制数据,就与java得到字节数据一样,实际都是字节
this.params.append("file",file.raw,file.name)
}
},
//方法2: 根据ID 回显课程信
loadCourse(id) {
this.loading = true;
axios.get("/course",{
params:{
methodName:"findCourseById",
id:id
}
}).then(resp => {
this.course = resp.data;
this.loading = false;
}).catch(error => {
this.$message.error("回显数据失败")
})
},
//跳转到错误
redirectToError() {
// TODO: Error components
this.$router.replace({ path: "/not-found" });
}
}
};
</script>
<style lang="scss">
.course-item {
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
.el-card {
margin-top: 20px;
}
.form-control-summary {
.el-form-item__content {
display: flex;
justify-content: space-between;
.el-input {
width: 49%;
}
&:before,
&:after {
display: none;
}
}
}
.actions {
display: flex;
justify-content: center;
.el-button {
margin: 40px 20px;
}
}
}
</style>
<template>
<section class="course-tasks">
<!-- 头部 -->
<div class="header">
<!-- 显示当前课程的名称 -->
<el-page-header @back="() => this.$router.back()" :content="addSectionForm.course_name"/>
<!-- 添加章节按钮 -->
<el-button type="primary" icon="el-icon-plus" @click="handleShowAddSection">添加章节</el-
button>
</div>
<!-- 树形控件,展示课程对应的章节信息 -->
<el-tree
:data="sections"
:props="treeProps"
v-loading="loading"
element-loading-text="数据加载中..."
>
<!-- slot-scope:代表当前树节点内容,有两个参数 data表示当前树节点, node表示当前节点状态 -->
<div class="inner" slot-scope="{ data, node }">
<span>{{ data.section_name || data.theme }}</span>
<!-- 操作按钮 -->
<span class="actions">
<!-- 编辑章节 @click.stop 阻止事件冒泡 -->
<el-button v-if="node.level == 1" size="small" @click.stop="handleEditSection(data)">编辑
</el-button>
<!-- 修改章节状态 -->
<el-button
v-if="node.level == 1"
size="small"
@click.stop="showStatus(data)"
>{{ statusMapping[data.status] }}</el-button>
</span>
</div>
</el-tree>
<!-- 树形控件,展示课程对应的章节信息 -->
<!-- 修改章节状态-对话框 -->
<el-dialog class="toggle-dialog" title="提示" :visible.sync="showStatusForm" width="30%">
<header class="toggle-header">
<i class="el-icon-info"></i>
<span>
当前状态:{{
statusForm.data && statusMapping[statusForm.data.status]
}}
</span>
<!--
在js里面&&和||作为结果时,根据决定性来进行返回
如&&,如果左边为true,那么右边决定结果,返回右边
否则左边决定结果,返回左边
若是||,左边是false,那么右边决定结果,返回右边
否则左边决定结果,返回左边
-->
</header>
<el-form label-position="right" label-width="110px" :model="statusForm">
<el-form-item label="状态变更为:">
<!-- v-model的值是字符串类型,就获得类似于:label对应的值(其中参数是该字符串类型)
否则就是v-model的值
这里只获得一次传递的值,那么后面使用时,一直是默认的,所以需要刷新页面,重置次数
@change="$forceUpdate()" 强制刷新
-->
<el-select @change="$forceUpdate()" v-model="statusForm.status" style="width: 100%">
<el-option
v-for="(item,index) in Object.keys(statusMapping)"
:key="index"
:label="statusMapping[item]"
:value="item"
></el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="showStatusForm = false">取 消</el-button>
<el-button type="primary" @click="updateStatus">确 定</el-button>
</span>
</el-dialog>
<!-- 修改章节状态 -->
<!-- 添加或修改章节 -->
<el-dialog class="add-dialog" title="章节信息" :visible.sync="showAddSection">
<el-form
label-position="right"
label-width="80px"
ref="addSectionForm"
:model="addSectionForm"
>
<el-form-item label="课程名称" prop="course_name">
<el-input v-model="addSectionForm.course_name" disabled></el-input>
</el-form-item>
<el-form-item label="章节名称" prop="section_name">
<el-input v-model="addSectionForm.section_name"></el-input>
</el-form-item>
<el-form-item label="章节描述" prop="description">
<el-input type="textarea" v-model="addSectionForm.description"></el-input>
</el-form-item>
<el-form-item label="章节排序" prop="order_num">
<el-input v-model="addSectionForm.order_num" type="number">
<template slot="append">数字控制排序,数字越大越靠后</template>
</el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="showAddSection = false">取 消</el-button>
<el-button type="primary" @click="handleAddSection">确 定</el-button>
</span>
</el-dialog>
<!-- 添加或修改章节 -->
</section>
</template>
<script>
//引入axios
import { axios } from "../utils";
export default {
name: "CourseTasks",
title: "课程结构",
data() {
//定义章节信息
const addSectionForm = {
course_id: undefined,
course_name: "",
section_name: "",
description: "",
order_num: 0
};
//章节与课时信息,树形结构
const treeProps = {
label: item => {
return item.section_name || item.theme;
},
children: "courseLessonList"
};
//定义章节状态信息
const statusMapping = {
0: "已隐藏",
1: "待更新",
2: "已更新"
};
const statusForm = {
id:undefined,
status: 0,
data:{}
};
return {
addSectionForm,
treeProps,
sections: [], //总data,即总数据
statusForm, //状态表单
statusMapping,
loading: false, //树形控件
showAddSection: false, //添加或修改章节
showStatusForm: false //状态修改
};
},
created() {
//1.显示当前页面在网站中的位置
this.$breadcrumbs = [
{ name: "Courses", text: "课程管理" },
{ text: "课程结构" }
];
//2. 从路由中获取传递的参数, 课程id
const id = this.$route.params.courseId;
if (!id) return this.redirectToError();
//3.加载课程信息
this.loadCourse(id);
//4.加载课程对应的章节与课时
this.loadChildren(id);
},
methods: {
//方法1: 加载课程信息
loadCourse(id) {
axios
.get("/courseContent", {
params: {
methodName: "findCourseById",
course_id: id
}
})
.then(resp => {
//将数据保存到表单对象中
this.addSectionForm.course_id = resp.data.id;
this.addSectionForm.course_name = resp.data.course_name;
})
.catch(error => {
this.$message.error("数据获取失败");
});
},
//方法2: 加载树(章节与课程)
loadChildren(id) {
this.loading = true;
axios
.get("/courseContent", {
params: {
methodName: "findSectionAndLessonByCourseId",
course_id: id
}
})
.then(resp => {
//将数据保存到sections里
this.sections = resp.data;
this.loading = false;
console.log(resp.data);
})
.catch(error => {
this.$message.error("数据获取失败");
this.loading = false;
});
},
//方法3: 显示添加章节表单,回显课程信息
handleShowAddSection() {
this.showAddSection = true;
},
//方法4: 添加&修改章节操作
handleAddSection() {
axios
.post("/courseContent", {
//这里传递过去的json,修改了后台的忽略大小写
methodName: "saveOrUpdateSection",
section: this.addSectionForm
})
.then(resp => {
//debugger;
//重新加载列表
return this.loadChildren(this.addSectionForm.course_id);
})
.then(() => {
//reset 重置表单
this.addSectionForm.section_name = "";
this.addSectionForm.description = "";
this.addSectionForm.order_num = 0;
this.showAddSection = false;
})
.catch(error => {
this.$message.error("操作执行失败");
this.showAddSection = false;
});
},
//方法5: 修改章节回显方法
handleEditSection(section) {
//将对应信息拷贝到addSectionForm,进行回显
//多余的键值对,则会添加到addSectionForm里面去
Object.assign(this.addSectionForm,section);
//this的当前表单开启
this.showAddSection = true;
},
//方法6: 显示章节状态
showStatus(data) {
//回显状态
this.statusForm.id = data.id;
//之所以要加上toString(),是因为状态变更那里,只识别字符串类型
//若你是number类型,那么就不会显示对应状态,而是直接显示对应数字
this.statusForm.status = data.status.toString();
this.statusForm.data = data;
//弹出框
this.showStatusForm = true;
},
//方法7: 修改章节状态
updateStatus(statusForm) {
axios
.get("/courseContent", {
params: {
methodName: "updateSectionStatus",
id:this.statusForm.id,
status:this.statusForm.status
}
})
.then(resp => {
//之所以进行改变,是因为需要set原来状态,否则修改后,不会边
this.statusForm.data.status = this.statusForm.status
this.statusForm = {};
this.showStatusForm = false;
})
.catch(error => {
this.$message.error("修改状态失败");
this.showStatusForm = false;
});
},
//跳转到错误页面
redirectToError() {
this.$router.replace({ path: "/not-found" });
}
}
};
</script>
<style lang="scss">
.course-tasks {
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
.el-tree {
margin-top: 20px;
}
.el-tree,
.el-tree__empty-block {
min-height: 200px;
}
.el-tree-node__content {
height: auto;
}
.inner {
display: flex;
flex: 1 0 0;
align-items: center;
padding: 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.actions {
margin-left: auto;
}
}
</style>
代码地址:
链接:https://pan.baidu.com/s/1Hku35l0bJOMDI7OwQtjCAQ
提取码:alsk