系统配置

This commit is contained in:
2025-11-18 18:46:14 +08:00
parent 9f3176194b
commit ca756dcfd7
23 changed files with 1039 additions and 49 deletions

View File

@@ -32,10 +32,20 @@ DROP TABLE IF EXISTS `tb_sys_config`;
CREATE TABLE `tb_sys_config` ( CREATE TABLE `tb_sys_config` (
`id` VARCHAR(50) NOT NULL COMMENT '配置ID', `id` VARCHAR(50) NOT NULL COMMENT '配置ID',
`config_key` VARCHAR(100) NOT NULL COMMENT '配置键', `config_key` VARCHAR(100) NOT NULL COMMENT '配置键',
`config_name` VARCHAR(100) DEFAULT NULL COMMENT '配置显示名称',
`config_value` TEXT COMMENT '配置值', `config_value` TEXT COMMENT '配置值',
`config_type` VARCHAR(50) DEFAULT 'string' COMMENT '配置类型string/number/boolean/json', `config_type` VARCHAR(50) DEFAULT 'string' COMMENT '数据类型string/number/boolean/json',
`render_type` VARCHAR(20) DEFAULT NULL COMMENT '前端渲染类型input/textarea/number/switch/select/password',
`config_group` VARCHAR(50) DEFAULT NULL COMMENT '配置分组', `config_group` VARCHAR(50) DEFAULT NULL COMMENT '配置分组',
`description` VARCHAR(255) DEFAULT NULL COMMENT '配置描述', `description` VARCHAR(255) DEFAULT NULL COMMENT '配置描述',
`placeholder` VARCHAR(200) DEFAULT NULL COMMENT '输入框占位符',
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注说明(显示在表单项下方)',
`rows` INT DEFAULT NULL COMMENT '文本框行数textarea用',
`min_value` INT DEFAULT NULL COMMENT '最小值number用',
`max_value` INT DEFAULT NULL COMMENT '最大值number用',
`unit` VARCHAR(20) DEFAULT NULL COMMENT '单位number用',
`options` TEXT COMMENT '下拉选项select用JSON格式',
`order_num` INT DEFAULT 0 COMMENT '排序号',
`is_system` TINYINT(1) DEFAULT 0 COMMENT '是否系统配置', `is_system` TINYINT(1) DEFAULT 0 COMMENT '是否系统配置',
`creator` VARCHAR(50) DEFAULT NULL COMMENT '创建者', `creator` VARCHAR(50) DEFAULT NULL COMMENT '创建者',
`updater` VARCHAR(50) DEFAULT NULL COMMENT '更新者', `updater` VARCHAR(50) DEFAULT NULL COMMENT '更新者',
@@ -45,7 +55,8 @@ CREATE TABLE `tb_sys_config` (
`deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `uk_config_key` (`config_key`), UNIQUE KEY `uk_config_key` (`config_key`),
KEY `idx_group` (`config_group`) KEY `idx_group` (`config_group`),
KEY `idx_order` (`order_num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统配置表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统配置表';

View File

@@ -25,22 +25,9 @@ INSERT INTO `tb_tag` (id, tag_id, name, color, description, tag_type, creator, c
('tag205', 'tag_task_005', '阶段任务', '#2d98da', '阶段性学习任务', 3, '1', now()); ('tag205', 'tag_task_005', '阶段任务', '#2d98da', '阶段性学习任务', 3, '1', now());
-- 插入系统配置数据 -- 插入系统配置数据
INSERT INTO `tb_sys_config` (id, config_key, config_value, config_type, config_group, description, is_system, creator, create_time) VALUES INSERT INTO `tb_sys_config` (`id`, `config_key`, `config_name`, `config_value`, `config_type`, `render_type`, `config_group`, `description`, `placeholder`, `remark`, `rows`, `min_value`, `max_value`, `unit`, `options`, `order_num`, `is_system`, `creator`, `create_time`) VALUES
('1', 'system.platform.name', '思政学习平台', 'string', 'platform', '平台名称', 1, '1', now()), ('1', 'crawler.pythonPath', 'Python路径', 'F:/Environment/Conda/envs/schoolNewsCrawler/python.exe', 'string', 'input', '爬虫配置', 'Python可执行文件路径', '请输入Python可执行文件完整路径', '爬虫使用的Python解释器路径', NULL, NULL, NULL, NULL, NULL, 1, 1, '1', now()),
('2', 'system.platform.logo', '/assets/logo.png', 'string', 'platform', '平台Logo', 1, '1', now()), ('2', 'crawler.basePath', '爬虫根目录', 'F:/Project/schoolNews/schoolNewsCrawler', 'string', 'input', '爬虫配置', '爬虫脚本根目录', '请输入爬虫脚本根目录路径', '爬虫脚本文件的根目录', NULL, NULL, NULL, NULL, NULL, 2, 1, '1', now());
('3', 'system.platform.school_badge', '/assets/school_badge.png', 'string', 'platform', '学校校徽', 1, '1', now()),
('4', 'system.menu.home', '首页', 'string', 'menu', '首页菜单名称', 0, '1', now()),
('5', 'system.menu.resource', '资源中心', 'string', 'menu', '资源中心菜单名称', 0, '1', now()),
('6', 'system.menu.learning', '学习计划', 'string', 'menu', '学习计划菜单名称', 0, '1', now()),
('7', 'system.menu.activity', '专题活动', 'string', 'menu', '专题活动菜单名称', 0, '1', now()),
('8', 'system.menu.culture', '红色常信', 'string', 'menu', '红色常信菜单名称', 0, '1', now()),
('9', 'system.banner.auto_play', 'true', 'boolean', 'banner', 'Banner自动播放', 0, '1', now()),
('10', 'system.banner.interval', '5000', 'number', 'banner', 'Banner切换间隔毫秒', 0, '1', now()),
('11', 'system.resource.auto_publish', 'false', 'boolean', 'resource', '资源自动发布', 0, '1', now()),
('12', 'system.resource.auto_publish_time', '08:00', 'string', 'resource', '自动发布时间', 0, '1', now()),
('13', 'system.ai.enabled', 'true', 'boolean', 'ai', '是否启用智能体', 0, '1', now()),
('14', 'crawler.pythonPath', 'F:/Environment/Conda/envs/schoolNewsCrawler/python.exe', 'string', 'crawler', 'Python可执行文件路径', 1, '1', now()),
('15', 'crawler.basePath', 'F:/Project/schoolNews/schoolNewsCrawler', 'string', 'crawler', '爬虫脚本根目录', 1, '1', now());
-- 注意默认superadmin用户已在 initMenuData.sql 中创建,此处无需重复创建 -- 注意默认superadmin用户已在 initMenuData.sql 中创建,此处无需重复创建

View File

@@ -157,12 +157,11 @@ INSERT INTO `tb_sys_menu` VALUES
('6000', 'menu_admin_ai_manage', '智能体管理', NULL, '', '', 'admin/agent.svg', 6, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:50', NULL, 0), ('6000', 'menu_admin_ai_manage', '智能体管理', NULL, '', '', 'admin/agent.svg', 6, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:50', NULL, 0),
('6002', 'menu_admin_ai_config', 'AI配置', 'menu_admin_ai_manage', '/admin/manage/ai/config', 'admin/manage/ai/AIConfigView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('6002', 'menu_admin_ai_config', 'AI配置', 'menu_admin_ai_manage', '/admin/manage/ai/config', 'admin/manage/ai/AIConfigView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('6003', 'menu_admin_knowledge', '知识库管理', 'menu_admin_ai_manage', '/admin/manage/ai/knowledge', 'admin/manage/ai/KnowledgeManagementView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('6003', 'menu_admin_knowledge', '知识库管理', 'menu_admin_ai_manage', '/admin/manage/ai/knowledge', 'admin/manage/ai/KnowledgeManagementView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('7000', 'menu_admin_logs_manage', '系统日志', NULL, '', '', 'admin/logs.svg', 7, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:53', NULL, 0), ('7000', 'menu_admin_logs_manage', '系统日志', 'menu_sys_manage', '', '', NULL, 6, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:53', NULL, 0),
('7001', 'menu_admin_system_logs', '系统日志', 'menu_admin_logs_manage', '/admin/manage/logs/system', 'admin/manage/logs/SystemLogsView', NULL, 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('7001', 'menu_admin_system_logs', '系统日志', 'menu_admin_logs_manage', '/admin/manage/logs/system', 'admin/manage/logs/SystemLogsView', NULL, 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('7002', 'menu_admin_login_logs', '登录日志', 'menu_admin_logs_manage', '/admin/manage/logs/login', 'admin/manage/logs/LoginLogsView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('7002', 'menu_admin_login_logs', '登录日志', 'menu_admin_logs_manage', '/admin/manage/logs/login', 'admin/manage/logs/LoginLogsView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
-- ('7003', 'menu_admin_operation_logs', '操作日志', 'menu_admin_logs_manage', '/admin/manage/logs/operation', 'admin/manage/logs/OperationLogsView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('7003', 'menu_admin_system_config', '系统配置', 'menu_sys_manage', '/admin/manage/system/config', 'admin/manage/system/SystemConfigView', NULL, 7, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
-- ('7004', 'menu_admin_system_config', '系统配置', 'menu_admin_logs_manage', '/admin/manage/logs/config', 'admin/manage/logs/SystemConfigView', NULL, 4, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('8000', 'menu_admin_crontab_manage', '定时任务管理', NULL, '', '', 'admin/crontab.svg', 8, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('8000', 'menu_admin_crontab_manage', '定时任务管理', NULL, '', '', NULL, 8, 0, 'SidebarLayout', '1', '/admin/crontab.svg', '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('8002', 'menu_admin_crontab_log', '执行日志', 'menu_admin_crontab_manage', '/admin/manage/crontab/log', 'admin/manage/crontab/LogManagementView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('8002', 'menu_admin_crontab_log', '执行日志', 'menu_admin_crontab_manage', '/admin/manage/crontab/log', 'admin/manage/crontab/LogManagementView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
('8003', 'menu_admin_news_crawler', '新闻爬虫配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/news-crawler', 'admin/manage/crontab/NewsCrawlerView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('8003', 'menu_admin_news_crawler', '新闻爬虫配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/news-crawler', 'admin/manage/crontab/NewsCrawlerView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0),
-- 消息通知模块菜单 (9000-9999) -- 消息通知模块菜单 (9000-9999)
@@ -170,6 +169,7 @@ INSERT INTO `tb_sys_menu` VALUES
-- 用户端消息中心菜单 (650-699) -- 用户端消息中心菜单 (650-699)
('650', 'menu_user_message_center', '消息中心', NULL, '/user/message', 'user/message/MyMessageListView', NULL, 7, 1, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0), ('650', 'menu_user_message_center', '消息中心', NULL, '/user/message', 'user/message/MyMessageListView', NULL, 7, 1, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0),
('651', 'menu_user_message_detail', '消息详情', 'menu_user_message_center', '/user/message/detail/:messageID', 'user/message/MyMessageDetailView', NULL, 1, 3, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0); ('651', 'menu_user_message_detail', '消息详情', 'menu_user_message_center', '/user/message/detail/:messageID', 'user/message/MyMessageDetailView', NULL, 1, 3, 'NavigationLayout', '1', NULL, '2025-11-13 10:00:00', '2025-11-13 10:00:00', NULL, 0);
-- 插入菜单权限关联数据 -- 插入菜单权限关联数据
INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, create_time) VALUES INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, create_time) VALUES
-- 前端菜单权限关联 -- 前端菜单权限关联
@@ -223,7 +223,6 @@ INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, creat
('227', 'perm_system_manage', 'menu_admin_logs_manage', '1', now()), ('227', 'perm_system_manage', 'menu_admin_logs_manage', '1', now()),
('228', 'perm_system_manage', 'menu_admin_system_logs', '1', now()), ('228', 'perm_system_manage', 'menu_admin_system_logs', '1', now()),
('229', 'perm_system_manage', 'menu_admin_login_logs', '1', now()), ('229', 'perm_system_manage', 'menu_admin_login_logs', '1', now()),
('230', 'perm_system_manage', 'menu_admin_operation_logs', '1', now()),
('231', 'perm_system_manage', 'menu_admin_system_config', '1', now()), ('231', 'perm_system_manage', 'menu_admin_system_config', '1', now()),
-- 定时任务管理菜单权限关联 -- 定时任务管理菜单权限关联

View File

@@ -1,5 +1,12 @@
package org.xyzh.api.system.config; package org.xyzh.api.system.config;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.dto.system.TbSysConfig;
import java.util.List;
import java.util.Map;
/** /**
* @description 系统配置服务 * @description 系统配置服务
* @filename SysConfigService.java * @filename SysConfigService.java
@@ -9,6 +16,8 @@ package org.xyzh.api.system.config;
*/ */
public interface SysConfigService { public interface SysConfigService {
// ==================== 配置读取接口(业务代码使用) ====================
Object getSysConfig(String key); Object getSysConfig(String key);
/** /**
@@ -45,4 +54,62 @@ public interface SysConfigService {
* @return 配置值如果不存在或解析失败返回null * @return 配置值如果不存在或解析失败返回null
*/ */
Long getLongConfig(String key); Long getLongConfig(String key);
// ==================== 配置管理接口(管理界面使用) ====================
/**
* 获取所有配置项
* @return 配置项列表
*/
ResultDomain<TbSysConfig> getAllConfigs();
/**
* 根据分组获取配置项
* @param groupKey 分组键
* @return 配置项列表
*/
ResultDomain<TbSysConfig> getConfigsByGroup(String groupKey);
/**
* 批量保存配置项(仅更新值)
* @param configs 配置项列表
* @return 保存结果
*/
ResultDomain<Boolean> batchSaveConfigs(List<Map<String, String>> configs);
/**
* 创建配置项
* @param config 配置项
* @return 创建结果
*/
ResultDomain<Boolean> createConfig(TbSysConfig config);
/**
* 更新配置项
* @param config 配置项
* @return 更新结果
*/
ResultDomain<Boolean> updateConfig(TbSysConfig config);
/**
* 删除配置项
* @param id 配置ID
* @return 删除结果
*/
ResultDomain<Boolean> deleteConfig(String id);
/**
* 分页查询配置项
* @param filter 查询条件
* @param pageParam 分页参数
* @return 配置项分页列表
*/
ResultDomain<TbSysConfig> getConfigPage(TbSysConfig filter, PageParam pageParam);
/**
* 根据ID获取配置项
* @param id 配置ID
* @return 配置项详情
*/
ResultDomain<TbSysConfig> getConfigById(String id);
} }

View File

@@ -18,16 +18,26 @@ public class TbSysConfig extends BaseDTO {
*/ */
private String configKey; private String configKey;
/**
* @description 配置显示名称
*/
private String configName;
/** /**
* @description 配置值 * @description 配置值
*/ */
private String configValue; private String configValue;
/** /**
* @description 配置类型string/number/boolean/json * @description 数据类型string/number/boolean/json
*/ */
private String configType; private String configType;
/**
* @description 前端渲染类型input/textarea/number/switch/select/password
*/
private String renderType;
/** /**
* @description 配置分组 * @description 配置分组
*/ */
@@ -38,6 +48,46 @@ public class TbSysConfig extends BaseDTO {
*/ */
private String description; private String description;
/**
* @description 输入框占位符
*/
private String placeholder;
/**
* @description 备注说明(显示在表单项下方)
*/
private String remark;
/**
* @description 文本框行数textarea用
*/
private Integer rows;
/**
* @description 最小值number用
*/
private Integer minValue;
/**
* @description 最大值number用
*/
private Integer maxValue;
/**
* @description 单位number用
*/
private String unit;
/**
* @description 下拉选项select用JSON格式
*/
private String options;
/**
* @description 排序号
*/
private Integer orderNum;
/** /**
* @description 是否系统配置 * @description 是否系统配置
*/ */
@@ -117,14 +167,97 @@ public class TbSysConfig extends BaseDTO {
this.updater = updater; this.updater = updater;
} }
public String getConfigName() {
return configName;
}
public void setConfigName(String configName) {
this.configName = configName;
}
public String getRenderType() {
return renderType;
}
public void setRenderType(String renderType) {
this.renderType = renderType;
}
public String getPlaceholder() {
return placeholder;
}
public void setPlaceholder(String placeholder) {
this.placeholder = placeholder;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Integer getRows() {
return rows;
}
public void setRows(Integer rows) {
this.rows = rows;
}
public Integer getMinValue() {
return minValue;
}
public void setMinValue(Integer minValue) {
this.minValue = minValue;
}
public Integer getMaxValue() {
return maxValue;
}
public void setMaxValue(Integer maxValue) {
this.maxValue = maxValue;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public String getOptions() {
return options;
}
public void setOptions(String options) {
this.options = options;
}
public Integer getOrderNum() {
return orderNum;
}
public void setOrderNum(Integer orderNum) {
this.orderNum = orderNum;
}
@Override @Override
public String toString() { public String toString() {
return "TbSysConfig{" + return "TbSysConfig{" +
"id=" + getID() + "id=" + getID() +
", configKey='" + configKey + '\'' + ", configKey='" + configKey + '\'' +
", configName='" + configName + '\'' +
", configValue='" + configValue + '\'' + ", configValue='" + configValue + '\'' +
", configType='" + configType + '\'' + ", configType='" + configType + '\'' +
", renderType='" + renderType + '\'' +
", configGroup='" + configGroup + '\'' + ", configGroup='" + configGroup + '\'' +
", orderNum=" + orderNum +
", isSystem=" + isSystem + ", isSystem=" + isSystem +
'}'; '}';
} }

View File

@@ -0,0 +1,107 @@
package org.xyzh.system.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.xyzh.api.system.config.SysConfigService;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.core.page.PageRequest;
import org.xyzh.common.dto.system.TbSysConfig;
import java.util.List;
import java.util.Map;
/**
* @description 系统配置管理控制器
* @filename ConfigController.java
* @author yslg
* @copyright xyzh
* @since 2025-11-18
*/
@RestController
@RequestMapping("/system/config")
public class ConfigController {
@Autowired
private SysConfigService sysConfigService;
/**
* @description 获取所有配置项(前端配置视图使用)
* @return ResultDomain<TbSysConfig> 配置项列表
*/
@GetMapping("/list")
public ResultDomain<TbSysConfig> getAllConfigs() {
return sysConfigService.getAllConfigs();
}
/**
* @description 根据分组获取配置项
* @param groupKey 分组键
* @return ResultDomain<TbSysConfig> 配置项列表
*/
@GetMapping("/group/{groupKey}")
public ResultDomain<TbSysConfig> getConfigsByGroup(@PathVariable String groupKey) {
return sysConfigService.getConfigsByGroup(groupKey);
}
/**
* @description 保存配置项(批量保存)
* @param configs 配置项列表
* @return ResultDomain<Boolean> 保存结果
*/
@PostMapping("/save")
public ResultDomain<Boolean> saveConfigs(@RequestBody List<Map<String, String>> configs) {
return sysConfigService.batchSaveConfigs(configs);
}
/**
* @description 创建单个配置项
* @param config 配置项
* @return ResultDomain<Boolean> 创建结果
*/
@PostMapping("/create")
public ResultDomain<Boolean> createConfig(@RequestBody TbSysConfig config) {
return sysConfigService.createConfig(config);
}
/**
* @description 更新单个配置项
* @param config 配置项
* @return ResultDomain<Boolean> 更新结果
*/
@PutMapping("/update")
public ResultDomain<Boolean> updateConfig(@RequestBody TbSysConfig config) {
return sysConfigService.updateConfig(config);
}
/**
* @description 删除配置项
* @param id 配置ID
* @return ResultDomain<Boolean> 删除结果
*/
@DeleteMapping("/{id}")
public ResultDomain<Boolean> deleteConfig(@PathVariable String id) {
return sysConfigService.deleteConfig(id);
}
/**
* @description 分页查询配置项
* @param pageRequest 分页请求
* @return ResultDomain<TbSysConfig> 配置项分页列表
*/
@PostMapping("/page")
public ResultDomain<TbSysConfig> getConfigPage(@RequestBody PageRequest<TbSysConfig> pageRequest) {
return sysConfigService.getConfigPage(pageRequest.getFilter(), pageRequest.getPageParam());
}
/**
* @description 根据ID获取配置项详情
* @param id 配置ID
* @return ResultDomain<TbSysConfig> 配置项详情
*/
@GetMapping("/{id}")
public ResultDomain<TbSysConfig> getConfigById(@PathVariable String id) {
return sysConfigService.getConfigById(id);
}
}

View File

@@ -5,9 +5,16 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.xyzh.api.system.config.SysConfigService; import org.xyzh.api.system.config.SysConfigService;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageDomain;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.dto.system.TbSysConfig; import org.xyzh.common.dto.system.TbSysConfig;
import org.xyzh.system.mapper.SysConfigMapper; import org.xyzh.system.mapper.SysConfigMapper;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/** /**
* @description 系统配置服务实现 * @description 系统配置服务实现
* @author AI Assistant * @author AI Assistant
@@ -205,4 +212,163 @@ public class SysConfigServiceImpl implements SysConfigService {
return null; return null;
} }
} }
// ==================== 配置管理接口实现 ====================
@Override
public ResultDomain<TbSysConfig> getAllConfigs() {
ResultDomain<TbSysConfig> resultDomain = new ResultDomain<>();
try {
List<TbSysConfig> configs = sysConfigMapper.selectAllSysConfig();
resultDomain.success("查询成功", configs);
return resultDomain;
} catch (Exception e) {
logger.error("获取所有配置失败", e);
resultDomain.fail("获取配置失败: " + e.getMessage());
return resultDomain;
}
}
@Override
public ResultDomain<TbSysConfig> getConfigsByGroup(String groupKey) {
ResultDomain<TbSysConfig> resultDomain = new ResultDomain<>();
try {
List<TbSysConfig> configs = sysConfigMapper.selectSysConfigByGroup(groupKey);
resultDomain.success("查询成功", configs);
return resultDomain;
} catch (Exception e) {
logger.error("根据分组获取配置失败: {}", groupKey, e);
resultDomain.fail("获取配置失败: " + e.getMessage());
return resultDomain;
}
}
@Override
public ResultDomain<Boolean> batchSaveConfigs(List<Map<String, String>> configs) {
ResultDomain<Boolean> resultDomain = new ResultDomain<>();
try {
for (Map<String, String> configMap : configs) {
String configKey = configMap.get("configKey");
String configValue = configMap.get("configValue");
TbSysConfig existingConfig = sysConfigMapper.selectSysConfigByKey(configKey);
if (existingConfig != null) {
existingConfig.setConfigValue(configValue);
sysConfigMapper.updateSysConfig(existingConfig);
} else {
logger.warn("配置项不存在,跳过: {}", configKey);
}
}
resultDomain.success("保存成功", true);
return resultDomain;
} catch (Exception e) {
logger.error("批量保存配置失败", e);
resultDomain.fail("保存配置失败: " + e.getMessage());
return resultDomain;
}
}
@Override
public ResultDomain<Boolean> createConfig(TbSysConfig config) {
ResultDomain<Boolean> resultDomain = new ResultDomain<>();
try {
TbSysConfig existingConfig = sysConfigMapper.selectSysConfigByKey(config.getConfigKey());
if (existingConfig != null) {
resultDomain.fail("配置键已存在: " + config.getConfigKey());
return resultDomain;
}
if (config.getID() == null || config.getID().isEmpty()) {
config.setID(UUID.randomUUID().toString());
}
int result = sysConfigMapper.insertSysConfig(config);
if (result > 0) {
resultDomain.success("创建成功", true);
return resultDomain;
} else {
resultDomain.fail("创建配置失败");
return resultDomain;
}
} catch (Exception e) {
logger.error("创建配置失败", e);
resultDomain.fail("创建配置失败: " + e.getMessage());
return resultDomain;
}
}
@Override
public ResultDomain<Boolean> updateConfig(TbSysConfig config) {
ResultDomain<Boolean> resultDomain = new ResultDomain<>();
try {
int result = sysConfigMapper.updateSysConfig(config);
if (result > 0) {
resultDomain.success("更新成功", true);
return resultDomain;
} else {
resultDomain.fail("更新配置失败");
return resultDomain;
}
} catch (Exception e) {
logger.error("更新配置失败", e);
resultDomain.fail("更新配置失败: " + e.getMessage());
return resultDomain;
}
}
@Override
public ResultDomain<Boolean> deleteConfig(String id) {
ResultDomain<Boolean> resultDomain = new ResultDomain<>();
try {
int result = sysConfigMapper.deleteSysConfig(id);
if (result > 0) {
resultDomain.success("删除成功", true);
return resultDomain;
} else {
resultDomain.fail("删除配置失败");
return resultDomain;
}
} catch (Exception e) {
logger.error("删除配置失败: {}", id, e);
resultDomain.fail("删除配置失败: " + e.getMessage());
return resultDomain;
}
}
@Override
public ResultDomain<TbSysConfig> getConfigPage(TbSysConfig filter, PageParam pageParam) {
ResultDomain<TbSysConfig> resultDomain = new ResultDomain<>();
try {
int total = sysConfigMapper.countSelectSysConfig(filter);
List<TbSysConfig> configs = sysConfigMapper.selectSysConfigPage(filter, pageParam);
pageParam.setTotalElements(total);
pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize()));
PageDomain<TbSysConfig> pageDomain = new PageDomain<>(pageParam,configs);
resultDomain.success("查询成功", pageDomain);
return resultDomain;
} catch (Exception e) {
logger.error("分页查询配置失败", e);
resultDomain.fail("查询配置失败: " + e.getMessage());
return resultDomain;
}
}
@Override
public ResultDomain<TbSysConfig> getConfigById(String id) {
ResultDomain<TbSysConfig> resultDomain = new ResultDomain<>();
try {
TbSysConfig config = sysConfigMapper.selectSysConfigById(id);
if (config != null) {
resultDomain.success("查询成功", config);
return resultDomain;
} else {
resultDomain.fail("配置不存在");
return resultDomain;
}
} catch (Exception e) {
logger.error("根据ID获取配置失败: {}", id, e);
resultDomain.fail("获取配置失败: " + e.getMessage());
return resultDomain;
}
}
} }

View File

@@ -6,10 +6,20 @@
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.system.TbSysConfig"> <resultMap id="BaseResultMap" type="org.xyzh.common.dto.system.TbSysConfig">
<id column="id" property="ID" /> <id column="id" property="ID" />
<result column="config_key" property="configKey" /> <result column="config_key" property="configKey" />
<result column="config_name" property="configName" />
<result column="config_value" property="configValue" /> <result column="config_value" property="configValue" />
<result column="config_type" property="configType" /> <result column="config_type" property="configType" />
<result column="render_type" property="renderType" />
<result column="config_group" property="configGroup" /> <result column="config_group" property="configGroup" />
<result column="description" property="description" /> <result column="description" property="description" />
<result column="placeholder" property="placeholder" />
<result column="remark" property="remark" />
<result column="rows" property="rows" />
<result column="min_value" property="minValue" />
<result column="max_value" property="maxValue" />
<result column="unit" property="unit" />
<result column="options" property="options" />
<result column="order_num" property="orderNum" />
<result column="is_system" property="isSystem" /> <result column="is_system" property="isSystem" />
<result column="creator" property="creator" /> <result column="creator" property="creator" />
<result column="updater" property="updater" /> <result column="updater" property="updater" />
@@ -21,7 +31,8 @@
<!-- 字段列表 --> <!-- 字段列表 -->
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, config_key, config_value, config_type, config_group, description, id, config_key, config_name, config_value, config_type, render_type, config_group,
description, placeholder, remark, `rows`, min_value, max_value, unit, `options`, order_num,
is_system, creator, updater, create_time, update_time, delete_time, deleted is_system, creator, updater, create_time, update_time, delete_time, deleted
</sql> </sql>
@@ -47,22 +58,35 @@
<!-- 插入系统配置 --> <!-- 插入系统配置 -->
<insert id="insertSysConfig" parameterType="org.xyzh.common.dto.system.TbSysConfig"> <insert id="insertSysConfig" parameterType="org.xyzh.common.dto.system.TbSysConfig">
INSERT INTO tb_sys_config ( INSERT INTO tb_sys_config (
id, config_key, config_value, config_type, config_group, description, id, config_key, config_name, config_value, config_type, render_type, config_group,
description, placeholder, remark, `rows`, min_value, max_value, unit, `options`, order_num,
is_system, creator, create_time is_system, creator, create_time
) VALUES ( ) VALUES (
#{config.ID}, #{config.configKey}, #{config.configValue}, #{config.configType}, #{config.ID}, #{config.configKey}, #{config.configName}, #{config.configValue},
#{config.configGroup}, #{config.description}, #{config.isSystem}, #{config.configType}, #{config.renderType}, #{config.configGroup}, #{config.description},
#{config.creator}, NOW() #{config.placeholder}, #{config.remark}, #{config.rows}, #{config.minValue},
#{config.maxValue}, #{config.unit}, #{config.options}, #{config.orderNum},
#{config.isSystem}, #{config.creator}, NOW()
) )
</insert> </insert>
<!-- 更新系统配置 --> <!-- 更新系统配置 -->
<update id="updateSysConfig" parameterType="org.xyzh.common.dto.system.TbSysConfig"> <update id="updateSysConfig" parameterType="org.xyzh.common.dto.system.TbSysConfig">
UPDATE tb_sys_config UPDATE tb_sys_config
SET config_value = #{config.configValue}, SET config_name = #{config.configName},
config_value = #{config.configValue},
config_type = #{config.configType}, config_type = #{config.configType},
render_type = #{config.renderType},
config_group = #{config.configGroup}, config_group = #{config.configGroup},
description = #{config.description}, description = #{config.description},
placeholder = #{config.placeholder},
remark = #{config.remark},
`rows` = #{config.rows},
min_value = #{config.minValue},
max_value = #{config.maxValue},
unit = #{config.unit},
`options` = #{config.options},
order_num = #{config.orderNum},
is_system = #{config.isSystem}, is_system = #{config.isSystem},
updater = #{config.updater}, updater = #{config.updater},
update_time = NOW() update_time = NOW()
@@ -100,7 +124,7 @@
SELECT <include refid="Base_Column_List" /> SELECT <include refid="Base_Column_List" />
FROM tb_sys_config FROM tb_sys_config
WHERE deleted = 0 WHERE deleted = 0
ORDER BY config_group, config_key ORDER BY config_group, order_num, config_key
</select> </select>
<!-- 根据分组查询 --> <!-- 根据分组查询 -->
@@ -109,7 +133,7 @@
FROM tb_sys_config FROM tb_sys_config
WHERE config_group = #{configGroup} WHERE config_group = #{configGroup}
AND deleted = 0 AND deleted = 0
ORDER BY config_key ORDER BY order_num, config_key
</select> </select>
<!-- 查询系统配置列表 --> <!-- 查询系统配置列表 -->
@@ -117,7 +141,7 @@
SELECT <include refid="Base_Column_List" /> SELECT <include refid="Base_Column_List" />
FROM tb_sys_config FROM tb_sys_config
<include refid="Base_Where_Clause" /> <include refid="Base_Where_Clause" />
ORDER BY config_group, config_key ORDER BY config_group, order_num, config_key
</select> </select>
<!-- 分页查询 --> <!-- 分页查询 -->
@@ -125,7 +149,7 @@
SELECT <include refid="Base_Column_List" /> SELECT <include refid="Base_Column_List" />
FROM tb_sys_config FROM tb_sys_config
<include refid="Base_Where_Clause" /> <include refid="Base_Where_Clause" />
ORDER BY config_group, config_key ORDER BY config_group, order_num, config_key
LIMIT #{pageParam.offset}, #{pageParam.pageSize} LIMIT #{pageParam.offset}, #{pageParam.pageSize}
</select> </select>

View File

@@ -0,0 +1,62 @@
/**
* @description 系统配置相关API
* @author yslg
* @since 2025-11-18
*/
import { api } from '@/apis/index';
import type { ResultDomain } from '@/types';
import type { ConfigItem, SaveConfigParam } from '@/types/system/config';
/**
* 系统配置API服务
*/
export const configApi = {
/**
* 获取所有配置项
* @returns Promise<ResultDomain<ConfigItem[]>>
*/
async getConfigs(): Promise<ResultDomain<ConfigItem[]>> {
const response = await api.get<ConfigItem[]>('/system/config/list');
return response.data;
},
/**
* 根据分组获取配置项
* @param groupKey 配置分组key
* @returns Promise<ResultDomain<ConfigItem[]>>
*/
async getConfigsByGroup(groupKey: string): Promise<ResultDomain<ConfigItem[]>> {
const response = await api.get<ConfigItem[]>(`/system/config/group/${groupKey}`);
return response.data;
},
/**
* 保存配置项
* @param configs 配置项列表
* @returns Promise<ResultDomain<boolean>>
*/
async saveConfigs(configs: SaveConfigParam[]): Promise<ResultDomain<boolean>> {
const response = await api.post<boolean>('/system/config/save', configs);
return response.data;
},
/**
* 根据配置键获取配置值
* @param configKey 配置键
* @returns Promise<ResultDomain<string>>
*/
async getConfigValue(configKey: string): Promise<ResultDomain<string>> {
const response = await api.get<string>(`/system/config/value/${configKey}`);
return response.data;
},
/**
* 删除配置项
* @param configKey 配置键
* @returns Promise<ResultDomain<boolean>>
*/
async deleteConfig(configKey: string): Promise<ResultDomain<boolean>> {
const response = await api.delete<boolean>(`/system/config/${configKey}`);
return response.data;
}
};

View File

@@ -14,4 +14,5 @@ export { authApi } from './auth';
export { fileApi } from './file'; export { fileApi } from './file';
export { moduleApi } from './module'; export { moduleApi } from './module';
export { logApi } from './log'; export { logApi } from './log';
export { configApi} from './config';

View File

@@ -4,10 +4,10 @@
* @since 2025-10-06 * @since 2025-10-06
*/ */
import { SysUser, SysUserInfo } from '../user'; import { SysUser, SysUserInfo } from '../system/user';
import { UserDeptRoleVO } from '../dept'; import { UserDeptRoleVO } from '../dept';
import { SysPermission } from '../permission'; import { SysPermission } from '../system/permission';
import { SysMenu } from '../menu'; import { SysMenu } from '../system/menu';
import { LoginType } from '../enums'; import { LoginType } from '../enums';
/** /**

View File

@@ -5,8 +5,8 @@
*/ */
import { BaseDTO } from '../base'; import { BaseDTO } from '../base';
import { SysRole } from '../role'; import { SysRole } from '../system/role';
import { SysUserDeptRole } from '../user'; import { SysUserDeptRole } from '../system/user';
/** /**
* 系统部门 * 系统部门

View File

@@ -8,22 +8,22 @@
export * from './base'; export * from './base';
// 用户相关 // 用户相关
export * from './user'; export * from './system/user';
// 角色相关 // 角色相关
export * from './role'; export * from './system/role';
// 部门相关 // 部门相关
export * from './dept'; export * from './dept';
// 菜单相关 // 菜单相关
export * from './menu'; export * from './system/menu';
// 权限相关 // 权限相关
export * from './permission'; export * from './system/permission';
// 系统相关 // 系统相关
export * from './module'; export * from './system/module';
export * from './achievement'; export * from './achievement';

View File

@@ -0,0 +1,37 @@
import type { ResultDomain } from '@/types';
/**
* 配置项接口定义(对应后端 TbSysConfig
*/
export interface ConfigItem {
// 后端 TbSysConfig 字段(完全匹配)
configKey: string; // 配置键
configName?: string; // 配置显示名称
configValue: string; // 配置值
configType: string; // 数据类型string/number/boolean/json
renderType?: string; // 前端渲染类型input/textarea/number/switch/select/password
configGroup: string; // 配置分组
description?: string; // 配置描述
placeholder?: string; // 输入框占位符
remark?: string; // 备注说明(显示在表单项下方)
rows?: number; // 文本框行数textarea用
minValue?: number; // 最小值number用
maxValue?: number; // 最大值number用
unit?: string; // 单位number用
options?: string; // 下拉选项select用JSON字符串
orderNum?: number; // 排序号
isSystem?: boolean; // 是否系统配置
creator?: string; // 创建者
updater?: string; // 更新者
createTime?: string; // 创建时间
updateTime?: string; // 更新时间
}
/**
* 保存配置项参数
*/
export interface SaveConfigParam {
configKey: string;
configValue: string;
configGroup: string;
}

View File

@@ -0,0 +1,6 @@
export * from './config';
export * from './menu';
export * from './module';
export * from './permission';
export * from './role';
export * from './user'

View File

@@ -5,9 +5,9 @@
*/ */
import { BaseDTO } from '../base'; import { BaseDTO } from '../base';
import { SysMenu } from '../menu'; import { SysMenu } from './menu';
import { SysRole } from '../role'; import { SysRole } from './role';
import { SysModule } from '../module'; import { SysModule } from './module';
/** /**
* *

View File

@@ -0,0 +1,390 @@
<template>
<AdminLayout title="系统配置" subtitle="系统参数配置管理">
<div class="system-config">
<!-- 配置分类标签 - 动态生成 -->
<el-tabs v-model="activeTab" type="border-card" v-loading="loading">
<el-tab-pane
v-for="group in configGroups"
:key="group.groupKey"
:label="group.groupName"
:name="group.groupKey"
>
<el-form
:model="configData[group.groupKey]"
label-width="140px"
class="config-form"
>
<!-- 动态渲染配置项 -->
<template v-for="item in group.items" :key="item.configKey">
<!-- 文本输入框 -->
<el-form-item
v-if="getRenderType(item) === 'input'"
:label="item.configName || item.configKey"
:prop="item.configKey"
>
<el-input
v-model="configData[group.groupKey][item.configKey]"
:placeholder="item.placeholder || `请输入${item.configName || item.configKey}`"
/>
<span v-if="item.remark" class="form-item-remark">{{ item.remark }}</span>
</el-form-item>
<!-- 多行文本框 -->
<el-form-item
v-else-if="getRenderType(item) === 'textarea'"
:label="item.configName || item.configKey"
:prop="item.configKey"
>
<el-input
v-model="configData[group.groupKey][item.configKey]"
type="textarea"
:rows="item.rows || 3"
:placeholder="item.placeholder || `请输入${item.configName || item.configKey}`"
/>
<span v-if="item.remark" class="form-item-remark">{{ item.remark }}</span>
</el-form-item>
<!-- 数字输入框 -->
<el-form-item
v-else-if="getRenderType(item) === 'number'"
:label="item.configName || item.configKey"
:prop="item.configKey"
>
<el-input-number
v-model="configData[group.groupKey][item.configKey]"
:min="item.minValue"
:max="item.maxValue"
:placeholder="item.placeholder"
/>
<span v-if="item.unit" class="form-item-unit">{{ item.unit }}</span>
<span v-if="item.remark" class="form-item-remark">{{ item.remark }}</span>
</el-form-item>
<!-- 开关 -->
<el-form-item
v-else-if="getRenderType(item) === 'switch'"
:label="item.configName || item.configKey"
:prop="item.configKey"
>
<el-switch v-model="configData[group.groupKey][item.configKey]" />
<span v-if="item.remark" class="form-item-remark">{{ item.remark }}</span>
</el-form-item>
<!-- 下拉选择 -->
<el-form-item
v-else-if="getRenderType(item) === 'select'"
:label="item.configName || item.configKey"
:prop="item.configKey"
>
<el-select
v-model="configData[group.groupKey][item.configKey]"
:placeholder="item.placeholder || `请选择${item.configName || item.configKey}`"
>
<el-option
v-for="option in parseOptions(item.options)"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
<span v-if="item.remark" class="form-item-remark">{{ item.remark }}</span>
</el-form-item>
<!-- 密码输入框 -->
<el-form-item
v-else-if="getRenderType(item) === 'password'"
:label="item.configName || item.configKey"
:prop="item.configKey"
>
<el-input
v-model="configData[group.groupKey][item.configKey]"
type="password"
show-password
:placeholder="item.placeholder || `请输入${item.configName || item.configKey}`"
/>
<span v-if="item.remark" class="form-item-remark">{{ item.remark }}</span>
</el-form-item>
</template>
<!-- 操作按钮 -->
<el-form-item>
<el-button
type="primary"
@click="saveConfig(group.groupKey)"
:loading="saving"
>
保存配置
</el-button>
<el-button @click="loadConfigs">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
</AdminLayout>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { AdminLayout } from '@/views/admin';
import { ElMessage } from 'element-plus';
import { configApi } from '@/apis/system';
import type { ConfigItem } from '@/types/system/config';
defineOptions({
name: 'SystemConfigView'
});
// 配置分组接口定义
interface ConfigGroup {
groupKey: string;
groupName: string;
orderNum: number;
items: ConfigItem[];
}
const loading = ref(false);
const saving = ref(false);
const activeTab = ref('');
// 配置分组列表
const configGroups = ref<ConfigGroup[]>([]);
// 配置数据对象,按分组存储
const configData = reactive<Record<string, Record<string, any>>>({});
onMounted(() => {
loadConfigs();
});
/**
* 获取配置项的渲染类型
* @param item 配置项
* @returns 渲染类型input/textarea/number/switch/select/password
*/
function getRenderType(item: ConfigItem): string {
// 如果明确指定了 renderType直接使用
if (item.renderType) {
return item.renderType;
}
// 根据 configType 自动推断渲染类型
switch (item.configType?.toLowerCase()) {
case 'boolean':
return 'switch';
case 'number':
case 'integer':
case 'long':
case 'double':
case 'float':
return 'number';
case 'json':
return 'textarea';
case 'string':
default:
// String 类型根据其他属性进一步判断
if (item.options) {
return 'select';
}
if (item.rows && item.rows > 1) {
return 'textarea';
}
if (item.configKey?.includes('password') || item.configKey?.includes('secret')) {
return 'password';
}
return 'input';
}
}
/**
* 解析 options JSON 字符串
* @param optionsStr JSON 字符串
* @returns 选项数组
*/
function parseOptions(optionsStr?: string): Array<{ label: string; value: any }> {
if (!optionsStr) {
return [];
}
try {
const parsed = JSON.parse(optionsStr);
return Array.isArray(parsed) ? parsed : [];
} catch (error) {
console.warn('解析 options 失败:', error);
return [];
}
}
/**
* 加载配置数据
*/
async function loadConfigs() {
try {
loading.value = true;
// 调用API获取配置
const result = await configApi.getConfigs();
if (!result.success) {
ElMessage.error(result.message || '加载配置失败');
return;
}
// 后端返回的列表数据在 dataList 字段中
const configItems: ConfigItem[] = (result.dataList || []) as unknown as ConfigItem[];
// 定义分组名称映射
const groupNames: Record<string, string> = {
basic: '基本配置',
email: '邮件配置',
storage: '存储配置',
system: '系统参数'
};
// 按分组组织配置
const groupMap = new Map<string, ConfigGroup>();
configItems.forEach((item: ConfigItem) => {
if (!groupMap.has(item.configGroup)) {
groupMap.set(item.configGroup, {
groupKey: item.configGroup,
groupName: groupNames[item.configGroup] || item.configGroup,
orderNum: Object.keys(groupNames).indexOf(item.configGroup),
items: []
});
configData[item.configGroup] = {};
}
const group = groupMap.get(item.configGroup)!;
group.items.push(item);
// 初始化配置值,根据 configType 转换数据类型
let value: any = item.configValue;
switch (item.configType?.toLowerCase()) {
case 'number':
case 'integer':
case 'long':
case 'double':
case 'float':
value = Number(value);
break;
case 'boolean':
value = value === 'true' || value === true || value === 1;
break;
case 'json':
try {
value = JSON.parse(value);
} catch {
value = value;
}
break;
default:
// string 类型保持原样
break;
}
configData[item.configGroup][item.configKey] = value;
});
// 转换为数组并排序
configGroups.value = Array.from(groupMap.values()).sort(
(a, b) => a.orderNum - b.orderNum
);
// 每个分组内的配置项也排序
configGroups.value.forEach(group => {
group.items.sort((a, b) => (a.orderNum || 0) - (b.orderNum || 0));
});
// 设置默认激活的标签页
if (configGroups.value.length > 0) {
activeTab.value = configGroups.value[0].groupKey;
}
} catch (error) {
console.error('加载配置失败:', error);
ElMessage.error('加载配置失败');
} finally {
loading.value = false;
}
}
/**
* 保存指定分组的配置
*/
async function saveConfig(groupKey: string) {
try {
saving.value = true;
const groupData = configData[groupKey];
const configItems = configGroups.value
.find(g => g.groupKey === groupKey)
?.items || [];
// 构建保存数据
const saveData = configItems.map(item => ({
configKey: item.configKey,
configValue: String(groupData[item.configKey]),
configGroup: groupKey
}));
// TODO: 调用API保存配置
// await configApi.saveConfigs(saveData);
console.log('保存配置:', saveData);
await new Promise(resolve => setTimeout(resolve, 1000));
ElMessage.success('配置保存成功');
} catch (error) {
console.error('保存配置失败:', error);
ElMessage.error('保存配置失败');
} finally {
saving.value = false;
}
}
</script>
<style lang="scss" scoped>
.system-config {
padding: 20px;
:deep(.el-tabs--border-card) {
border: none;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
:deep(.el-tabs__content) {
padding: 24px;
}
}
.config-form {
max-width: 800px;
:deep(.el-form-item__label) {
font-weight: 500;
}
:deep(.el-input),
:deep(.el-select),
:deep(.el-input-number) {
width: 100%;
}
:deep(.el-textarea__inner) {
font-family: inherit;
}
.form-item-remark {
display: inline-block;
margin-left: 10px;
color: #909399;
font-size: 13px;
}
.form-item-unit {
display: inline-block;
margin-left: 10px;
color: #606266;
font-size: 14px;
}
}
</style>

View File

@@ -314,7 +314,7 @@ import { resourceTagApi } from '@/apis/resource';
import { GenericSelector } from '@/components/base'; import { GenericSelector } from '@/components/base';
import type { TaskVO, Course, TaskItemVO } from '@/types/study'; import type { TaskVO, Course, TaskItemVO } from '@/types/study';
import type { Resource, Tag } from '@/types/resource'; import type { Resource, Tag } from '@/types/resource';
import type { SysUser } from '@/types/user'; import type { SysUser } from '@/types/system/user';
defineOptions({ defineOptions({
name: 'LearningTaskAdd' name: 'LearningTaskAdd'

View File

@@ -20,7 +20,7 @@ import { useRoute } from 'vue-router';
import { FloatingSidebar } from '@/components/base'; import { FloatingSidebar } from '@/components/base';
import { UserCard } from '@/views/user/user-center/components'; import { UserCard } from '@/views/user/user-center/components';
import { getParentChildrenRoutes } from '@/utils/routeUtils'; import { getParentChildrenRoutes } from '@/utils/routeUtils';
import type { SysMenu } from '@/types/menu'; import type { SysMenu } from '@/types/system/menu';
const route = useRoute(); const route = useRoute();