网站首页 > 基础教程 正文
项目前后端接口联调
联调准备
运行后台项目
clean 清空项目的编译文件
compile 重新编译项目
将项目部署到 Tomcat:项目名为 lagou_edu_home,端口号: 8080,使用 war 方式部署
部署图片上传路径为 Tomcat 的 webapps 目录下的 upload 目录
最后运行前端项目
首先导入前端项目到 VS Code
运行项目
课程管理首页
Courses.vue 的视图部分代码
<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>
获取课程列表
Courses.vue JS 部分代码
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.loading = false; // 关闭加载
this.courses = resp.data; // 取出数据
})
.catch(error => {
this.$message.error("数据获取失败! ! !");
});
},
}
};
条件查询课程信息
Courses.vue JS 代码的 methods
//方法 2: 条件查询课程信息
filterQuery() {
this.loading = true;
// 定义查询条件对象
const search = {};
// 保存用户输入的条件
if (this.filter.course_name) search.course_name = this.filter.course_name;
if (this.filter.status) search.status = this.filter.status;
// 请求后台条件查询接口
return axios
.get("/course", {
// 准备参数
params: {
methodName: "findByCourseNameAndStatus",
course_name: search.course_name,
status: search.status
}
})
.then(resp => {
console.log(resp);
this.loading = false;
// 将响应数据保存到 courses
this.courses = resp.data;
})
.catch(error => {
this.$message.error("数据获取失败! ! !");
});
},
跳转到新建课程页面
Courses.vue JS 代码的 methods
// 方法 3: 添加课程跳转方法
addCourse() {
// 路由跳转到 CourseItem.vue 组件
this.$router.push({ name: "CourseItem", params: { courseId: "new" } });
},
修改课程状态
Courses.vue JS 代码的 methods
// 方法 4: 修改课程状态
updateStatus(item) {
// item 表示选中的数据 = Course 对象
axios
.get("/course", {
params: {
methodName: "updateCourseStatus",
id: item.id
}
})
.then(res => {
console.log(res);
// 将返回的状态字段,封装到对象中
Object.assign(item, res.data);
// 重新加载页面
window.location.reload;
});
},
跳转课程营销或内容管理
Courses.vue JS 代码的 methods
// 方法 5: 根据路由名称,导航到对应组件
handleNavigate(name, id) {
this.$router.push({ name, params: { courseId: id } });
},
新建 & 修改课程
Course 组件中的跳转方法
<!-- 营销信息按钮 -->
<el-button
size="mini"
@click="handleNavigate('CourseItem', scope.row.id)"
>营销信息</el-button>
// 方法 3: 添加课程跳转方法
addCourse() {
// 路由跳转到 CourseItem.vue 组件
this.$router.push({ name: "CourseItem", params: { courseId: "new" } });
},
router.js 路由
找到 name 为: CourseItem 的路由
// 添加课程的路由
{
path: "/courses/:courseId", // 路径,携带参数: 课程 ID
name: "CourseItem",
// 路由导航到的组件
component: () =>
import(/* webpackChunkName: 'courses' */ "../views/CourseItem.vue")
},
CourseItem 组件
CourseItem.vue 的视图部分代码
<template>
<section class="course-item">
<!-- 头部 -->
<div class="header">
<!-- 返回上一页 -->
<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"
>
<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>
CourseItem.vue 的 JS 部分代码
data() {
// 数据
return {
rules, //规则
course: {}, //课程
loading: false,
params: {} //参数对象
};
},
// 钩子函数
created() {
// 1.显示当前页面在网站中的位置
this.$breadcrumbs = [
{ name: "Courses", text: "课程管理" },
{ text: "营销信息" }
];
?
// 2.从路由中获取传递的参数, 课程 id
const id = this.$route.params.courseId;
?
// 3.判断 id 是否有值
if (!id) return this.redirectToError();
?
// 4.判断是 new 还是具体的id
if (id === "new") {
// new 代表新增
this.course.title = "新增课程";
} else {
// 否则就是修改
this.loadCourse(id);
}
},
图片上传分析
页面部分
<!-- 上传图片部分 -->
<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"
>
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-input>
</el-form-item>
FormData 使用演示
// 通过 FormData 构造函数创建一个空对象
var formdata = new FormData();
// 可以通过 append() 方法来追加数据
formdata.append("name","laotie");
// 通过 get 方法对值进行读取
console.log(formdata.get("name")); // laotie
// 通过 set 方法对值进行设置
formdata.set("name","laoliu");
console.log(formdata.get("name")); // laoliu
- 将 form 表单元素的 name 与 value 进行组合,实现表单数据的序列化,从而减少表单元素的拼接,提高工作效率。
- 异步上传文件
- 创建 FormData 对象
CourseItem.vue 的 JS 的 methods的 created 部分的代码
//5.创建FormData对象,将图片与表单一同上传
this.params = new FormData();
methods 添加方法
// 文件上传
onchange(file) {
// 判断文件不为空
if (file != null) {
// 将文件信息保存到 params 中
this.params.append("file", file.raw, file.name);
}
},
新建课程信息
CourseItem.vue 的 JS 的 methods 的代码
// 方法 1: 保存和修改课程信息
handleSave() {
// 检查是否拿到了正确的需要验证的 form
this.$refs.form.validate(valid => {
if (!valid) return false;
?
// 1.设置 Content-Type 为多部件上传
let config = {
headers: {
"Content-Type": "multipart/form-data"
}
};
?
// 2.获取表单中的数据,保存到 params (params 就是 FromData对象)
for (let key in this.course) {
//debugger
console.log(key + "---" + this.course[key]);
this.params.append(key, this.course[key]);
}
?
// 3.保存课程信息
axios
.post("/courseSalesInfo", this.params, config)
.then(res => {
//debugger
if (res.data.status == 0) {
// 保存成功,跳转到首页
this.$router.back();
} else if (res.data.status == 1) {
this.$message({
type: "error",
message: res.data.msg
});
}
})
.catch(err => {
this.$message.error("保存失败! ! !");
});
});
},
修改课程信息
CourseItem.vue 的 JS 的 methods 的代码
// 方法 2: 根据 ID 回显课程信
loadCourse(id) {
this.loading = true;
axios
.get("/course", {
params: {
methodName: "findCourseById",
id: id
}
})
.then(res => {
this.loading = false;
this.course = res.data;
})
.catch(() => {
this.$message.error("回显数据失败! ! !");
});
},
内容管理
Course 组件中的跳转方法
<!-- 内容管理按钮 -->
<el-button
size="mini"
@click="handleNavigate('CourseTasks', scope.row.id)">内容管理</el-button>
// 方法 5: 根据路由名称, 导航到对应组件
handleNavigate(name, id) {
this.$router.push({ name, params: { courseId: id } });
},
router.js 路由
// 内容管理的路由
{
path: "/courses/:courseId/tasks",
name: "CourseTasks",
meta: { requireAuth: true },
component: () =>
import(/* webpackChunkName: 'courses' */ "../views/CourseTasks.vue")
}
CourseTasks 组件
树形控件测试
- 打开之前编写 Element UI 的项目
- 在 component 目录下添加一个组件 TestTree.vue
<template>
</template>
<script>
export default {
?
}
</script>
<style scoped>
</style>
- 在 Index.vue 组件中的导航菜单位置添加一个树形控件导航;注意要设置 index 的路径为 “/tree”
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航菜单</span>
</template>
<el-menu-item-group>
<el-menu-item index="/course">
<i class="el-icon-menu"></i>
<span>课程管理</span>
</el-menu-item>
<el-menu-item index="/tree">
<i class="el-icon-menu"></i>
<span>树形控件</span>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
- 在 router/index.js 路由文件中进行配置,在布vue局路由中再添加一个子路由
// 导入树形控件组件
import Tree from "@/components/TestTree.vue"
?
...
{
path: "/index",
name: "index",
component: Index,
children: [
{
path: "/course",
name: "course",
component: Course
},
// Tree控件测试路由
{
path: "/tree",
name: "tree",
component: Tree
}
]
}
...
- 在 ElementUI 官网中查找树形控件
- 先来查看基础用法,赋值代码到 TestTree.vue
<template>
<el-tree :data="data" :props="defaultProps"></el-tree>
</template>
?
<script>
export default {
data() {
return {
data: [{
label: '一级 1',
children: [{
label: '二级 1-1',
children: [{
label: '三级 1-1-1'
}]
}]
}, {
label: '一级 2',
children: [{
label: '二级 2-1',
children: [{
label: '三级 2-1-1'
}]
}, {
label: '二级 2-2',
children: [{
label: '三级 2-2-1'
}]
}]
}, {
label: '一级 3',
children: [{
label: '二级 3-1',
children: [{
label: '三级 3-1-1'
}]
}, {
label: '二级 3-2',
children: [{
label: '三级 3-2-1'
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label'
}
};
},
methods: {
}
};
</script>
?
<style scoped>
</style>
- Tree 组件属性分析
data:展示数据
props:配置树形结构;label - 设置节点名称,children - 指定生成子树的属性名称
- 自定义树节点内容:data - 数据对象,node - 节点对象
<el-tree :data="data" :props="defaultProps">
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ data.label }}</span>
<span> 级别:{{node.level}}</span>
</span>
</el-tree>
- 展示树形结构章节与课时
<template>
<el-tree :data="data" :props="defaultProps">
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ data.section_name || data.theme }}</span>
<span> 级别:{{node.level}}</span>
</span>
</el-tree>
</template>
?
<script>
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 => {
return item.section_name || item.theme;
},
},
};
}
};
</script>
显示当前课程的名称
<el-page-header
@back="() => this.$router.back()"
:content="addSectionForm.course_name"
/>
data 数据
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: "lessonList"
};
?
// 定义章节状态信息
const statusMapping = {
0: "已隐藏",
1: "待更新",
2: "已更新"
};
?
const statusForm = {
status: 0
};
?
return {
addSectionForm,
treeProps,
sections: [],
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(res => {
// 将数据保存到表单对象中
this.addSectionForm.course_id = res.data.id;
this.addSectionForm.course_name = res.data.course_name;
})
.catch(error => {
this.loading = false;
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;
})
.catch(error => {
this.loading = false;
this.$message.error("数据获取失败! ! !");
});
},
Element UI 树形控件 el-tree::data 列表数据;:props 配置选项。
<!-- 树形控件, 展示课程对应的章节信息 -->
<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>
...
</div>
...
</el-tree>
:props 配置选项:label - 指定节点标签为节点对象的某个属性值;children - 指定子树为节点对象的某个属性值。
// 章节与课时信息, 树形结构
const treeProps = {
label: item => {
return item.section_name || item.theme;
},
children: "lessonList"
};
操作按钮显示:node.level 获取当前节点的级别;@click.stop 事件冒泡,点击哪个元素就执行哪个元素绑定的事件
<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="handleShowToggleStatus(data)"
>{{ statusMapping[data.status] }}</el-button>
</span>
回显信息
<el-button type="primary" icon="el-icon-plus" @click="handleShowAddSection">添加章节</el-button>
JS 代码
// 方法 3: 显示添加章节表单, 回显课程信息
handleShowAddSection() {
// 显示表单
this.showAddSection = true;
},
添加章节
<el-button type="primary" @click="handleAddSection">确 定</el-button>
JS 代码
// 方法 4: 添加&修改章节操作
handleAddSection() {
axios
.post("/courseContent", {
methodName: "saveOrUpdateSection",
section: this.addSectionForm
})
.then(resp => {
this.showAddSection = false;
// 重新加载列表
return this.loadChildren(this.addSectionForm.course_id);
})
.catch(error => {
this.loading = false;
this.$message.error("操作执行失败! ! !");
});
},
后台接口问题解决
BaseServlet 中代码修改
if("application/json;charset=utf-8".equals(contentType)) {
...
}
// 修改为:
?
if("application/json;charset=utf-8".equalsIgnoreCase(contentType)) {
...
}
CourseContentServlet 中的 saveOrUpdateSection 方法修改
// 使用 BeanUtils 的 copyProperties 方法将 map 中的数据封装到 section 对象里
BeanUtils.copyProperties(section,map.get("section"));
修改章节
<el-button v-if="node.level == 1" size="small"
@click.stop="handleEditSection(data)">编辑</el-button>
JS 回显示方法
// 方法 5: 修改章节回显方法
handleEditSection(section) {
// 对象拷贝
Object.assign(this.addSectionForm, section);
this.showAddSection = true;
},
事件冒泡:当点击子元素的事件。如果父元素也有同样的事件的话。他就会一并的触发。
解决冒泡事件的方法:@click.stop
<body>
<!-- 事件冒泡: 解决方案 @click.stop -->
<div id="app" @click="log('div点击事件')">
<button @click.stop="log('button点击事件')">事件冒泡</button>
</div>
</body>
<script src="./js/vue.min.js"></script>
<script>
var VM = new Vue({
el: "#app",
methods: {
log(t) {
alert(t);
},
},
});
</script>
章节状态回显
<!-- 章节状态按钮 -->
<el-button
v-if="node.level == 1"
size="small"
@click.stop="showStatus(data)"
>{{ statusMapping[data.status] }}</el-button>
JS 代码
// data 函数中定义的章节状态
const statusMapping = {
0: "已隐藏",
1: "待更新",
2: "已更新"
};
// 方法 6: 显示章节状态
showStatus(data) {
this.statusForm.id = data.id;
this.statusForm.status = data.status.toString();
this.statusForm.data = data;
this.showStatusForm = true;// 显示状态表单
},
Select 选择器
- 打开之前编写 Element UI 的项目
- 在 component 目录下添加一个组件 TestSelect.vue
<template></template>
<script>
export default {};
</script>
<style scoped>
</style>
- 在 Index.vue 组件中的导航菜单位置添加一个 Select 选择器导航;注意要设置 index 的路径为 /select
<el-menu-item index="/select">
<i class="el-icon-menu"></i>Select选择器
</el-menu-item>
- 在 index.js 路由文件中进行配置,在布局路由中再添加一个子路由
import Select from "@/components/TestSelect.vue"
{
path: "/select",
name: "select",
component: Select,
},
- 在 Element UI 官网中查找 Select 选择器
- 查看基础用法,将代码复制到 TestSelect.vue 中
<template>
<el-select v-model="value" placeholder="请选择">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</template>
?
<script>
export default {
data() {
return {
options: [{
value: '选项1',
label: '黄金糕'
}, {
value: '选项2',
label: '双皮奶'
}, {
value: '选项3',
label: '蚵仔煎'
}, {
value: '选项4',
label: '龙须面'
}, {
value: '选项5',
label: '北京烤鸭'
}],
value: ''
}
}
}
</script>
?
<style scoped>
</style>
- select 选择器属性分析
v-model 的值为当前被选中的 el-option 的 value 属性值
el-option 选项:label 选项的标签名;value 选择的值
- 使用 Select 选择器展示状态信息
<template>
<el-select v-model="status" placeholder="请选择">
<el-option
v-for="index in Object.keys(statusMappings)"
:key="index"
:label="statusMappings[index]"
:value="index"
></el-option>
</el-select>
</template>
?
<script>
export default {
data() {
return {
statusMappings: {
0: "已隐藏",
1: "待更新",
2: "已更新",
},
status: "",
};
},
};
</script>
?
<style scoped>
</style>
Object.keys()
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
章节状态修改
<el-button type="primary" @click="updateStatus">确 定</el-button>
JS 部分
//方法7: 修改章节状态
updateStatus(statusForm) {
axios
.get("/courseContent", {
params: {
methodName: "updateSectionStatus",
status: this.statusForm.status,
id: this.statusForm.id
}
})
.then(resp => {
this.statusForm.data.status = this.statusForm.status;
this.statusForm = {};
this.showStatusForm = false;
})
.catch(error => {
this.showStatusForm = false;
this.$message.error("修改状态失败! ! !");
});
},
v-for 里面数据层次太多, 修改过数据变了,页面没有重新渲染,需手动强制刷新。
<!-- 强制刷新 @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>
项目上线部署发布
简介
服务器与操作系统
服务器是计算机的一种,它比普通计算机运行更快、负载更高、价格更贵。
服务器从硬件上等同于电脑 PC。而服务器跟 PC 都是由 CPU、内存、主板、硬盘、电源等组成;但服务器的性能要远远超过 PC,因为它要保证全年无休。
操作系统是作为应用程序与计算机硬件之间的一个接口。
没有安装操作系统的计算机,被称为裸机, 如果想在裸机上运行自己的程序,就需要使用机器语言。
安装操作系统之后,就可以配置一些高级语言的环境,进行高级语言的开发。
Linux 系统是最具稳定性的系统。
Linux 比 Windows 更具安全性。
Linux 服务器在应用开发上更能节约成本。
项目的发布部署
项目的开发流程大致要经过一下几个步骤:
- 项目立项
- 需求分析阶段
- 原型图设计阶段
- 开发阶段
- 测试阶段
- 系统上线
后台项目部署
安装虚拟机
在 Linux 阶段已经安装过了虚拟机,使用的是 Linux 操作系统 CentOS 7 版本。
安装软件环境
以下软件在 Linux 阶段都已安装完成:
- JDK 11
- Tomcat 8.5
- MySQL 5.7
查看 Java 版本
java -version
查看 tomcat 是否能够正常启动
# 进入到 tomcat 目录
cd /usr/tomcat/
?
# 启动 tomcat
./bin/startup.sh
?
# 关闭 tomcat
./bin/shutdown.sh
关闭防火墙
#查看已经开放的端口:
firewall-cmd --list-ports
?
#开启端口
firewall-cmd --zone=public --add-port=8080/tcp --permanent
?
#命令含义:
–zone #作用域
–add-port=8080/tcp #添加端口,格式为:端口/通讯协议
–permanent #永久生效,没有此参数重启后失效
?
#重启防火墙
firewall-cmd --reload
?
# 关闭防火墙
# 停止 firewall
systemctl stop firewalld.service
# 禁止 firewall 开机启动
systemctl disable firewalld.service
# 查看默认防火墙状态(关闭后显示 notrunning,开启后显示 running)
firewall-cmd --state
登录 MySQL 检查数库连接是否正常
-- 创建用户
CREATE USER 'JiuYuan'@'%' IDENTIFIED BY 'JiuYuan@123';
?
-- 授予所有权限
GRANT ALL PRIVILEGES ON *.* TO 'JiuYuan'@'%' IDENTIFIED BY 'JiuYuan@123';
?
-- 刷新
FLUSH PRIVILEGES;
使用 SQLYog 连接 Linux 上的 MySQL,导入 SQL 脚本创建项目所需的数据库
项目打包发布
- 修改项目的数据库配置文件:数据库的 IP、用户名、密码。
- 修改 Constants 常量类中的项目 URL
// 生产环境地址
public static final String LOCAL_URL = "http://192.168.91.128:8080";
- 修改后启动项目,测试一下 保证数据库连接没有问题
- 检查 POM 文件打包方式必须是 war 编译版本为 JDK 11
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
- 执行打包命令
// 清除 target 文件夹
clean
// 打包跳过测试
package
- 复制出 target 目录下的 war 包
- 修改一下 war 包名称为 lagou_edu_home.war
- 上传到 tomcat 的 webapps 目录中启动测试,访问接口:
http://192.168.91.128:8080/lagou_edu_home/course?methodName=findCourseList
前端项目部署
修改配置文件
前端项目的配置文件有两个:一个是开发环境的配置文件,一个是生产环境的配置文件。
先修改一下开发环境文件 .env.development 的后端服务器访问地址,然后进行一下测试:
VUE_APP_API_BASE = http://192.168.91.128:8080/lagou_edu_home
修改生产环境的配置文件 .env.production:
VUE_APP_API_BASE = http://192.168.91.128:8080/lagou_edu_home
前端项目打包
修改 vue.con?g.js 配置文件
module.exports = {
// relative path for dev
publicPath: process.env.NODE_ENV === "production" ? "/edu-boss/" : "./",
// for gh-pages
indexPath: "index.html",
assetsDir: "static",
lintOnSave: process.env.NODE_ENV !== "production",
productionSourceMap: false,
css: {
// sourceMap: process.env.NODE_ENV !== 'production'
},
devServer: {
open: true,
port: 8081
}
};
执行下面的打包命令:
npm run build
在项目下会生成一个 dist 目录
在本地 Tomcat 的 webapps 目录下创建一个 edu-boss 文件夹将 dist 目录中的文件拷贝到里面
启动本地 Tomcat 访问前端项目路径为
http://localhost:8081/edu-boss/
前端项目发布
- 验证没有问题后将 edu-boss 项目压缩上传到 Tomcat 服务器
# 复制一份 Tomcat
cp -r /usr/tomcat/ /usr/tomcat2
# 上传 edu-boss.zip 并解压
unzip edu-boss.zip
# 删除 edu-boss.zip
rm -rf edu-boss.zip
- 修改 tomcat2 的 server.xml 配置文件的 3 个端口避免与 tomcat 冲突,修改结果如下:
...
<Server port="8006" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
...
<Service name="Catalina">
...
<Connector port="8081" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8444" />
...
</Service>
</Server>
- 在部署后端项目的 tomcat1 的 webapps 目录下创建一个 upload 文件夹保存图片
- 运行前端项目:
# 进入 tomcat2,启动项目
./bin/startup.sh
# 动态查看日志
tail -f logs/catalina.out
- 运行后端项目
# 进入 tomcat1,启动项目
./bin/startup.sh
# 动态查看日志
tail -f logs/catalina.out
- 前后端都启动后进行测试
猜你喜欢
- 2024-11-08 你居然只知道蓝绿发布?今天教你全链路灰度
- 2024-11-08 redis 分布式锁的 5个坑,真是又大又深
- 2024-11-08 Blazor OIDC 单点登录授权实例7 - Blazor hybird app 端授权
- 2024-11-08 Spring Boot利用filter实现xss防御
- 2024-11-08 Spring连环CVE-2015-5211和CVE-2020-5421漏洞升级教程
- 2024-11-08 如何进行权限系统设计,一文吃透 如何设计一个权限系统
- 2024-11-08 基于Spring Boot的注解驱动式公众号极速开发框架FastBootWeixin
- 2024-11-08 HTTP通讯框架选型HttpClient/OkHttp
- 2024-11-08 微信公众号自动回复功能开发 微信公众号平台自动回复功能
- 2024-11-08 Java对象复制系列三: 手把手带你写一个 Spring BeanUtils
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)