目录
4.2 编写application.properties阿里云配置文件
1、查询用户列表
1.1 查询用户列表接口实现
/**
* 分页查询用户信息
*
* @param page
* @param userQueryVo
* @return
*/
@Override
public IPage<User> findUserListByPage(IPage<User> page, UserQueryVo userQueryVo) {
//创建条件构造器对象
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
//部门编号
queryWrapper.eq(!ObjectUtils.isEmpty(userQueryVo.getDepartmentId()),"department_id",userQueryVo.getDepartmentId());
//用户名
queryWrapper.like(!ObjectUtils.isEmpty(userQueryVo.getUsername()),"username",userQueryVo.getUsername());
//真实姓名
queryWrapper.like(!ObjectUtils.isEmpty(userQueryVo.getRealName()),"real_name",userQueryVo.getRealName());
//电话
queryWrapper.like(!ObjectUtils.isEmpty(userQueryVo.getPhone()),"phone",userQueryVo.getPhone());
//查询并返回数据
return baseMapper.selectPage(page,queryWrapper);
}
/**
* 查询用户列表
* @param userQueryVo
* @return
*/
@GetMapping("/list")
public Result list(UserQueryVo userQueryVo) {
//创建分页信息
IPage<User> page = new Page<User>(userQueryVo.getPageNo(), userQueryVo.getPageSize());
//调用分页查询方法
userService.findUserListByPage(page, userQueryVo);
//返回数据
return Result.ok(page);
}
1.2 查询用户列表前端实现
<!-- 左侧部门树形菜单列表 -->
<el-aside style="padding: 10px 0px 0px 0px;background: #fff;border-right: 1px solid #dfe6ec;" width="220px">
<el-tree
style="font-size: 14px"
ref="leftTree"
:data="deptList"
node-key="id"
:props="defaultProps"
default-expand-all
empty-text="暂无数据"
:show-checkbox="false"
:highlight-current="true"
:expand-on-click-node="false"
@node-click="handleNodeClick">
<div class="custom-tree-node" slot-scope="{ node, data }">
<div>
<span v-if="data.children.length == 0">
<i class="el-icon-document"></i>
</span>
<span v-else @click.stop="changeIcon(data)">
<svg-icon v-if="data.open" icon-class="add-s"/>
<svg-icon v-else icon-class="sub-s"/>
</span>
<!-- 名称 -->
<span style="margin-left: 3px">{{ node.label }}</span>
</div>
</div>
</el-tree>
</el-aside>
<!-- 右侧用户数据 -->
<!-- 表格数据 -->
<el-main>
<!-- 查询条件 -->
<el-form :model="searchModel" ref="searchForm" label-width="80px" :inline="true" size="small">
<el-form-item>
<el-input v-model="searchModel.username" placeholder="请输入用户名"/>
</el-form-item>
<el-form-item>
<el-input v-model="searchModel.realName" placeholder="请输入真实姓名"/>
</el-form-item>
<el-form-item>
<el-input v-model="searchModel.phone" placeholder="请输入电话"/>
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="search(departmentId, pageNo, pageSize)">查询</el-button>
<el-button icon="el-icon-delete" @click="resetValue()">重置</el-button>
<el-button icon="el-icon-plus" size="small" type="success" @click="openAddWindow()" v-if="hasPermission('sys:user:add')">新增</el-button>
</el-form-item>
</el-form>
<!-- 用户表格数据 -->
<el-table :height="tableHeight" :data="userList" border stripe style="width: 100%; margin-bottom: 10px">
<el-table-column prop="username" label="用户名"></el-table-column>
<el-table-column prop="realName" label="姓名"></el-table-column>
<el-table-column prop="departmentName" label="所属部门"></el-table-column>
<el-table-column prop="phone" label="电话"></el-table-column>
<el-table-column prop="email" label="邮箱"></el-table-column>
<el-table-column align="center" width="290" label="操作">
<template slot-scope="scope">
<el-button icon="el-icon-edit" type="primary" size="mini" @click="handleEdit(scope.row)" v-if="hasPermission('sys:user:edit')" >编辑</el-button>
<el-button icon="el-icon-delete" type="danger" size="mini" @click="handleDelete(scope.row)" v-if="hasPermission('sys:user:delete')">删除</el-button>
<el-button icon="el-icon-setting" type="primary" size="mini" @click="assignRole(scope.row)" v-if="hasPermission('sys:user:assign')">分配角色</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页工具栏 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNo"
:page-sizes="[10, 20, 30, 40, 50]"
:page-size="10"
layout="total, sizes, prev, pager, next, jumper"
:total="total" />
export default {
name: 'userList',
//注册组件
components:{ SystemDialog },
data() {
//自定义验证规则
let validatePhone = (rule, value, callback) => {
if (!value) {
callback(new Error("请输入手机号码"));
//使用正则表达式进行验证手机号码
}else if (!/^1[3456789]\d{9}$/.test(value)) {
callback(new Error("手机号格式不正确"));
}else{
callback();
}
};
return {
containerHeight: 0, //容器高度
deptList: [], //左侧部门树形菜单列表
//树节点属性
defaultProps: {
children: 'children',
label: 'departmentName'
},
//查询条件对象
searchModel: {
username: "",
realName:"",
phone: "",
departmentId: "",
pageNo: 1,
pageSize: 10,
},
userList: [], //用户列表
tableHeight: 0, //表格高度
pageNo: 1, //当前页码
pageSize: 10, //每页显示数量
total: 0, //总数量
departmentId: "", //部门编号
//添加和修改用户窗口属性
userDialog: {
title: '',
height: 410,
width: 610,
visible: false
},
//用户对象
user: {
id: '',
departmentId: '',
departmentName: '',
email: '',
realName: '',
phone: '',
nickName: '',
password: '',
username: '',
gender: '',
avatar:''
},
rules: {
departmentName: [{ required: true, trigger: 'change', message: '请选择所属部门' }],
realName: [{ required: true, trigger: 'blur', message: '请填写姓名' }],
phone: [{ trigger: 'blur', validator: validatePhone }],
username: [{ required: true, trigger: 'blur', message: '请填写登录名' }],
password: [{ required: true, trigger: 'blur', message: '请填写登录密码' }],
gender: [{ required: true, trigger: 'change', message: '请选择性别' }]
},
//选择所属部门窗口属性
parentDialog: {
title: '选择所属部门',
width: 300,
height: 450,
visible: false
},
//树节点属性
parentProps: {
children: 'children',
label: 'departmentName'
},
parentList: [], //所属部门节点数据
//上传需要携带的数据
uploadHeader:{"token":getToken()},
//分配角色窗口属性
assignDialog: {
title: "",
visible: false,
width: 800,
height: 410,
},
//角色对象
roleVo: {
pageNo: 1,
pageSize: 10,
userId: "",
total: 0,
},
assignRoleList: [], //角色列表
assignHeight: 0, //分配角色表格高度
selectedIds: [], //被选中的角色id
selectedUserId: "", //被分配角色的用户ID
}
},
created() {
//查询部门列表
this.getDeptList();
//调用查询用户列表
this.search(this.departmentId);
},
mounted() {
this.$nextTick(() => {
//内容高度
this.containerHeight = window.innerHeight - 85
//表格高度
this.tableHeight = window.innerHeight - 220
//角色表格高度
this.assignHeight = window.innerHeight - 350;
})
},
methods: {
/**
* 查询部门列表
*/
async getDeptList() {
//发送查询请求
let res = await departmentApi.getDepartmentList(null);
//判断是否成功
if (res.success) {
this.deptList = res.data;
//树加载完成后,默认点击第一个节点
this.$nextTick(() => {
const firstNode = document.querySelector(".el-tree-node");
firstNode.click();
});
}
},
/**
* 左侧树节点点击事件
*/
handleNodeClick(data) {
//给部门编号赋值
this.departmentId = data.id;
//查询数据
this.search(this.departmentId);
},
/**
* 查询用户列表
*/
async search(departmentId, pageNo = 1, pageSize = 10) {
this.searchModel.pageNo = pageNo;
this.searchModel.pageSize = pageSize;
this.searchModel.departmentId = departmentId;
//发送查询请求
let res = await userApi.getUserList(this.searchModel);
if (res.success) {
this.userList = res.data.records;
this.total = res.data.total;
}
},
/**
* 当每页数量发生变化时触发该事件
*/
handleSizeChange(size) {
this.pageSize = size; //将每页显示的数量交给成员变量
this.search(this.departmentId, this.pageNo, size);
},
/**
* 当页码发生变化时触发该事件
*/
handleCurrentChange(page) {
this.pageNo = page;
//调用查询方法
this.search(this.departmentId, page, this.pageSize);
},
/**
* 重置查询条件
*/
resetValue() {
//清空查询条件
this.searchModel.username = ""
this.searchModel.realName = ""
this.searchModel.phone = ""
this.searchModel.departmentId = ""
this.searchModel.pageNo = 1
this.searchModel.pageSize = 10
//重新查询
this.search(this.departmentId);
},
<style lang="scss">
::v-deep .el-tree {
.el-tree-node {
position: relative;
padding-left: 10px;
}
.el-tree-node__children {
padding-left: 20px;
}
.el-tree-node :last-child:before {
height: 40px;
}
.el-tree > .el-tree-node:before {
border-left: none;
}
.el-tree > .el-tree-node:after {
border-top: none;
}
.el-tree-node:before,
.el-tree-node:after {
content: "";
left: -4px;
position: absolute;
right: auto;
border-width: 1px;
}
.tree :first-child .el-tree-node:before {
border-left: none;
}
.el-tree-node:before {
border-left: 1px dotted #d9d9d9;
bottom: 0px;
height: 100%;
top: -25px;
width: 1px;
}
.el-tree-node:after {
border-top: 1px dotted #d9d9d9;
height: 20px;
top: 14px;
width: 24px;
}
.el-tree-node__expand-icon.is-leaf {
width: 8px;
}
.el-tree-node__content > .el-tree-node__expand-icon {
display: none;
}
.el-tree-node__content {
line-height: 30px;
height: 30px;
padding-left: 10px !important;
}
}
::v-deep .el-tree > div {
&::before {
display: none;
}
&::after {
display: none;
}
}
//用户头像
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9 !important;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader .avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar-uploader img {
width: 178px;
height: 178px;
display: block;
}
</style>
2、新增用户
2.1 效果图
2.2 新增用户后端接口实现
/**
* 添加用户
*
* @param user
* @return
*/
@PostMapping("/add")
@PreAuthorize("hasAuthority('sys:user:add')")
public Result add(@RequestBody User user) {
//查询用户
User item = userService.findUserByUserName(user.getUsername());
//判断对象是否为空
if (item != null) {
return Result.error().message("该登录名称已被使用,请重新输入!");
}
//密码加密
user.setPassword(passwordEncoder.encode(user.getPassword()));
//调用保存用户信息的方法
if(userService.save(user)){
return Result.ok().message("用户添加成功");
}
return Result.error().message("用户添加失败");
}
2.3 新增用户前端页面实现
<!-- 添加和编辑用户窗口 -->
<system-dialog
:title="userDialog.title"
:height="userDialog.height"
:width="userDialog.width"
:visible="userDialog.visible"
@onClose="onClose"
@onConfirm="onConfirm">
<div slot="content">
<el-form
:model="user"
ref="userForm"
:rules="rules"
label-width="80px"
:inline="true"
size="small">
<el-form-item prop="username" label="用户名">
<el-input v-model="user.username"></el-input>
</el-form-item>
<el-form-item prop="password" v-if="user.id === ''" label="密码">
<el-input type="password" v-model="user.password"></el-input>
</el-form-item>
<el-form-item prop="departmentName" label="所属部门">
<el-input v-model="user.departmentName" :readonly="true" @click.native="selectDepartment()"></el-input>
</el-form-item>
<el-form-item prop="realName" label="姓名">
<el-input v-model="user.realName"></el-input>
</el-form-item>
<el-form-item prop="phone" label="电话">
<el-input v-model="user.phone"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="user.nickName"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="user.email"></el-input>
</el-form-item>
<el-form-item prop="gender" label="性别">
<el-radio-group v-model="user.gender">
<el-radio :label="0">男</el-radio>
<el-radio :label="1">女</el-radio>
</el-radio-group>
</el-form-item>
<br>
<!-- 用户头像 -->
<el-form-item label="头像">
<el-upload
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
class="avatar-uploader"
:data="uploadHeader"
action="http://localhost:8888/api/oss/file/upload?module=avatar">
<img v-if="user.avatar" :src="user.avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
</el-form>
</div>
</system-dialog>
/**
* 打开添加窗口
*/
openAddWindow() {
this.$resetForm('userForm', this.user) //清空表单
this.userDialog.visible = true //显示窗口
this.userDialog.title = '新增用户' //设置标题
},
/**
* 新增或编辑取消事件
*/
onClose() {
this.userDialog.visible = false //关闭窗口
},
/**
* 新增或编辑确认事件
*/
onConfirm() {
//表单验证
this.$refs.userForm.validate(async (valid) => {
if (valid) {
let res = null
// 判断是新增还是修改操作(user.id是否为空)
if (this.user.id === ""){
// 发送新增用户请求
res = await userApi.addUser(this.user)
}else{
// 发送修改用户请求
//res = await userApi.updateUser(this.user)
}
if (res.success){
// 提示成功
this.$message.success(res.message)
// 刷新数据
this.search(this.departmentId, this.pageNo, this.pageSize);
//关闭窗口
this.userDialog.visible = false;
}else{
this.$message.error(res.message)
}
}
})
},
/**
* 编辑用户
* @param row
*/
handleEdit(row) {
//数据回显
this.$objCopy(row, this.user);
//设置窗口标题
this.userDialog.title = "编辑用户";
//显示编辑部门窗口
this.userDialog.visible = true;
},
2.4 选择所属部门
<!-- 所属部门弹框 -->
<system-dialog
:title="parentDialog.title"
:width="parentDialog.width"
:height="parentDialog.height"
:visible="parentDialog.visible"
@onClose="onParentClose"
@onConfirm="onParentConfirm">
<div slot="content">
<el-tree
ref="parentTree"
:data="parentList"
default-expand-all
node-key="id"
:props="parentProps"
:show-checkbox="false"
:highlight-current="true"
:expand-on-click-node="false"
@node-click="parentClick">
<div class="customer-tree-node" slot-scope="{ node, data }">
<span v-if="data.children.length == 0">
<i class="el-icon-document"/>
</span>
<span v-else @click.stop="changeIcon(data)">
<svg-icon v-if="data.open"icon-class="add-s"/>
<svg-icon v-else icon-class="sub-s" />
</span>
<span style="margin-left: 3px">{{ node.label }}</span>
</div>
</el-tree>
</div>
</system-dialog>
/**
* 打开选择所属部门窗口
*/
async selectDepartment() {
//查询上级部门数据
let res = await departmentApi.getDepartmentList(null)
//判断是否成功
if (res.success) {
this.parentList = res.data
}
//显示窗口
this.parentDialog.visible = true
},
/**
* 选择上级部门取消事件
*/
onParentClose() {
this.parentDialog.visible = false //关闭窗口
},
/**
* 选择上级部门确认事件
*/
onParentConfirm() {
this.parentDialog.visible = false
},
//上级部门树节点点击事件
parentClick(data) {
this.user.departmentId = data.id
this.user.departmentName = data.departmentName
},
/**
* 新增或编辑确认事件
*/
onConfirm() {
this.$refs.userForm.validate(async(valid) => {
if (valid) {
let res = null
//判断用户ID是否为空
if (this.user.id === '') {
//新增
//发送添加请求
res = await userApi.addUser(this.user)
} else {
//发送修改请求
res = await userApi.updateUser(this.user)
}
//判断是否成功
if (res.success) {
this.$message.success(res.message)
//刷新
this.search(this.departmentId, this.pageNo, this.pageSize);
//关闭窗口
this.userDialog.visible = false
} else {
this.$message.error(res.message)
}
}
})
},
3、阿里云对象存储OSS服务
3.1 开通阿里云对象存储OSS服务
3.1.1 申请阿里云账号
3.1.2 实名认证
3.1.3 开通OSS对象存储
3.2 使用阿里云OSS对象存储
3.2.1 创建Bucket存储空间
3.2.2 文件管理
3.2.3 使用Java SDK操作OSS
4、使用OSS实现文件上传
4.1 添加OSS依赖
<!-- 阿里云OSS文件上传开始 -->
<!-- 阿里云 OSS -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.14.1</version>
</dependency>
<!--日期时间工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.14</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
<!-- 阿里云OSS文件上传结束 -->
4.2 编写application.properties阿里云配置文件
#阿里云OSS配置
spring.cloud.alicloud.secret-key=cGwJuRlPsS9eRvCijuLjA3wfxZ58aQ
spring.cloud.alicloud.access-key=LTAI5tLmxqCDAEVL9pXaBde4
spring.cloud.alicloud.oss.endpoint=oss-cn-hangzhou.aliyuncs.com
spring.cloud.alicloud.oss.bucket=gulimall-station
#bucket名称
aliyun.oss.file.bucketname=guli-school-online
4.3 读取配置文件信息
4.4 编写文件上传代码
package com.cizhu.service;
import org.springframework.web.multipart.MultipartFile;
/**
* @author bingo
* @description 功能描述
* @date 2022-11-08
*/
public interface FileService {
/**
* 文件上传
* @param file 文件上传对象
* @param module 文件夹名称
* @return
*/
String upload(MultipartFile file, String module);
/**
* 删除文件
* @param url
*/
void deleteFile(String url);
}
package com.cizhu.service.impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.cizhu.config.oss.OssProperties;
import com.cizhu.service.FileService;
import org.apache.commons.io.FilenameUtils;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
/**
* @author bingo
* @description 功能描述
* @date 2022-11-08
*/
@Service
@Transactional
public class FileServiceImpl implements FileService {
/**
* 文件上传
*
* @param file 文件上传对象
* @param module 文件夹名称
* @return
*/
@Resource
private OssProperties ossProperties;
/**
* 文件上传
*
* @param file 文件上传对象
* @param module 文件夹名称
* @return
*/
@Override
public String upload(MultipartFile file, String module) {
//获取地域节点
String endPoint = ossProperties.getEndPoint();
//获取AccessKeyId
String keyId = ossProperties.getKeyId();
//获取AccessKeySecret
String keySecret = ossProperties.getKeySecret();
//获取BucketName
String bucketName = ossProperties.getBucketName();
try {
//创建OSSClient实例
OSS ossClient = new OSSClientBuilder().build(endPoint, keyId,
keySecret);
//上传文件流
InputStream inputStream = file.getInputStream();
//获取旧名称
String originalFilename = file.getOriginalFilename();
//获取文件后缀名
String extension = FilenameUtils.getExtension(originalFilename);
//将文件名重命名
String newFileName = UUID.randomUUID().toString().replace("-", "")+"."+extension;
//使用当前日期进行分类管理
String datePath = new DateTime().toString("yyyy/MM/dd");
//构建文件名
newFileName = module + "/" + datePath + "/" + newFileName;
//调用OSS文件上传的方法
ossClient.putObject(bucketName, newFileName, inputStream);
//关闭OSSClient
ossClient.shutdown();
//返回文件地址
return "https://"+bucketName+"."+endPoint+"/"+newFileName;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 删除文件
*
* @param url
*/
@Override
public void deleteFile(String url) {
//获取地域节点
String endPoint = ossProperties.getEndPoint();
//获取AccessKeyId
String keyId = ossProperties.getKeyId();
//获取AccessKeySecret
String keySecret = ossProperties.getKeySecret();
//获取BucketName
String bucketName = ossProperties.getBucketName();
try {
//创建OSSClient实例
OSS ossClient = new OSSClientBuilder().build(endPoint, keyId, keySecret);
//组装文件地址
String host = "https://"+bucketName+"."+endPoint+"/";
//获取文件名称
String objectName = url.substring(host.length());
//删除文件
ossClient.deleteObject(bucketName,objectName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.5 文件上传控制器
package com.cizhu.controller;
import com.cizhu.service.FileService;
import com.cizhu.utils.Result;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
/**
* @author bingo
* @description 功能描述
* @date 2022-11-08
*/
@RestController
@RequestMapping("/api/oss/file")
public class OSSController {
@Resource
private FileService fileService;
/**
* 文件上传
* @param file
* @param module
* @return
*/
@PostMapping("/upload")
public Result upload(MultipartFile file, String module){
//返回上传到oss的路径
String url = fileService.upload(file,module);
return Result.ok(url).message("文件上传成功");
}
}
4.6 上传用户头像
4.6.1 定义页面组件模版
<!-- 用户头像 -->
<el-form-item label="头像">
<el-upload
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
class="avatar-uploader"
:data="uploadHeader"
action="http://localhost:8888/api/oss/file/upload?module=avatar">
<img v-if="user.avatar" :src="user.avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
头像样式美化
//用户头像
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9 !important;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader .avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar-uploader img {
width: 178px;
height: 178px;
display: block;
}
4.6.2 定义页面组件脚本代码
引入token
//导入token
import {getToken} from '@/utils/auth'
定义上传需要携带的token
上传的事件方法
/**
* 上传成功回调
* @param res
* @param file
*/
handleAvatarSuccess(res, file) {
this.user.avatar = res.data
// 强制重新渲染
this.$forceUpdate()
},
/**
* 上传校验
* @param file
* @returns {boolean}
*/
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!')
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!')
}
return isJPG && isLt2M
},