目录

  • 可行性研究与计划
  • 需求分析
  • 软件设计
    • 前端架构设计
    • 数据库设计
    • 后端架构设计
  • 编程开发
  • 功能
    • 登录注册
      • 前端
      • 后端
    • 选择机构
      • 前端
      • 后端
    • 首页
      • 前端
      • 后端
    • 个人信息页
      • 前端
      • 后端
    • 角色管理
      • 前端
      • 后端
    • 用户管理
      • 前端
      • 后端
    • 机构管理
      • 前端
      • 后端
    • 组织架构
      • 前端
      • 后端
    • 项目模块管理
      • 前端
      • 后端
    • 日报模块
    • 消息模块
      • 前端
      • 后端
  • 预览
  • 源码
  • 感谢

可行性研究与计划

作为企业的员工,相信很多小伙伴们每天上班的第一件事情,就是需要写工作日报,来向上级汇报今天的工作计划、上个工作日的工作完成情况以及汇报工作中遇到的项目延误等问题。工作日报不仅是对工作的目标有一个更清晰的规划也是上级了解工作情况的重要的信息来源。

那么,仅仅通过简单的聊天工具发送日报,既不能直观方便地对每个人的每天工作日报进行一个归类和管理,也不方便上级查看历史的工作情况,从而对整个团队的工作不好进行一个整体的分析。

综合以上几点,那么一个企业可以管理所有员工的工作情况,进行方便分类管理和查阅是有必要的。

需求分析

首先系统面对的是企业所有的员工。以企业为独立单位,每个企业有对应的部门组织架构,每个组织架构中包含管理者。每个组织中的管理者可以收集和查阅对应组织以及他的下级发送的日报。譬如:

一个企业中包含技术部组织,下面有前端组、后端组、产品组、测试组,前端组包含一个组织管理者,他可以查看其组织的其他成员所发送的日报,但不能查看其他组织的成员发送的日报。而技术部组织的管理者则可以查看其下所有组织成员的发送的日报。并且要做到及时通知,否则将不能保证日报的发送及时性。

需要实现的功能:企业机构管理,用户管理,角色管理,权限管理,项目模块管理,组织管理,日报管理,导出Excel,消息模块等功能。

软件设计

前端架构设计

技术栈:Vue3 + vuex + vue-router + less + element-plus + axios + echats + mitt + websocket + webworker + canvas
使用了 vite 作为开发和打包工具

  • — src:
  • |— api:存放请求的接口;方便统一管理接口。
  • |— assets:存放静态文件;图片、字体图标等。
  • |— components:存放公共组件;方便组件复用。
  • |— config:存放配置文件;请求域名、文件访问路径、websocket请求路径等。
  • |— router:存放页面路由、路由拦截。
  • |— store:存放公共状态管理。
  • |— utils:存放通用的js文件,工具函数等。
  • |— views:存放页面

在main.js 中,通过使用 vite 工具的 import.meta.globEager 自动导入api下的模块,挂载到app.config.globalProperties下,方便全局使用:
api/index.js:

const modulesFiles = import.meta.globEager("./*/*api.js");for (const modulePath in modulesFiles) {const path = modulePath.replace(/^\.\/(.*)\.api\.\w+$/, "$1");const pathArr = path.split("/");const moduleName = pathArr.length ? pathArr[pathArr.length - 1] : pathArr;apiObj[moduleName] = modulesFiles[modulePath];
}export default apiObj;

main.js:

// 引入api模块
import api from "./api/index.js";app.config.globalProperties.$api = api;

引入自定义指令:

import directives from "./utils/directives";Object.keys(directives).forEach((key) => {app.directive(key, directives[key]);
});

引入权限编码:

import permission from "./utils/permission";app.config.globalProperties.$permission = permission;

权限判断:

/*** 是否是超级管理员** @returns Boolean*/
app.config.globalProperties.$isSupperAdmin = function () {if (store.state.user.userInfo.isSupperAdmin) {return true;}return false;
};/*** 是否有某个权限** @param {*} permissionCode 权限编码* @returns Boolean*/
app.config.globalProperties.$hasPermission = function (permissionCode) {if (store.state.user.userInfo.isSupperAdmin) {return true;}let has = false;const userPermission = store.state.user.userInfo.permissions;if (userPermission && userPermission.includes(permissionCode)) {has = true;}return has;
};/*** 是否有多个权限中至少一个** @param {*} permissionCodeList 权限编码数组* @returns Boolean*/
app.config.globalProperties.$hasOneOfPermissions = function (permissionCodeList
) {if (store.state.user.userInfo.isSupperAdmin) {return true;}let has = false;const userPermission = store.state.user.userInfo.permissions;if (userPermission &&permissionCodeList &&Array.isArray(permissionCodeList)) {has = permissionCodeList.some((item) => userPermission.includes(item));}return has;
};/*** 是否有全部权限** @param {*} permissionCodeList 权限编码数组* @returns Boolean*/
app.config.globalProperties.$havePermissions = function (permissionCodeList) {if (store.state.user.userInfo.isSupperAdmin) {return true;}let has = false;const userPermission = store.state.user.userInfo.permissions;if (userPermission &&permissionCodeList &&Array.isArray(permissionCodeList)) {has = permissionCodeList.every((item) => userPermission.includes(item));}return has;
};

因为Vue3已经不再支持过滤器,这里写个全局方法代替过滤器的功能:

/*** 全局过滤器*/
app.config.globalProperties.$filters = {// 性别过滤sexFilter(val) {let name = "";switch (val) {case "1":name = "男";break;case "0":name = "女";break;default:name = "未填写";}return name;},
};

数据库设计

使用MySQL作为数据库

后端架构设计

技术栈:springBoot + myBatisPlus + MySQL + easyexcel + websocket + mybatis-plus-generator
使用 swagger 作为接口文档工具

  • — cofig :存放配置文件;接口登录拦截配置、跨域配置、mybatisPlus的自动填充配置等。
  • — interceptor :存放拦截器类;对需要进行登录验证或者权限验证的接口,进行拦截并验证,通过才给放行。
  • — modules :存放业务模块;对不同的业务模块进行分开管理,方便多人进行开发。
  • |— controller :存放对应模块的前端控制器。
  • |— dto : 存放对应模块的数据交换类。
  • |— entity:存放对应模块的数据库表对应的实体类。
  • |— enums:存放对应模块使用到的枚举类。
  • |— exception:存放对应模块使用到的异常类。
  • |— vo:存放返回给前端的数据类。
  • |— service:存放对应模块的业务接口。
  • |— | — impl:存放对应业务接口实现类。
  • — util :存放工具类。
  • — ws: 存放 wobsocket 模块相关。

通过实现 WebMvcConfigurer 接口,覆写 addInterceptors 和 addCorsMappings 配置请求拦截和跨域:

@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authHandlerInterceptor)// 拦截所有请求,通过判断是否有 @UserLoginToken 注解 决定是否需要登录.addPathPatterns("/**");
//                .excludePathPatterns("/user/**");}@Override
public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedHeaders("Content-Type", "X-Requested-With", "accept,Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", "login-token").allowedMethods("*").allowedOrigins("*").allowCredentials(true);
}

使用拦截器通过自定义 @UserLoginToken 注解来判断是否需要进行登录和权限验证:

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {/*** 是否开启token验证 (默认开启)* @return*/boolean required() default true;/*** 用户角色权限 (默认普通用户)* @return*/PermissionEnum[] permission() default {};
}
@Log4j2
@Component
public class AuthHandlerInterceptor implements HandlerInterceptor {@AutowiredIUserService userService;@AutowiredIRoleService roleService;@AutowiredTokenUtil tokenUtil;@AutowiredTokenConfiguration tokenConfiguration;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("login-token");// 如果不是映射到方法直接通过if (!(handler instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();Class clazz = handlerMethod.getBeanType();// 1. 检查请求的【方法】中是否有 passtoken 注解,有则直接跳过认证if (method.isAnnotationPresent(PassToken.class)) {PassToken passToken = method.getAnnotation(PassToken.class);if (passToken.required()) {return true;}}// 2. 检查请求的【方法】或者【类】中有没有需要用户权限 UserLoginToken 的注解if (method.isAnnotationPresent(UserLoginToken.class) || clazz.isAnnotationPresent(UserLoginToken.class)) {UserLoginToken userLoginToken;if (method.isAnnotationPresent(UserLoginToken.class)) {userLoginToken = method.getAnnotation(UserLoginToken.class);} else {userLoginToken = (UserLoginToken) clazz.getAnnotation(UserLoginToken.class);}if (userLoginToken.required()) {// 执行 token 认证if (null == token || "".equals(token.trim())) {throw new TokenAuthExpiredException("需要登录才能访问,请登录!");}Map<String, Long> tokenDataMap = tokenUtil.parseToken(token);Integer userId = Math.toIntExact(tokenDataMap.get("userId"));long timeOfToken = System.currentTimeMillis() - tokenDataMap.get("timeStamp");// 1.判断 token 是否过期// 年轻 tokenif (timeOfToken < tokenConfiguration.getYangToken()) {//                   log.info("token 未过期且不需要刷新");System.out.println("\t年轻 token 不需要刷新");}// 老年 token 就刷新 tokenelse if (timeOfToken >= tokenConfiguration.getYangToken() && timeOfToken <= tokenConfiguration.getOldToken()) {System.out.println("\t老年 token 需要刷新");response.addHeader("login-token", tokenUtil.getToken(userId));}// 过期 token 就返回 token 无效else {throw new TokenAuthExpiredException("token 已过期,请重新登录!");}// 根据 token 中的 userId 获取用户信息UserEntity user = userService.getById(userId);// 拦截不存在或已被停用的用户if (ObjectUtil.isEmpty(user) || IsEnum.YES.equals(user.getDeleted())) {throw new TokenAuthExpiredException("用户不存在,请重新登录");}// 把 用户信息 存在当前线程的缓存中UserChacheFromToken.setUser(user);// 超级管理员跳过权限验证if (!ObjectUtil.isEmpty(user.getIsSupperAdmin()) && user.getIsSupperAdmin()) {log.info("超级管理员跳过权限验证");return true;}// 2.角色匹配PermissionEnum[] needPermissionList = userLoginToken.permission();System.err.println("\t当前接口需要的权限 ====>" + Arrays.toString(needPermissionList));// 接口需要权限if (needPermissionList.length > 0) {// 因为角色权限是跟机构绑定,如果没有绑定机构,则优先提示机构未绑定if (ObjectUtil.isEmpty(user.getOrgId())) {throw new HasNoPermissionException("当前用户未关联机构,请先关联");}if (ObjectUtil.isEmpty(user.getRoleId())) {throw new HasNoPermissionException("当前用户未关联角色,请联系管理员");}Role userRole = roleService.getById(user.getRoleId());if (ObjectUtil.isEmpty(userRole)) {throw new HasNoPermissionException("当前用户关联角色不存在,请联系管理员");}List<String> userPermissionList = userRole.getPermissions();log.info("当前用户权限列表 ===>" + userPermissionList);for (PermissionEnum needPermission: needPermissionList) {for (String userPermission: userPermissionList) {if (needPermission.getValue().equals(userPermission)) {return true;}}}throw new HasNoPermissionException("抱歉,当前用户没有权限,请联系管理员");}return true;}}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 执行结束后释放 ThreadLocal 资源防止oom(资源溢出)UserChacheFromToken.removeUser();}
}

编程开发

功能

登录注册


前端
  • 做了记住密码的功能,加密处理之后把密码存入 localstorage中,取出时再进行解密。
    登录成功之后存入
const loginMethod = () => {$api.users.login(formData).then((res) => {ElMessage.success("登录成功");window.localStorage.setItem(REMEMBER_PASSWORD,JSON.stringify({username: formData.username || "",password: encryptData(formData.password),}));// 登录之后把用户信息存入 store 中store.commit("user/login", res.data);router.replace("/");}).catch((err) => {console.error("login error: ", err);}).finally(() => {submitLoading.value = false;});};

打开页面时取出:

// 取出
let rememberPassword = window.localStorage.getItem(REMEMBER_PASSWORD);
if (rememberPassword) {rememberPassword = JSON.parse(rememberPassword);formData.username = rememberPassword.username || "";formData.password = decryptData(rememberPassword.password);
}
  • 使用了动态背景图片,每次打开或者刷新会从图库里随机抽取一张图作为背景。
// 随机背景图
const pageBgIndex = getRandom(0, imgArr.length - 1, true);
const pageBgUrl = ref(imgArr[pageBgIndex]);
<divref="containerRef"class="login-wrap":style="{backgroundImage: `url(${pageBgUrl})`,}">
  • 加了个个人认为比较好看的烟花特效 。
// 烟花背景参数
const fireworksData = {canvas: "",ctx: "",canvasWidth: "",canvasHeight: "",fworks: null,lastTime: "",newTime: "",};const initFireworks = (w, h) => {fireworksData.fworks = new Fireworks(w, h);fireworksData.lastTime = new Date();animateStart();};const animateStart = () => {timer = window.requestAnimFrame(animateStart);fireworksData.newTime = new Date();if (fireworksData.newTime - fireworksData.lastTime >800 + (fireworksData.canvasHeight - 2200) / 2) {fireworksData.fworks.color = Math.random() * 10 > 2 ? 1 : 0;const x = getRandom(fireworksData.canvasWidth / 10,(fireworksData.canvasWidth * 9) / 10);const y = getRandom(50, (fireworksData.canvasHeight * 2) / 3);const bigboom = fireworksData.fworks.createFireworks(x,fireworksData.canvasHeight,x,y);fireworksData.lastTime = fireworksData.newTime;}};onMounted(() => {fireworksData.canvas = document.getElementById("canvas");fireworksData.canvas.width = fireworksData.canvasWidth =containerRef.value.offsetWidth;fireworksData.canvas.height = fireworksData.canvasHeight =containerRef.value.offsetHeight;initFireworks(fireworksData.canvasWidth, fireworksData.canvasHeight);});onUnmounted(() => {if (scrollTimer) clearInterval(scrollTimer);if (timer) window.cancelAnimationFrame(timer);});
  • 图形验证码,使用了canvas的一些绘图技巧。
<PuzzleVerificationv-model:modelValue="isVerificationShow":puzzleImgList="puzzleImgList"@success="loginMethod"@close="submitLoading = false"/>
后端
  • 由于是小项目没有使用 Shiro 和 Spring Security 框架,使用了比较简单的 JWT 生成 token 做的登录。
  • 使用 UserLoginToken 注解 + 拦截器对请求进行是否登录的检验,配置了token过期时间和自动刷新的时间。
  • 接口除了检验登录之外,还会对配有权限的接口进行校验角色权限,也是在拦截器中进行。
  • 后期 token 和角色权限都可以使用 radis 缓存,这样不用频繁地去查库,减少IO消耗。
  • 使用md5 + 盐值 进行密码的加密。

选择机构

前端
  • 因为是个 saas 系统,所以登录完必须要选择机构才能使用,这给了任意人新建机构的入口。默认新建机构的人会得到机构管理员的权限。一个账号只能绑定一个机构。
  • 使用了路由守卫,如果没有绑定机构是进入不了其他页面(除了登录)。
  • 可以为加入机构的人设置问题验证(嘿嘿,是不是很熟悉^_^),选择开启或者关闭验证。
  • 关闭验证之后其他用户可以直接进入关联你的机构。
后端
  • 机构是单独作为一个表去维护,机构关联了很多的业务模块。包括角色,项目模块等等,每个机构都不同,需要区分。
  • 创建机构会初始化一些必要的数据,譬如该机构的初始化角色……

首页


前端
  • 首页在原来的基础上是没有太多的改动的,中间代办事项那里重新使用 vxe-table 重构了一下,这里写了个 webworker 线程,原本打算根据时间去使用子线程去监听未做的事情,虽然代码基本实现了,但是功能没有用到,因为感觉前端做这个意义不大。
  • 左侧菜单栏做了权限隐藏控制,普通用户没有对应的查看权限即不会显示对应菜单。
  • 左下方做了一个精小玲珑的小时钟,嘿嘿,感觉挺有意思^_^
  • 菜单的折叠状态都充分地使用了 vuex + sessionStorage 进行缓存处理(又是一个小细节……)。
  • 顶部tag标签栏,在原来的基础上重新进行了封装,增加了左右滑动按钮控制。
后端
  • 增加了登录时间记录,和根据登录的ip查询归属地,记录登录地点(这个可以做个日志记录,暂时没有做)。
  • 消息数量统计,当前一周的日报提交数量统计和当前年份的每个月的日报提交数量统计。

个人信息页


前端
  • 编辑个人信息,点编辑按钮进入编辑,如果修改了,没有保存,会回复原始状态。
  • 上传头像,做了一些限制,头像过大(超过1M)不能上传,尽管后端已经已经把质量压缩到了0.2 ^_^,但是谁会嫌自己的服务器硬盘够大呢?
  • 这里显示了当前用户的机构信息,机构管理员还可以编辑机构信息,可以修改验证等等。
  • 修改密码,做了一个密码强度的验证,规则嘛,自己试试看 ^_^~
后端
  • 用户信息的修改保存。
  • 机构信息的修改保存。
  • 文件的上传处理,保存文件流。

角色管理

前端
  • 可以新建角色,授权,删除角色等操作。
  • 默认的角色不可被删除。
后端
  • 角色权限目前我设计的是使用的是枚举来控制的,针对每一个接口或者按钮都可以做到权限的控制。
  • 具体可看代码,不过多介绍。

用户管理

前端
  • 使用 vxe-table 的好处之一在于方便进行列筛选,可以控制哪些列需要进行展示,防止表格列过多的横向拖动。
  • 用户列表只对有查看用户列表权限的人才开放,可以进行修改用户基本信息,授权角色,重置密码,设为停用等操作。
后端
  • 用户列表的条件查询,对应的修改信息、授权、停用等接口。

机构管理

前端
  • 机构管理只有超级管理员才有此查看权限。
  • 可以修改、设为不可加入、停用、删除等操作。
后端
  • 机构列表的查询修改,设为不可加入、停用、删除等接口。

组织架构



前端
  • 通过递归组件实现了这个组织架构图的功能。
  • 可以不限层级地添加组织和人员,每个组织可以设置一名组织管理员,拥有对应角色的权限。
  • 下拉菜单实现了按钮级权限。
后端
  • 通过 parentId 关联组织数据,形成树型结构数据,通过递归遍历查询数据。当前数据较少,也可以一次查出当前机构所有组织数据,再进行数据处理。

项目模块管理

前端
  • 写日报必须要告诉工作的具体项目模块,所以得有一个列表。
  • 多个根节点的树型数据,采用树型数据列表展示,之前使用懒加载,但实际使用效果不怎么好,取消了。
后端
  • 通过一次查出该机构所有项目模块数据,再进行树型数据的处理。

日报模块

发布日报
可以发布今天的日报

我的日报
可以根据日期发布日报,左下角显示当前的周数,右上角的左右箭头可以切换周数。

点击不同星期,展现不同颜色,每一天对应一种颜色,代表每天不一样的心情^_^~

我的日报-新建
发布日报必须填写姓名,否则不知道是谁。这里单独使用了一个字段保存,避免影响到昵称等。

我的日报-填写姓名
这里跳转过来做了一个提示动画,又是一个小细节^_^~

我的日报-新建-选择模块
根据模块管理配置的级联选择

我的日报-编辑
快捷拖动进度条选择进度

发送日报
自动获取今天和上个工作日的日报,可以取消掉不发送的日报

组长可以自动获取到所有组员的日报,同样可以编辑处理

已发送日报管理
可查看历史发送记录,每天只能发送一次哦,只可以撤回今天的。这里做了一个复制的功能,直接帮助排版好,粘贴到聊天工具也是可以的哦^_^~

已发送日报管理-折叠

我的日报-一键添加周报
可以自动获取本周一到今天的所有日报记录,添加到周报列表

我的日报-周报数据预览
预览一周的日报,点击导出,可以导出Excel文件

日报管理列表
这里更方便的看到自己的日报记录

发送日报-通知
组员发送和撤回日报,组长会受到一个实时的消息通知

日报汇总
这里组长可以看到所有组员和下级组员的已发送的日报,组员可以看到自己组员发送的日报,再也不用一个一个去问了

消息模块



前端
  • 前端使用了stompjs 和 sockjs.js 模块实现了websoket通信。
  • 采用消息订阅的方式接收服务端转发的消息。
  • 目前消息没有做详情,只是简单做了已读。
后端
  • 使用 spring-boot-starter-websocket 模块实现 socket 通信。
  • 使用 simpMessagingTemplate 给指定用户发送消息。
  • 也可以使用 Netty 去实现更多的功能。

预览

线上地址:
https://workreport.yunfengzhijia.cn/

源码

gitee:
前端: https://gitee.com/Kevin-269581661/work-report-front
后端:https://gitee.com/Kevin-269581661/work-report-svc

感谢

也是在前人的肩膀上,经过了多番实践和创造才得来的成果,虽然仍然还有很多可以继续改进的。

如果这篇文章有帮助到您,请简单给个赞吧,谢谢~

【Vue3 + SpringBoot】搭建企业日报管理saas系统相关推荐

  1. 搭建企业知识管理平台,走出“数据孤岛”困境

    编者按:企业发展过程中产生的数据信息,无疑是企业的一笔重要知识资产,而"数据孤岛"问题是很多企业不得不面对的难题,如何走出这种困境,有效对企业知识资产进行挖掘保护,对企业管理者和企 ...

  2. 制造企业信息化时代——SaaS系统下沉,移动端上升

    如今这个时代,我们是不是有很多岗位一定是要在电脑前面完成?如果我们让部分岗位的办公室人员离开电脑,让他们通过移动端来完成工作,这又会产生出一个什么样的变化?是否意味着可以有更多的时间在一线生产制造现场 ...

  3. 计算机毕业设计springboot区域企业多维评价系统

    最新计算机专业原创毕业设计参考选题都有源码+数据库是近期作品 你的选题刚好在下面有,有时间看到机会给您发 1 springboot短视频网站 2 ssm校园二手交易平台 3 ssm留学生交流互动论坛网 ...

  4. 基于springboot搭建的个人博客系统

    源码下载地址:blog blog是基于springboot搭建的个人博客,响应式 前端技术:html.css.js.jq.bootstrap 后台技术:springboot.thymeleaf.myb ...

  5. 搭建企业线上培训系统

    企业线上培训系统的出现,一些集团公司的内部例会,决策会议或培训会议,可以通过线上统一进行,不再需要集中企业所有员工到指定地点培训.普通企业可以在外网租用线上培训系统,而对于一些涉密单位数据如何保障安全 ...

  6. SpringBoot搭建一个简单的天气预报系统(一)

    章节目录 1. 前言 2. 数据来源 3. 实战 3.1 开发环境 3.2 功能需求 3.3 手动编码 3.3.1 vo层 3.3.2 service层 3.3.3 controller层 3.3.4 ...

  7. 通过Azure AD 搭建企业安全身份标识系统

    最近,"安全"成为行业最热的词,而身份安全在企业上云背景下,也在逐渐成为企业基础架构不可或缺的一部分.但提到身份安全,仍然有不少用户并不了解该从何下手.今天我们将邀请微软数字化转型 ...

  8. 企业微信小程序可用存储空间不足_如何用微信“小程序商城+企业微信”,搭建企业智慧新零售系统?...

    许多企业或是个体商家,都做了微信小程序商城,或者是正打算做小程序商城,但是对于小程序商城开发制作好之后的运营却是一头雾水,不知道怎么运营.今天我们说一说"微信小程序商城+企业微信" ...

  9. 计算机毕业设计ssm企业部门报销管理g9d62系统+程序+源码+lw+远程部署

    计算机毕业设计ssm企业部门报销管理g9d62系统+程序+源码+lw+远程部署 计算机毕业设计ssm企业部门报销管理g9d62系统+程序+源码+lw+远程部署 本源码技术栈: 项目架构:B/S架构 开 ...

  10. java报销系统的参考文献_java毕业设计_springboot框架的企业报销管理与实现

    这是一个基于java的毕业设计项目,毕设课题为springboot框架的企业报销管理与实现, 是一个采用b/s结构的javaweb项目, 开发工具eclipsei/eclipse, 项目框架jsp+s ...

最新文章

  1. Delphi XE4 For IOS之部署问题
  2. 小程序获取用户手机号_社区团购小程序应该如何推广才能获取更多用户?
  3. 某金融产品妹纸:四种姿势教你跪舔程序员不要不要的
  4. 实力封装:Unity打包AssetBundle(二)
  5. Hadoop本地运行模式了解~
  6. Android笔记进程优先级
  7. 笨办法学 Python · 续 练习 19:改善性能
  8. mysql建立作业_mysql创建作业及问题解决
  9. php dll是下32还是64位,windows下关于boost 64位和32位库的编译详细介绍
  10. 大黄蜂vep视频转成MP4格式提取工具的使用
  11. 微信小程序开发者工具平台+Hbuilder开发平台下载
  12. Linux系统防火墙概述
  13. 记一次蓝牙音箱无声音的原因
  14. php使用逻辑运算符提交程序运行效率
  15. JavaScript 教程,很好的!
  16. 计算机图片处理是什么应用,计算机图像处理技术及其应用领域
  17. Markdown 图片排版
  18. C++11特性《 右值引用-<完美转发>、lambda表达式》
  19. javascript--经典实例锦集
  20. zk4元年拆解_减配实锤!Kobe4 开箱+拆解:你凭什么叫Protro?

热门文章

  1. 内存条上面参数详解_【硬件篇】第4期:内存条知识(台式机)
  2. 汽车方向盘电子助力转向器如何接线_案例 | 看3D打印如何助力汽车电子连接器模具冷却水路的设计优化...
  3. rabbitmq 一个生产者多个消费者_RabbitMQ入门学习系列(二),单生产者消费者
  4. MSYS2 安装和配置
  5. 如何设置PDFjs 页面标题
  6. angular6之Http服务
  7. 重构28-Rename boolean method(重命名布尔方法)
  8. FZU《C语言程序综合设计》
  9. [贴图]博客园T恤男士版
  10. 深度学习图像分类(二):AlexNet