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

6 篇文章 1 订阅

可行性研究与计划

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

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

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

需求分析

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

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

需要实现的功能:企业机构管理,用户管理,角色管理,权限管理,项目模块管理,组织管理,日报管理,导出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 {
    @Autowired
    IUserService userService;

    @Autowired
    IRoleService roleService;

    @Autowired
    TokenUtil tokenUtil;

    @Autowired
    TokenConfiguration tokenConfiguration;

    @Override
    public 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 是否过期
                // 年轻 token
                if (timeOfToken < tokenConfiguration.getYangToken()) {
//                   log.info("token 未过期且不需要刷新");
                    System.out.println("\t年轻 token 不需要刷新");
                }
                // 老年 token 就刷新 token
                else 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;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public 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]);
<div
    ref="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的一些绘图技巧。
<PuzzleVerification
  v-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

感谢

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

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

  • 7
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: Vue 3 和 Spring Boot 是一对强大的技术组合,可用于搭建现代化的后台管理系统。下面是一个基本的指南,介绍如何从零开始搭建一个 Vue 3 + Spring Boot 后台管理系统。 首先,我们需要准备开发环境。在电脑上安装好 Node.js 和 Java JDK,并配置好环境变量。然后,安装 Vue CLI 和 Spring Boot CLI,这些工具将帮助我们创建和管理项目。 第二步是创建一个新的 Spring Boot 项目。使用 Spring Boot CLI,我们可以快速创建一个空的 Spring Boot 项目,并进行基本的配置。可以使用命令行,输入如下命令来创建项目: ``` $ spring init --name=my-project --groupId=com.example --artifactId=my-project --dependencies=web my-project ``` 这将创建一个名为 my-project 的 Spring Boot 项目。 接下来,我们可以创建一个 Vue 3 项目。使用 Vue CLI,我们可以选择一个预定义的模板,例如 Vue Router 和 Vuex,以及一些常用的插件。可以使用命令行,输入如下命令来创建项目: ``` $ vue create my-project ``` 这将创建一个名为 my-project 的 Vue 3 项目。 现在,我们已经有了一个空的 Spring Boot 项目和一个空的 Vue 3 项目。接下来,我们需要将这两者连接起来。 在 Vue 3 项目中,可以使用 axios 或者 fetch 来发送请求到后端。在 Spring Boot 项目中,可以使用 Spring Data JPA 来管理数据库,Spring Boot Security 来进行身份验证和授权。 我们可以编写 RESTful API 接口,用于在前端和后端之间传输数据。同时,也需要编写前端组件和页面,以及后端的 Controller 和 Service 层代码,来实现各种功能。 最后,我们可以使用打包和部署工具,将项目打包为可部署的文件,并将其部署到服务器上。例如,可以使用 Maven 将 Spring Boot 项目打包为 Java 可执行文件(JAR 文件),并使用 Nginx 或 Apache 将 Vue 3 项目部署为静态文件。 总之,使用 Vue 3 和 Spring Boot 可以快速搭建一个功能丰富的后台管理系统。只需按照上述步骤创建项目、编写代码、连接前后端,最后打包部署即可。当然,在实际开发过程中还需要考虑安全性、性能优化和代码质量等方面的问题,这些都需要进一步的学习和实践。 ### 回答2: Vue3是一个流行的JavaScript框架,用于构建用户界面。Spring Boot是一个基于Java的框架,用于构建快速且易于配置的应用程序。下面是在Vue3和Spring Boot中从零搭建后台管理系统的步骤: 1. 搭建Spring Boot后端: - 在IDE中创建一个新的Spring Boot项目。 - 添加所需的依赖项,如Spring Security、Spring Data JPA和MySQL数据库驱动程序。 - 创建实体类和存储库接口,用于管理系统数据的持久化。 - 创建控制器类,用于处理来自前端的请求,并调用适当的服务方法。 - 配置数据库连接和安全性设置。 - 运行应用程序,确保后端正常工作。 2. 搭建Vue3前端: - 在命令行中使用Vue CLI创建一个新的Vue3项目。 - 在Vue3项目中安装所需的依赖项,如Vue Router和Axios。 - 创建路由配置文件,定义前端路由和对应的组件。 - 创建后台API服务文件,使用Axios发送HTTP请求到后端。 - 创建所需的组件,如登录、注册、用户管理和权限管理。 - 配置应用程序的主要入口点,并将路由和组件添加到Vue实例中。 - 运行应用程序,确保前端正常工作。 3. 连接前端和后端: - 在Vue3中使用Axios调用后端API。 - 在Spring Boot中创建适当的控制器和服务方法,以接收和处理来自前端的请求。 - 在Vue3中处理返回的数据,并根据需要进行展示和处理。 4. 完善功能和界面设计: - 根据系统需求和设计规范,完善后台管理系统的功能和界面。 - 添加用户认证和授权功能,确保只有授权用户才能访问特定页面和功能。 - 使用Vue3的组件化和响应式特性,实现良好的用户体验。 - 进行测试和调试,确保系统稳定性和安全性。 以上是使用Vue3和Spring Boot搭建后台管理系统的一般步骤,具体的实施过程可能因项目需求和个人偏好而有所不同。在开始搭建项目之前,建议先了解Vue3和Spring Boot的基本知识,并参考官方文档和教程,以帮助顺利完成项目。 ### 回答3: 搭建一个基于Vue3和Spring Boot的后台管理系统,需要经过以下步骤: 1. 确保你已经安装了Node.js和Java开发环境。可以从官网上下载并按照指引进行安装。 2. 创建Vue3项目。使用命令行工具或者Vue CLI来创建一个新的Vue3项目。运行命令`vue create project-name`,然后根据指引选择需要的配置项,比如包管理工具、路由、状态管理等。等待项目创建完成。 3. 构建前端界面。在Vue3项目中,根据需求使用Vue组件来搭建前端界面。可以使用Vue3提供的Composition API来编写组件逻辑,通过Vue Router来管理路由,使用Vuex来管理状态。 4. 编写API接口。使用Spring Boot来构建后端服务。创建一个Spring Boot项目,添加所需的依赖,如Spring Web、Spring Data JPA等。编写API接口的Controller类,定义各个接口的URL映射和请求处理方法。 5. 连接数据库。使用Spring Data JPA或其他适当的技术在后台系统中连接数据库。配置数据库连接信息,创建实体类和仓库接口,实现对数据库的增删改查操作。 6. 实现前后端交互。在Vue3项目中,使用axios或其他合适的HTTP库发送HTTP请求到后端接口,获取数据并进行展示。前端页面通过调用API接口来实现数据的增删改查操作。 7. 运行和部署。在开发过程中可以使用命令行运行Vue3前端项目和Spring Boot后端项目,通过不同的端口来访问前后端。在开发完成后,可以使用打包工具如Webpack将前端项目打包成静态文件,然后将打包结果部署到服务器,运行Spring Boot项目。 通过以上步骤,你就可以搭建一个基于Vue3和Spring Boot的后台管理系统。这个系统可以实现前后端分离,通过API接口进行数据交互,具备良好的可扩展性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值