Skip to content

升级指南

v1.3.0-beta

1. 登录密码传输升级:支持 AES-GCM 加密,提升安全性

为强化系统账户安全,登录相关密码参数现已全面支持 AES-GCM 加密传输。前后端交互时,用户密码字段(password)将以 AES-GCM 算法进行加密后发送,最大限度保障密码信息不被窃取或篡改。

image-20251107144430926

一、登录请求示例

前端登录请求体示例:

  • 加密字段:password
  • 随机因子字段:iv
  • 其他辅助参数:clientId、requestId 等

后端会依据 iv 参数完成解密过程,确保密码传输安全可靠。

二、登录请求数限制参数优化

在参数管理模块新增和优化了如下登录安全管控参数:

参数名称Key说明
登录请求次数限制sys.login.requestLimit某时间周期内的登录请求次数限制,按 requestId(Ip+UA)判断,0为不限制
登录次数计数周期(分)sys.login.requestCycle登录请求次数统计的周期,单位为分钟,默认 10 分钟

image-20251107165653113

  • sys.login.requestLimit:限制一段时间内的登录请求次数。若超限,将触发登录保护措施。requestId 由客户端 IP 和 UserAgent 组合生成。
  • sys.login.requestCycle:指定登录请求次数的统计时间范围,默认值为 10 分钟,可根据实际需求灵活调整。

2. 【代码生成器】多文件上传(fileUpload)功能

本次升级,代码生成器新增对多文件上传的支持,涉及后端接口类型变更、前端表单与列表渲染方式,以及数据库表结构调整。请务必阅读并据此更新相关模块,避免兼容性问题。

一、后端类型调整

新增 Java 类型 List<UploadResult> 用于后端服务存储文件。只需在字段定义中选择该类型,代码生成器将在表单中使用 <upload-files> 组件进行多文件上传渲染。 增加java类型List<UploadResult> 用于后端服务的存储,设置此类型后生成的Form表单将使用<upload-files>组件进行渲染。

image-20251107145417091

二、前端表单增强

设置字段类型为 List<UploadResult> 后,表单展示样式升级为“多文件上传”。用户可通过拖拽或选择方式上传多个文件。上传区域及回显效果如下:

image-20251107150013379

重要说明: 在使用 <upload-files> 组件时,需通过 dir 属性指定目标文件夹路径。
代码生成器生成的默认上传目录为 tmp,如需自定义上传存储目录,例如按业务归类(如 "teacher"),请在代码相应位置调整 dir 属性的赋值。

示例代码:

vue
<upload-files
  v-model:modelValue="fileUrls"
  :limit="5" 				// 指定文件上传数量,代码生成器默认指定5个
  :file-size="3"  			// 指定单文件大小3M,代码生成器默认指定3M 
  :dir="'teacher'"          // 指定上传目标文件夹,代码生成器默认指定tmp文件夹
  :debug="true"
  @all-success="handleAllSuccess"
/>

建议在不同业务场景下合理规划上传目录(dir),以便于文件管理和归档。如需修改默认上传路径,请手动调整前端赋值逻辑。

三、列表页面回显方式

需要在前端指定组件类型fileUpload,此项配置后,ProTable 列表页面会自动采用 slot + <file-download-list> 回显附件列表:

image-20251107145728212

列表页面的附件列表展示效果如下:

image-20251107151159581

四、数据库结构变更注意事项

此次升级需调整表结构,将 url 字段类型由原 varchar 修改为 json,即:

url json DEFAULT NULL COMMENT '文件地址(JSON)'

此举带来不兼容性更新,如需升级请确保先备份数据再做迁移及历史兼容性处理。

存储后的 JSON 结构示例

每个附件对应一个对象,整体保存为 JSON 数组,如下所示:

json
[
    {
        "url": "https://minioapi.szadmin.cn/test/teacher/20251021/bg1.png",
        "etag": "73be62dc9778dc13478c59f0c236feca",
        "size": 105636,
        "dirTag": "teacher",
        "fileId": 158,
        "filename": "bg1.png",
        "metaData": {
            "original-filename": "bg1.png"
        },
        "objectName": "teacher/20251021/bg1.png",
        "contextType": "image/png"
    },
    {
        "url": "https://minioapi.szadmin.cn/test/teacher/20251021/bg7.png",
        "etag": "6b6857c36b4fded86645d0ed2662869b",
        "size": 66861,
        "dirTag": "teacher",
        "fileId": 156,
        "filename": "bg7.png",
        "metaData": {
            "original-filename": "bg7.png"
        },
        "objectName": "teacher/20251021/bg7.png",
        "contextType": "image/png"
    },
    {
        "url": "https://minioapi.szadmin.cn/test/teacher/20251021/bg10.png",
        "etag": "9a546cb494af380c9e2cf5efac2ae362",
        "size": 87531,
        "dirTag": "teacher",
        "fileId": 157,
        "filename": "bg10.png",
        "metaData": {
            "original-filename": "bg10.png"
        },
        "objectName": "teacher/20251021/bg10.png",
        "contextType": "image/png"
    }
]

3. 账户管理新增账户类型

本次升级账户管理模块,新增了账户类型管理功能,为系统账户的身份和权限提供更灵活的控制。 通过账户类型,可精准指定“超级管理员账户”,并针对超管账户自动提升其数据权限与菜单权限,实现更高的系统管控能力。

image-20251107162735184

一、账户类型管理功能说明

  • 系统账户的身份由账户类型决定。可在账户管理页面,对账户类型进行选择与配置。
  • 新增“超级管理员账户”类型,该类型账户拥有更高的数据和菜单访问权限,适合平台级运维及系统配置管理场景。

二、超级管理员权限配置

  • 超管账户的权限受账户类型与角色参数双重控制。系统支持通过参数管理模块进行超级管理员角色的配置。
  • 参数 key:sys.admin.superAdminRoleId
    • 用于指定拥有超级管理员权责的角色 ID,对应的角色将被赋予超管账户的特权。

image-20251107163148337

4. 重构角色权限管理(破坏性更新)

此次升级对角色权限管理模块进行了重构,涉及数据表结构调整权限配置合并,属于破坏性更新。原有权限配置可能失效,请升级后务必参考新版方案重新进行角色和权限设置,谨防数据丢失。

一、调整内容与迁移说明

  • 数据权限角色已删除,相关功能已合并至系统角色管理
  • 数据权限管理菜单已移除,数据权限配置入口合并到【角色管理】-【权限按钮】页面完成。
  • 只有开启数据权限的菜单才能进行配置,并且需要代码声明的对应实现,详见 数据权限-使用指南

二、全新权限配置交互说明

新版的权限设置页面,采用目录(菜单)与属性结构的分区样式。

  • 左侧为目录和菜单,支持菜单分组、筛选。
  • 右侧为当前选中菜单的权限配置区:包括功能权限(按钮、操作)和数据权限(访问范围)。
  • 数据权限的开关需在菜单管理中配置,仅开启数据权限的菜单才能在角色编辑页面配置数据范围,且需后台代码声明相应实现。

具体交互效果如下:

1. 功能权限启用但数据权限未启用

image-20251107220138609

仅可勾选功能层面的操作,数据权限配置不可用。

2. 数据权限启用(非自定义模式)

image-20251107220232601

3. 数据权限启用(自定义模式)

image-20251107220303273

支持针对用户、部门等维度灵活自定义数据访问范围,可精细化授权权限颗粒。

4. 功能权限与数据权限均未启用,仅设置菜单外链

image-20251107220853552

仅保留菜单授权功能,如外部链接等,其他权限与数据范围控制关闭。

三、数据库变更注意事项

  • sys_data_role_menusys_data_role 标记为废弃,相关业务已合并至 sys_rolesys_data_role_relation 表。

5. [ImportExcel] 组件模板信息功能优化

本次对 ImportExcel 组件的模板区域进行了升级优化,实现了更加灵活的模板文件下载配置。


一、模板区域结构调整说明

组件升级后,模板信息区域需显示模板的“标识”及“文件名”,并通过下载按钮提供模板文件下载入口。通过指定标识和文件名,系统能够准确定位并下载唯一的模板文件。

image-20251107222226972


二、使用方式说明

在调用 ImportExcel 组件时,需通过参数指定模板标识(alias)和模板文件名(fileName)。组件接口会自动根据这两个参数查询模板文件,并完成下载。

示例代码:

vue
// 导入
const ImportExcelRef = ref<InstanceType<typeof ImportExcel>>();
const importData = () => {
  const params = {
    title: '教师统计',
    tempApi: downloadTemplate,
    importApi: importTeacherStatisticsExcelApi,
    getTableList: proTableRef.value?.getTableList,
    alias: 'jstj',  // 指定模板标识
    fileName: '教师统计模板.xlsx'  // 指定模板文件名
  };
  ImportExcelRef.value?.acceptParams(params);
};

注意:aliasfileName 应与【模板文件管理】模块中的标识和模板名称保持一致。


三、模板文件管理操作说明

模板文件的标识和文件名来源于【模板文件管理】模块,需提前在系统中进行相关配置。
下图为标识和模板文件名管理操作示例:

image-20251107224137369

image-20251107224155806

  • 标识:唯一标记模板文件(如 ‘jstj’)。
  • 模板名称:实际上传的模板文件名(如 ‘教师统计模板.xlsx’)。
  • 备注:可用于区分不同用途模板,便于后续维护。

新增模板文件时,请确保标识和文件名参数正确填写,以便 ImportExcel 组件能正常完成模板定位和下载。

6. 菜单管理操作优化说明

本次对菜单管理操作进行全面优化,无论是目录、菜单还是按钮类型,其创建所需属性均进行了大幅精简,移除非必要字段,目的是提升用户创建效率。


一、属性精简及权限标识逻辑调整

  • 简化菜单创建:不同类型所需属性更精炼,仅需填写必填项即可完成创建。
  • 权限标识调整:移除目录、菜单类型上的权限标识,将权限标识配置逻辑完全转移到按钮类型,只有按钮支持单独设置权限标识,更接近权限实际管控场景。
  • 更合理的权限查询:查询权限标识统一由按钮决定,简化权限配置和后续维护。

二、菜单路由名称优化及升级提示

  • 所有系统组件相关菜单的路由名称进行了简化。例如,角色管理由 /system/roleManage 更改为 /system/role 等。此更改可能导致原有路由失效,升级后请及时检查和修正引用路径。
  • 路由名称的简化有助于统一风格和便于快速定位。

三、不同菜单类型创建所需属性示例

1. 目录类型

image-20251107230237707

  • 必填属性:上级、类型、图标、名称、显示状态、排序
2. 菜单类型

image-20251107230251313

  • 必填属性:上级、类型、图标、名称、路由相关(名称、地址、组件路径)、显示状态、是否外链、是否全屏/缓存、固定标签页、排序
3. 按钮类型

image-20251107230301991

  • 必填属性:上级、类型、名称、权限标识、显示状态、排序

只有按钮类型可设置权限标识,目录与菜单类型不能再独立设置权限标识。


7. 【代码生成器】数据权限功能优化

代码生成器现已全面支持自动创建数据权限,让数据权限集成更加高效、便捷。在“生成信息”Tab页下,新增了“自动创建数据权限”选项,且默认开启。启用该功能后,生成的菜单项会自动配置数据权限,并在后台自动生成数据权限控制相关的声明和实现代码。

image-20251108131619810

一、功能说明与使用要点

  • 自动创建数据权限:开启后,代码生成器会为新建的菜单自动配置数据权限,无需手动二次设置。
  • 后端自动实现声明:关联的实体、查询将自动集成数据权限控制,提升开发效率与规范性。
  • 风险提示:大部分 SQL 查询将会自动受数据权限控制影响。对于特殊或复杂的 SQL 场景,请务必验证生成结果,复杂情况下有可能导致部分查询无效或异常,建议开发者谨慎使用本功能并根据实际需求对代码进行必要调整。

二、角色权限与数据权限配置

生成代码后,进入“角色管理”,即可为各角色分配对应菜单的访问权限,并灵活调整每个角色的数据权限范围,实现精细化的数据管控。

image-20251108131804762

v1.2.6-beta

    1. 优化: [mysql.yml] - 指定 liquibase 版本表名,兼容不同环境表名大小写,避免版本控制失效。
yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/sz_admin_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: Yanfa2023@
    hikari:
      #连接池名
      pool-name: HikariCP
      #最小空闲连接数
      minimum-idle: 5
      # 空闲连接存活最大时间,默认10分钟
      idle-timeout: 600000
      # 连接池最大连接数,默认是10
      maximum-pool-size: 10
      # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
      auto-commit: true
      # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
      max-lifetime: 1800000
      # 数据库连接超时时间,默认30秒
      connection-timeout: 30000
      # 连接测试query
      connection-test-query: SELECT 1
  # -- 切换数据库脚本管理工具为 liquibase --    
  liquibase:
    change-log: classpath:db/changelog/changelog-master.xml
    enabled: true
    database-change-log-table: databasechangelog # 增加配置项
    database-change-log-lock-table: databasechangeloglock # 增加配置项
    1. 优化:[ProTable-TableColumn组件] 支持多标签展示

      • 支持 tag=false 且 enum 配置时,自动翻译多值并逗号拼接展示。
      • 支持 tag=true 且 enum 配置时,多标签分组展示及收起。
      • 支持 tag 未设置且 enum 配置时,正常翻译多值展示。
      • 兼容多种数据格式(数组、逗号分隔字符串)。
      • 空值统一展示为 "--"。
      • 新增 tagLimit 属性说明:设置展示标签数量,超出部分通过 Popover 收起展示(tagLimit=-1 时展示全部标签,默认值tagLimit=3)。

      badf4d1921354fe79949b3e8bcfaa627

v1.2.5-beta

  • 重构:字典加载器相关类的包结构和接口定义,优化动态字典加载逻辑

    1. 新增 DynamicDictLoader 接口,专用于动态字典处理,规范动态字典加载流程。

    2. UserOptionDictLoader.java为例,其他相关类依照新接口实现同步调整,具体变动包括:

      • 类继承自 DynamicDictLoader,替换原有 DictLoader 接口;

      • 实现 getTypeCode()getTypeName() 等新接口方法,统一字典类型标识与名称的获取方式;

      • 优化 loadDict() 方法,简化动态字典的缓存与回源逻辑,提升性能和易读性;

      • 代码层面将部分依赖 DynamicDictEnum 的调用方式进行标准化。

    3. 更新 DynamicDictEnum 枚举类,移除 typeCode 的 dynamic_ 前缀,由 DynamicDictLoader 接口自动拼接。

java
@Component
@RequiredArgsConstructor
public class UserOptionDictLoader implements DictLoader {
public class UserOptionDictLoader implements DynamicDictLoader {

    private final RedisCache redisCache;

    private final SysUserService sysUserService;

    @Override
    public DynamicDictEnum getDynamicTypeCode() {
        return DynamicDictEnum.DYNAMIC_USER_OPTIONS;
    }

    @Override
    public String getTypeCode() {
        return DynamicDictEnum.DYNAMIC_USER_OPTIONS.getTypeCode();
    }

    @Override
    public String getTypeName() {
        return DynamicDictEnum.DYNAMIC_USER_OPTIONS.getName();
    }

    @Override
    public Map<String, List<DictVO>> loadDict() {
        String key = getDynamicTypeCode().getTypeCode();
        String name = getDynamicTypeCode().getName();
        String key = getDynamicTypeCode();
        String name = getTypeName();
        if (redisCache.hasHashKey(key)) {
            return Map.of(key, redisCache.getDictByType(key));
        }

        DictVO dictVO;
        List<DictVO> list = new ArrayList<>();
        List<UserOptionVO> userOptions = sysUserService.getUserOptions();
        for (int i = 0; i < userOptions.size(); i++) {
            UserOptionVO option = userOptions.get(i);
            dictVO = DictVO.builder().id(option.getId().toString()).codeName(option.getNickname()).alias(option.getUsername()).sort(i + 1).sysDictTypeCode(key)
                    .sysDictTypeName(name).callbackShowStyle("primary").isDynamic(true).isLock("F").isShow("T").build();
            list.add(dictVO);
        }
        redisCache.setDict(key, list);
        return Map.of(key, list);
    }

    @Override
    public List<DictVO> getDict(String typeCode) {
        return loadDict().get(typeCode);
    }

}
java
@Getter
@RequiredArgsConstructor
public enum DynamicDictEnum {

    // @formatter:off
    DYNAMIC_USER_OPTIONS("dynamic_user_options", "用户信息");
    DYNAMIC_USER_OPTIONS("user_options", "用户信息");
    // @formatter:on
    private final String typeCode; // 类型代码

    private final String name; // 名称

}

v1.2.2-beta

  • 优化UploadFiles多文件上传组件, 简化使用方式并修复一些已知问题。

    上传中:

    image-20250528172638236

    上传完毕:

    image-20250528173006129

vue
<UploadFiles
  v-model:file-list="fileList" // file-list
  multiple
  :limit="5"
  :file-size="3"
  width="300px"
  height="200px"
  @change="fileChange"
  :accept="'.xlsx,.xls,.doc,.docx'"
  tip="支持上传 .xlsx, .xls, .docx, .doc, .pdf 格式的文件,单个文件大小不能超过 3M"
>
<template #tip> 支持上传 .xlsx, .xls, .docx, .doc, .pdf 格式的文件,单个文件大小不能超过 3M </template>
</UploadFiles>
<script setup lang="ts">
...
const fileList = ref<UploadUserFile[]>();
...

// 接收父组件传过来的参数
const acceptParams = (params: View.DefaultParams) => {
  paramsProps.value = params;
  visible.value = true;
  fileList.value = params.row.url   //  入参赋值,回显
    ? [ 
        {
          name: getFileNameFromUrl(params.row.url),
          url: params.row.url
        }
      ] 
    : [];
  fileUrls.value = params.row.url;
};
...
</script>
vue
<UploadFiles
  v-model:modelValue="fileUrls" // 使用modelValue
  multiple
  :limit="5"
  :file-size="3"
  @change="fileChange"
  accept=".xlsx,.xls,.docx,.doc,.pdf"
  tip="支持上传 .xlsx, .xls, .docx, .doc, .pdf 格式的文件,单个文件大小不能超过 3M" // [可选] 使用tip自定义显示信息,如不输入也会生成默认值,效果与所示话术一致
>
</UploadFiles>
<script setup lang="ts">
...
const fileUrls = ref<string[]>([]);
...

// 接收父组件传过来的参数
const acceptParams = (params: View.DefaultParams) => {
  paramsProps.value = params;
  visible.value = true;
  fileUrls.value = params.row.url;  // 入参赋值,回显
};

// 提交数据(新增/编辑)
const ruleFormRef = ref<InstanceType<typeof ElForm>>();
const handleSubmit = () => {
  ruleFormRef.value!.validate(async valid => {
    if (!valid) return;
    try {
      const urlArr = fileUrls.value; // 提交时获取值。如果接受参数是数组,无需处理
      paramsProps.value.row.url = urlArr[0];
      await paramsProps.value.api!(paramsProps.value.row);
      ElMessage.success({ message: `${paramsProps.value.title}成功!` });
      paramsProps.value.getTableList!();
      visible.value = false;
    } catch (error) {
      console.log(error);
    }
  });
};

</script>

v1.2.1-beta

  • 新增Liquibase管理数据库脚本
yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/sz_admin_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: Yanfa2023@
    hikari:
      #连接池名
      pool-name: HikariCP
      #最小空闲连接数
      minimum-idle: 5
      # 空闲连接存活最大时间,默认10分钟
      idle-timeout: 600000
      # 连接池最大连接数,默认是10
      maximum-pool-size: 10
      # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
      auto-commit: true
      # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
      max-lifetime: 1800000
      # 数据库连接超时时间,默认30秒
      connection-timeout: 30000
      # 连接测试query
      connection-test-query: SELECT 1
  # -- 切换数据库脚本管理工具为 liquibase --    
  liquibase: #
    change-log: classpath:db/changelog/changelog-master.xml #
    enabled: true #

IMPORTANT

结构变动:

shell
└─resources
    ├─db
  ├─business
      V0.1__README_DDL.sql
  ├─changelog   # Liquibase脚本目录
  changelog-master.xml # changelog主配置
  ├─business # 业务脚本
  └─1.0.0 # 版本号
          000_demo.sql #//
  └─framework # 框架脚本
      ├─1.1.0-beta # 版本号
      001_system.sql # sql脚本
      └─1.2.0-beta # 版本号
              001_sys_message.sql # sql脚本

  └─framework
          V1.1__20230509_Init_DDL.sql
          V1.2__20240511_Update_DDL.sql
          V1.3__20240530_Update_DDL.sql
          V1.4__20240603_Update_DDL.sql
	...	
          V2.6__20250422_Update_DDL.sql
  • 关闭Flyway脚本管理工具(Flyway将于v1.3.0-beta版本弃用
yml
flyway:
  framework: # 框架迁移脚本管理
    enabled: true #
    enabled: false #
    locations: classpath:/db/framework
    baseline-on-migrate: true
    baseline-version: 1
    table: t_db_version
    validate-on-migrate: true
  business: # 业务迁移脚本管理
    enabled: true #
    enabled: false #
    locations: classpath:db/business
    baseline-on-migrate: true
    baseline-version: 1
    table: t_db_version_business
    validate-on-migrate: true

v1.2.0-beta

  • 代码生成忽略表前缀功能

    yml
    sz:
      # 生成工具
      generator:
        path:
          # 前端项目地址
          web:
          # 后端项目地址,默认自动检测springboot项目路径,无需配置。
          api: E://code//Gitlab//sz-framework//sz-admin
        # 模块名,指定代码生成的模块
        module-name: sz-service
        service-name: sz-service-admin        
        global:
          author: sz-admin
          packages: com.sz.admin
           # 忽略的表前缀。若启用此配置,以表名 `t_db_version` 为例,生成的代码和实体类将自动去除前缀 `t_`,仅使用 `db_version` 或 `DbVersion`。
          ignore-table-prefix:	#
            enabled: true	#
            prefixes:	#
              - t_	#

v1.1.0-beta

  • 升级Eslint9

    1. 删除 .eslintignore、.eslintrc.cjs,切换Eslint配置为eslint.config.js

    2. 遵循Eslint和TypeScript最佳实践,避免使用namespace,切换至type。

    3. 切换目录/api/interface/api/types

    下面以/api/interface/captcha.ts为例:

    ts
    // 登录模块
    export namespace ICaptcha {
      export interface Info {
        bigImageBase64: string;
        bigWidth: number;
        bigHeight: number;
        smallImageBase64: string;
        smallWidth: number;
        smallHeight: number;
        requestId: string;
        posY: number;
        secretKey: string;
      }
      export interface VerifyImageParams {
        requestId: string;
        moveEncrypted: string;
      }
    }
    ts
    // 登录模块
    export type CaptchaInfo = {
      bigImageBase64: string;
      bigWidth: number;
      bigHeight: number;
      smallImageBase64: string;
      smallWidth: number;
      smallHeight: number;
      requestId: string;
      posY: number;
      secretKey: string;
    };
    export type CaptchaVerifyImageParams = {
      requestId: string;
      moveEncrypted: string;
    }
  • 新增useDictOptionshook,用于简化optionsStore.getDictOptions('dynamic_user_options')写法

    ts
    import { useOptionsStore } from '@/stores/modules/options';
    import { useDictOptions } from "@/hooks/useDictOptions";
    // 先声明store
    const optionsStore = useOptionsStore();
    // 使用时
    // 表格配置项
    const columns: ColumnProps<SysTempFileRow>[] = [
      { type: 'selection', width: 80 },
      { prop: 'id', label: '模板标识', width: 120 },
      { prop: 'tempName', label: '模版名' },
      { prop: 'url', label: '文件', width: 120 },
      { prop: 'remark', label: '备注' },
      { prop: 'history', label: '历史' },
      {
        prop: 'createId',
        label: '创建人',
        tag: true,
        enum: optionsStore.getDictOptions('dynamic_user_options'),
        enum: useDictOptions('dynamic_user_options'),
        fieldNames: {
          label: 'codeName',
          value: 'id',
          tagType: 'callbackShowStyle'
        }
      },
      { prop: 'createTime', label: '创建时间' },
      {
        prop: 'updateId',
        label: '更新人',
        tag: true,
        enum: optionsStore.getDictOptions('dynamic_user_options'),
        enum: useDictOptions('dynamic_user_options'),
        fieldNames: {
          label: 'codeName',
          value: 'id',
          tagType: 'callbackShowStyle'
        }
      },
      { prop: 'updateTime', label: '更新时间' },
      { prop: 'operation', label: '操作', width: 250, fixed: 'right' }
    ]
    ts
    <template>
      <el-dialog v-model="visible" :title="`${paramsProps.title}`" :destroy-on-close="true" width="580px" draggable>
        <el-form
          ref="ruleFormRef"
          label-width="140px"
          label-suffix=" :"
          :rules="rules"
          :model="paramsProps.row"
          @submit.enter.prevent="handleSubmit"
        >
        
    		...
            
          <el-form-item label="讲师区分类型" prop="teacherCommonType">
            <el-select v-model="paramsProps.row.teacherCommonType" clearable placeholder="请选择讲师区分类型">
              <el-option
                v-for="item in optionsStore.getDictOptions('account_status')"
                v-for="item in accountStatusOption"
                :key="item.id"
                :label="item.codeName"
                :value="Number(item.id)"
              />
            </el-select>
          </el-form-item>
    		
    		...
    		
        </el-form>
        <template #footer>
          <el-button @click="visible = false"> 取消 </el-button>
          <el-button type="primary" @click="handleSubmit"> 确定 </el-button>
        </template>
      </el-dialog>
    </template>
    
    <script setup lang="ts">
    import { ref, reactive } from 'vue';
    import { type ElForm, ElMessage } from 'element-plus';
    import { useDictOptions } from '@/hooks/useDictOptions';
    
    defineOptions({
      name: 'TeacherStatisticsForm'
    });
    
    const optionsStore = useOptionsStore();
    const accountStatusOption = useDictOptions('account_status');
    
    ...
    
    defineExpose({
      acceptParams
    });
    </script>
    
    <style scoped lang="scss"></style>
  • (sass)官方已经不推荐使用 @import 规则来导入 SCSS 文件,而是提倡使用新的 @use 规则

    vue
    <style scoped lang="scss">
    @import './index.scss';  // [!code --]
    @use './index'; // [!code ++]
    </style>
  • 升级sa-token 1.40.0 至 1.41.0,官方更换了sa-session的序列化对象结构,因此对redis序列化可能失效,导致账户登录失败。

    解决方案:清空redis中的Authorizationkey下的缓存对象,重新登陆即可!