1、线下会议管理功能
2、审批会议申请功能
3、 TRTC在线视频会议功能
4、罚款模块
5、请假管理
6、报销管理
那么,如何去开发这样一个在线办公的系统,这样的系统用到哪些技术才能实现?接下来,我将带着大家一步步来开发这样的办公系统。
这个项目我用的技术有:SpringBoot 2.4.1,SpringMVC 5.3.1,MyBatis 3.5.7,VUE 3.0.3,ElementUI plus 1.0.2。
第一步,环境搭建
数据库mysql我用的是8.0版本,使用Navicat作为MySQL的客户端,大家可以到网上下载Navicat安装文件。另外,强烈建议大家的操作系统要用Win10,不建议大家使用Win7或者Win11系统。
安装客户端程序(RedisDesktopManager),如果是MacOS的用户,可以到软件商店中查找免费的Redis客户端软件。
MongoDB的客户端,我们使用Navicat就可以,写上正确的连接信息就能连接上MongoDB。我们不需要向MongoDB导入数据,将来使用的过程中,MongoDB会积累业务数据。
安装JDK,大家本地的JDK尽量使用1.8+的版本吧
安装Maven环境,后端Java项目使用Maven构建,所以大家要在本地建立Maven环境
第二步就是最关键的代码实战部分:
用上了GROUP_CONCAT()函数,我们的SQL语句变成了下面的样子
SELECT u.username,
d.dept_name AS deptName,
(
SELECT GROUP_CONCAT( role_name separator "," )
FROM tb_role
WHERE JSON_CONTAINS ( u.role, CONVERT ( id, CHAR ) )
) AS roles
FROM tb_user u
JOIN tb_role r ON JSON_CONTAINS ( u.role, CONVERT ( r.id, CHAR ) )
LEFT JOIN tb_dept d ON u.dept_id = d.id
WHERE r.role_name="超级管理员"
了解过SQL语句的各种语法之后,下面才是我们正式要写的SQL语句。
<select id="searchUserByPage" parameterType="HashMap" resultType="HashMap">
SELECT
DISTINCT u.id,
u.name,
u.sex,
u.tel,
u.email,
d.dept_name AS dept,
u.hiredate,
u.root,
u.status,
( SELECT GROUP_CONCAT( role_name separator "," ) FROM tb_role WHERE JSON_CONTAINS ( u.role, CONVERT ( id, CHAR ) ) ) AS roles
FROM tb_user u
JOIN tb_role r ON JSON_CONTAINS ( u.role, CONVERT ( r.id, CHAR ) )
LEFT JOIN tb_dept d ON u.dept_id = d.id
WHERE 1=1
<if test="name!=null">
AND u.name LIKE "%${name}%"
</if>
<if test="sex!=null">
AND u.sex=#{sex}
</if>
<if test="role!=null">
AND r.role_name=#{role}
</if>
<if test="deptId!=null">
AND d.id=#{deptId}
</if>
<if test="status!=null">
AND u.status=#{status}
</if>
LIMIT #{start}, #{length}
</select>
<select id="searchUserCount" parameterType="HashMap" resultType="long">
SELECT
COUNT(DISTINCT u.id)
FROM tb_user u
JOIN tb_role r ON JSON_CONTAINS ( u.role, CONVERT ( r.id, CHAR ) )
WHERE 1=1
<if test="name!=null">
AND u.name LIKE "%${name}%"
</if>
<if test="sex!=null">
AND u.sex=#{sex}
</if>
<if test="role!=null">
AND r.role_name=#{role}
</if>
<if test="deptId!=null">
AND u.dept_id=#{deptId}
</if>
<if test="status!=null">
AND u.status=#{status}
</if>
</select>
在UserServiceImpl.java类中实现抽象方法。
public class UserServiceImpl implements UserService {
……
@Override
public PageUtils searchUserByPage(HashMap param) {
ArrayList<HashMap> list = userDao.searchUserByPage(param);
long count = userDao.searchUserCount(param);
int start = (Integer) param.get("start");
int length = (Integer) param.get("length");
PageUtils pageUtils = new PageUtils(list, count, start, length);
return pageUtils;
}
}
在Vue的声明周期回调函数created()中调用了loadRoleList()和loadDeptList()函数,所以可以保证用户管理页面显示的时候,部门列表和角色列表的数据都是从数据库中查询得来的。
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
cell-style="padding: 4px 0"
style="width: 100%;"
size="medium"
>
<el-table-column type="selection" header-align="center" align="center" width="50" />
<el-table-column type="index" header-align="center" align="center" width="100" label="序号">
<template #default="scope">
<span>{{ (pageIndex - 1) * pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column prop="name" header-align="center" align="center" min-width="100" label="姓名" />
<el-table-column prop="sex" header-align="center" align="center" min-width="60" label="性别" />
<el-table-column prop="tel" header-align="center" align="center" min-width="130" label="电话" />
<el-table-column
prop="email"
header-align="center"
align="center"
min-width="240"
label="邮箱"
:show-overflow-tooltip="true"
/>
<el-table-column prop="hiredate" header-align="center" align="center" min-width="130" label="入职日期" />
<el-table-column
prop="roles"
header-align="center"
align="center"
min-width="150"
label="角色"
:show-overflow-tooltip="true"
/>
<el-table-column prop="dept" header-align="center" align="center" min-width="120" label="部门" />
<el-table-column prop="status" header-align="center" align="center" min-width="100" label="状态" />
<el-table-column header-align="center" align="center" width="150" label="操作">
<template #default="scope">
<el-button
type="text"
size="medium"
v-if="isAuth(['ROOT', 'USER:UPDATE'])"
@click="updateHandle(scope.row.id)"
>
修改
</el-button>
<el-button
type="text"
size="medium"
v-if="isAuth(['ROOT', 'USER:UPDATE'])"
:disabled="scope.row.status == '离职' || scope.row.root"
@click="dimissHandle(scope.row.id)"
>
离职
</el-button>
<el-button
type="text"
size="medium"
:disabled="scope.row.root"
v-if="isAuth(['ROOT', 'USER:DELETE'])"
@click="deleteHandle(scope.row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
在TbRoleDao.xml文件中,定义SQL用于查询角色分页数据。看上面的截图可知,在角色管理页面上,只有按照角色名字模糊查询。所以在SQL语句中,WHERE子句里面只有一个查询条件。由于在页面表格中要显示每个角色拥有的权限数量,而且tb_role表的permissions字段是JSON数组格式,所以我们统计数组的元素数量,就是该角色拥有的权限数量。恰好JSON_LENGTH()函数能获取JSON数组的长度,所以我就用在SQL语句中了。
@Data
@Schema(description = "查询角色分页表单")
public class SearchRoleByPageForm {
@Pattern(regexp = "^[0-9a-zA-Z\\u4e00-\\u9fa5]{1,10}$", message = "roleName内容不正确")
@Schema(description = "角色名称")
private String roleName;
@NotNull(message = "page不能为空")
@Min(value = 1, message = "page不能小于1")
@Schema(description = "页数")
private Integer page;
@NotNull(message = "length不能为空")
@Range(min = 10, max = 50, message = "length必须在10~50之间")
@Schema(description = "每页记录数")
private Integer length;
}
在TbDeptDao.xml文件中,定义SQL用于查询部门分页数据。看上面的截图可知,在部门管理页面上,只有按照部门名字模糊查询。所以在SQL语句中,WHERE子句里面只有一个查询条件。由于在页面表格中要显示每个部门拥有的员工数量,所以用了COUNT()汇总函数。
public class DeptServiceImpl implements DeptService {
……
@Override
public PageUtils searchDeptByPage(HashMap param) {
ArrayList<HashMap> list = deptDao.searchDeptByPage(param);
long count = deptDao.searchDeptCount(param);
int start = (Integer) param.get("start");
int length = (Integer) param.get("length");
PageUtils pageUtils = new PageUtils(list, count, start, length);
return pageUtils;
}
}
在DeptController.java类中,定义Web方法,然后大家就可以在Swagger页面测试Web方法了。
public class DeptController {
……
@PostMapping("/searchDeptByPage")
@Operation(summary = "查询部门分页数据")
@SaCheckPermission(value = {"ROOT", "DEPT:SELECT"}, mode = SaMode.OR)
public R searchDeptByPage(@Valid @RequestBody SearchDeptByPageForm form) {
int page = form.getPage();
int length = form.getLength();
int start = (page - 1) * length;
HashMap param = JSONUtil.parse(form).toBean(HashMap.class);
param.put("start", start);
PageUtils pageUtils = deptService.searchDeptByPage(param);
return R.ok().put("page", pageUtils);
}
}
本文到此告一段落,感谢大家的观看!