////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//             佛祖保佑       永不宕机      永无BUG                 //
////////////////////////////////////////////////////////////////////

一、快速入门——自动生成代码功能

①启动前端

执行命令

npm install
npm run dev

启动完成后,浏览器自动打开若依后台管理登录页面

②导入数据库表

准备SQL并导入数据库,在左侧列表找到“系统工具-代码生成

导入需要生成代码的数据库表

③生成代码 

导入后将页面拖动到底部,最后一个表即为新导入的数据库表

点击“编辑”可以进行详细设置

选择后点击“生成

 浏览器自动下载导出.zip文件

解压后

  • main——后端代码
  • vue——前端代码
  • ***.sql——动态菜单的SQL文件

然后使用CV大法导入到对应的项目文件中

 二、功能详解——权限控制

RBAC(Role-Based Access Control,基于角色的访问控制)是一种广泛使用的访问控制模型,通过角色来分配和管理用户的菜单权限

(0基础,学IT,月薪过万,就来黑马程序员!) 

通过系统管理进行简单配置即可实现权限控制

 

三、功能详解——数据字典

 用于维护系统中常见静态数据。如:性别,状态...

 四、功能详解——其他功能

五、功能详解——监控相关

六、功能详解——定时任务

示例:每隔5秒控制台输出当前时间

七、功能详解——表单构建

示例:

①制作表单并导出,通过拖拽的方式即可快速完成表单的构建

②复制到对应的前端工程

③创建动态菜单,配置路由地址组件路径

八、功能详解——代码生成

九、功能详解——系统工具

 十、项目结构

①ruoyi-admin

②ruoyi-common

③ruoyi-framework

④ruoyi-system

⑤后端模块依赖关系

⑥前端Vue

⑦表结构

十一、前端源码分析

①html


<el-form 
  :model="queryParams"  <!-- 表单数据对象,表单内的字段会与queryParams中的属性双向绑定 -->
  ref="queryRef"       <!-- 表单的引用标识,可通过this.$refs.queryRef在Vue实例中访问该表单实例,用于表单验证、重置等操作 -->
  :inline="true"       <!-- 开启行内表单模式,表单项会水平排列,适合搜索框等紧凑布局场景 -->
  v-show="showSearch"  <!-- 根据showSearch变量的值控制表单显示/隐藏,v-show通过CSS display实现,元素始终存在于DOM中 -->
  label-width="68px"   <!-- 设置表单标签的固定宽度为68px,确保标签对齐,提升表单美观度 -->
>
</el-form>
<el-form-item label="课程编码" prop="code">
  <el-input
    v-model="queryParams.code"  <!-- 双向绑定查询参数对象的 code 字段,实时同步输入值 -->
    placeholder="请输入课程编码"  <!-- 输入框默认提示文本,引导用户输入 -->
    clearable  <!-- 开启输入框右侧的「清空」按钮,点击可快速清空内容 -->
    @keyup.enter="handleQuery"  <!-- 监听键盘回车键事件,触发查询函数 handleQuery -->
  />
</el-form-item>
<!-- 课程学科选择器:使用 Element UI 的 el-select 组件实现 -->
<el-select 
  v-model="queryParams.subject"  <!-- 双向绑定:选中的学科值会同步到 queryParams.subject -->
  placeholder="请选择课程学科"    <!-- 占位提示:未选择时显示的提示文本 -->
  clearable                     <!-- 清空功能:允许用户点击清除已选内容 -->
>
  <!-- 遍历课程学科字典数组:
       course_subject 是预先定义的字典数组(格式:[{label: '语文', value: 'chinese'}, ...]) -->
  <el-option 
    v-for="dict in course_subject" 
    :key="dict.value"  <!-- 唯一标识:Vue 渲染列表时需要唯一 key,避免重复渲染问题 -->
    :label="dict.label"  <!-- 选项显示文本:用户看到的下拉选项文字(如“数学”) -->
    :value="dict.value"  <!-- 选项实际值:选中后绑定到 v-model 的值(如“math”) -->
  />
</el-select>
<!-- Element UI 栅格系统的列组件,:span="1.5" 表示该列占栅格系统的 1.5 份(基于 24 分栏体系) -->
<el-col :span="1.5">
  <!-- Element UI 按钮组件,实现「修改」功能 -->
  <el-button
    type="success" <!-- 按钮类型:success 对应绿色主题 -->
    plain <!-- 朴素按钮样式(仅保留边框和文字颜色,无背景色) -->
    icon="Edit" <!-- 按钮左侧图标:Edit 图标(需确保图标已注册或引入) -->
    :disabled="single" <!-- 动态禁用:根据 single 变量的值决定是否禁用按钮(true 则禁用) -->
    @click="handleUpdate" <!-- 点击事件:触发 handleUpdate 方法(需在组件 methods 中定义) -->
    v-hasPermi="['course:course:edit']" <!-- 权限控制指令:检查用户是否拥有「course:course:edit」权限,无权限则隐藏/禁用按钮(需自定义指令支持) -->
  >
    修改 <!-- 按钮显示的文本内容 -->
  </el-button>
</el-col>
<!-- 数据展示表格 start -->
<!-- 
  el-table:Element UI 表格组件,用于展示课程数据
  v-loading:绑定加载状态,加载时显示加载动画(loading 为组件内定义的响应式变量)
  :data:表格数据源,绑定课程列表数据(courseList 为组件内定义的响应式数组)
  @selection-change:可选事件(代码中未完整写处理函数,通常用于监听行选择变化,如多选操作)
-->
<el-table v-loading="loading" :data="courseList" @selection-change>

  <!-- 多选列:用于批量选择行数据,固定宽度 55px,内容居中 -->
  <el-table-column type="selection" width="55" align="center" />

  <!-- 课程ID列:显示课程的唯一标识,prop 绑定数据中的 `id` 字段 -->
  <el-table-column label="课程id" align="center" prop="id" />

  <!-- 课程编码列:显示课程编码,prop 绑定数据中的 `code` 字段 -->
  <el-table-column label="课程编码" align="center" prop="code" />

  <!-- 课程学科列:通过字典组件展示(若依框架的 dict-tag 组件) -->
  <el-table-column label="课程学科" align="center" prop="subject">
    <!-- 
      作用域插槽(#default="scope"):获取当前行数据(scope.row 为当前行对象)
      dict-tag:若依封装的字典标签组件,用于将字典值转换为文本
      :options:绑定字典数据源(course_subject 为组件内定义的字典数据,如学科枚举)
      :value:绑定当前行的学科值(scope.row.subject),用于匹配字典文本
    -->
    <template #default="scope">
      <dict-tag :options="course_subject" :value="scope.row.subject"/>
    </template>
  </el-table-column>

  <!-- 课程名称列:显示课程名称,prop 绑定数据中的 `name` 字段 -->
  <el-table-column label="课程名称" align="center" prop="name" />

  <!-- 价格列:显示课程价格,prop 绑定数据中的 `price` 字段 -->
  <el-table-column label="价格" align="center" prop="price" />

  <!-- 适用人群列:显示课程适用人群,prop 绑定数据中的 `applicablePerson` 字段 -->
  <el-table-column label="适用人群" align="center" prop="applicablePerson" />

  <!-- 课程介绍列:显示课程描述信息,prop 绑定数据中的 `info` 字段 -->
  <el-table-column label="课程介绍" align="center" prop="info" />

  <!-- 操作列:自定义操作按钮,设置特殊样式(small-padding fixed-width) -->
  <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
    <template #default="scope">
      <!-- 
        修改按钮:
        - link 类型:文字按钮样式
        - type="primary":主题色(通常为蓝色)
        - icon="Edit":Element UI 内置编辑图标
        - @click:点击触发 handleUpdate 方法(传入当前行数据 scope.row)
        - v-hasPermi:若依权限指令,判断用户是否有 `course:course:edit` 权限,有权限才显示按钮
      -->
      <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['course:course:edit']">修改</el-button>

      <!-- 
        删除按钮:
        - 逻辑同修改按钮,权限为 `course:course:remove`,点击触发 handleDelete 方法
      -->
      <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['course:course:remove']">删除</el-button>
    </template>
  </el-table-column>

</el-table>
<!-- 分页组件:用于数据列表的分页展示,以下为各属性说明 -->
<pagination
  v-show="total>0" <!-- 控制分页组件的显示逻辑:仅当总数据量 total 大于 0 时,才渲染该组件 -->
  :total="total"   <!-- 向分页组件传递总数据条数,用于计算总页数等逻辑 -->
  v-model:page="queryParams.pageNum" <!-- 双向绑定当前页码:分页组件的页码变化会同步更新 queryParams.pageNum -->
  v-model:limit="queryParams.pageSize" <!-- 双向绑定每页条数:分页组件的每页数量变化会同步更新 queryParams.pageSize -->
  @pagination="getList" <!-- 监听分页组件的 pagination 事件(如页码切换、每页条数变化),触发时调用 getList 函数重新获取数据列表 -->
/>
<!-- 课程管理的添加/修改对话框组件 -->
<el-dialog 
  :title="title"  // 对话框标题,新增时为"添加课程",编辑时为"修改课程"
  v-model="open"  // 控制对话框显示/隐藏的双向绑定变量
  width="500px"   // 对话框宽度
  append-to-body  // 将对话框插入到body元素下,避免父容器样式影响
>
  <!-- 课程信息表单 -->
  <el-form 
    ref="courseRef"  // 表单引用,用于调用表单校验等方法
    :model="form"    // 绑定的表单数据对象,存储课程相关字段
    :rules="rules"   // 表单校验规则,定义各字段的验证逻辑
    label-width="80px"  // 表单标签宽度
  >
    <!-- 课程编码输入项 -->
    <el-form-item label="课程编码" prop="code">  // label显示文本,prop关联form.code及对应校验规则
      <el-input v-model="form.code" placeholder="请输入课程编码" />  // 双向绑定课程编码字段
    </el-form-item>

    <el-form-item label="课程学科" prop="subject">...</el-form-item>  <!-- 课程学科输入项,prop关联form.subject -->
    <el-form-item label="课程名称" prop="name">...</el-form-item>      <!-- 课程名称输入项,prop关联form.name -->
    <el-form-item label="价格" prop="price">...</el-form-item>         <!-- 价格输入项,prop关联form.price -->
    <el-form-item label="适用人群" prop="applicablePerson">...</el-form-item> <!-- 适用人群输入项,prop关联form.applicablePerson -->
    <el-form-item label="课程介绍" prop="info">...</el-form-item>      <!-- 课程介绍输入项,prop关联form.info -->
  </el-form>

  <!-- 对话框底部区域(自定义插槽) -->
  <template #footer>  
    <div class="dialog-footer">  <!-- 底部按钮容器,通常用于放置操作按钮 -->
      <!-- 确定按钮:点击触发表单提交逻辑 -->
      <el-button type="primary" @click="submitForm">确定</el-button>  
      <!-- 取消按钮:点击触发关闭对话框等逻辑 -->
      <el-button @click="cancel">取消</el-button>  
    </div>
  </template>
</el-dialog>

②JavaScript 

// 导入课程相关的API接口函数,分别用于获取课程列表、获取单个课程详情、删除课程、添加课程和更新课程
import { listCourse, getCourse, delCourse, addCourse, updateCourse } from "@/api/course/course";

// 获取当前组件实例的代理对象,用于在setup中访问组件的属性和方法(类似Vue2中的this)
const { proxy } = getCurrentInstance();

// 响应式变量定义
const courseList = ref([]); // 存储课程列表数据
const open = ref(false); // 控制弹窗(如新增/编辑课程弹窗)的显示与隐藏
const loading = ref(true); // 加载状态标识,用于显示加载动画
const showSearch = ref(true); // 控制搜索区域的显示与隐藏
const ids = ref([]); // 存储选中的课程ID数组,用于批量操作
const single = ref(true); // 控制是否允许单选(可能用于表格选择功能)
const multiple = ref(true); // 控制是否允许多选(可能用于表格选择功能)
const total = ref(0); // 存储课程总数,用于分页展示
const title = ref(""); // 弹窗标题(如"新增课程"或"编辑课程")

// 响应式对象,包含表单数据、查询参数和表单验证规则
const data = reactive({
  form: {}, // 存储新增/编辑课程的表单数据
  queryParams: { // 存储课程列表的查询参数
    pageNum: 1, // 当前页码
    pageSize: 10, // 每页条数
    code: null, // 课程编码查询条件
    subject: null, // 课程学科查询条件
    name: null, // 课程名称查询条件
    applicablePerson: null, // 适用人群查询条件
  },
  rules: { // 表单验证规则,用于新增/编辑课程时的表单验证
    code: [
      { required: true, message: "课程编码不能为空", trigger: "blur" } // 课程编码必填验证
    ],
    subject: [
      { required: true, message: "课程学科不能为空", trigger: "blur" } // 课程学科必填验证
    ],
    name: [
      { required: true, message: "课程名称不能为空", trigger: "blur" } // 课程名称必填验证
    ],
    price: [
      { required: true, message: "价格不能为空", trigger: "blur" } // 价格必填验证
    ],
    applicablePerson: [
      { required: true, message: "适用人群不能为空", trigger: "blur" } // 适用人群必填验证
    ],
    info: [
      { required: true, message: "课程介绍不能为空", trigger: "blur" } // 课程介绍必填验证
    ],
  }
});

// 将data对象中的属性转为ref类型,方便在模板中使用(保持响应式)
const { queryParams, form, rules } = toRefs(data);
/** 查询课程管理列表 */
function getList() {
  loading.value = true;
  listCourse(queryParams.value).then(response => {
    courseList.value = response.rows;
    total.value = response.total;
    loading.value = false;
  });
}

// 取消按钮
function cancel() {
  open.value = false;
  reset();
}

// 表单重置
function reset() {
  form.value = {
    id: null,
    code: null,
    subject: null,
    name: null,
    price: null,
    applicablePerson: null,
    info: null,
    createTime: null,
    updateTime: null
  };
  proxy.resetForm("courseRef");
}

/** 搜索按钮操作 */
function handleQuery() {
  queryParams.value.pageNum = 1;
  getList();
}

/** 重置按钮操作 */
function resetQuery() {
  proxy.resetForm("queryRef");
  handleQuery();
}

// 多选框选中数据
function handleSelectionChange(selection) {
  ids.value = selection.map(item => item.id);
  single.value = selection.length != 1;
  multiple.value = !selection.length;
}

/** 新增按钮操作 */
function handleAdd() {
  reset();
  open.value = true;
  title.value = "添加课程管理";
}

/** 修改按钮操作 */
function handleUpdate(row) {
  reset();
  const _id = row.id || ids.value
  getCourse(_id).then(response => {
    form.value = response.data;
    open.value = true;
    title.value = "修改课程管理";
  });
}

/** 提交按钮 */
function submitForm() {
  proxy.$refs["courseRef"].validate(valid => {
    if (valid) {
      if (form.value.id != null) {
        updateCourse(form.value).then(response => {
          proxy.$modal.msgSuccess("修改成功");
          open.value = false;
          getList();
        });
      } else {
        addCourse(form.value).then(response => {
          proxy.$modal.msgSuccess("新增成功");
          open.value = false;
          getList();
        });
      }
    }
  });
}

/** 删除按钮操作 */
function handleDelete(row) {
  const _ids = row.id || ids.value;
  proxy.$modal.confirm('是否确认删除课程管理编号为"' + _ids + '"的数据项?').then(function() {
    return delCourse(_ids);
  }).then(() => {
    getList();
    proxy.$modal.msgSuccess("删除成功");
  }).catch(() => {});
}

/** 导出按钮操作 */
function handleExport() {
  proxy.download('course/course/export', {
    ...queryParams.value
  }, `course_${new Date().getTime()}.xlsx`)
}

getList();

十二、后端源码分析

/**
 * 查询课程管理列表
 * 这是JavaDoc注释,用于描述当前方法的功能,便于团队协作和代码维护
 */
// Spring Security权限注解,方法执行前会先校验权限
// @ss是若依框架自定义的权限表达式前缀,对应SecurityUtils工具类
// hasPermi方法会检查当前登录用户是否拥有"course:course:list"这个权限标识
// 权限标识在若依的"菜单管理"中配置,通过角色-用户关联实现权限控制
@PreAuthorize("@ss.hasPermi('course:course:list')")
// Spring MVC注解,将HTTP GET请求映射到/list路径
// 结合控制器类上的@RequestMapping前缀,完整访问路径为/模块名/list
@GetMapping("/list")
// 方法返回值TableDataInfo是若依封装的表格数据对象,包含分页信息和数据列表
// 参数Course course由Spring MVC自动进行参数绑定,接收前端传递的查询条件
public TableDataInfo list(Course course) {
    // 开启分页功能,调用的是BaseController中的方法
    // 底层使用PageHelper分页插件,会解析前端传入的page(页码)和limit(每页条数)参数
    // 通过ThreadLocal存储分页参数,后续MyBatis执行SQL时会自动添加LIMIT条件
    startPage();
    // 调用Service层方法查询课程列表,传入查询条件对象
    // Service层会进一步调用Mapper接口,MyBatis会根据条件生成动态SQL
    // 由于PageHelper已开启分页,这里查询的实际是当前页的数据
    List<Course> list = courseService.selectCourseList(course);
    // 封装查询结果为TableDataInfo对象并返回
    // 底层会通过PageInfo获取总记录数,设置到TableDataInfo的total属性中
    // 返回的数据结构包含code状态码、msg提示信息、rows数据列表和total总条数
    return getDataTable(list);
}

对于courseService中的selectCourseList方法,执行的是分页查询的操作,其底层逻辑为:

①BaseEntity 

/**
 * BaseEntity 解析说明
 * 
 * 一、类的基础定位  
 * BaseEntity 是一个基础实体类,实现 Serializable 接口(支持序列化,用于网络传输、持久化等场景)
 * 通常被业务实体类继承,用于抽取公共字段,避免重复定义
 */
public class BaseEntity implements Serializable {

    /**
     * 存储搜索条件
     * 注解 @JsonIgnore 表示序列化(转JSON)时忽略该字段,避免暴露给前端
     */
    @JsonIgnore
    private String searchValue;

    /**
     * 记录数据创建者(如用户ID或用户名)
     */
    private String createBy;

    /**
     * 记录创建时间
     * 注解 @JsonFormat 指定 JSON 序列化/反序列化时的日期格式(yyyy-MM-dd HH:mm:ss)
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    /**
     * 记录数据更新者(如用户ID或用户名)
     */
    private String updateBy;

    /**
     * 记录更新时间
     * 注解 @JsonFormat 指定 JSON 序列化/反序列化时的日期格式(yyyy-MM-dd HH:mm:ss)
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;

    /**
     * 存储备注信息(业务自定义说明)
     */
    private String remark;

    /**
     * 存储动态请求参数(如分页、排序条件)
     * 注解 @JsonInclude(NON_EMPTY) 表示仅当Map非空时才序列化到JSON,避免空字段冗余
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private Map<String, Object> params;


    /**
     * 三、设计思路与价值  
     * 1. 公共字段复用:
     * 将创建者、创建时间、更新者、更新时间等通用字段抽取到基类,业务实体(如User、Order)继承后直接复用,减少代码冗余
     * 
     * 2. JSON 序列化控制:
     * - @JsonIgnore:隐藏敏感/临时字段(如searchValue仅用于接收前端参数,无需返回)
     * - @JsonFormat:统一日期格式,避免前端解析混乱
     * - @JsonInclude(NON_EMPTY):精简JSON输出,空数据不序列化,提升传输效率
     * 
     * 3. 扩展性:
     * params为Map类型,支持灵活传递动态参数(如分页参数pageNum、pageSize),无需为每个业务定义固定字段
     */
}

十三、权限注解

 十四、前后端交互流程

// 创建axios实例
const service =axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL :import.meta.env.VITE_APP_BASE_API,
// 超时
timeout:10000
})
  • .env.development(开发环境)
  • .env.production(生产环境)
  • .env.staging(测试环境)
// request拦截器:用于在请求发送前对请求配置进行处理
service.interceptors.request.use(config => {
  // 判断是否需要设置token(默认需要,除非显式指定isToken为false)
  // 从请求头中获取isToken配置,若未配置则默认视为需要token
  const isToken = (config.headers || {}).isToken === false;
  
  // 判断是否需要防止数据重复提交(默认需要,除非显式指定repeatSubmit为false)
  const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;

  // 如果存在token且当前请求需要携带token,则在请求头中添加Authorization
  if (getToken() && !isToken) {
    // 按照Bearer Token规范,在token前添加"Bearer "前缀
    config.headers['Authorization'] = 'Bearer ' + getToken(); 
    // 注:实际项目中可根据后端要求调整token格式和请求头字段
  }

  // 处理GET请求的参数:将params参数转换为URL查询字符串
  if (config.method === 'get' && config.params) {
    // 调用tansParams函数(自定义参数转换函数)将params转为key=value&...格式
    let url = config.url + '?' + tansParams(config.params);
    // 去除最后一个多余的&符号(如果存在)
    url = url.slice(0, -1);
    // 清空params,避免重复携带参数
    config.params = {};
    // 更新请求URL为拼接后的完整URL
    config.url = url;
  }

  // 处理防重复提交逻辑:仅对POST/PUT请求生效,且未禁用防重复提交时
  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
    // 构建当前请求的标识对象,用于后续对比
    const requestObj = {
      url: config.url, // 请求地址
      // 将请求数据转为字符串(确保对象类型的数据可比较)
      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
      time: new Date().getTime() // 请求发送时间戳
    };

    // 计算当前请求数据的大小(字节)
    const requestSize = Object.keys(JSON.stringify(requestObj)).length;
    // 限制缓存的请求数据大小为5M(防止占用过多内存)
    const limitSize = 5 * 1024 * 1024;

    // 如果请求数据超过5M,不进行防重复提交验证(避免缓存过大)
    if (requestSize >= limitSize) {
      console.warn(`[${config.url}]: 请求数据大小超出允许的5M限制,无法进行防重复提交验证。`);
      return config;
    }

    // 从sessionStorage中获取上一次的请求缓存(通过自定义cache工具)
    const sessionObj = cache.session.getJSON('sessionObj');

    // 如果缓存中没有历史请求记录,直接将当前请求存入缓存
    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
      cache.session.setJSON('sessionObj', requestObj);
    } else {
      // 提取历史请求的关键信息
      const s_url = sessionObj.url;       // 历史请求地址
      const s_data = sessionObj.data;     // 历史请求数据
      const s_time = sessionObj.time;     // 历史请求时间
      const interval = 1000;              // 重复提交判断间隔(1秒内视为重复)

      // 判断是否为重复提交:地址相同 + 数据相同 + 时间间隔小于1秒
      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
        const message = '数据正在处理,请勿重复提交';
        console.warn(`[${s_url}]: ` + message);
        // 抛出错误阻止请求发送,由业务代码捕获处理
        return Promise.reject(new Error(message));
      } else {
        // 不是重复提交,更新缓存为当前请求
        cache.session.setJSON('sessionObj', requestObj);
      }
    }
  }

  // 返回处理后的配置,继续发送请求
  return config;
}, error => {
  // 请求拦截器错误处理:打印错误信息并返回错误Promise
  console.log(error);
  return Promise.reject(error);
});
// 响应拦截器
service.interceptors.response.use(res => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200;
    // 获取错误信息
    const msg = errorCode[code] || res.data.msg || errorCode['default']
    // 二进制数据则直接返回
    if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {
      return res.data
    }
    if (code === 401) {
      if (!isRelogin.show) {
        isRelogin.show = true;
        MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
          isRelogin.show = false;
          store.dispatch('LogOut').then(() => {
            location.href = '/index';
          })
      }).catch(() => {
        isRelogin.show = false;
      });
    }
      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
    } else if (code === 500) {
      Message({ message: msg, type: 'error' })
      return Promise.reject(new Error(msg))
    } else if (code === 601) {
      Message({ message: msg, type: 'warning' })
      return Promise.reject('error')
    } else if (code !== 200) {
      Notification.error({ title: msg })
      return Promise.reject('error')
    } else {
      return res.data
    }
  },
  error => {
    console.log('err' + error)
    let { message } = error;
    if (message == "Network Error") {
      message = "后端接口连接异常";
    } else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    } else if (message.includes("Request failed with status code")) {
      message = "系统接口" + message.substr(message.length - 3) + "异常";
    }
    Message({ message: message, type: 'error', duration: 5 * 1000 })
    return Promise.reject(error)
  }
)

十五、新增模块

①新增模块

②在新增模块的xml文件中添加

    <dependencies>
        <dependency>
            <groupId>com.RB</groupId>
            <artifactId>RB-framework</artifactId>
        </dependency>
    </dependencies>

③在父工程中进行版本锁定

            <dependency>
                <groupId>com.RB</groupId>
                <artifactId>RB-music</artifactId>
                <version>${RB.version}</version>
            </dependency>

④在admin中添加

        <dependency>
            <groupId>com.RB</groupId>
            <artifactId>RB-music</artifactId>
        </dependency>

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐