谷粒学院
搭建项目前端页面环境
一、vue-element-admin
1、简介
而vue-element-admin是基于element-ui
的一套后台管理系统集成方案。
功能:https://panjiachen.github.io/vue-element-admin-site/zh/guide/#%E5%8A%9F%E8%83%BD
GitHub地址:https://github.com/PanJiaChen/vue-element-admin
项目在线预览:https://panjiachen.gitee.io/vue-element-admin/#/login?redirect=%2Fdashboard
2、安装
# 解压压缩包
# 进入目录
cd vue-element-admin-master
# 安装依赖,安装后会出现node_modules的文件夹,里面存放依赖
npm install
# 启动。执行后,浏览器自动弹出并访问http://localhost:9527/
npm run dev
阿昌遇到的问题:
这里我的npm install
一直报错,所有我直接去找老师的依赖直接导入 不下载了,然后通过npm run dev
发现也报错,说无法编译,不支持window64位的Node.js 10.x,我上网找了答案如下:
首先在项目目录下依次安装以下文件:
npm install node-sass
npm i node-sass -D
如果继续报错,会提醒安装element-ui就可以运行项目了,我们继续在项目目录下安装它就好了:
npm install --save element-ui
以上操作后项目可以通过npm run dev
运行起来
此步已经整完了后台系统前端项目的环境搭建。
二、项目的创建和基本配置
1、创建项目
将vue-admin-template-master重命名为guli-admin
2、修改项目信息
package.json
{
"name": "guli-admin",
......
"description": "谷粒学院后台管理系统",
"author": "achang <[email protected]>",
......
}
3、如果需要修改端口号
config/index.js 中修改
port: 9528
4、不使用Eslint语法检查
5、修改后端接口访问地址
6、项目的目录结构
.
├── build // 构建脚本
├── config // 全局配置
├── node_modules // 项目依赖模块
├── src //项目源代码
├── static // 静态资源
└── package.jspon // 项目信息和依赖配置
src
├── api // 各种接口
├── assets // 图片等资源
├── components // 各种公共组件,非公共组件在各自view下维护
├── icons //svg icon
├── router // 路由表
├── store // 存储
├── styles // 各种样式
├── utils // 公共工具,非公共工具,在各自view下维护
├── views // 各种layout
├── App.vue //***项目顶层组件***
├── main.js //***项目入口文件***
└── permission.js //认证入口
7、运行项目
npm run dev
三、登录页修改
src/views/login/index.vue(登录组件)
<h3 class="title">阿昌之谷粒学院后台管理系统</h3>
<el-button :loading="loading" type="primary" style="width:100%;"
@click.native.prevent="handleLogin">
登录
</el-button>
四、页面零星修改
1、国际化设置
打开 src/main.js(项目的js入口),第7行,修改语言为 zh-CN,使用中文语言环境,例如:日期时间组件
2、导航栏文字
src/views/layout/components(当前项目的布局组件)
src/views/layout/components/Navbar.vue
13行
<el-dropdown-item>
首页
</el-dropdown-item>
17 行
<span style="display:block;" @click="logout">登出</span>
后台系统登录功能改造
一、模拟登录
- 注意下面的https和http,我们这里使用http,不是https;
- https是需要相关部门认证才可以使用
- 后端根据这个返回下面的(1)、(2)
- 【开发接口】创建controller/EduLoginController
@RestController
@RequestMapping("/eduService/user")
public class EduLoginController {
//login
@PostMapping("/login")
public R login(){
return R.ok().data("token","admin");
}
//info
@GetMapping("/info")
public R info(){
return R.ok().data("roles","admin").data("name","阿昌").data("avatar","http://www.weixintouxiang.cn/weixin/20140607090832328.gif");
}
}
- 修改前端地址,api/login.js修改为上面的接口路径
- 重启前端服务器,开启后端服务器,测试
- 出现了【跨域问题】
什么是跨域问题:
通过一个地址去访问另外一个地址,这个过程中如果有三个地方任何不一样
哪三个地方:访问协议、ip地址、端口号
跨域解决方式:
- 后端的EduLoginController类上加上
@CrossOrigin
@RestController
@RequestMapping("/eduService/user")
@CrossOrigin //解决跨域问题
public class EduLoginController {
...
}
- 再测试访问
- 发现一个现象
他的请求都发送两次是为什么?
他第一次发送的请求方式是:OPTIONS
,他测试服务器是否连通,不会获取数据
第二次请求就会是发送数据,获取数据
二、前端框架使用过程
三、讲师列表的前端实现
1、添加路由
在src/router/index.js中,复制一份example做代码修改
{
//地址输入
path: '/teacher',
component: Layout,
//redirect:重定向地址
redirect: '/teacher/table',
name: '讲师管理',
//title:显示标签 , icon:显示图标
meta: {
title: '讲师管理', icon: 'example' },
children: [
{
path: 'table',
name: '讲师列表',
component: () => import('@/views/table/index'),
meta: {
title: '讲师列表', icon: 'table' }
},
{
path: 'save',
name: '添加讲师',
component: () => import('@/views/tree/index'),
meta: {
title: '添加讲师', icon: 'tree' }
}
]
},
2、创建路由对应的组件
创建src/views/edu/teacher文件夹存放组件,并创建list.vue和save.vue
将对应的路由设置为对应的组件页面
3、定义访问的接口地址
在src/api文件创建teacher/teacher.js定义访问的接口地址
import request from '@/utils/request' //引入已经封装好的axios 和 拦截器
export default{
//1、讲师列表(多条件分页查询)
//page:当前页,limit:每页记录数,teacherQuery:条件对象
getTeacherListPage(page,limit,teacherQuery){
return request({
// url: '/eduservice/edu-teacher/pageTeacherCondition/'+page+'/'+limit,
url: `/eduservice/edu-teacher/pageTeacherCondition/${
page}/${
limit}`,
method: 'post',
//teacherQuery条件对象,如果后端使用RequestBody获取数据
//data表示把对象转换成json对象进行传递到接口里
data: teacherQuery
})
}
}
4、组件页面调用定义接口方法
在讲师列表页面list.vue页面调用定义的接口方法,得到接口返回数据
.<template>
<div class="app-container">讲师列表</div>
</template>
<script>
//引入要调用的方法,teacher.js文件
import teacher from "@/api/teacher/teacher.js";
export default {
//写核心代码位置
data() {
//1、定义变量和初始值
return {
list: null, //查询之后给接口返回的数据装的集合
page: 1, //当前页
limit: 10, //每页显示记录数
teacherQuery: {
}, //条件封装对象
total: 0, //总记录数
};
},
created() {
//页面渲染之前执行,调用method定义的方法
//调用
this.getList();
},
methods: {
//调用具体的方法,调用teacher.js定义的方法
//讲师列表的方法
getList() {
teacher
.getTeacherListPage(this.page, this.limit, this.teacherQuery)
.then((resp) => {
//resp接口返回的数据
// console.log(resp);
this.list = resp.data.rows;
console.log(this.list);
this.total = resp.data.total;
console.log(this.total);
}) //请求成功
.catch((err) => {
console.log(err);
}); //请求失败
},
},
};
</script>
<style></style>
5、测试
6、使用element-ui快速构建页面
.<template>
<div>
<el-table
:data="list"
style="width: 100%"
border
fit
highlight-current-row
element-loading-text="数据加载中"
v-loading="listLoading"
>
<el-table-column prop="date" label="序号" width="70" align="center">
<template slot-scope="scope">
{
{ (page - 1) * limit + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="name" label="名称" width="80"> </el-table-column>
<el-table-column label="头衔" width="80">
<template slot-scope="scope">
{
{ scope.row.level === 1 ? "高级讲师" : "首席讲师" }}
</template>
</el-table-column>
<el-table-column prop="intro" label="资历" />
<el-table-column prop="gmtCreate" label="添加时间" width="160" />
<el-table-column prop="sort" label="排序" width="60" />
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<router-link :to="'/edu/teacher/edit/' + scope.row.id">
<el-button type="primary" size="mini" icon="el-icon-edit"
>修改</el-button
>
</router-link>
<el-button
type="danger"
size="mini"
icon="el-icon-delete"
@click="removeDataById(scope.row.id)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</div>
</template>
7、分页功能实现
- getList方法的修改
<!--分页组件-->
<el-pagination
background
layout="prev, pager, next,total,jumper"
:total="total"
:page-size="limit"
style="padding: 30px 0; text-align: center"
:current-page="page"
@current-change="getList"
>
</el-pagination>
8、讲师条件查询
<!--多条件查询表单-->
<el-form
:inline="true"
class="demo-form-inline"
style="margin-left: 20px; margin-top: 12px;"
>
<el-form-item label="名称">
<el-input
v-model="teacherQuery.name"
placeholder="请输入名称"
></el-input>
</el-form-item>
<el-form-item label="级别">
<el-select v-model="teacherQuery.level" placeholder="讲师头衔">
<el-option label="高级讲师" :value="1"></el-option>
<el-option label="特级讲师" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="添加时间">
<el-time-picker
placeholder="选择开始时间"
v-model="teacherQuery.begin"
value-format="yyyy-MM-dd HH:mm:ss"
default-time="00:00:00"
type="datetime"
></el-time-picker>
</el-form-item>
<el-form-item>
<el-time-picker
placeholder="选择截止时间"
v-model="teacherQuery.end"
value-format="yyyy-MM-dd HH:mm:ss"
default-time="00:00:00"
type="datetime"
></el-time-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="getList()"
>查询</el-button
>
<el-button type="default" @click="resetData()">清空</el-button>
</el-form-item>
</el-form>
方法调用:
data() {
return {
...
teacherQuery: {
}, //条件封装对象
};
},
methods: {
getList(page = 1) {
this.page = page;
teacher
.getTeacherListPage(this.page, this.limit, this.teacherQuery)
.then((resp) => {
// console.log(resp);
this.list = resp.data.rows;
console.log(this.list);
this.total = resp.data.total;
console.log(this.total);
})
.catch((err) => {
console.log(err);
});
},
//清空方法
resetData() {
//表单输入项数据清空
this.teacherQuery = {
};
//查询所有讲师数据
this.getList();
},
},
9、讲师删除
1)定义api:
src/api/teacher/teacher.js
export default{
...
//根据id删除讲师
removeById(id){
return request({
url: `/eduservice/edu-teacher/deleteTeacherById/${
id}`,
method: 'delete',
})
}
}
2)定义methods:
src/views/edu/teacher/list.vue
使用MessageBox 弹框组件
removeById(id) {
this.$confirm("此操作将永久删除该讲师记录, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
//点击确定,执行then方法
teacher.removeById(id)
.then((resp) => {
//删除成功
//提示信息
this.$message({
type: "success",
message: "删除成功!",
});
//刷新页面
this.getList();
});
});
}
- 测试
10、讲师添加
1)、定义api方法:
src/api/teacher/teacher.js
//新增讲师
saveTeacher(teacher){
return request({
url: `/eduservice/edu-teacher/save`,
method: `post`,
data: teacher
})
}
2、初始化组件:
src/views/edu/teacher/save.vue
- html:
.<template>
<div class="app-container">
<el-form label-width="120px">
<el-form-item label="讲师名称">
<el-input v-model="teacher.name" />
</el-form-item>
<el-form-item label="讲师排序">
<el-input-number
v-model="teacher.sort"
controls-position="right"
min="0"
/>
</el-form-item>
<el-form-item label="讲师头衔">
<el-select v-model="teacher.level" clearable placeholder="选择讲师头衔">
<!--
数据类型一定要和取出的json中的一致,否则没法回填
因此,这里value使用动态绑定的值,保证其数据类型是number
-->
<el-option :value="1" label="高级讲师" />
<el-option :value="2" label="首席讲师" />
</el-select>
</el-form-item>
<el-form-item label="讲师资历">
<el-input v-model="teacher.career" />
</el-form-item>
<el-form-item label="讲师简介">
<el-input v-model="teacher.intro" :rows="10" type="textarea" />
</el-form-item>
<!-- 讲师头像:TODO -->
<el-form-item>
<el-button
:disabled="saveBtnDisabled"
type="primary"
@click="saveOrUpdate"
>保存</el-button
>
</el-form-item>
</el-form>
</div>
</template>
- js:
<script>
//引入对应的新增api方法
import teacherApi from "@/api/teacher/teacher.js";
export default {
data() {
return {
teacher: {
name: "",
sort: 0,
level: 1,
career: "",
intro: "",
avatar: "",
},
saveBtnDisabled: false, // 保存按钮是否禁用,
};
},
methods: {
saveOrUpdate() {
this.saveBtnDisabled = true;
this.saveData();
},
// 保存
saveData() {
teacherApi.saveTeacher(this.teacher).then((resp) => {
//添加成功
//提示信息
this.$message({
message: "添加讲师记录成功",
type: "success",
});
//回到讲师列表 路由跳转
this.$router.push({
path: '/teacher/table'})
});
},
},
};
</script>
- 后端按创建时间排序
eduservice/service/impl/EduTeacherServiceImpl
//排序
wrapper.orderByDesc("gmt_create");
- 测试
11、讲师信息回显
添加一个隐藏路由:
src/router/index.js,/teacher路由的子路由没让他去save.vue添加讲师组件
{
path: 'edit/:id',
name: 'EduTeacherEdit',
component: () => import('@/views/edu/teacher/save.vue'),
meta: {
title: '编辑讲师', noCache: true },
hidden: true
}
- 通过router-link 的:to去’/teacher/edit/【:id携带id】'也就是上面这个路由,然后路由转换到save.vue组件上
在表单页面实现数据回显:
- 定义根据id查询讲师信息的api方法
//根据id查询讲师
updateById(id){
return request({
url: `/eduservice/edu-teacher/getById/${
id}`,
method: `get`,
})
}
- src/edu/teacher/save.vue
//根据id查询,数据回显
getInfoById(id){
teacherApi.updateById(id)
.then(resp =>{
this.teacher = resp.data.item
})
}
- src/edu/teacher/save.vue 中created()数据渲染前进行判断请求中是否有id值
created() {
//在页面渲染之前
//判断路径中是否有id值
if(this.$route.params && this.$route.params.id){
//从路径中获取id值
const id = this.$route.params.id
//调用根据id查询的方法
this.getInfoById(id)
}
}
- 数据回显
12、讲师修改
- guli-admin\src\api\teacher\teacher.js,定义api讲师修改方法
//修改讲师信息
updateTeacherInfo(teacher){
return request({
url: `/eduservice/edu-teacher/updateById`,
method: `post`,
data: teacher
})
}
- guli-admin\src\views\edu\teacher\save.vue,修改方法
//修改讲师的方法
updateTeacher() {
teacherApi.updateTeacherInfo(this.teacher).then((resp) => {
//提示信息
this.$message({
message: "修改讲师记录成功",
type: "success",
});
//回到讲师列表 路由跳转
this.$router.push({
path: "/teacher/table" });
});
}
- 将修改和新增方法合一,进行判断
saveOrUpdate() {
//判断修改还是新增操作
//根据teacher对象是否有id值来判断
if (!this.teacher.id) {
//没有id值,做【新增操作】
this.saveBtnDisabled = true;
this.saveData();
}else{
//有id值,做【修改操作】
this.updateTeacher()
}
}
阿昌遇到的问题:
一直报跨域问题,检查发现后端EduTeacherController的@PostMapping()中的地址后面多加了/,一直报错跨域问题
13、存在BUG问题
- 解决方法
通过使用监听器监听
将原本created()里面的发按法封装到init()中,在created()中调用,并通过监听器监听路由变化就执行init()判断路径是否有id值,有就获取id值,没有就清空数据;
$route(to, from)
固定写法,意思为监听路由变化
...
init() {
//判断路径中是否有id值
if (this.$route.params && this.$route.params.id) {
//从路径中获取id值
const id = this.$route.params.id;
//调用根据id查询的方法
this.getInfoById(id);
}else{
this.teacher = {
};
}
},
},
created() {
//在页面渲染之前
this.init()
},
watch: {
$route(to, from) {
//路由变化方式,当路由发送变化,方法就执行
console.log("watch $route");
this.init()
},
}
至此,讲师管理模块的前端增删改查功能完成