续 静态资源服务器
上传文件的回显
我们使用异步上传文件后,页面并不会跳转
我们可以在页面上编写一个img标签
然后让这个img标签的src属性指向刚刚上传完成的静态资源路径地址
转到portal项目
我们需要对上传代码进行细节的补充
1.application.properties配置文件中添加配置
# 上传文件回显需要的配置信息
knows.resource.path=file:F:/upload
knows.resource.host=http://localhost:8899
下面要修改SystemController来实现返回上传文件的路径
// 从application.properties文件中获得配置信息
@Value("${knows.resource.path}")
private File resourcePath;
@Value("${knows.resource.host}")
private String resourceHost;
// 上传图片必须是post
@PostMapping("/upload/file")
public String upload(MultipartFile imageFile) throws IOException {
// 我们要确定文件\图片保存的位置
// 文件保存的路径是F:/upload/年/月/日
// 先获得当前日期的字符串做路径
// path: 2022/03/11
String path = DateTimeFormatter.ofPattern("yyyy/MM/dd")
.format(LocalDateTime.now());
// 确定要上传的文件夹
// folder= F:/upload/2022/03/11
// ↓↓↓↓↓↓↓↓↓↓↓↓
File folder = new File(resourcePath, path);
// ---------------中间代码未变化 略--------------
// 执行上传
imageFile.transferTo(file);
// 为了实现回显,我们需要返回可以访问上传的图片的路径
// 我们上传的图片要想访问,需要访问静态资源服务器的路径,可能的格式如下
// http://localhost:8899/2022/03/14/xxx-xxx-xxx.jpg
// resourceHost / path / name
String url = resourceHost + "/" + path + "/" + name;
log.debug("回显图片的路径为:{}",url);
// 返回成功上传文件的静态资源服务器路径
return url;
}
最后还要在js(axios)代码中获得返回的url并将它赋值给img标签的src属性
upload.html尾部的js(axios)代码修改为
.then(function (response){
console.log(response.data);
// 将上传图片显示用的url赋值给img标签的src属性
$("#image").attr("src",response.data);
})
重启服务(重启knows-portal项目,同时保证knows-resource项目正在运行)
然后尝试在upload.html页面上传图片,点击上传后该图片应该在当前页面显示
富文本编辑器实现文件上传效果
上面章节完成了upload.html页面的异步上传和回显
下面我们最终的目标是在富文本编辑器中实现上面图片和回显
create.html页面尾部,已经存在了关于富文本编辑器的js代码
我们需要在这个js代码中编写,最终代码如下
$(document).ready(function() {
$('#summernote').summernote({
height: 300,
tabsize: 2,
lang: 'zh-CN',
placeholder: '请输入问题的详细描述...',
callbacks:{ // 回调,表示发生特定操作时,运行指定方法
// 当用户在富文本编辑器中选中图片时,运行下面方法
onImageUpload:function(files){
// 方法的参数就是用户选中的图片,也是个数组
let file=files[0];
//创建表单,将文件保存在表单
let form=new FormData();
form.append("imageFile",file);
// axios发送异步请求
axios({
url:"/upload/file",
method:"post",
data:form
}).then(function(response){
console.log(response.data);
// 创建一个Img标签
let img=new Image();
// 将img标签的src属性,设置为上传的图片的路径
img.src=response.data;
// 将这个img标签添加到富文本编辑器中以显示
$("#summernote").summernote("insertNode",img);
})
}
}
});
$('select').select2({placeholder:'请选择...'});
});
重启knows-portal项目
同时保证knows-resource项目也在启动状态
在富文本编辑器中选择图片测试
图片能够上传到指定位置并能够显示在富文本编辑器中表示一切正常
显示问题列表的状态
我们现在的学生首页中
问题列表中问题状态全部是已解决,这明显是不对的
我们应该利用question的status属性,来显示它对应的状态
- 0:未回复
- 1:已回复
- 2:已解决
我们使用v-show这个vue属性决定是否显示对应状态的图章
index_student.html页面中修改
192行附近修改为
<div class="col-md-12 col-lg-2">
<!--
当前问题的状态有三种,这个三种状态对应下面三个span
应该根据当前问题的实际状态显示其中一个
v-show可以满足上面的需求
v-show="[boolean]" 布尔类型为真就显示,布尔类型为假就隐藏
-->
<span class="badge badge-pill badge-warning"
style="display: none"
v-show="question.status==0">未回复</span>
<span class="badge badge-pill badge-info"
style="display: none"
v-show="question.status==1">已回复</span>
<span class="badge badge-pill badge-success"
v-show="question.status==2">已解决</span>
</div>
开发显示用户信息面板
用户的首页应该显示用户的一些基本状态
上面的图片显示了用户的昵称还有一些问题信息
我们针对学生的身份,要实现问题数和收藏数的功能
课上带大家完成整体流程和问题数的显示,收藏数留为作业完成
创建用户信息面板的VO类
我们需要创建一个能够保存用户信息和问题数\收藏数的类
vo包中创建UserVO类代码如下
@Data
// 支持链式set赋值
@Accessors(chain = true)
public class UserVO implements Serializable {
private Integer id;
private String username;
private String nickname;
// 问题数
private int questions;
// 收藏数
private int collections;
}
// (作业)调用方法获得当前用户的收藏数......
// 实例化UserVO对象赋值并返回
UserVO userVO=new UserVO()
.setId(user.getId())
.setUsername(user.getUsername())
.setNickname(user.getNickname())
.setQuestions(questions);
//(作业) 赋值收藏数到userVO对象
// 别忘了返回userVO
return userVO;
}
## 编写控制层代码
UserController中添加方法调用业务逻辑层方法,返回UserVO对象
```java
// 查询当前登录用户的信息面板
@GetMapping("/me")
public UserVO me(
@AuthenticationPrincipal UserDetails user){
// 调用业务逻辑层方法
UserVO userVO=userService.getUserVO(user.getUsername());
return userVO;
}
重启\启动portal项目
浏览器地址栏输入同步请求
localhost:8080/v1/users/me
Vue绑定和js代码
先来绑定页面的信息
index_student.html页面
280行附近
<!--个人信息-->
<div class="container-fluid font-weight-light"
id="userApp">
<!-- ↑↑↑↑↑↑↑ -->
<div class="card">
<h5 class="card-header"
v-text="user.nickname">陈某</h5>\
<!-- ↑↑↑↑↑↑↑ -->
<div class="card-body">
<div class="list-inline mb-1 ">
<a class="list-inline-item mx-3 my-1 text-center">
<div><strong>10</strong></div>
<div>回答</div>
</a>
<a class="list-inline-item mx-3 my-1 text-center" href="personal/myQuestion.html">
<div>
<strong v-text="user.questions">10</strong>
<!-- ↑↑↑↑↑↑↑ -->
</div>
<div>提问</div>
</a>
<a class="list-inline-item mx-3 my-1 text-center" href="personal/collect.html">
<div>
<strong v-text="user.collections">10</strong>
<!-- ↑↑↑↑↑↑↑ -->
</div>
<div>收藏</div>
</a>
<a class="list-inline-item mx-3 my-1 text-center" href="personal/task.html">
<div><strong>10</strong></div>
<div>任务</div>
</a>
</div>
</div>
</div>
</div>
下面要开始进行js代码的编写了
这是我们第一次正式编写包含Vue的js文件
为了防止我们忘记引用创建的js文件,
推荐大家先添加文件的依赖
<script src="js/utils.js"></script>
<script src="js/tags_nav.js"></script>
<script src="js/index.js"></script>
<script src="js/user_info.js"></script>
<!-- ↑↑↑↑↑↑↑ -->
</body>
然后创建这个user_info.js
代码如下
let userApp=new Vue({
el:"#userApp",
data:{
user:{}
},
methods:{
loadUserVO:function(){
axios({
url:"/v1/users/me",
method:"get"
}).then(function(response){
// 将控制器返回的UserVO对象,赋值给当前VUE声明的变量user
// then方法中调用当前Vue对象的变量必须使用Vue对象名称
// 不能使用this,因为这里的this指的是axios对象,不是Vue对象
userApp.user=response.data;
})
}
},
created:function(){
//页面加载完毕之后运行的方法
// 一般要调用methods中的方法
this.loadUserVO();
}
})
根据用户身份跳转不同首页
回顾问答流程
我们已经编写完问答流程的第一步:学生提问的功能
下面的目标是讲师回复的功能
但是在完成回复之前,需要先完成讲师登录之后显示讲师首页,才能支持之后的功能
而且我们学生和讲师登录使用同一个登录页面,这样就需要在登录时编写根据不同身份跳转不同页面的功能
业务实现步骤
具体步骤实现如下
1.我们要先编写一个查询,能够根据用户id查询出数据库关系表中这个用户拥有的所有角色
2.将查询出来的所有角色保存在Spring-Security框架的UserDetails中,以便在需要时能够从Spring-Security中获取
3.新建一个控制器\控制器方法,专门从Spring-Security中获取判断当前登录用户的角色,判断角色的种类以跳转到不同页面
数据访问层编写用户角色的查询
之前我们编写过根据用户id查询用户权限的Mapper方法(五表关联查询)
现在要添加一个根据用户id查询用户角色的Mapper方法(三表关联查询)
首先确定sql语句
SELECT r.id , r.name
FROM user u
LEFT JOIN user_role ur ON u.id=ur.user_id
LEFT JOIN role r ON r.id=ur.role_id
WHERE u.id=3
UserMapper接口中添加这个方法
// 根据用户id查询用户所有角色的方法
@Select("SELECT r.id , r.name\n" +
"FROM user u\n" +
"LEFT JOIN user_role ur ON u.id=ur.user_id\n" +
"LEFT JOIN role r ON r.id=ur.role_id\n" +
"WHERE u.id=#{id}")
List<Role> findUserRolesById(Integer userId);
将用户角色保存在Spring-Security
我们在完成登录时,主要编写了UserDetailsServiceImpl类
类中完成了所有权限的保存,现在要将当前登录用户的所有角色也保存在auth数组中
String[] auth=new String[permissions.size()];
int i=0;
for(Permission p:permissions){
auth[i]=p.getName();
i++;
}
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 查询当前登录用户所有角色,将角色也添加到auth数组中
// 根据用户id查询所有角色
List<Role> roles=userMapper.findUserRolesById(user.getId());
// 数组扩容,以保证能够保存角色信息
auth= Arrays.copyOf(auth,auth.length+roles.size());
// auth={"/add","/delete","/save","/update",null}
// 将角色信息保存到auth中
for (Role role:roles){
auth[i]=role.getName();
i++;
}
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
编写跳转首页的控制器
当前登录用户已经在Spring-Security框架中保存了角色信息
我们需要做的就是编写一个控制器方法,在方法中判断当前登录用户是讲师还是学生,以跳转对应首页
controller包中创建HomeController编写代码如下
// 本控制器的目标是根据不同用户的角色,跳转对应的首页
// 而@RestController注解针对异步请求,控制器方法返回的信息都会以字符串或json格式
// 返回给axios来处理,不会有页面跳转效果
// @Controller标记的控制器,支持我们返回字符串特定格式时,能够实现重定向到某个页面的效果
@Controller
public class HomeController {
// Spring-Security框架中角色\权限是框架设置好的类型
// 要判断具体的某个角色,建议将这个角色声明为常量类型,判断时使用
public static final GrantedAuthority STUDENT=
new SimpleGrantedAuthority("ROLE_STUDENT");
public static final GrantedAuthority TEACHER=
new SimpleGrantedAuthority("ROLE_TEACHER");
// 一般网站都是通过"localhost:8080/index.html"访问首页,
// 我们现在的项目并没有这个路径
// 我们可以将当前控制器方法设置为这个路径
// 还有等用户只输入"localhost:8080/"时,我们也可以设置访问这个控制器方法
@GetMapping(value = {"/index.html","/"})
public String index(
@AuthenticationPrincipal UserDetails user){
// 判断当前登录用户是否包含讲师角色
if(user.getAuthorities().contains(TEACHER)){
// 如果包含讲师角色,跳转到讲师首页
return "redirect:/index_teacher.html";
}else if(user.getAuthorities().contains(STUDENT)){
// 如果不包含讲师角色,包含学生角色,跳转到学生首页
return "redirect:/index_student.html";
}
// 既不是讲师也不是学生的用户暂不考虑,直接返回null
return null;
}
}
程序有一个bug
在访问login.html页面时登录老师用户,会跳转到学生首页
这个bug的原因是因为我们设置了默认情况下登录成功显示学生首页导致的
我们需要到security包中的SecurityConfig类中修改默认路径如下
.defaultSuccessUrl("/index.html") //登录成功后的页面*
就能解决这个bug了!
讲师首页复用模板
现在讲师登录可以跳转到讲师首页了
但是讲师首页上的标签列表和用户信息面板都没有显示
而且这些内容是之前编写过到了可以使用Vue模板复用的
复用标签列表
标签列表的模板文件在之前create.html页面中已经使用过
因为之前定义过,所以只需要调用模板和引用模板即可
index_teacher.html讲师首页
1.添加axios的引用
<!--引入CDN服务器的框架文件-->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
</head>
2.调用模板
<!--引入标签的导航栏-->
<div class="container-fluid">
<tags-app id="tagsApp" :tags="tags"></tags-app>
</div>
3.页面尾部添加引用
<script src="js/utils.js"></script>
<script src="js/tags_nav_temp.js"></script>
<script src="js/tags_nav.js"></script>
</body>
重启服务,登录讲师首页
观察是否显示所有标签
复用用户信息面板
复用用户信息面板是第一次执行
1.定义模板
2.调用模板
3.引用模板
在js文件夹中定义一个模板文件user_info_temp.js