1. 初识若依
-
网站链接
1.1. 若依搭建
-
后端项目克隆与初始化
-
拉取项目:
git clone https://gitee.com/y_project/RuoYi-Vue.git -
从gitee下载项目完毕后,每个模块高亮加

- 如果没有下载完毕,就在maven生命周期中依次点击clean => package
-
-
基础配置
-
创建数据库
这里我将数据库创建到云端,并用navicat连接了,也可以本地创建数据库

执行sql文件夹的两个sql文件,建立表格并初始化

配置项目数据库连接(改为自己的数据库)

-
配置redis
这里redis在本地的话不需要怎么配置,如果像我一样redis也连的服务器(因为我懒得每次开发都启动),就要改一下

-
-
启动项目成功

可能遇到的警告
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by io.netty.util.internal.PlatformDependent0$4 (file:/C:/Users/xxx/.m2/repository/io/netty/netty-common/4.1.92.Final/netty-common-4.1.92.Final.jar)
WARNING: Please consider reporting this to the maintainers of class io.netty.util.internal.PlatformDependent0$4
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
关键说明
- 这只是 警告而非错误,不会影响项目运行(项目已正常启动,功能不受影响);
- 根源是 JDK 9+ 引入的「模块系统(Module)」,对 System.load、System.loadLibrary 等调用 native 代码的方法做了权限限制,高版本 JDK(如 JDK 17+、24)会强化这种限制并给出警告;
- Tomcat 9 是较稳定的版本,但未针对 JDK 24 做模块权限适配,导致触发警告
-
前端项目搭建
-
拉取项目:
git clone https://gitee.com/ys-gitee/RuoYi-Vue3 -
运行
npm install下载依赖 -
运行
npm run dev运行项目即可
输入验证码完成登录

-
1.2. 入门案例
使用若依代码生成器,生成课程管理的前后端代码
-
准备sql语句并导入数据库

sql建表语句
sql-- MySQL dump 10.13 Distrib 8.0.31, for Win64 (x86_64) -- -- Host: 127.0.0.1 Database: ry-vue -- ------------------------------------------------------ -- Server version 8.0.31 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!50503 SET NAMES utf8mb4 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `tb_course` -- DROP TABLE IF EXISTS `tb_course`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `tb_course` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '课程id', `code` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '课程编码', `subject` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '课程学科', `name` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '课程名称', `price` int DEFAULT NULL COMMENT '价格(元)', `applicable_person` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '适用人群', `info` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '课程介绍', `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin COMMENT='课程管理'; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `tb_course` -- LOCK TABLES `tb_course` WRITE; /*!40000 ALTER TABLE `tb_course` DISABLE KEYS */; INSERT INTO `tb_course` VALUES (1,'cp123456','javaEE','JavaSE基础',199,'小白学员','JavaSE基础','2024-04-20 09:57:35','2024-04-20 09:57:35'),(2,'cp123457','javaEE','JavaWeb',188,'初级开发者','JavaWeb','2024-04-20 09:57:35','2024-04-20 09:57:35'),(3,'cp123458','Python+大数据','Python入门',555,'小白学员','Python入门','2024-04-20 09:57:35','2024-04-20 09:57:35'),(4,'cp123459','Python+大数据','PythonWeb',88,'初级开发者','PythonWeb','2024-04-20 09:57:35','2024-04-20 09:57:35'),(5,'cp123460','鸿蒙应用开发','鸿蒙入门',99,'小白学员','鸿蒙入门','2024-04-20 09:57:35','2024-04-20 09:57:35'),(6,'cp123461','鸿蒙应用开发','鸿蒙商城实战',59,'初级开发者','鸿蒙商城实战','2024-04-20 09:57:35','2024-04-20 09:57:35'); /*!40000 ALTER TABLE `tb_course` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2024-04-21 17:45:54 -
配置代码生成信息
-
配置基本信息

-
配置字段信息

-
配置生成信息

-
下载代码

-
-
导入代码
-
数据库导入
解压后如图

执行sql文件,发现sys_menu表中,课程菜单名已经导入

-
前端导入
复制api和views下的course文件夹,到前端项目对应的api和views文件夹下
注意:course文件夹下还有一个course文件夹,++我们要复制的是外层的course文件夹++
-
后端导入
复制java的com文件夹和resources下的mapper文件夹,项目中的java文件夹和resources文件夹下

课程模块已经开发完毕!!!

-
-
我们上面配置
配置生成信息时上级菜单是0所以,在根目录下,我们还可以如图选择课程管理在菜单中的位置
2. 功能详解
2.1. 系统管理
2.1.1. 权限控制
-
若依内置了强大的权限控制系统,为企业级项目提供了通用的解决方案。
-
以CRM系统为例,演示下权限功能(地址:https://huike-crm.itheima.net/)
- demo账号(超级管理员),查看所有功能菜单
- zhangsan账号(市场专员),查看线索菜单
- yueyue账号(销售专员),查看商机、合同等菜单
-
RBAC
RBAC(基于角色的访问控制)是一种广泛使用的访问控制模型,通过角色来分配和管理用户的菜单权限

-
数据表
用户表和角色表,角色表和权限表,都是多对多关系,所以一共需要5张表

着5张表对应的是3张主体表和2张中间表,下面是表关系说明

2.1.2. 菜单管理
-
添加菜单
-
上级菜单:主目录表示说顶级菜单,没有父级菜单
-
菜单类型
菜单类型 功能定位 显示位置 目录 分组管理下级菜单 菜单列表的上层节点 菜单 对应具体功能页面 菜单列表的可点击项 按钮 对应页面内的操作功能 所属菜单的页面中 -
显示排序:菜单的排列优先级,越小越靠前
-
路由地址 :++必须和前端路由地址对应++ ,如果对应不上,会显示空白,
目录通过菜单层级来映射路由层级以下面的配置为例,数据分析菜单的路由地址为data_nalysis,如果其他菜单的上级菜单选择了数据分析菜单作为上级菜单,然后路由地址配置为output,那么对应的前端真实路由地址为:
/data_nalysis/output
创建完毕后刷新页面即可(如图,页面空白,因为我们没有编写对应的前端页面做路由绑定)

-
-
权限菜单实验
- 创建数据分析员并开放权限如图(这里的权限字符和路由无关哈,自己定义)

2. 创建角色为数据分析员的用户
3. 登录我刚刚创建的用户账号,只能看到首页和用户数据了
2.1.3. 字典管理
-
数据字典
若依内置的数据字典,用于维护系统中常见的静态数据(不经常改变)。例如:性别、状态...

-
修改性别称谓
我们可以通过修改数据字典,来实现对项目中使用该字典的所有值的修改

性别称谓发生改变

-
数据字典的表结构
-
数字字典发表分为字典类型表和字典数据表,两个表是一对多的关系,通过dict_id和dict_code连接
-
数据字典在数据库储存的是0,1,2等简单的数据(dict_value),这可以优化数据库性能

-
-
将课程管理的学科字段改为数据字典来维护
-
添加字典类型和数据
创建数据字典

创建数据字典学科字段

-
修改代码生成信息

-
下载代码,导入前端
用代码生成器生成新的代码,并覆盖前端代码(因为后端代码是没有变的,变的只是数据库数据和前端代码,数据库变的是数值和对应字段的映射,前端则是提交方式改为了下拉框)。
修改完后,课程管理的编辑弹窗的课程学科,由文本框改为了下拉菜单,下拉选项也变为了刚刚我们填写的数据字典的字段。

当我们选择JavaSE基础并提交后,可以看到,我们前端虽然还是传的数字"0",直接储存到数据库,如果要取查数据的话,查到对应的数字,再通过sys_dict_data表查出具体的名称返回

-
2.1.4. 其他功能
-
参数设置
通过参数设置,可以对系统中的参数进行动态维护(不需要改源码,也不需要重启服务器,就可以设置成果生效),比如这里可以将验证码关闭

-
通知公告
若依仅支持公告、通知的基础功能,如果需要对接第三方服务需要自己进行二次开发,后续有二次开发的公告案例

-
日志管理
若依的日志管理包括了操作日志和登录日志,但是随之程序运行时间增大,日志会越来越大,但是我们删除日志又有一定的风险,我们可以设置定任务定时导出日志。

2.2. 系统监控
2.2.1. 定时任务
若依为定时任务功能提供方便友好的web界面,实现动态管理任务。
-
编码实现定时任务
如果要修改,要定位代码位置并修改

-
若依定时任务
-
创建任务类
在quartz模块下的task包创建任务类
-
添加任务规则
只需要写需要执行的任务的逻辑,这里以打印时间为例

-
启动任务
重启项目,为项目新增定时任务如图
-
任务方法
-
Bean调用示例:ryTask.ryParams('ry') -
Class类调用示例:com.ruoyi.quarz.task.RyTask.ryParams('ry)
参数说明:支持字符串,布尔类型,长整型,浮点型,整型
-
-
cron执行表达式
就是执行周期设置,这里我设置5秒执行一次

-
是否并发
取决于时间任务,一般任务会支持并发提高效率。
-
执行策略
举个例子,我们如果服务器宕机了,处理不了任务,任务就会堆在那里,加入堆了5个任务(注意!!!,这里的任务就是指的我们上面编写的代码逻辑),现在服务器能做出运行了,下面是不同执行策略的任务执行情况。
-
立即执行:挨个执行者5个任务
-
执行一次:只执行这5个任务的最后一个任务
-
放弃执行:直接跳过,不执行这5个任务

创建好任务后,重启并开启仍未即可执行任务

-
-
-
-
2.2.2. 监控模块
若依的监控系统包括
-
在线用户
展示当前系统中活跃用户的信息(登录账号、所属部门、登录 IP / 地点、操作系统、浏览器、登录时间等)。
++用户登录后信息会存入 Redis 缓存++,该功能通过查询缓存实现,支持管理员查看和管理在线用户。

-
数据监控
集成
Druid 监控工具(非若依自研,需二次登录),核心功能包括:
-
数据源监控:查看数据库连接池的配置和使用状态;
-
SQL 监控:记录 SQL 执行耗时,快速定位慢 SQL 并优化;
-
SQL 防火墙:拦截危险 SQL(比如无 where 条件的更新操作),避免系统崩溃。

密码在Druid分配置文件,初始账号为:
ruoyi,密码为:123456
-
-
服务监控
实时展示服务器资源状态:CPU 使用率、内存(JVM 堆内存 + 系统物理内存)、磁盘空间、系统负载(1/5/15 分钟趋势)等。
作用:帮助运维人员及时发现资源过载问题(比如 CPU 持续 > 85%),是排查系统卡顿、任务失败的关键工具。

-
缓存监控
针对 Redis 等缓存的状态监控:缓存命中率、当前连接数、内存使用占比等,支持对缓存数据的查询、删除、清空等操作,便于管理缓存资源。

-
缓存列表
直接展示系统中缓存的具体数据内容(比如用户会话、配置参数等),方便管理员直观查看缓存信息。
这些功能覆盖了从用户行为到服务器资源、从业务数据到缓存状态的全链路监控,是保障若依系统稳定运行的重要工具。
2.3. 系统工具
2.3.1. 表单构建
只需要开发者通过图形界面和拖拽等操作,可以快速构建复杂的表单(相比于代码生成的表单生成,表单构建能构建更加复制的表单)。
-
表单制作按钮
要求通过表单构建实现如图表单

-
制作表单并导出
-
表单制作模块介绍

-
表单制作容器布局
给组件外部添加行容器,相当于给单行文本添加了父盒子,行容器和组件都有表单栅格数属性,原理就是前端的栅格布局),栅格属性的数值可理解为将父分成24份,其中子盒子占比多少份。在没有添加行容器前,表单组件的父盒子是预览视图,但是加了行容器后,表单组件的父盒子就是行容器。好像没有什么区别,但是需要注意的是,组件单独占一行,不添加行容器的话,组件就算再小,下一个组件还是会换行。

组件属性包括是否可以清空、禁用、必填、正则校验等常规设置项

然后再完成整个表单(默认就有确认和取消按钮)

导出代码

-
-
复制到前端工程
将代码放在course目录下

-
创建动态菜单
这里注意组件路径是组件和views的相对路径

刷新网页,添加课程表单创建完毕

-
2.3.2. 代码生成
代码生成器,根据数据库表结构自动生成前后端CRUD代码,若依提供三种生成模板:单表、树表、主子表(一对多),其中单表我们已经在入门案例中体验了。

-
树表
树表是一种展示层级数据的表格,能展开折叠,清晰呈现父子关系,便于管理,如公司和部门的关系。

-
树表生成
导入部门表

如图,树表的层级是通过dept_id(树编码)和parent_id(父树编码)体现的。

创建代码生成项好后,我们还可以预览根据我们刚刚的配置生成的代码

-
-
主子表
如下图中的表单,一个表单涉及到菜品表和口味表两种表的提交

2.3.3. 系统接口

当然,在若依系统中也是通过iframe来集成的swagger,你也可以直接通过swagger接口地址来访问接口文档
地址(如果在本地跑,而且是8080端口的话):http://localhost:8080/swagger-ui/index.html
-
JWT令牌
当然除了登录接口,都需要token,如果请求头不携带token,就会报错**
401**。若依的token存在了cookie里面,我们可以复制令牌(或者使用登录接口来获取token)
填写token

JWT认证过来之后,右边的小锁图标会变为锁定状态

-
关于swagger的配置说明 :
- 服务器端口:8080(在 application.yml 中配置)
- Swagger请求前缀: /dev-api (在 application.yml 的 swagger.pathMapping 中配置)
- Swagger UI路径: /swagger-ui/index.html (在 ResourcesConfig.java 中配置)
- Swagger状态:已启用(在 application.yml 的 swagger.enabled 中配置)
-
去除请求前缀

3. 源码阅读
3.1. 项目结构
-
前端项目结构

-
后端项目结构
-
后台服务 (
ruoyi-admin)
-
通用工具 (
ruoyi-common)
-
核心工具 (
ruoyi-framework)
-
代码生成 (
ruoyi-generator)和定时任务(ruoyi-quartz)
-
-
模块的依赖关系

-
表结构

3.2. 代码分析
-
前端代码分析
关于前端代码分享这里就不多叙述了,属性vue3的同学看看就知道了,有些若依自定义的属性,上午查一些或者问AI也很容易明白。感兴趣的同学可以将我们生成的课程模块的前端代码过一遍。
-
后端代码分析
我基于前面代码生成器生成的后端代码进行分析(注意我这里文件名和老师有些不一样)

-
CourseController
接收前端请求,调用service处理逻辑并返回处理后的数据,遵循
restful风格。-
注解
其中,
@Log记录用户操作日志(涉及AOP和异步管理器),@PreAuthorize做++权限校验控制++
-
查询课程表(分页)
java@PreAuthorize("@ss.hasPermi('course:course:list')") @GetMapping("/list") public TableDataInfo list(TbCourse tbCourse) { // 1. 开启分页 startPage(); // 2. 查询课程列表 List<TbCourse> list = tbCourseService.selectTbCourseList(tbCourse); // 3.返回表格分页数据对象 return getDataTable(list); }- 开始分页
鼠标左键+Ctrl点击
startPage(),找到了所属类BaseController,是web层通用的数据处理类
CourseController也继承与这个类

我们按住Alt + 7可以看到整个类的属性方法

通过以下操作,我们可以找分页的具体处理逻辑
javapublic static void startPage() { // 1. 创建一个PageDomain的对象,用于接收前端分页的参数 PageDomain pageDomain = TableSupport.buildPageRequest(); // 2. 从PageDomain的对象中提取除当前页码、每页大小、排序条件 Integer pageNum = pageDomain.getPageNum(); Integer pageSize = pageDomain.getPageSize(); // 3. 对排序条件进行转义处理,防止SQl注入 String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); Boolean reasonable = pageDomain.getReasonable(); // 获取是否合理分页 // 4. 使用PageHelper开启分页,设置页码、每页大小排序条件、是否合理分页 PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); }
2. 查询课程列表分页插件的逻辑如下

3. 返回表格分页数据对象返回分页查询的数据

总结出来,分类查询返回如图

-
导出课表
使用了一个wxcel工具类

-
其他操作(非分页)
例如获取课程管理详细信息(非分页查询),使用了success对象返回,代码如下
java@PreAuthorize("@ss.hasPermi('course:course:query')") @GetMapping(value = "/{id}") public AjaxResult getInfo(@PathVariable("id") Long id) { return success(tbCourseService.selectTbCourseById(id)); }使用了(AjaxResult:操作消息提醒)来返回数据,

其他操作(例如新增),则返回toAjax对象,主要来返回影响数据的调试
java@PreAuthorize("@ss.hasPermi('course:course:add')") @Log(title = "课程管理", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody TbCourse tbCourse) { return toAjax(tbCourseService.insertTbCourse(tbCourse)); } -
BaseEntity
Entity的基类,其中
searchValue为全文检索时,记录前端传递的参数;params为map集合的请求参数,是为了灵活接收参数。
-
-
CourseService及实现类
-
接口类
声明方法定义规范,让实现类完成具体的操作
其中,新增、修改、删除返回值为int类型,是因为我们在进行数据库操作时,增删改会返回影响的行数,当影响行数大于0,表明操作成功。

-
实现类
实现接口,重写所有的抽象方法,调用mapper完成数据库的相关操作,其中添加和修改操作有字段填充字段。

-
-
CourseMapper及映射文件
完成对数据从操作
-
Course
继承BaseEntity基类,和数据库的表字段完成一一对应

-
-
前后端交互流程
-
查询课程管理列表
查询请求

前端交互

反向代理,解决前端路径和后端请求不一致的问题

在前端项目中的vite.config.js中有以下代码,来进行反向代理(不过这种方式只有在开发模式生效,如果打包为dist部署到服务器,就需要勇nginx来配置反向代理),并对
/dev-api进行重写。jsserver: { proxy: { '/dev-api': { target: 'http://localhost:8080', changeOrigin: true, rewrite: (p) => p.replace(/^\/dev-api/, '') } } }
-
3.3. 权限注解
-
@PreAuthorize注解是 Spring Security 框架中用来做权限检查的。 -
它在运行方法前先验证权限,权限够就放行,不够就拦截。

-
权限控制流程图

4. 二次开发
4.1. 新建业务模块
-
若依脚手架修改器
若依框架修改器是一个可以一键修改RuoYi框架包名、项目名等的工具。
地址:
https://gitee.com/lpf_project/RuoYi-MT/releases下载好后是一个exe文件,我们将若依项目压缩为zip压缩包,然后双击启动整个exe文件就可以继续包名修改了

项目修改成功,如果项目能正常运行,就没有什么问题了。

-
创建外卖管理系统系统模块
-
创建子模块
鼠标右击父工程,新建模块

-
引入核心模块 (++为了让项目能够使用到framework模块++)
在admin模块的pom.xml中找到核心模块坐标
xml<!-- 核心模块--> <dependency> <groupId>com.jia</groupId> <artifactId>jia-framework</artifactId> </dependency>在我们创建的模块的pom.xml中,添加
xml<!-- 核心模块--> <dependencies> <dependency> <groupId>com.jia</groupId> <artifactId>jia-framework</artifactId> </dependency> </dependencies>
-
-
父工程版本锁定
在父工程中,对新建的模块进行版本锁定,在父工程的pom.xml中,添加下面配置
xml<!-- 商家管理 --> <dependency> <groupId>com.jia</groupId> <artifactId>sky-merchant</artifactId> <version>${jia.version}</version> </dependency> -
sky-admin依赖
在admin模块的pom.xml中添加下面配置(不需要指定版本了,父工程已经指定)
xml<!-- 商家管理 --> <dependency> <groupId>com.jia</groupId> <artifactId>sky-merchant</artifactId> </dependency>
-
4.2. 菜品管理
利用若依代码生成器(主子表模板),生成菜品管理的前后端代码。这一部分很多和入门案例很像,这里就不写那么详细了。
-
导入sql
sql-- MySQL dump 10.13 Distrib 8.0.31, for Win64 (x86_64) -- -- Host: 127.0.0.1 Database: ry-vue -- ------------------------------------------------------ -- Server version 8.0.31 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!50503 SET NAMES utf8mb4 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `tb_dish` -- DROP TABLE IF EXISTS `tb_dish`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `tb_dish` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT '菜品名称', `price` decimal(10,2) DEFAULT NULL COMMENT '菜品价格', `image` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '图片', `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '描述信息', `status` int DEFAULT '1' COMMENT '0 停售 1 起售', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `idx_dish_name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin COMMENT='菜品管理'; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `tb_dish` -- LOCK TABLES `tb_dish` WRITE; /*!40000 ALTER TABLE `tb_dish` DISABLE KEYS */; INSERT INTO `tb_dish` VALUES (30,'干锅牛蛙',38.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/56395e06-6d86-4d16-8d0e-18b2da085b8a.jpg','干锅牛蛙',1,'2023-04-13 23:14:12','2023-04-16 22:04:25'),(50,'馒头',1.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/dadaf543-b305-4139-9147-4e9f19f4c84c.jpg','优质面粉',1,'2022-06-10 09:34:28','2023-04-14 09:34:45'),(74,'黑金鲍鱼',68.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/833cf1ae-0835-4278-a374-00395cd4cbe9.jpg','新西兰黑金鲍鱼',1,'2023-02-16 09:48:28','2023-04-13 22:44:08'),(75,'波士顿龙虾',128.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/833cf1ae-0835-4278-a374-00395cd4cbe9.jpg','2斤重 波斯顿龙虾',1,'2023-02-16 09:50:06','2024-05-09 09:53:58'),(76,'香辣烤乌江鱼3斤',108.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/833cf1ae-0835-4278-a374-00395cd4cbe9.jpg','香辣烤乌江鱼3斤',1,'2023-02-16 09:52:30','2023-04-13 22:41:29'),(77,'香辣烤鱼3斤',78.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/dadaf543-b305-4139-9147-4e9f19f4c84c.jpg','香辣烤鱼3斤 草鱼',1,'2023-02-17 15:27:02','2023-04-13 22:39:20'),(80,'宽粉',8.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/c04d2598-0b96-44d7-8ea9-72792b9be66a.jpg','宽粉',0,'2023-04-13 22:48:57','2023-04-15 13:45:03'),(81,'青笋',10.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/3685a52b-46ca-4e2a-8fd2-d90205fa9405.jpg','青笋',1,'2023-04-13 22:49:21','2023-04-13 22:49:21'),(82,'鲜豆皮',8.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/c04d2598-0b96-44d7-8ea9-72792b9be66a.jpg','鲜豆皮',1,'2023-04-13 22:49:52','2023-04-13 22:49:52'),(83,'娃娃菜',6.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/0bf2be55-5ab3-4e4c-8b1c-98e17b515a7a.jpg','娃娃菜',1,'2023-04-13 22:50:26','2023-04-13 22:50:26'),(84,'土豆片',6.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/56395e06-6d86-4d16-8d0e-18b2da085b8a.jpg','土豆片',1,'2023-04-13 22:50:59','2023-04-13 22:50:59'),(85,'鱼豆腐',6.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/76e6258a-1df9-4561-b814-39cb9dc56e5e.jpg','鱼豆腐',1,'2023-04-13 22:53:07','2024-04-29 21:42:07'),(86,'重庆毛血旺',58.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/cc5e7eb5-c9f4-48c1-80e9-a167ae100086.jpg','重庆毛血旺',1,'2023-04-13 22:56:18','2023-04-13 22:56:18'),(87,'重庆辣子鸡',48.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/cc5e7eb5-c9f4-48c1-80e9-a167ae100086.jpg','重庆辣子鸡',1,'2023-04-13 22:56:57','2023-04-13 22:56:57'),(88,'渝乡沸腾鱼片',79.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/833cf1ae-0835-4278-a374-00395cd4cbe9.jpg','渝乡沸腾鱼片 草鱼',1,'2023-04-13 22:58:29','2023-04-13 22:58:29'),(89,'皮蛋豆腐',28.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/df727356-66ba-465c-9da2-c62923d4a1e8.jpg','皮蛋豆腐 鸭蛋 松花江皮蛋',1,'2023-04-13 22:59:45','2023-04-13 22:59:45'),(90,'果仁菠菜',18.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/ca875acb-f103-405f-8379-a6a4c309d6f8.jpg','果仁菠菜 花生 菠菜',1,'2023-04-13 23:01:32','2023-04-13 23:01:32'),(91,'蒜泥黄瓜',12.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/ca875acb-f103-405f-8379-a6a4c309d6f8.jpg','蒜泥黄瓜 大蒜 剁椒',1,'2023-04-13 23:02:32','2023-04-13 23:02:32'),(92,'老醋花生',12.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/56395e06-6d86-4d16-8d0e-18b2da085b8a.jpg','老醋花生 老陈醋',1,'2023-04-13 23:03:15','2023-04-13 23:03:15'),(93,'干锅花菜',28.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/3685a52b-46ca-4e2a-8fd2-d90205fa9405.jpg','干锅花菜',1,'2023-04-13 23:10:32','2023-04-13 23:10:32'),(94,'干锅千页豆腐',30.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/76e6258a-1df9-4561-b814-39cb9dc56e5e.jpg','干锅千页豆腐',1,'2023-04-13 23:11:08','2023-04-13 23:11:08'),(95,'干锅鸡胗',38.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/f805ac90-3c62-4795-ad34-d22f83af78ae.jpg','干锅鸡胗',1,'2023-04-13 23:11:46','2023-04-13 23:11:46'),(96,'干锅藕片',28.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/dadaf543-b305-4139-9147-4e9f19f4c84c.jpg','干锅藕片',1,'2023-04-13 23:13:18','2023-04-13 23:13:18'),(98,'扬州炒饭',12.00,'https://yjy-rjwm-oss.oss-cn-hangzhou.aliyuncs.com/fb11b202-c8f5-4d9e-9e68-d1d3f13bb3f6.jpg','扬州炒饭',1,'2023-04-13 23:17:52','2023-04-16 22:04:29'); /*!40000 ALTER TABLE `tb_dish` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `tb_dish_flavor` -- DROP TABLE IF EXISTS `tb_dish_flavor`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `tb_dish_flavor` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `dish_id` bigint NOT NULL COMMENT '菜品', `name` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '口味名称', `value` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '口味列表', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=198 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin COMMENT='菜品口味关系表'; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `tb_dish_flavor` -- LOCK TABLES `tb_dish_flavor` WRITE; /*!40000 ALTER TABLE `tb_dish_flavor` DISABLE KEYS */; INSERT INTO `tb_dish_flavor` VALUES (1,63,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(111,63,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(112,60,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(113,60,'甜味','[\"无糖\",\"少糖\",\"半糖\",\"多糖\"]'),(114,57,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(115,57,'辣度','[\"不辣\",\"微辣\",\"中辣\"]'),(116,56,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(117,54,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(118,52,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(119,52,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(122,73,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(136,77,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(137,77,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(138,76,'辣度','[\"微辣\",\"中辣\"]'),(139,76,'忌口','[\"不要葱\",\"不要香菜\",\"不要辣\"]'),(140,75,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(141,75,'辣度','[\"不辣\",\"中辣\",\"重辣\"]'),(142,74,'忌口','[\"不要葱\",\"不要蒜\"]'),(143,74,'甜味','[\"无糖\",\"少糖\",\"半糖\"]'),(144,86,'辣度','[\"微辣\",\"中辣\",\"重辣\"]'),(145,87,'辣度','[\"微辣\",\"中辣\",\"重辣\"]'),(146,88,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(147,88,'辣度','[\"微辣\",\"中辣\"]'),(148,89,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(149,89,'甜味','[\"无糖\",\"少糖\"]'),(150,90,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(151,90,'辣度','[\"不辣\",\"微辣\"]'),(152,91,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(153,91,'辣度','[\"不辣\",\"微辣\"]'),(154,92,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(155,51,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(156,51,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(157,72,'辣度','[\"微辣\"]'),(159,53,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(160,53,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(161,93,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]'),(162,93,'辣度','[\"微辣\",\"中辣\"]'),(163,94,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\"]'),(164,94,'辣度','[\"不辣\",\"微辣\"]'),(165,95,'辣度','[\"微辣\",\"中辣\"]'),(166,96,'辣度','[\"微辣\",\"中辣\"]'),(167,97,'忌口','[\"不要蒜\",\"不要香菜\"]'),(168,97,'辣度','[\"微辣\",\"中辣\"]'),(169,67,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(170,66,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(172,65,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]'),(182,85,'甜味','[\"全糖\",\"半糖\",\"少糖\"]'); /*!40000 ALTER TABLE `tb_dish_flavor` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2024-08-28 11:11:06 -
配置代码生成信息
-
创建起售、停售的数据字典

-
生成菜品表代码
-
菜品表

-
口味表

-
-
-
下载代码并导入项目
下载解压菜品表后目录如图,执行sql语句,导入菜品菜单表

导入前端代码

导入后端代码

遗留的问题
启动项目如图,但是还是有一些问题
-
项目不需要展示主键(售价最好添加人民币标志符,格式化更新时间)
-
菜品口味输入是文本框,用户交互不友好(口味名可以优化为文本框,口味标签可以优化成多标签选择器)
-
编辑弹框回显时图片不能正常显示(测试数据不能回显,自己添加的数据可以回显)

-
-
解决遗留问题
老师是用ai来解决这些依赖问题的,目的是带大家熟悉AI的使用,但是我AI已经用了很久了,就直接给出修改方案了。
-
删除主键
在
src\views\merchant\dish\index.vue中,注释相关代码html<!-- <el-table-column label="主键" align="center" prop="id" /> -->添加人民币标志符
html<el-table-column label="售价" align="center" prop="price"> <template #default="scope"> <span>¥{{ scope.row.price }}</span> </template> </el-table-column>格式化更新时间
html<el-table-column label="更新时间" align="center" prop="updateTime" width="180"> <template #default="scope"> <span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span> </template> </el-table-column> -
口味交互优化
通过前端抓包,可以看到

-
将口味名称改为下拉框
设置口味列表数组(一般在正式项目中,dishFlavorListSelect一般是个空表,数据应该来源于后端接口,而不是写死)
js// 定位口味名称和口味列表静态数据 const dishFlavorListSelect = ref([ { name: "辣度", value: ["不辣","微辣","中辣","重辣"] }, { name: "忌口", value: ["不要葱","不要蒜","不要香菜"] }, { name: "甜味", value: ["无糖","少糖","半糖","多糖"] }, ])150行左右,讲输入框改为下拉框
html<template #default="scope"> <!-- <el-input v-model="scope.row.name" placeholder="请输入口味名称" /> --> <el-select v-model="scope.row.name" placeholder="请选择口味名称" > <el-option v-for="dict in dishFlavorListSelect" :key="dict.value" :label="dict.name" :value="dict.name" ></el-option> </el-select> </template> -
口味标签可以优化成多标签选择器
这里没有用多标签选择器,而是下拉框,下面是
创建口味列表数组和更新口味名称时触发的函数js// 存储当前选中口味列表数组 const checkValueList = ref([]) // 定义改变口味名称时更新当前选中的口味的列表 const changeFlavorName = (row) => { // 清空当前选中的口味列表 row.value = [] checkValueList.value = dishFlavorListSelect.value.find(item => item.name === row.name).value }绑定触发函数并将口味列表改为下拉框,其实这里还是有一些bug,像忌口可以多选,但是辣度和甜度只能单选,这个也不难,有兴趣可以自己实现玩玩(这里提供一些思路,从multiple下手,给口味名称添加一个isultiple属性来绑定multiple即可)。
html<el-table-column label="口味名称" prop="name" width="150"> <template #default="scope"> <!-- <el-input v-model="scope.row.name" placeholder="请输入口味名称" /> --> <el-select v-model="scope.row.name" placeholder="请选择口味名称" @change="changeFlavorName(scope.row)" > <el-option v-for="dict in dishFlavorListSelect" :key="dict.value" :label="dict.name" :value="dict.name" ></el-option> </el-select> </template> </el-table-column> <el-table-column label="口味列表" prop="value" width="150"> <template #default="scope"> <el-select v-model="scope.row.value" placeholder="请选择口味列表" multiple> <el-option v-for="value in checkValueList" :key="value" :label="value" :value="value" ></el-option> </el-select> </template> </el-table-column>效果如图

-
解决传输数据不一致问题
按照上面的代码提交口味修改如图

这是因为我们前端传输给后端的是数组(而不是后端需要的字符串),我们需要将口味列表的元素序列号
js/** 提交按钮 */ // ... if (valid) { form.value.dishFlavorList = dishFlavorList.value // 处理口味列表数组为字符串 if (dishFlavorList.value === mull) { // 处理口味列表数组为字符串 form.value.dishFlavorList.forEach(item => { item.value = JSON.parse(item.value) }) } // ... } -
解决口味列表不回显问题
虽然我们将数组序列化为字符串传给后端了,但是后端传给前端的依旧是字符串,如果没有转换,前端自然无法识别
js/** 修改按钮操作 */ // ... getDish(_id).then(response => { form.value = response.data dishFlavorList.value = response.data.dishFlavorList if (dishFlavorList.value != null) { // 处理口味列表数组为字符串 dishFlavorList.value.forEach(item => { item.value = JSON.parse(item.value) }) } open.value = true title.value = "修改菜品管理" }) } -
解决依旧有数据就无法获取焦点

编写获取焦点方法
js// 定义口味列表获取焦点时更新当前选中的口味列表 const focusFlavorName = (row) => { checkValueList.value = dishFlavorListSelect.value.find(item => item.name === row.name).value }@focus绑定vue<el-select v-model="scope.row.value" placeholder="请选择口味列表" multiple @focus="focusFlavorName(scope.row)"> </el-select>其余什么改样式的画,大家感兴趣就改不,我有点困了。
-
-
图片正常回显
本地上传的图片若依默认给存到了本地,老师给的测试数据给的是阿里oss的链接,阿里云是完整的资源访问路径,而若依是若依后端配置和数据库存储路径的拼接

若依的储存地址配置在后端文件sdmin模块的
src/main/resources/application.yml中,大概第10行左右,默认地址为D:/ruoyi/uploadPath在老师的案例中,请求地址会拼接
/profile/upload/xxx,但是我下载的若依版本比较靠后,没有这个问题,但是图片还是没有正常回显。但是,我按照老师的方法修改同样有效
这个补充一下,如果你不能Ctrl + 鼠标右击调整到imaguoload组件,可以安装组件**
vue peek**在
src\components\ImageUpload\index.vue中,的81行左右可以看出baseUrl赋值了vite的环境变量jsconst baseUrl = import.meta.env.VITE_APP_BASE_API;96行左右修改(注意是修改if语句的条件,其中i
tem.indexOf(baseUrl) === -1的意思是:判断变量 item中是否不包含baseUrl这个字符串),然后这个问题就解决了jsif (item.indexOf(baseUrl) === -1 && item.indexOf("http") === -1) { item = { name: baseUrl + item, url: baseUrl + item }; }
-
-
我遇到的坑-
包名不同导致的包扫描问题
可以看到,我的菜品模块的包名为sky ,而其他模块为jia,这也给我带来了很多问题

-
MyBatis类型别名不匹配
在DishMapper.xml中使用了Dish和DishFlavor作为类型别名,但MyBatis的typeAliasesPackage配置只扫描了com.jia. .domain包,而这些类实际上在com.sky.merchant.domain包中。
-
报错示例
Failed to parse mapping resource: 'file [E:\Study\20251212230258\jia\sky-merchant\target\classes\mapper\merchant\DishMapper.xml]' -
解决方法
在admin中的application.yml文件中,添加对jia包的别名匹配
ymltypeAliasesPackage: com.jia.**.domain,com.sky.**.domain
-
-
启动类没有扫描包
-
报错示例
JiaApplication.java的@SpringBootApplication注解没有指定扫描com.sky包,导致DishController无法被Spring Boot扫描到(请求报错404)

-
解决方法
在JiaApplication.java(启动类)中
java@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }, scanBasePackages = {"com.jia", "com.sky"})
-
-
-
数据库表名不对
-
报错示例
Field dishMapper in com.sky.merchant.service.impl.DishServiceImpl required a bean of type 'com.sky.merchant.mapper.DishMapper' that could not be found. The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true) -
解决方法
数据库表名和代码生成表名对不上,大家在生成代码时要把表名称改为和数据库表名一样,如果生成好了代码并导入了项目,就修改数据库表明,这样比较快。

-
-
4.3. 页面调整
-
浏览器标签页icon、标题
-
标签页icon
如果没有icon图像,可以在比特虫 https://www.bitbug.net/上转换(我记得好像这个还是很多年前pink老师的课上用到的)
然后在根目录的index.html中替换
link标签的路径即可(或者直接用同名的icon替换favicon.ico的图像),/favicon.ico在public目录下html<link rel="icon" href="/favicon.ico"> -
标题
根目录的index.html中
html<title>气Π的demo</title>但是不生效,这是因为有环境配置文件给覆盖了,如果是开发环境,就修改根目录下的
.env.development文件# 页面标题 VITE_APP_TITLE = 气Π的demo改好后效果如图

-
-
系统页面中的logo
替换
src\assets\logo\logo.png,图片尺寸改为64 x 64比较合适
-
去除源码 & 文档
在
src\layout\components\Navbar.vue注释掉相关代码html<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom"> <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> </el-tooltip> <el-tooltip content="文档地址" effect="dark" placement="bottom"> <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" /> </el-tooltip> -->
-
主题和自定义图标
-
主题
在头像下拉框 => 布局设置中修改

-
自定义图标
在图标下载网址(注意图标能不能商用),如阿里矢量图标库(iconfont-阿里巴巴矢量图标库)下载svg图标,再复制到前端代码中的
src\assets\icons\svg\下面,然后在若依系统的图标设置就可以使用我们刚刚下载的svg图标了
-
-
登录页面中标题、背景图
-
标题
在
src\views\login.vue中,替换下面即可html<h3 class="title">气Π的demo</h3> -
背景图
在
src\views\login.vue中可以看到,背景图片路径在src\assets\images\login-background.jpg中,替换即可。
-
5. 打包发布
5.1. 后端项目的打包发布
-
修改端口
在admin模块的application.yml中修改后端端口

还是这个文件,把redis配置页改一下

2. 修改数据库连接在application-druid.yml中修改数据库连接

-
打包后端文件
双击生命周期的
package
生成目录的jar路径,会在打包时会在控制台打印出来

在这个目录下运行
java -jar jia-admin.jar,下面有RY的图标就代表打包的jar包能做出运行
-
发布后端项目
-
上传项目
将项目上传到宝塔文件的
/www/wwwroot/xxx文件下面
-
创建项目
创建为java项目并添加jdk信息(你可以下载宝塔里面的jdk,而可以上传自己的jdk)

-
解决日志路径不匹配问题
然后项目报错如图,分析了一下大概是说我服务器没有若依配置中的日志存储路径

就是我们在admin模块的
logback.xml的日志存放路径设置,你可以在服务器创建/home/ruoyi/logs这个路径,或者改为服务器已经存在的路径yml<!-- 日志存放路径 --> <property name="log.path" value="/home/ruoyi/logs" />重新启动项目,并能启动成功,并能访问服务器的swagger文档http://your-sever:8002/swagger-ui/index.html,就代表后端项目发布成功(在开发阶段可以开发测试文档的访问,正式部署记得关闭)
-
5.2. 前端项目的打包发布
-
修改测试环境的后端服务路径
在前端根目录下的
vite.config.js文件,将your-server改为你的后端服务器地址(如果端口部署8002),的话,也可以改,但是前面也说了,这是这个配置只有在测试环境生效jsserver: { port: 80, host: true, open: true, proxy: { // https://cn.vitejs.dev/config/#server-proxy '/dev-api': { target: 'http://your-server:8002', // target: 'https://api.wzs.pub/mock/13', changeOrigin: true, rewrite: (p) => p.replace(/^\/dev-api/, '') } } }, -
不同的环境
在前端项目根目录下,有如下环境配置文件

其中
.env.development内容如图,开发环境的地址为/dev-api,被我们上面的vite.config.js代理转发了# 页面标题 VITE_APP_TITLE = 气Π的demo # 开发环境配置 VITE_APP_ENV = 'development' # 若依管理系统/开发环境 VITE_APP_BASE_API = '/dev-api'-
生产环境和预发布 / 测试的区别
对比维度 .env.production(生产环境) .env.staging(预发布 / 测试环境) 环境标识 VITE_APP_ENV = 'production' VITE_APP_ENV = 'staging' API 基础路径 VITE_APP_BASE_API = '/prod-api' VITE_APP_BASE_API = '/stage-api' 构建命令 npm run build(默认加载该配置)npm run build:stage(通过 --mode staging 加载)核心作用场景 正式部署上线,面向最终用户使用 预发布测试、功能验证、环境适配测试 配置应用范围 生产环境 API 连接、正式部署 URL 路径配置 预发布环境 API 连接、测试环境 URL 路径配置
-
-
修改测试环境的后端服务路径
我们这里就直接用生产环境打包了,配置
.env.production,将下面的/prod-api改为http/https +your-server# 若依管理系统/生产环境 VITE_APP_BASE_API = '/prod-api'然后运行打包命令
npm run build,将项目的打包文件(项目根目录下的dist文件夹),放到宝塔文件夹中,
然后创建php站点如下,域名可以选择IP地址格式或者已经解析的域名,根目录选择上面的dist文件夹,下面的配置不用改

-
解决内容混合问题
一般的同学用IP地址格式解析配置的话不会遇到这个问题,但如果你绑定了SSL证书和域名,而且http和https混用就会导致内容混合问题,浏览器控制台报错如下

这玩意就有点复杂了,但是我相信看到这里的各位基本上都有点东西了,我又就不藏着掖着了
-
首先,你得需要域名是吧,取阿里或腾讯注册一个,然后进行子域名划分(域名云解析),如图(记录值就是你的服务器的主机地址)

-
然后对前后端进行域名配置(要和你上面域名划分的要一样)

-
绑定了域名,就可以申请免费的
Lets Encrypt证书,然后勾选域名就可以申请了,很简单

申请好证书后可以开启强制https访问

然后者时候就有人会问了,
Lets Encrypt证书有效期只有三个月,到期了怎么办,有的孩子,有的,宝塔有任务计划,可以自动执行证书续签脚本,只有执行周期小于三个月就可以实现永久续杯。
-
外网映射
这个在后端项目(我们这里是java)中才有,开启的前提是后端项目配置域名。它的功能有的像反向代理,可以转发重写,没有特殊要求的话就直接写
/(域名请求全部映射到8002端口)
然后改一下前端打包环境的请求路径为域名,后端配置一下跨域设置,就跨域成功运行项目啦。
-