一. 搭建项目前端页面环境
1. vue-admin-template-master
1. 简介
-
vue-element-admin是基于element-ui 的一套后台管理系统集成方案。
-
功能: https://panjiachen.github.io/vue-element-admin-site/zh/guide/#功能
-
GitHub地址: https://github.com/PanJiaChen/vue-element-admin
2. 安装
-
命令
# 解压压缩包 # 进入目录 cd vue-element-admin-master # 安装依赖 npm install # 启动。执行后,浏览器自动弹出并访问http://localhost:9527/ npm run dev
2. vue-admin-template
1. 简介
-
vueAdmin-template是基于vue-element-admin的一套后台管理系统基础模板(最少精简版),可作为模板进行二次开发。
-
**GitHub地址:**https://github.com/PanJiaChen/vue-admin-template
-
**建议:**你可以在
vue-admin-template
的基础上进行二次开发,把vue-element-admin
当做工具箱,想要什么功能或者组件就去vue-element-admin
那里复制过来。
2. 安装
-
命令
# 解压压缩包 # 进入目录 cd vue-admin-template-master # 安装依赖 npm install # 启动。执行后,浏览器自动弹出并访问http://localhost:9528/ # 使用dev配置文件: dev.env.js npm run dev
3. vue-admin-template结构说明
-
架构框架入口
index.html
main.js
-
前端页面环境使用框架
vue-admin-template模板 = vue + element-ui
-
框架build目录
放项目构建的脚本文件
-
config目录
index.js --> useEslint: true 修改为false
自动整理代码格式, 规则十分严格, 所以这里不使用
dev.env.js --> 开发环境, 修改访问后端接口地址
prod.env.js --> 测试环境
-
node_modules
下载的依赖
4. src目录
- api: 定义调用方法
- assets: 静态资源
- components: 插件/组件
- icons: 图标
- router: 路由
- store: 脚本文件, 一般不改
- styles: 样式文件, 一般不改
- utils: 工具类, 一般不改
- views: 视图, 具体页面
3. 实现简易登录
只是为了能登录前端页面而实现的简单功能, 后面还要重新实现包括权限控制在内的功能.
1. 修改前端的访问路径
-
修改前端中
dev.env.js
中指定的访问路径# 把登录请求地址改为本地: http://localhost:8001 BASE_API: '"http://localhost:8001"',
2. 后端实现登录功能
-
前端登录需要传递的参数
guli_web\src\store\modules\user.js
进行登录调用2个方法, Login登录操作方法, GetInfo登录后获取用户信息方法. 所以, 创建接口两个方法实现登录; 其中Login方法, 返回token值, GetInfo返回roles, name和avatar三个值
-
在后端中创建一个登录的controller
根据前端需要的参数名, 传递指定的参数
@RestController @RequestMapping("/eduservice/user") public class EduLoginController { // login @PostMapping("/login") public ReturnType login(){ // Login方法, 返回token值 return ReturnType.ok().data("token", "admin"); } // info @GetMapping("/info") public ReturnType info(){ // GetInfo返回roles, name和avatar三个值 return ReturnType.ok() .data("roles", "[admin]") .data("name", "admin") .data("avatar", "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"); } }
-
修改前端登录时的访问地址
guli_web\src\api\login.js
根据controller中指定的访问路径, 进行填写
import request from '@/utils/request' export function login(username, password) { return request({ // 当前请求地址题提交方式 // 需要和后端Controller中指定的路径保持一致 url: '/eduservice/user/login', method: 'post', data: { username, password } }) } export function getInfo(token) { return request({ // 需要和后端Controller中指定的路径保持一致 url: '/eduservice/user/info', method: 'get', params: { token } }) } export function logout() { return request({ url: '/user/logout', method: 'post' }) }
-
前端登录时遇到跨域问题
4. 跨域问题
1. 出现原因
-
通过一个地址去访问另一个地址, 这个过程中如果访问协议, IP地址和端口号三个中任何一个不一样, 都会出现跨域问题
-
前端访问的是http://localhost:9528/ , 但是后端访问的却是http://localhost:8001/ . 端口号不一样, 所以会出现跨域的问题
2. 解决方式
-
在后端接口controller中添加注解
@CrossOrigin
[最主要的方法] -
使用网关解决 [后面会讲]
5. 框架的使用
-
在router中
index.js
中添加路由guli_web\src\router\index.js
-
点击具体路由时, 显示指定页面的信息
index.js
中的component指定路由的对应页面component: () => import('@/views/dashboard/index')
-
在api文件夹中创建js文件, 定义接口地址和参数
export function login(username, password) { return request({ url: '/eduservice/user/login', method: 'post', data: { username, password } }) }
-
在创建vue页面引入js文件, 调用方法实现功能
import { getList } from '@/api/table' export default { data() { ... }, created() { ... }, methods: { ... }
二. 讲师查询功能
1. 分页查询
-
在vue目录下创建list.vue和save.vue两个页面
<!-- list.vue页面内容 --> <template> <div class="app-container"> 讲师列表 </div> </template> <!-- save.vue页面内容 --> <template> <div class="app-container"> 添加讲师 </div> </template>
-
添加路由
{ path: '/teacher', component: Layout, redirect: '/teacher/table', name: '讲师管理', meta: { title: '讲师管理', icon: 'example' }, children: [ { path: 'table', name: '讲师列表', component: () => import('@/views/edu/teacher/list'), meta: { title: '讲师列表', icon: 'table' } }, { path: 'tree', name: '添加讲师', component: () => import('@/views/edu/teacher/save'), meta: { title: '添加讲师', icon: 'tree' } } ] },
-
编写teacher.js
在api文件夹中创建teacher.js, 定义访问的接口地址
import request from '@/utils/request' export default { // 1. 讲师列表(条件查询分页) // current: 当前页; limit: 每页记录数; teacherQuery: 条件对象 getTeacherListPage(current, limit, teacherQuery){ return request({ // url: '/eduservice/teacher/pageTeacherCondition/' + current + '/' + limit, url: `/eduservice/teacher/pageTeacherCondition/${current}/${limit}`, method:'post', // teacherQuery条件对象, 后端使用Req1uestBody获取数据 // data表示把对象转换成json进行传递到接口里面 data: teacherQuery }) } }
-
list.vue
<template> <div class = "app-container"> <!-- :data="数据集" stripe: 创建带斑马纹的表格 border: 添加竖直边框 --> <el-table v-loading="listLoading" :data="list" border stripe style="width: 80%"> <!-- id --> <!-- prop属性对应对象中的键名即可填入数据 label属性来定义表格的列名 width: 对应宽度像素 min-width: 对应宽度百分比 --> <el-table-column prop="id" label="ID" min-width="10%" align="center"> <!-- 这里不是读取的数据库中存放的id, 而是通过页码计算出来的id --> <template slot-scope="scope"> {{(page - 1) * limit + scope.$index + 1}} </template> </el-table-column> <!-- 姓名 --> <el-table-column prop="name" label="姓名" min-width="10%" align="center"/> <!-- 头衔 --> <el-table-column prop="level" label="头衔" min-width="15%" align="center"> <!-- scope: 整个表格范围 scope.row: 整行 scope.row.level: 每行的level属性 ==: 判断值 ===: 判断类型和值 --> <template slot-scope="scope"> {{scope.row.level === 1? '高级讲师': '首席讲师'}} </template> </el-table-column> <!-- 资历 --> <el-table-column prop="intro" label="资历" min-width="15%" align="center" /> <!-- 添加时间 --> <el-table-column prop="gmtCreate" label="添加时间" min-width="20%" align="center" /> <!-- 排序 --> <el-table-column prop="sort" label="排序" min-width="10%" align="center" /> <!-- 操作 --> <el-table-column label="操作" min-width="20%" 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> <!-- :total:总页数 :current-page: 当前页 :page-size: 页码数 :pager-count: 显示页码数 @current-change: 页码跳转时执行的函数 layout: 表示需要显示的内容,用逗号分隔,布局元素会依次显示。 --> <el-pagination :total="total" background :current-page="page" :page-size="limit" :pager-count="7" @current-change="getList" style="text-align: right; width: 80%" layout="total, prev, pager, next, jumper"> </el-pagination> </div> </template> <script> // 引入调用teacher.js文件 import teacher from '@/api/edu/teacher' export default { // 核心代码 // 定义变量和初始值 // 方式1 // data: { // }, // 方式2 data() { return { page: 1, // 当前页 limit: 5, // 页面大小 teacherQuery: {}, // 条件封装对象 total: 0, // 总记录数 list: null // 查询之后接口返回集合 } }, // 在页面渲染之前执行 created() { this.getList() }, // 创建具体的方法, 调用teacher.js中定义的方法 methods: { // 讲师列表的方法 // 如果不传参数, 默认显示第一页的内容 // 如果传参数, 则显示指定页的内容 getList(page = 1){ this.page = page teacher.getTeacherListPage(this.page, this.limit, this.teacherQuery) .then(response => { // 请求成功 // response接口返回的数据 // console.log(response) this.list = response.data.rows this.total = response.data.total console.log(this.list) console.log(this.total) }) .catch( error => { // 请求失败 console.log(error) }) } } } </script>
-
显示效果
注意: 别忘了在后端代码中添加跨域的注解:
@CrossOrigin
2. 多条件组合查询带分页
-
在表格上面添加查询的表单
<!-- :inline="true": 一行显示 --> <el-form :inline="true" class="demo-form-inline"> <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" clearable placeholder="讲师头衔"> <el-option label="高级讲师" :value="1"></el-option> <el-option label="首席讲师" :value="2"></el-option> </el-select> </el-form-item> <el-form-item> <el-date-picker v-model="teacherQuery.begin" type="datetime" placeholder="选择开始时间" value-format="yyyy-MM-dd HH:mm:ss" default-time="00:00:00" /> </el-form-item> <el-form-item> <el-date-picker v-model="teacherQuery.end" type="datetime" placeholder="选择截止时间" value-format="yyyy-MM-dd HH:mm:ss" default-time="00:00:00" /> </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>
-
在methods中创建方法
resetData(){ // 清空表单数据 this.teacherQuery = {} // 查询所有讲师数据 this.getList() }
-
结果
三. 讲师删除功能
-
添加删除按钮, 并为按钮绑定事件
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeTeacherById()">删除</el-button>
-
在绑定事件的方法传递删除讲师的ID
scope.row.id: 获取id
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeTeacherById(scope.row.id)">删除</el-button>
-
teacher.js中编写删除讲师的函数
// 2. 删除讲师 delTeacherById(id){ return request({ url: `/eduservice/teacher/del/${id}`, method:'delete' }) }
-
调用
removeTeacherById
方法进行删除removeTeacherById(id){ this.$confirm('此操作将永久删除该讲师, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) .then(() => { // 调用删除的方法 teacher.delTeacherById(id) .then(response => { // 删除成功 // 提示信息 this.$message({ type: 'success', message: '删除成功!' }); // 回到列表页面 this.getList() }) }); }
四. 讲师添加功能
-
在teacher.js中创建添加讲师的方法
// 3. 添加讲师 addTeacher(teacher){ return request({ url: `/eduservice/teacher/addTeacher`, method: 'post', data: teacher }) }
-
save.vue
<template> <div class="app-container"> <el-form label-width="120px"> <el-form-item label="讲师名称"> <el-input v-model="teacher.name"></el-input> </el-form-item> <el-form-item label="讲师排序"> <el-input-number v-model="teacher.sort" controls-position="right" /> </el-form-item> <el-form-item label="讲师头衔"> <el-select v-model="teacher.level" clearable 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-input v-model="teacher.career"></el-input> </el-form-item> <el-form-item label="讲师简介"> <el-input v-model="teacher.intro" :rows="10" type="textarea"></el-input> </el-form-item> <el-form-item> <!-- disabled="saveBtnDisabled": 按钮是否禁用, 防止多次提交 --> <el-button type="primary" @click="saveOrUpdate()" :disabled="saveBtnDisabled">保存</el-button> <el-button @click="resetData()">重置</el-button> </el-form-item> </el-form> </div> </template> <script> // 引入调用teacher.js文件 import teacher from '@/api/edu/teacher' export default { data(){ return { // 封装初始化值 teacher: { name: '', sort: 0, level: 1, career: '', intro: '', avatar: '' }, saveBtnDisabled: false } }, methods: { saveOrUpdate(){ // 添加 this.saveTeacher() }, // 添加讲师 saveTeacher(){ teacher.addTeacher(this.teacher) .then(response => { // 1. 提示信息 this.$message({ type: 'success', message: '添加成功' }) // 2. 回到列表页面, 路由跳转 this.$router.push({path: '/teacher/list'}) }) .catch(error => { console.log(error + "添加失败") }) }, resetData(){ this.teacher = {} } } } </script>
五. 讲师修改功能
1. 在router中添加编辑讲师的路由
-
代码
{ // :id相当于是占位符 path: 'edit/:id', name: '编辑讲师', component: () => import('@/views/edu/teacher/save'), meta: { title: '编辑讲师', noCache: true }, // 隐藏不显示 hidden: true }
-
截图
2. list.vue页面添加一个修改按钮, 并通过路由方法跳转
-
代码
<!-- 路由方法 --> <!-- 这里的路径要和router中指定的路由保持一致 --> <router-link :to="'/teacher/edit/' + scope.row.id"> <el-button type="primary" size="mini" icon="el-icon-edit">修改</el-button> </router-link>
-
截图
3. 在表单页面实现数据回显
-
在
teacher.js
中定义根据id查询的接口// 4. 通过id查询讲师(用于修改) getTeacherInfo(id) { return request({ url: `/eduservice/teacher/getTeacher/${id}`, method: 'get' }) }
-
在
save.vue
页面中执行, 通过id查询出讲师信息的方法// 根据讲师id查询 getInfo(id){ teacher.getTeacherInfo(id) .then(response => { // 将查询到的讲师信息, 赋值给teacher this.teacher = response.data.teacher }) }
-
调用
因为添加和修改都使用save页面,
区别添加还是修改:
添加时路径中没有id, 不调用回显
修改时路径中有id, 调用回显
所以可通过判断路径中是否包含id来判断是否调用回显
在save.vue中创建created方法, 当路径中包含id时, 创建调用之前定义的方法
created(){ if(this.$route.params && this.$route.params.id) { // 得到路由中的id参数值 const id = this.$route.params.id this.getInfo(id) } },
4. 修改讲师信息
-
在
teacher.js
中创建修改讲师的方法// 5. 修改讲师 updateTeacherInfo(teacher) { return request({ url: `/eduservice/teacher/updateTeacher`, method: 'post', data: teacher }) }
-
在
save.vue
中创建执行修改操作// 修改讲师 updateTeacher(){ teacher.updateTeacherInfo(this.teacher) .then(response => { // 1. 提示信息 this.$message({ type: 'success', message: '修改成功' }) // 2. 回到列表页面, 路由跳转 this.$router.push({path: '/teacher/list'}) }) }
-
表单提交时判断是添加讲师还是修改讲师
saveOrUpdate(){ // 判断是修改还是添加 // 根据teacher中是否有id, 来判断是修改还是添加 if(!this.teacher.id){ this.saveTeacher() } else { this.updateTeacher() } },
5. 存在的问题
-
问题
在修改页面时, 点击添加页面, 虽然路径变成了添加的路径, 但是表单中依然保留着修改页面中查询出来的值
-
错误的解决办法(如果时添加讲师, 则在created中将teacher清空)
created(){ // 如果路径中有id, 做修改 if(this.$route.params && this.$route.params.id) { // 得到路由中的id参数值 const id = this.$route.params.id this.getInfo(id) } else { // 路径中没有id, 做添加 this.teacher = {} } },
-
错误原因
多次路由跳转到同一页面了, 在页面中
created
方法只会在第一次执行, 后面再进行跳转并不会执行
5. 最终的解决方法(监听)
当路由发生变化时, 监听方法就会被执行
-
将上述的create的函数体抽取出来, 定义再methods中
init(){ // 如果路径中有id, 做修改 if(this.$route.params && this.$route.params.id) { // 得到路由中的id参数值 const id = this.$route.params.id this.getInfo(id) } else { // 路径中没有id, 做添加 this.teacher = { } },
-
在created中调用
created(){ this.init() },
-
在监听方法中调用
// 监听 watch: { // 路由变化的方式 // 当路由发生变化时, 方法就会执行 $route(to, from) { this.init() } },
六. 上传头像到阿里云OSS
准备工作: 创建操作阿里云OSS许可证(阿里云颁发id和密钥)
1. 新建云存储微服务
1. 创建service_oss
模块
-
在
service
模块中创建service_oss
子模块 -
添加依赖
https://help.aliyun.com/document_detail/32009.html?spm=a2c4g.11186623.6.939.aeb746a1bEOIBh
<!-- 阿里云OSS依赖 --> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> </dependency> <!-- 日期工具栏依赖 --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> </dependency>
-
创建application.properties配置文件
在 https://usercenter.console.aliyun.com/#/manage/ak里查看自己AccessKey信息
# 服务端口 server.port=8002 # 服务名 spring.application.name=service_oss # 环境设置: dev, test, prod spring.profiles.active=dev # 阿里云OSS # 前后都不能有多余的空格 # 不同的服务器, 地址不同 # 地域节点 aliyun.oss.file.endpoint=oss-cn-hangzhou.aliyuncs.com aliyun.oss.file.keyid=LTAI4GEp9bvmCBpFukScHVAv aliyun.oss.file.keysecret=6osNI3UUgGrN96exJCzREMtPNn37zc # bucket可以在控制台创建, 也可以使用java代码创建 aliyun.oss.file.bucketname=guli-avatar-warehouse
2. 启动模块
-
效果
-
原因
因为现在的模块不需要操作数据库, 只做上传到OSS功能, 没有配置数据库
-
解决方法
在启动类上面添加属性, 默认不去加载数据库配置
// exclude = DataSourceAutoConfiguration.class: 不自动加载数据源 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class OssApplication { public static void main(String[] args) { SpringApplication.run(OssApplication.class, args); } }
2. 实现文件上传
1. 从配置文件读取常量
-
在
com.hjf.utils
包下创建一个创建常量读取工具类:ConstantPropertiesUtils
package com.hjf.utils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; // 常量类,读取配置文件application.properties中的配置 @Component // 用spring的 InitializingBean 的 afterPropertiesSet 来初始化配置信息,这个方法将在所有的属性被初始化后调用。 public class ConstantPropertiesUtils implements InitializingBean { // 使用@Value读取application.properties里的配置内容 @Value("${aliyun.oss.file.endpoint}") private String endpoint; // 地域节点 @Value("${aliyun.oss.file.keyid}") private String keyId; @Value("${aliyun.oss.file.keysecret}") private String keySecret; // 密钥 @Value("${aliyun.oss.file.bucketname}") private String bucketName; // 定义公开的静态常量 public static String END_POINT; public static String KEY_ID; public static String KEY_SECRET; public static String BUCKET_NAME; // 属性值被初始化后, 会执行此方法 @Override public void afterPropertiesSet() throws Exception { END_POINT = endpoint; KEY_ID = keyId; KEY_SECRET = keySecret; BUCKET_NAME = bucketName; } }
2. 实现Service层
-
OssService
public interface OssService { // 上传头像到OSS public String uploadFileAvatar(MultipartFile file); }
-
OssServiceImpl
https://help.aliyun.com/document_detail/84781.htm?spm=a2c4g.11186623.2.2.5d437a74XlvD2f#concept-84781-zh
@Service public class OssServiceImpl implements OssService { @Override public String uploadFileAvatar(MultipartFile file) { // 工具类获取值 String endpoint = ConstantPropertiesUtils.END_POINT; String accessKeyId = ConstantPropertiesUtils.KEY_ID; String accessKeySecret = ConstantPropertiesUtils.KEY_SECRET; String bucketName = ConstantPropertiesUtils.BUCKET_NAME; try { // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); // 获取上传文件输入流。 InputStream inputStream = file.getInputStream(); // 获取文件名称 String fileName = file.getOriginalFilename(); /** * 调用oss方法实现上传 * 1. 第一个参数: Bucket名称 * 2. 第二个参数: 上传到oss文件路径和文件名称 * 3. 第三个参数: 上传文件输入流 */ ossClient.putObject(bucketName, fileName, inputStream); // 关闭OSSClient。 ossClient.shutdown(); // 把上传之后的文件路径返回 // 需要把上传到阿里云OSS路径手动拼接起来 // https://guli-avatar-warehouse.oss-cn-hangzhou.aliyuncs.com/01.png String url = "https://" + bucketName + "." + endpoint + "/" + fileName; return url; } catch (IOException e) { e.printStackTrace(); return ""; } } }
3. 实现controller层
-
代码
@RestController @RequestMapping("/eduoss/fileoss") @CrossOrigin public class OssController { @Autowired private OssService ossService; // 上传头像 @PostMapping public ReturnType uploadOssFile(MultipartFile file){ // 获取上传的文件 // 返回上传到OSS的路径 String url = ossService.uploadFileAvatar(file); return ReturnType.ok().data("url", url); } }
5. 效果
-
通过swagger上传图片
-
上传到阿里云OSS
6. 存在的问题
-
多次上传相同名称的文件时, 后面上传的文件会覆盖之前同名的文件
在文件名称添加随机唯一值, 让每个文件名称不同
-
把文件进行分类管理
根据日期进行分类(根据年月日)
-
实现
// 1. 在文件名称里面添加随机唯一的的值 String uuid = UUID.randomUUID().toString().replace("-", ""); // 2. 把文件按照日期进行分类 // 获取当前日期: 2020/10/06 String datePath = new DateTime().toString("yyyy/MM/dd"); // 拼接 fileName = datePath + "/" + uuid + fileName;
3. 前端实现
1. 创建上传组件,
在添加讲师页面, 创建上传组件, 实现上传
-
复制源码中的
ImageCropper
和PanThumb
组件到src/components
中
-
添加讲师页面使用这个上传组件
<!-- 讲师头像 TODO --> <el-form-item label="讲师头像"> <!-- 头衔缩略图 --> <pan-thumb :image="teacher.avatar"/> <!-- 文件上传按钮 --> <el-button type="primary" icon="el-icon-upload" @click="imagecropperShow=true">更换头像 </el-button> <!-- v-show:是否显示上传组件 :key:类似于id,如果一个页面多个图片上传控件,可以做区分 :url:后台上传的url地址 field="file": 文件上传 @close:关闭上传组件 @crop-upload-success:上传成功后的回调 --> <image-cropper v-show="imagecropperShow" :width="300" :height="300" :key="imagecropperKey" :url="BASE_API+'/admin/oss/file/upload'" field="file" @close="close" @crop-upload-success="cropSuccess"/> </el-form-item>
2. 使用上传组件
-
data()中定义变量和赋初始值
saveBtnDisabled: false, // 上传弹框组件是否显示 imagecropperShow: false, // 上传组件唯一标识 imagecropperKey: 0, // 获取dev.env.js里面的地址 BASE_API: process.env.BASE_API,
-
引入组件
// 引入组件 import ImageCropper from '@/components/ImageCropper' import PanThumb from '@/components/PanThumb'
-
声明组件
// 声明组件 components: { ImageCropper, PanThumb },
-
修改上传接口地址
-
关闭弹窗的方法
// 关闭上传弹框的方法 close(){ this.imagecropperShow = false },
-
上传成功的方法
// 上传成功方法 cropSuccess(data){ this.close() // 上传之后接口返回图片地址 this.teacher.avatar = data.url }
3. 存在的问题
-
问题
点击更换头像后, 如果要再次更换头像, 此时之前选中的头像会直接上传到阿里云的OSS, 然后再次点击更换头像, 才能生效
-
原因
因为上传组件的唯一标识
imagecropperShow
没有更改的原因 -
解决方法
每次关闭弹窗后(调用close方法时), 都修改唯一标识
这里选择每次调用close时就将
imagecropperShow
+ 1close(){ this.imagecropperShow = false this.imagecropperKey = this.imagecropperKey + 1 },
七. Nginx
1. 功能
- 请求转发
- 负载均衡
- 动静分离
2. 配置
-
修改nginx默认端口号 80 --> 81
server { # listen 80; listen 81; server_name localhost;
-
配置nginx转发规则
server { # 对外监听端口 listen 9001; # 主机地址 server_name localhost; # 匹配路径 location ~ /eduservice/ { # 转发服务器地址 proxy_pass http://localhost:8001; } location ~ /eduoss/ { proxy_pass http://localhost:8002; } }
-
将dev.env.js中BASE_API改为nginx的地址
3. 启动项目
-
登录
-
修改