| | |
| | | (1321, 1300, '活动通知', 'system_activity_notice', 'C', '/system/settings/activity-notice', 'System/ActivityNotice', NULL, 13, 1, 1, 'system:activity-notice:view', '活动通知菜单', 0), |
| | | (1322, 1321, '新增活动通知', 'system_activity_notice_add', 'B', NULL, NULL, NULL, 1, 1, 1, 'system:activity-notice:add', '活动通知新增按钮', 0), |
| | | (1323, 1321, '删除活动通知', 'system_activity_notice_delete', 'B', NULL, NULL, NULL, 2, 1, 1, 'system:activity-notice:delete', '活动通知删除按钮', 0), |
| | | (1324, 1300, '平台资质', 'system_platform_qualification', 'C', '/system/settings/platform-qualification', 'System/PlatformQualification', NULL, 14, 1, 1, 'system:platform-qualification:view', '平台资质菜单', 0), |
| | | (1325, 1324, '新增平台资质', 'system_platform_qualification_add', 'B', NULL, NULL, NULL, 1, 1, 1, 'system:platform-qualification:add', '平台资质新增按钮', 0), |
| | | (1326, 1324, '查看平台资质', 'system_platform_qualification_detail', 'B', NULL, NULL, NULL, 2, 1, 1, 'system:platform-qualification:detail', '平台资质详情按钮', 0), |
| | | (1327, 1324, '编辑平台资质', 'system_platform_qualification_edit', 'B', NULL, NULL, NULL, 3, 1, 1, 'system:platform-qualification:edit', '平台资质编辑按钮', 0), |
| | | (1328, 1324, '删除平台资质', 'system_platform_qualification_delete', 'B', NULL, NULL, NULL, 4, 1, 1, 'system:platform-qualification:delete', '平台资质删除按钮', 0), |
| | | (1329, 1324, '切换平台资质上架状态', 'system_platform_qualification_publish_status', 'B', NULL, NULL, NULL, 5, 1, 1, 'system:platform-qualification:publish-status', '平台资质上架状态按钮', 0), |
| | | (1310, 1300, '积分设置', 'system_points_settings', 'C', '/system/settings/points', 'System/PointsSettings', NULL, 10, 1, 1, 'system:points-settings:view', '积分设置菜单', 0), |
| | | (1311, 1310, '编辑积分设置', 'system_points_settings_edit', 'B', NULL, NULL, NULL, 1, 1, 1, 'system:points-settings:edit', '积分设置编辑按钮', 0), |
| | | (2000, 0, '用户与会员', 'member_root', 'M', '/member', NULL, 'team', 200, 1, 1, NULL, '用户与会员模块', 0), |
| | |
| | | INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) |
| | | VALUES |
| | | (1, 1000), (1, 1100), (1, 1101), (1, 1102), (1, 1103), (1, 1104), (1, 1105), |
| | | (1, 1200), (1, 1201), (1, 1202), (1, 1203), (1, 1204), (1, 1300), (1, 1305), (1, 1306), (1, 1307), (1, 1308), (1, 1309), (1, 1310), (1, 1311), (1, 1312), (1, 1313), (1, 1314), (1, 1315), (1, 1316), (1, 1317), (1, 1318), (1, 1319), (1, 1320), (1, 1321), (1, 1322), (1, 1323), |
| | | (1, 1200), (1, 1201), (1, 1202), (1, 1203), (1, 1204), (1, 1300), (1, 1305), (1, 1306), (1, 1307), (1, 1308), (1, 1309), (1, 1310), (1, 1311), (1, 1312), (1, 1313), (1, 1314), (1, 1315), (1, 1316), (1, 1317), (1, 1318), (1, 1319), (1, 1320), (1, 1321), (1, 1322), (1, 1323), (1, 1324), (1, 1325), (1, 1326), (1, 1327), (1, 1328), (1, 1329), |
| | | (1, 2000), (1, 2100), (1, 2110), (1, 2111), (1, 2112), (1, 2113), (1, 2114), (1, 2115), (1, 2116), (1, 2117), (1, 2118), (1, 2119), |
| | | (1, 2200), (1, 2210), (1, 2211), (1, 2220), (1, 2221), |
| | | (1, 3000), (1, 3050), (1, 3060), (1, 3061), (1, 3062), (1, 3063), |
| | |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='活动通知表'; |
| | | |
| | | -- ---------------------------- |
| | | -- 平台资质 |
| | | -- ---------------------------- |
| | | CREATE TABLE IF NOT EXISTS `tcm_platform_qualification` ( |
| | | `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, |
| | | `qualification_name` VARCHAR(64) NOT NULL COMMENT '资质名称', |
| | | `qualification_type` VARCHAR(16) NOT NULL COMMENT '资质类型:RICH_TEXT/LINK', |
| | | `record_number` VARCHAR(64) NOT NULL COMMENT '备案号', |
| | | `content_html` LONGTEXT DEFAULT NULL COMMENT '资质内容富文本', |
| | | `link_url` VARCHAR(512) DEFAULT NULL COMMENT '跳转链接', |
| | | `sort` INT NOT NULL DEFAULT 0 COMMENT '排序', |
| | | `publish_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否上架:1是 0否', |
| | | `deleted` TINYINT(1) NOT NULL DEFAULT 0, |
| | | `created_by` VARCHAR(64) NOT NULL DEFAULT '系统初始化' COMMENT '创建人', |
| | | `updated_by` VARCHAR(64) NOT NULL DEFAULT '系统初始化' COMMENT '更新人', |
| | | `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_platform_qualification_created_at` (`created_at`), |
| | | KEY `idx_platform_qualification_publish_enabled` (`publish_enabled`), |
| | | KEY `idx_platform_qualification_type` (`qualification_type`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台资质表'; |
| | | |
| | | -- ---------------------------- |
| | | -- 积分设置 |
| | | -- ---------------------------- |
| | | CREATE TABLE IF NOT EXISTS `tcm_points_config` ( |
| New file |
| | |
| | | CREATE TABLE IF NOT EXISTS `tcm_platform_qualification` ( |
| | | `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, |
| | | `qualification_name` VARCHAR(64) NOT NULL COMMENT '资质名称', |
| | | `qualification_type` VARCHAR(16) NOT NULL COMMENT '资质类型:RICH_TEXT/LINK', |
| | | `record_number` VARCHAR(64) NOT NULL COMMENT '备案号', |
| | | `content_html` LONGTEXT DEFAULT NULL COMMENT '资质内容富文本', |
| | | `link_url` VARCHAR(512) DEFAULT NULL COMMENT '跳转链接', |
| | | `sort` INT NOT NULL DEFAULT 0 COMMENT '排序', |
| | | `publish_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否上架:1是 0否', |
| | | `deleted` TINYINT(1) NOT NULL DEFAULT 0, |
| | | `created_by` VARCHAR(64) NOT NULL DEFAULT '系统初始化' COMMENT '创建人', |
| | | `updated_by` VARCHAR(64) NOT NULL DEFAULT '系统初始化' COMMENT '更新人', |
| | | `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_platform_qualification_created_at` (`created_at`), |
| | | KEY `idx_platform_qualification_publish_enabled` (`publish_enabled`), |
| | | KEY `idx_platform_qualification_type` (`qualification_type`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台资质表'; |
| | | |
| | | INSERT IGNORE INTO `sys_menu` |
| | | (`id`, `parent_id`, `name`, `menu_code`, `type`, `path`, `component`, `icon`, `sort`, `status`, `visible`, `permission_key`, `remark`, `deleted`) |
| | | VALUES |
| | | (1300, 1000, '系统设置', 'system_setting_group', 'M', '/system/settings', NULL, NULL, 30, 1, 1, NULL, '系统设置目录', 0), |
| | | (1324, 1300, '平台资质', 'system_platform_qualification', 'C', '/system/settings/platform-qualification', 'System/PlatformQualification', NULL, 14, 1, 1, 'system:platform-qualification:view', '平台资质菜单', 0), |
| | | (1325, 1324, '新增平台资质', 'system_platform_qualification_add', 'B', NULL, NULL, NULL, 1, 1, 1, 'system:platform-qualification:add', '平台资质新增按钮', 0), |
| | | (1326, 1324, '查看平台资质', 'system_platform_qualification_detail', 'B', NULL, NULL, NULL, 2, 1, 1, 'system:platform-qualification:detail', '平台资质详情按钮', 0), |
| | | (1327, 1324, '编辑平台资质', 'system_platform_qualification_edit', 'B', NULL, NULL, NULL, 3, 1, 1, 'system:platform-qualification:edit', '平台资质编辑按钮', 0), |
| | | (1328, 1324, '删除平台资质', 'system_platform_qualification_delete', 'B', NULL, NULL, NULL, 4, 1, 1, 'system:platform-qualification:delete', '平台资质删除按钮', 0), |
| | | (1329, 1324, '切换平台资质上架状态', 'system_platform_qualification_publish_status', 'B', NULL, NULL, NULL, 5, 1, 1, 'system:platform-qualification:publish-status', '平台资质上架状态按钮', 0); |
| | | |
| | | INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`) |
| | | VALUES |
| | | (1, 1300), (1, 1324), (1, 1325), (1, 1326), (1, 1327), (1, 1328), (1, 1329); |
| | |
| | | null, null, null, 1, "system:activity-notice:add", "活动通知新增按钮")); |
| | | menuIds.add(saveMenu(1323L, 1321L, "删除活动通知", "system_activity_notice_delete", "B", |
| | | null, null, null, 2, "system:activity-notice:delete", "活动通知删除按钮")); |
| | | menuIds.add(saveMenu(1324L, 1300L, "平台资质", "system_platform_qualification", "C", |
| | | "/system/settings/platform-qualification", "System/PlatformQualification", null, 14, "system:platform-qualification:view", "平台资质菜单")); |
| | | menuIds.add(saveMenu(1325L, 1324L, "新增平台资质", "system_platform_qualification_add", "B", |
| | | null, null, null, 1, "system:platform-qualification:add", "平台资质新增按钮")); |
| | | menuIds.add(saveMenu(1326L, 1324L, "查看平台资质", "system_platform_qualification_detail", "B", |
| | | null, null, null, 2, "system:platform-qualification:detail", "平台资质详情按钮")); |
| | | menuIds.add(saveMenu(1327L, 1324L, "编辑平台资质", "system_platform_qualification_edit", "B", |
| | | null, null, null, 3, "system:platform-qualification:edit", "平台资质编辑按钮")); |
| | | menuIds.add(saveMenu(1328L, 1324L, "删除平台资质", "system_platform_qualification_delete", "B", |
| | | null, null, null, 4, "system:platform-qualification:delete", "平台资质删除按钮")); |
| | | menuIds.add(saveMenu(1329L, 1324L, "切换平台资质上架状态", "system_platform_qualification_publish_status", "B", |
| | | null, null, null, 5, "system:platform-qualification:publish-status", "平台资质上架状态按钮")); |
| | | menuIds.add(saveMenu(1310L, 1300L, "积分设置", "system_points_settings", "C", |
| | | "/system/settings/points", "System/PointsSettings", null, 10, "system:points-settings:view", "积分设置菜单")); |
| | | menuIds.add(saveMenu(1311L, 1310L, "编辑积分设置", "system_points_settings_edit", "B", |
| New file |
| | |
| | | package com.tcm.training.admin.system.controller; |
| | | |
| | | import com.tcm.training.admin.common.ApiResponse; |
| | | import com.tcm.training.admin.common.PageResponse; |
| | | import com.tcm.training.admin.system.dto.PlatformQualificationPublishStatusRequest; |
| | | import com.tcm.training.admin.system.dto.PlatformQualificationSaveRequest; |
| | | import com.tcm.training.admin.system.service.PlatformQualificationAppService; |
| | | import com.tcm.training.admin.system.vo.PlatformQualificationDetailVO; |
| | | import com.tcm.training.admin.system.vo.PlatformQualificationListItemVO; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PatchMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.validation.Valid; |
| | | |
| | | @Validated |
| | | @RestController |
| | | @RequestMapping({"/api/v1", "/api"}) |
| | | public class PlatformQualificationController { |
| | | |
| | | private final PlatformQualificationAppService platformQualificationAppService; |
| | | |
| | | public PlatformQualificationController(PlatformQualificationAppService platformQualificationAppService) { |
| | | this.platformQualificationAppService = platformQualificationAppService; |
| | | } |
| | | |
| | | @GetMapping("/platform-qualifications") |
| | | public ApiResponse<PageResponse<PlatformQualificationListItemVO>> page( |
| | | @RequestParam(required = false, defaultValue = "1") int page, |
| | | @RequestParam(required = false, defaultValue = "10") int pageSize, |
| | | @RequestParam(required = false) String qualificationName) { |
| | | return ApiResponse.ok(platformQualificationAppService.page(page, pageSize, qualificationName)); |
| | | } |
| | | |
| | | @GetMapping("/platform-qualifications/{id}") |
| | | public ApiResponse<PlatformQualificationDetailVO> detail(@PathVariable("id") Long id) { |
| | | return ApiResponse.ok(platformQualificationAppService.detail(id)); |
| | | } |
| | | |
| | | @PostMapping("/platform-qualifications") |
| | | public ApiResponse<PlatformQualificationDetailVO> create(@Valid @RequestBody PlatformQualificationSaveRequest request) { |
| | | return ApiResponse.ok(platformQualificationAppService.create(request)); |
| | | } |
| | | |
| | | @PutMapping("/platform-qualifications/{id}") |
| | | public ApiResponse<PlatformQualificationDetailVO> update(@PathVariable("id") Long id, |
| | | @Valid @RequestBody PlatformQualificationSaveRequest request) { |
| | | return ApiResponse.ok(platformQualificationAppService.update(id, request)); |
| | | } |
| | | |
| | | @PatchMapping("/platform-qualifications/{id}/publish-status") |
| | | public ApiResponse<Void> patchPublishStatus(@PathVariable("id") Long id, |
| | | @Valid @RequestBody PlatformQualificationPublishStatusRequest request) { |
| | | platformQualificationAppService.patchPublishStatus(id, request); |
| | | return ApiResponse.ok(null); |
| | | } |
| | | |
| | | @DeleteMapping("/platform-qualifications/{id}") |
| | | public ApiResponse<Void> delete(@PathVariable("id") Long id) { |
| | | platformQualificationAppService.delete(id); |
| | | return ApiResponse.ok(null); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.tcm.training.admin.system.dto; |
| | | |
| | | import lombok.Getter; |
| | | import lombok.NoArgsConstructor; |
| | | import lombok.Setter; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | @Getter |
| | | @Setter |
| | | @NoArgsConstructor |
| | | public class PlatformQualificationPublishStatusRequest { |
| | | |
| | | @NotNull(message = "请选择上架状态") |
| | | private Boolean publishEnabled; |
| | | } |
| New file |
| | |
| | | package com.tcm.training.admin.system.dto; |
| | | |
| | | import lombok.Getter; |
| | | import lombok.NoArgsConstructor; |
| | | import lombok.Setter; |
| | | |
| | | import javax.validation.constraints.Min; |
| | | import javax.validation.constraints.NotBlank; |
| | | import javax.validation.constraints.NotNull; |
| | | import javax.validation.constraints.Size; |
| | | |
| | | @Getter |
| | | @Setter |
| | | @NoArgsConstructor |
| | | public class PlatformQualificationSaveRequest { |
| | | |
| | | @NotBlank(message = "请输入资质名称") |
| | | @Size(max = 64, message = "资质名称长度不能超过64个字符") |
| | | private String qualificationName; |
| | | |
| | | @NotBlank(message = "请选择资质类型") |
| | | @Size(max = 16, message = "资质类型长度不能超过16个字符") |
| | | private String qualificationType; |
| | | |
| | | @NotBlank(message = "请输入备案号") |
| | | @Size(max = 64, message = "备案号长度不能超过64个字符") |
| | | private String recordNumber; |
| | | |
| | | private String contentHtml; |
| | | |
| | | @Size(max = 512, message = "跳转链接长度不能超过512个字符") |
| | | private String linkUrl; |
| | | |
| | | @NotNull(message = "请输入排序") |
| | | @Min(value = 0, message = "排序不能小于0") |
| | | private Integer sort; |
| | | |
| | | @NotNull(message = "请选择上架状态") |
| | | private Boolean publishEnabled; |
| | | } |
| New file |
| | |
| | | package com.tcm.training.admin.system.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Getter; |
| | | import lombok.NoArgsConstructor; |
| | | import lombok.Setter; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @TableName("tcm_platform_qualification") |
| | | @Getter |
| | | @Setter |
| | | @NoArgsConstructor |
| | | public class PlatformQualification { |
| | | |
| | | public static final String TYPE_RICH_TEXT = "RICH_TEXT"; |
| | | public static final String TYPE_LINK = "LINK"; |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @TableField("qualification_name") |
| | | private String qualificationName; |
| | | |
| | | @TableField("qualification_type") |
| | | private String qualificationType; |
| | | |
| | | @TableField("record_number") |
| | | private String recordNumber; |
| | | |
| | | @TableField("content_html") |
| | | private String contentHtml; |
| | | |
| | | @TableField("link_url") |
| | | private String linkUrl; |
| | | |
| | | @TableField("sort") |
| | | private Integer sort; |
| | | |
| | | @TableField("publish_enabled") |
| | | private Integer publishEnabled; |
| | | |
| | | @TableLogic |
| | | private Integer deleted; |
| | | |
| | | @TableField(value = "created_by", fill = FieldFill.INSERT) |
| | | private String createdBy; |
| | | |
| | | @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE) |
| | | private String updatedBy; |
| | | |
| | | @TableField("created_at") |
| | | private LocalDateTime createdAt; |
| | | |
| | | @TableField("updated_at") |
| | | private LocalDateTime updatedAt; |
| | | } |
| New file |
| | |
| | | package com.tcm.training.admin.system.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.tcm.training.admin.system.entity.PlatformQualification; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface PlatformQualificationMapper extends BaseMapper<PlatformQualification> { |
| | | } |
| New file |
| | |
| | | package com.tcm.training.admin.system.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.tcm.training.admin.common.PageResponse; |
| | | import com.tcm.training.admin.system.dto.PlatformQualificationPublishStatusRequest; |
| | | import com.tcm.training.admin.system.dto.PlatformQualificationSaveRequest; |
| | | import com.tcm.training.admin.system.entity.PlatformQualification; |
| | | import com.tcm.training.admin.system.mapper.PlatformQualificationMapper; |
| | | import com.tcm.training.admin.system.vo.PlatformQualificationDetailVO; |
| | | import com.tcm.training.admin.system.vo.PlatformQualificationListItemVO; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | public class PlatformQualificationAppService { |
| | | |
| | | private final PlatformQualificationMapper platformQualificationMapper; |
| | | |
| | | public PlatformQualificationAppService(PlatformQualificationMapper platformQualificationMapper) { |
| | | this.platformQualificationMapper = platformQualificationMapper; |
| | | } |
| | | |
| | | public PageResponse<PlatformQualificationListItemVO> page(int page, int pageSize, String qualificationName) { |
| | | LambdaQueryWrapper<PlatformQualification> queryWrapper = new LambdaQueryWrapper<PlatformQualification>() |
| | | .eq(PlatformQualification::getDeleted, 0) |
| | | .like(qualificationName != null && !qualificationName.trim().isEmpty(), |
| | | PlatformQualification::getQualificationName, |
| | | qualificationName == null ? null : qualificationName.trim()) |
| | | .orderByDesc(PlatformQualification::getCreatedAt) |
| | | .orderByDesc(PlatformQualification::getId); |
| | | IPage<PlatformQualification> result = platformQualificationMapper.selectPage( |
| | | new Page<PlatformQualification>(page, pageSize), |
| | | queryWrapper |
| | | ); |
| | | List<PlatformQualificationListItemVO> list = new ArrayList<PlatformQualificationListItemVO>(); |
| | | for (PlatformQualification item : result.getRecords()) { |
| | | list.add(toListItem(item)); |
| | | } |
| | | return PageResponse.of(list, result.getTotal(), result.getCurrent(), result.getSize()); |
| | | } |
| | | |
| | | public PlatformQualificationDetailVO detail(Long id) { |
| | | return toDetailVO(requireItem(id)); |
| | | } |
| | | |
| | | @Transactional |
| | | public PlatformQualificationDetailVO create(PlatformQualificationSaveRequest request) { |
| | | PlatformQualification item = new PlatformQualification(); |
| | | fillEntity(item, request); |
| | | item.setDeleted(0); |
| | | platformQualificationMapper.insert(item); |
| | | return detail(item.getId()); |
| | | } |
| | | |
| | | @Transactional |
| | | public PlatformQualificationDetailVO update(Long id, PlatformQualificationSaveRequest request) { |
| | | PlatformQualification item = requireItem(id); |
| | | fillEntity(item, request); |
| | | platformQualificationMapper.updateById(item); |
| | | return detail(id); |
| | | } |
| | | |
| | | @Transactional |
| | | public void delete(Long id) { |
| | | requireItem(id); |
| | | platformQualificationMapper.deleteById(id); |
| | | } |
| | | |
| | | @Transactional |
| | | public void patchPublishStatus(Long id, PlatformQualificationPublishStatusRequest request) { |
| | | PlatformQualification item = requireItem(id); |
| | | item.setPublishEnabled(booleanToInteger(request.getPublishEnabled())); |
| | | platformQualificationMapper.updateById(item); |
| | | } |
| | | |
| | | private void fillEntity(PlatformQualification item, PlatformQualificationSaveRequest request) { |
| | | String qualificationType = normalizeQualificationType(request.getQualificationType()); |
| | | item.setQualificationName(normalizeRequired(request.getQualificationName(), "请输入资质名称")); |
| | | item.setQualificationType(qualificationType); |
| | | item.setRecordNumber(normalizeRequired(request.getRecordNumber(), "请输入备案号")); |
| | | item.setSort(request.getSort()); |
| | | item.setPublishEnabled(booleanToInteger(request.getPublishEnabled())); |
| | | |
| | | if (PlatformQualification.TYPE_RICH_TEXT.equals(qualificationType)) { |
| | | item.setContentHtml(normalizeContent(request.getContentHtml())); |
| | | item.setLinkUrl(null); |
| | | return; |
| | | } |
| | | |
| | | item.setLinkUrl(normalizeRequired(request.getLinkUrl(), "请输入跳转链接")); |
| | | item.setContentHtml(null); |
| | | } |
| | | |
| | | private PlatformQualification requireItem(Long id) { |
| | | PlatformQualification item = platformQualificationMapper.selectOne(new LambdaQueryWrapper<PlatformQualification>() |
| | | .eq(PlatformQualification::getId, id) |
| | | .eq(PlatformQualification::getDeleted, 0) |
| | | .last("limit 1")); |
| | | if (item == null) { |
| | | throw new IllegalArgumentException("平台资质不存在"); |
| | | } |
| | | return item; |
| | | } |
| | | |
| | | private PlatformQualificationListItemVO toListItem(PlatformQualification item) { |
| | | PlatformQualificationListItemVO vo = new PlatformQualificationListItemVO(); |
| | | vo.setId(item.getId()); |
| | | vo.setQualificationName(item.getQualificationName()); |
| | | vo.setQualificationType(item.getQualificationType()); |
| | | vo.setRecordNumber(item.getRecordNumber()); |
| | | vo.setSort(item.getSort()); |
| | | vo.setPublishEnabled(integerToBoolean(item.getPublishEnabled())); |
| | | vo.setCreatedBy(item.getCreatedBy()); |
| | | vo.setCreatedAt(item.getCreatedAt()); |
| | | return vo; |
| | | } |
| | | |
| | | private PlatformQualificationDetailVO toDetailVO(PlatformQualification item) { |
| | | PlatformQualificationDetailVO vo = new PlatformQualificationDetailVO(); |
| | | vo.setId(item.getId()); |
| | | vo.setQualificationName(item.getQualificationName()); |
| | | vo.setQualificationType(item.getQualificationType()); |
| | | vo.setRecordNumber(item.getRecordNumber()); |
| | | vo.setContentHtml(item.getContentHtml()); |
| | | vo.setLinkUrl(item.getLinkUrl()); |
| | | vo.setSort(item.getSort()); |
| | | vo.setPublishEnabled(integerToBoolean(item.getPublishEnabled())); |
| | | vo.setCreatedBy(item.getCreatedBy()); |
| | | vo.setUpdatedBy(item.getUpdatedBy()); |
| | | vo.setCreatedAt(item.getCreatedAt()); |
| | | vo.setUpdatedAt(item.getUpdatedAt()); |
| | | return vo; |
| | | } |
| | | |
| | | private String normalizeQualificationType(String qualificationType) { |
| | | String normalized = normalizeRequired(qualificationType, "请选择资质类型").toUpperCase(); |
| | | if (PlatformQualification.TYPE_RICH_TEXT.equals(normalized) || PlatformQualification.TYPE_LINK.equals(normalized)) { |
| | | return normalized; |
| | | } |
| | | throw new IllegalArgumentException("资质类型不合法"); |
| | | } |
| | | |
| | | private String normalizeRequired(String value, String message) { |
| | | String trimmed = trimToNull(value); |
| | | if (trimmed == null) { |
| | | throw new IllegalArgumentException(message); |
| | | } |
| | | return trimmed; |
| | | } |
| | | |
| | | private String normalizeContent(String contentHtml) { |
| | | String trimmed = trimToNull(contentHtml); |
| | | if (trimmed == null || isBlankRichText(trimmed)) { |
| | | throw new IllegalArgumentException("请输入资质内容"); |
| | | } |
| | | return trimmed; |
| | | } |
| | | |
| | | private boolean isBlankRichText(String html) { |
| | | String normalized = html |
| | | .replaceAll("(?i)<br\\s*/?>", "") |
| | | .replaceAll("(?i)</p>", "") |
| | | .replace(" ", "") |
| | | .replace(" ", "") |
| | | .replaceAll("<[^>]+>", "") |
| | | .trim(); |
| | | if (!normalized.isEmpty()) { |
| | | return false; |
| | | } |
| | | String lowerCaseHtml = html.toLowerCase(); |
| | | return !lowerCaseHtml.contains("<img") |
| | | && !lowerCaseHtml.contains("<video") |
| | | && !lowerCaseHtml.contains("<audio") |
| | | && !lowerCaseHtml.contains("<iframe"); |
| | | } |
| | | |
| | | private Integer booleanToInteger(Boolean value) { |
| | | return Boolean.TRUE.equals(value) ? 1 : 0; |
| | | } |
| | | |
| | | private Boolean integerToBoolean(Integer value) { |
| | | return value != null && value == 1; |
| | | } |
| | | |
| | | private String trimToNull(String value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | String trimmed = value.trim(); |
| | | return trimmed.isEmpty() ? null : trimmed; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.tcm.training.admin.system.vo; |
| | | |
| | | import lombok.Getter; |
| | | import lombok.NoArgsConstructor; |
| | | import lombok.Setter; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Getter |
| | | @Setter |
| | | @NoArgsConstructor |
| | | public class PlatformQualificationDetailVO { |
| | | |
| | | private Long id; |
| | | private String qualificationName; |
| | | private String qualificationType; |
| | | private String recordNumber; |
| | | private String contentHtml; |
| | | private String linkUrl; |
| | | private Integer sort; |
| | | private Boolean publishEnabled; |
| | | private String createdBy; |
| | | private String updatedBy; |
| | | private LocalDateTime createdAt; |
| | | private LocalDateTime updatedAt; |
| | | } |
| New file |
| | |
| | | package com.tcm.training.admin.system.vo; |
| | | |
| | | import lombok.Getter; |
| | | import lombok.NoArgsConstructor; |
| | | import lombok.Setter; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Getter |
| | | @Setter |
| | | @NoArgsConstructor |
| | | public class PlatformQualificationListItemVO { |
| | | |
| | | private Long id; |
| | | private String qualificationName; |
| | | private String qualificationType; |
| | | private String recordNumber; |
| | | private Integer sort; |
| | | private Boolean publishEnabled; |
| | | private String createdBy; |
| | | private LocalDateTime createdAt; |
| | | } |
| New file |
| | |
| | | package com.tcm.training.admin.system.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.MybatisConfiguration; |
| | | import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; |
| | | import com.tcm.training.admin.system.dto.PlatformQualificationPublishStatusRequest; |
| | | import com.tcm.training.admin.system.dto.PlatformQualificationSaveRequest; |
| | | import com.tcm.training.admin.system.entity.PlatformQualification; |
| | | import com.tcm.training.admin.system.mapper.PlatformQualificationMapper; |
| | | import com.tcm.training.admin.system.vo.PlatformQualificationDetailVO; |
| | | import org.apache.ibatis.builder.MapperBuilderAssistant; |
| | | import org.junit.jupiter.api.BeforeAll; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.ArgumentCaptor; |
| | | import org.mockito.Captor; |
| | | import org.mockito.InjectMocks; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.junit.jupiter.api.Assertions.assertNull; |
| | | import static org.junit.jupiter.api.Assertions.assertThrows; |
| | | import static org.mockito.ArgumentMatchers.any; |
| | | import static org.mockito.Mockito.doAnswer; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | class PlatformQualificationAppServiceTest { |
| | | |
| | | @BeforeAll |
| | | static void initLambdaCache() { |
| | | MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), ""); |
| | | TableInfoHelper.initTableInfo(assistant, PlatformQualification.class); |
| | | } |
| | | |
| | | @Mock |
| | | private PlatformQualificationMapper platformQualificationMapper; |
| | | |
| | | @InjectMocks |
| | | private PlatformQualificationAppService platformQualificationAppService; |
| | | |
| | | @Captor |
| | | private ArgumentCaptor<PlatformQualification> platformQualificationCaptor; |
| | | |
| | | @Test |
| | | void createShouldPersistRichTextQualification() { |
| | | doAnswer(invocation -> { |
| | | PlatformQualification item = invocation.getArgument(0); |
| | | item.setId(1L); |
| | | return 1; |
| | | }).when(platformQualificationMapper).insert(any(PlatformQualification.class)); |
| | | |
| | | PlatformQualification persisted = new PlatformQualification(); |
| | | persisted.setId(1L); |
| | | persisted.setQualificationName("增值电信业务经营许可证"); |
| | | persisted.setQualificationType(PlatformQualification.TYPE_RICH_TEXT); |
| | | persisted.setRecordNumber("1654156"); |
| | | persisted.setContentHtml("<p>资质内容</p>"); |
| | | persisted.setSort(99); |
| | | persisted.setPublishEnabled(1); |
| | | persisted.setCreatedAt(LocalDateTime.of(2026, 3, 26, 21, 10, 0)); |
| | | when(platformQualificationMapper.selectOne(any())).thenReturn(persisted); |
| | | |
| | | PlatformQualificationSaveRequest request = new PlatformQualificationSaveRequest(); |
| | | request.setQualificationName(" 增值电信业务经营许可证 "); |
| | | request.setQualificationType("rich_text"); |
| | | request.setRecordNumber(" 1654156 "); |
| | | request.setContentHtml("<p>资质内容</p>"); |
| | | request.setSort(99); |
| | | request.setPublishEnabled(true); |
| | | |
| | | PlatformQualificationDetailVO result = platformQualificationAppService.create(request); |
| | | |
| | | verify(platformQualificationMapper).insert(platformQualificationCaptor.capture()); |
| | | assertEquals("增值电信业务经营许可证", platformQualificationCaptor.getValue().getQualificationName()); |
| | | assertEquals("1654156", platformQualificationCaptor.getValue().getRecordNumber()); |
| | | assertEquals("<p>资质内容</p>", platformQualificationCaptor.getValue().getContentHtml()); |
| | | assertNull(platformQualificationCaptor.getValue().getLinkUrl()); |
| | | assertEquals("增值电信业务经营许可证", result.getQualificationName()); |
| | | } |
| | | |
| | | @Test |
| | | void createShouldPersistLinkQualification() { |
| | | doAnswer(invocation -> { |
| | | PlatformQualification item = invocation.getArgument(0); |
| | | item.setId(2L); |
| | | return 1; |
| | | }).when(platformQualificationMapper).insert(any(PlatformQualification.class)); |
| | | |
| | | PlatformQualification persisted = new PlatformQualification(); |
| | | persisted.setId(2L); |
| | | persisted.setQualificationName("营业执照"); |
| | | persisted.setQualificationType(PlatformQualification.TYPE_LINK); |
| | | persisted.setRecordNumber("ICP-2026"); |
| | | persisted.setLinkUrl("https://example.com/license"); |
| | | persisted.setSort(10); |
| | | persisted.setPublishEnabled(0); |
| | | when(platformQualificationMapper.selectOne(any())).thenReturn(persisted); |
| | | |
| | | PlatformQualificationSaveRequest request = new PlatformQualificationSaveRequest(); |
| | | request.setQualificationName("营业执照"); |
| | | request.setQualificationType("LINK"); |
| | | request.setRecordNumber("ICP-2026"); |
| | | request.setLinkUrl(" https://example.com/license "); |
| | | request.setSort(10); |
| | | request.setPublishEnabled(false); |
| | | |
| | | platformQualificationAppService.create(request); |
| | | |
| | | verify(platformQualificationMapper).insert(platformQualificationCaptor.capture()); |
| | | assertEquals(PlatformQualification.TYPE_LINK, platformQualificationCaptor.getValue().getQualificationType()); |
| | | assertEquals("https://example.com/license", platformQualificationCaptor.getValue().getLinkUrl()); |
| | | assertNull(platformQualificationCaptor.getValue().getContentHtml()); |
| | | } |
| | | |
| | | @Test |
| | | void createShouldRejectBlankRichTextContent() { |
| | | PlatformQualificationSaveRequest request = new PlatformQualificationSaveRequest(); |
| | | request.setQualificationName("活动资质"); |
| | | request.setQualificationType("RICH_TEXT"); |
| | | request.setRecordNumber("A-001"); |
| | | request.setContentHtml("<p><br/></p>"); |
| | | request.setSort(1); |
| | | request.setPublishEnabled(true); |
| | | |
| | | IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, |
| | | () -> platformQualificationAppService.create(request)); |
| | | |
| | | assertEquals("请输入资质内容", exception.getMessage()); |
| | | } |
| | | |
| | | @Test |
| | | void createShouldRejectEmptyLinkUrl() { |
| | | PlatformQualificationSaveRequest request = new PlatformQualificationSaveRequest(); |
| | | request.setQualificationName("活动资质"); |
| | | request.setQualificationType("LINK"); |
| | | request.setRecordNumber("A-001"); |
| | | request.setLinkUrl(" "); |
| | | request.setSort(1); |
| | | request.setPublishEnabled(true); |
| | | |
| | | IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, |
| | | () -> platformQualificationAppService.create(request)); |
| | | |
| | | assertEquals("请输入跳转链接", exception.getMessage()); |
| | | } |
| | | |
| | | @Test |
| | | void patchPublishStatusShouldUpdateFlag() { |
| | | PlatformQualification persisted = new PlatformQualification(); |
| | | persisted.setId(3L); |
| | | persisted.setQualificationName("备案资质"); |
| | | persisted.setQualificationType(PlatformQualification.TYPE_LINK); |
| | | persisted.setRecordNumber("A-002"); |
| | | persisted.setLinkUrl("https://example.com"); |
| | | persisted.setSort(2); |
| | | persisted.setPublishEnabled(0); |
| | | when(platformQualificationMapper.selectOne(any())).thenReturn(persisted); |
| | | |
| | | PlatformQualificationPublishStatusRequest request = new PlatformQualificationPublishStatusRequest(); |
| | | request.setPublishEnabled(true); |
| | | |
| | | platformQualificationAppService.patchPublishStatus(3L, request); |
| | | |
| | | verify(platformQualificationMapper).updateById(platformQualificationCaptor.capture()); |
| | | assertEquals(Integer.valueOf(1), platformQualificationCaptor.getValue().getPublishEnabled()); |
| | | } |
| | | } |
| | |
| | | component: './system/activity-notice', |
| | | }, |
| | | { |
| | | name: '平台资质', |
| | | path: '/system/settings/platform-qualification', |
| | | access: 'system:platform-qualification:view', |
| | | component: './system/platform-qualification', |
| | | }, |
| | | { |
| | | name: '消息推送配置', |
| | | path: '/system/settings/message-push', |
| | | access: 'system:message-push:view', |
| | |
| | | !!permission?.['system:app-version:view'] || |
| | | !!permission?.['system:banner:view'] || |
| | | !!permission?.['system:activity-notice:view'] || |
| | | !!permission?.['system:platform-qualification:view'] || |
| | | !!permission?.['system:message-push:view'] || |
| | | !!permission?.['system:points-settings:view'], |
| | | canViewMemberModule: canViewMemberUserGroup || canViewMemberVipGroup, |
| New file |
| | |
| | | import BraftEditorCom from '@/components/BraftEditor'; |
| | | import { PlusOutlined } from '@ant-design/icons'; |
| | | import { history, useAccess } from '@umijs/max'; |
| | | import { Button, Form, Input, message, Modal, Pagination, Radio, Space, Table, Tag } from 'antd'; |
| | | import type { ColumnsType } from 'antd/es/table'; |
| | | import React, { useEffect, useState } from 'react'; |
| | | import { |
| | | createPlatformQualification, |
| | | deletePlatformQualification, |
| | | getPlatformQualificationDetail, |
| | | getPlatformQualificationPage, |
| | | updatePlatformQualification, |
| | | updatePlatformQualificationPublishStatus, |
| | | } from './service'; |
| | | import './style.less'; |
| | | |
| | | type SearchValues = { |
| | | qualificationName?: string; |
| | | }; |
| | | |
| | | type QualificationItem = { |
| | | id: number; |
| | | qualificationName?: string; |
| | | qualificationType?: string; |
| | | recordNumber?: string; |
| | | sort?: number; |
| | | publishEnabled?: boolean; |
| | | createdBy?: string; |
| | | createdAt?: string; |
| | | }; |
| | | |
| | | type QualificationDetail = QualificationItem & { |
| | | contentHtml?: string; |
| | | linkUrl?: string; |
| | | updatedAt?: string; |
| | | updatedBy?: string; |
| | | }; |
| | | |
| | | type QualificationFormValues = { |
| | | qualificationName?: string; |
| | | qualificationType?: string; |
| | | recordNumber?: string; |
| | | contentHtml?: string; |
| | | linkUrl?: string; |
| | | sort?: string; |
| | | publishEnabled?: boolean; |
| | | }; |
| | | |
| | | type ModalMode = 'create' | 'edit' | 'detail'; |
| | | |
| | | const TYPE_RICH_TEXT = 'RICH_TEXT'; |
| | | const TYPE_LINK = 'LINK'; |
| | | |
| | | const typeOptions = [ |
| | | { label: '富文本说明', value: TYPE_RICH_TEXT }, |
| | | { label: '跳转链接', value: TYPE_LINK }, |
| | | ]; |
| | | |
| | | const defaultValues: QualificationFormValues = { |
| | | qualificationName: '', |
| | | qualificationType: TYPE_RICH_TEXT, |
| | | recordNumber: '', |
| | | contentHtml: '', |
| | | linkUrl: '', |
| | | sort: '0', |
| | | publishEnabled: true, |
| | | }; |
| | | |
| | | const trimSearchValues = (values: SearchValues) => ({ |
| | | qualificationName: values.qualificationName?.trim() || undefined, |
| | | }); |
| | | |
| | | const isBlankRichText = (html?: string) => { |
| | | const value = (html || '').trim(); |
| | | if (!value) { |
| | | return true; |
| | | } |
| | | const normalized = value |
| | | .replace(/<br\s*\/?>/gi, '') |
| | | .replace(/<\/p>/gi, '') |
| | | .replace(/ /gi, '') |
| | | .replace(/ /gi, '') |
| | | .replace(/<[^>]+>/g, '') |
| | | .trim(); |
| | | if (normalized) { |
| | | return false; |
| | | } |
| | | const lowerCaseValue = value.toLowerCase(); |
| | | return !lowerCaseValue.includes('<img') && !lowerCaseValue.includes('<video'); |
| | | }; |
| | | |
| | | const validateSort = async (_: unknown, value: string | undefined) => { |
| | | const normalized = String(value || '').trim(); |
| | | if (!normalized) { |
| | | throw new Error('请输入排序'); |
| | | } |
| | | if (!/^(0|[1-9]\d*)$/.test(normalized)) { |
| | | throw new Error('排序必须为0或正整数'); |
| | | } |
| | | }; |
| | | |
| | | const getTypeLabel = (value?: string) => { |
| | | if (value === TYPE_LINK) { |
| | | return '跳转链接'; |
| | | } |
| | | return '富文本说明'; |
| | | }; |
| | | |
| | | const PlatformQualificationPage: React.FC = () => { |
| | | const access = useAccess(); |
| | | const [searchForm] = Form.useForm<SearchValues>(); |
| | | const [form] = Form.useForm<QualificationFormValues>(); |
| | | const [list, setList] = useState<QualificationItem[]>([]); |
| | | const [loading, setLoading] = useState(false); |
| | | const [submitting, setSubmitting] = useState(false); |
| | | const [modalOpen, setModalOpen] = useState(false); |
| | | const [modalLoading, setModalLoading] = useState(false); |
| | | const [modalMode, setModalMode] = useState<ModalMode>('create'); |
| | | const [editingId, setEditingId] = useState<number>(); |
| | | const [pagination, setPagination] = useState({ page: 1, pageSize: 10, total: 0 }); |
| | | |
| | | const canView = access['system:platform-qualification:view']; |
| | | const canAdd = access['system:platform-qualification:add']; |
| | | const canDetail = access['system:platform-qualification:detail']; |
| | | const canEdit = access['system:platform-qualification:edit']; |
| | | const canDelete = access['system:platform-qualification:delete']; |
| | | const canPublishStatus = access['system:platform-qualification:publish-status']; |
| | | |
| | | const qualificationType = Form.useWatch('qualificationType', form); |
| | | const isDetailMode = modalMode === 'detail'; |
| | | const showRichTextField = qualificationType !== TYPE_LINK; |
| | | const showLinkField = qualificationType === TYPE_LINK; |
| | | |
| | | const loadList = async (page = pagination.page, pageSize = pagination.pageSize) => { |
| | | setLoading(true); |
| | | try { |
| | | const res = await getPlatformQualificationPage({ |
| | | page, |
| | | pageSize, |
| | | ...trimSearchValues(searchForm.getFieldsValue()), |
| | | }); |
| | | setList(res.data?.list || []); |
| | | setPagination({ |
| | | page: Number(res.data?.page || page), |
| | | pageSize: Number(res.data?.pageSize || pageSize), |
| | | total: Number(res.data?.total || 0), |
| | | }); |
| | | } catch (error) { |
| | | message.error((error as Error)?.message || '平台资质列表加载失败'); |
| | | setList([]); |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | if (!canView) { |
| | | history.replace('/Welcome'); |
| | | return; |
| | | } |
| | | loadList(1, 10); |
| | | }, [canView]); |
| | | |
| | | const handleCloseModal = () => { |
| | | if (submitting) { |
| | | return; |
| | | } |
| | | setModalOpen(false); |
| | | setModalLoading(false); |
| | | setEditingId(undefined); |
| | | form.resetFields(); |
| | | }; |
| | | |
| | | const openCreateModal = () => { |
| | | setModalMode('create'); |
| | | setEditingId(undefined); |
| | | setModalLoading(false); |
| | | form.resetFields(); |
| | | form.setFieldsValue(defaultValues); |
| | | setModalOpen(true); |
| | | }; |
| | | |
| | | const openModal = async (record: QualificationItem, mode: ModalMode) => { |
| | | setModalMode(mode); |
| | | setEditingId(record.id); |
| | | setModalOpen(true); |
| | | setModalLoading(true); |
| | | form.resetFields(); |
| | | try { |
| | | const res = await getPlatformQualificationDetail(record.id); |
| | | const detail = (res.data || {}) as QualificationDetail; |
| | | form.setFieldsValue({ |
| | | qualificationName: detail.qualificationName || '', |
| | | qualificationType: detail.qualificationType || TYPE_RICH_TEXT, |
| | | recordNumber: detail.recordNumber || '', |
| | | contentHtml: detail.contentHtml || '', |
| | | linkUrl: detail.linkUrl || '', |
| | | sort: |
| | | detail.sort === undefined || detail.sort === null |
| | | ? defaultValues.sort |
| | | : String(detail.sort), |
| | | publishEnabled: detail.publishEnabled ?? true, |
| | | }); |
| | | } catch (error) { |
| | | message.error((error as Error)?.message || '平台资质详情加载失败'); |
| | | setModalOpen(false); |
| | | } finally { |
| | | setModalLoading(false); |
| | | } |
| | | }; |
| | | |
| | | const handleDelete = (record: QualificationItem) => { |
| | | Modal.confirm({ |
| | | title: '确定要删除该平台资质吗?', |
| | | okText: '删除', |
| | | cancelText: '取消', |
| | | onOk: async () => { |
| | | try { |
| | | await deletePlatformQualification(record.id); |
| | | message.success('删除成功'); |
| | | const nextPage = |
| | | list.length === 1 && pagination.page > 1 ? pagination.page - 1 : pagination.page; |
| | | await loadList(nextPage, pagination.pageSize); |
| | | } catch (error) { |
| | | message.error((error as Error)?.message || '删除失败,请稍后重试'); |
| | | throw error; |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | const handlePublishStatus = async (record: QualificationItem, publishEnabled: boolean) => { |
| | | try { |
| | | await updatePlatformQualificationPublishStatus(record.id, publishEnabled); |
| | | message.success(publishEnabled ? '上架成功' : '下架成功'); |
| | | await loadList(pagination.page, pagination.pageSize); |
| | | } catch (error) { |
| | | message.error((error as Error)?.message || '状态更新失败,请稍后重试'); |
| | | } |
| | | }; |
| | | |
| | | const handleSubmit = async () => { |
| | | if (isDetailMode) { |
| | | handleCloseModal(); |
| | | return; |
| | | } |
| | | const values = await form.validateFields(); |
| | | if (showRichTextField && isBlankRichText(values.contentHtml)) { |
| | | form.setFields([ |
| | | { |
| | | name: 'contentHtml', |
| | | errors: ['请输入资质内容'], |
| | | }, |
| | | ]); |
| | | return; |
| | | } |
| | | const payload = { |
| | | qualificationName: values.qualificationName?.trim(), |
| | | qualificationType: values.qualificationType, |
| | | recordNumber: values.recordNumber?.trim(), |
| | | contentHtml: showRichTextField ? values.contentHtml?.trim() : undefined, |
| | | linkUrl: showLinkField ? values.linkUrl?.trim() : undefined, |
| | | sort: Number(values.sort), |
| | | publishEnabled: !!values.publishEnabled, |
| | | }; |
| | | setSubmitting(true); |
| | | try { |
| | | if (modalMode === 'create') { |
| | | await createPlatformQualification(payload); |
| | | message.success('新增成功'); |
| | | } else if (editingId) { |
| | | await updatePlatformQualification(editingId, payload); |
| | | message.success('保存成功'); |
| | | } |
| | | setModalOpen(false); |
| | | form.resetFields(); |
| | | await loadList(modalMode === 'create' ? 1 : pagination.page, pagination.pageSize); |
| | | } catch (error) { |
| | | message.error((error as Error)?.message || '保存失败,请稍后重试'); |
| | | } finally { |
| | | setSubmitting(false); |
| | | } |
| | | }; |
| | | |
| | | const columns: ColumnsType<QualificationItem> = [ |
| | | { |
| | | title: '序号', |
| | | width: 80, |
| | | render: (_, __, index) => (pagination.page - 1) * pagination.pageSize + index + 1, |
| | | }, |
| | | { |
| | | title: '资质名称', |
| | | dataIndex: 'qualificationName', |
| | | width: 180, |
| | | ellipsis: true, |
| | | }, |
| | | { |
| | | title: '备案号', |
| | | dataIndex: 'recordNumber', |
| | | width: 180, |
| | | ellipsis: true, |
| | | }, |
| | | { |
| | | title: '类型', |
| | | dataIndex: 'qualificationType', |
| | | width: 120, |
| | | render: (value) => getTypeLabel(value), |
| | | }, |
| | | { |
| | | title: '添加人', |
| | | dataIndex: 'createdBy', |
| | | width: 120, |
| | | render: (value) => value || '-', |
| | | }, |
| | | { |
| | | title: '添加时间', |
| | | dataIndex: 'createdAt', |
| | | width: 180, |
| | | render: (value) => value || '-', |
| | | }, |
| | | { |
| | | title: '排序', |
| | | dataIndex: 'sort', |
| | | width: 100, |
| | | render: (value) => (value === undefined || value === null ? '-' : value), |
| | | }, |
| | | { |
| | | title: '状态', |
| | | dataIndex: 'publishEnabled', |
| | | width: 110, |
| | | render: (value) => |
| | | value ? <Tag color="blue">上架中</Tag> : <Tag color="default">已下架</Tag>, |
| | | }, |
| | | { |
| | | title: '操作', |
| | | width: 240, |
| | | render: (_, record) => ( |
| | | <Space size={0} wrap> |
| | | {canDetail ? ( |
| | | <Button type="link" size="small" onClick={() => openModal(record, 'detail')}> |
| | | 详情 |
| | | </Button> |
| | | ) : null} |
| | | {canEdit ? ( |
| | | <Button type="link" size="small" onClick={() => openModal(record, 'edit')}> |
| | | 编辑 |
| | | </Button> |
| | | ) : null} |
| | | {canDelete ? ( |
| | | <Button type="link" size="small" onClick={() => handleDelete(record)}> |
| | | 删除 |
| | | </Button> |
| | | ) : null} |
| | | {canPublishStatus ? ( |
| | | <Button |
| | | type="link" |
| | | size="small" |
| | | onClick={() => handlePublishStatus(record, !record.publishEnabled)} |
| | | > |
| | | {record.publishEnabled ? '下架' : '上架'} |
| | | </Button> |
| | | ) : null} |
| | | </Space> |
| | | ), |
| | | }, |
| | | ]; |
| | | |
| | | return ( |
| | | <div className="platform-qualification-page"> |
| | | <div className="platform-qualification-card"> |
| | | <div className="platform-qualification-card__title">平台资质</div> |
| | | <div className="platform-qualification-search-card"> |
| | | <Form |
| | | form={searchForm} |
| | | layout="horizontal" |
| | | className="platform-qualification-search-form" |
| | | > |
| | | <Form.Item label="资质名称" name="qualificationName"> |
| | | <Input placeholder="请输入" allowClear /> |
| | | </Form.Item> |
| | | <div className="platform-qualification-search-form__actions"> |
| | | <Button |
| | | onClick={async () => { |
| | | searchForm.resetFields(); |
| | | await loadList(1, pagination.pageSize); |
| | | }} |
| | | > |
| | | 重置 |
| | | </Button> |
| | | <Button type="primary" onClick={() => loadList(1, pagination.pageSize)}> |
| | | 搜索 |
| | | </Button> |
| | | </div> |
| | | </Form> |
| | | </div> |
| | | |
| | | <div className="platform-qualification-toolbar"> |
| | | {canAdd ? ( |
| | | <Button type="primary" icon={<PlusOutlined />} onClick={openCreateModal}> |
| | | 新建 |
| | | </Button> |
| | | ) : null} |
| | | </div> |
| | | |
| | | <div className="platform-qualification-table-card"> |
| | | <Table<QualificationItem> |
| | | rowKey="id" |
| | | columns={columns} |
| | | dataSource={list} |
| | | loading={loading} |
| | | pagination={false} |
| | | scroll={{ x: 1320 }} |
| | | /> |
| | | <div className="platform-qualification-pagination"> |
| | | <Pagination |
| | | current={pagination.page} |
| | | pageSize={pagination.pageSize} |
| | | total={pagination.total} |
| | | showSizeChanger |
| | | showQuickJumper |
| | | showTotal={(total) => `共 ${total} 条`} |
| | | onChange={(page, pageSize) => loadList(page, pageSize)} |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <Modal |
| | | open={modalOpen} |
| | | className="platform-qualification-modal" |
| | | destroyOnClose |
| | | centered |
| | | width={860} |
| | | title={modalMode === 'create' ? '添加资质' : modalMode === 'edit' ? '编辑资质' : '资质详情'} |
| | | onCancel={handleCloseModal} |
| | | footer={[ |
| | | <Button key="cancel" onClick={handleCloseModal} disabled={submitting}> |
| | | {isDetailMode ? '关闭' : '取消'} |
| | | </Button>, |
| | | isDetailMode ? null : ( |
| | | <Button key="submit" type="primary" loading={submitting} onClick={handleSubmit}> |
| | | 确定 |
| | | </Button> |
| | | ), |
| | | ]} |
| | | > |
| | | <Form |
| | | form={form} |
| | | layout="horizontal" |
| | | className="platform-qualification-form" |
| | | labelCol={{ span: 5 }} |
| | | disabled={isDetailMode || modalLoading} |
| | | > |
| | | <div className="platform-qualification-form__body"> |
| | | <Form.Item |
| | | label="资质名称" |
| | | name="qualificationName" |
| | | rules={[ |
| | | { required: true, message: '请输入资质名称' }, |
| | | { max: 64, message: '资质名称长度不能超过64个字符' }, |
| | | ]} |
| | | > |
| | | <Input placeholder="请输入" maxLength={64} /> |
| | | </Form.Item> |
| | | <Form.Item |
| | | label="类型" |
| | | name="qualificationType" |
| | | rules={[{ required: true, message: '请选择资质类型' }]} |
| | | > |
| | | <Radio.Group |
| | | options={typeOptions} |
| | | onChange={(event) => { |
| | | const nextType = event.target.value; |
| | | if (nextType === TYPE_RICH_TEXT) { |
| | | form.setFieldsValue({ linkUrl: '' }); |
| | | } else { |
| | | form.setFieldsValue({ contentHtml: '' }); |
| | | } |
| | | }} |
| | | /> |
| | | </Form.Item> |
| | | <Form.Item |
| | | label="备案号" |
| | | name="recordNumber" |
| | | rules={[ |
| | | { required: true, message: '请输入备案号' }, |
| | | { max: 64, message: '备案号长度不能超过64个字符' }, |
| | | ]} |
| | | > |
| | | <Input placeholder="请输入" maxLength={64} /> |
| | | </Form.Item> |
| | | {showRichTextField ? ( |
| | | <Form.Item |
| | | label="资质内容" |
| | | name="contentHtml" |
| | | rules={[ |
| | | { required: true, message: '请输入资质内容' }, |
| | | { |
| | | validator: async (_, value) => { |
| | | if (isBlankRichText(value)) { |
| | | throw new Error('请输入资质内容'); |
| | | } |
| | | }, |
| | | }, |
| | | ]} |
| | | > |
| | | <div className="platform-qualification-form__editor"> |
| | | <BraftEditorCom |
| | | data={form.getFieldValue('contentHtml') || ''} |
| | | readOnly={isDetailMode} |
| | | resultCallback={(html: string) => form.setFieldsValue({ contentHtml: html })} |
| | | /> |
| | | </div> |
| | | </Form.Item> |
| | | ) : null} |
| | | {showLinkField ? ( |
| | | <Form.Item |
| | | label="跳转链接" |
| | | name="linkUrl" |
| | | rules={[ |
| | | { required: true, message: '请输入跳转链接' }, |
| | | { max: 512, message: '跳转链接长度不能超过512个字符' }, |
| | | ]} |
| | | > |
| | | <Input placeholder="请输入" maxLength={512} /> |
| | | </Form.Item> |
| | | ) : null} |
| | | <Form.Item |
| | | label="排序" |
| | | name="sort" |
| | | rules={[{ required: true, message: '请输入排序' }, { validator: validateSort }]} |
| | | > |
| | | <Input placeholder="请输入" maxLength={9} /> |
| | | </Form.Item> |
| | | <Form.Item |
| | | label="上架状态" |
| | | name="publishEnabled" |
| | | rules={[{ required: true, message: '请选择上架状态' }]} |
| | | > |
| | | <Radio.Group |
| | | options={[ |
| | | { label: '上架', value: true }, |
| | | { label: '下架', value: false }, |
| | | ]} |
| | | /> |
| | | </Form.Item> |
| | | </div> |
| | | </Form> |
| | | {modalLoading ? ( |
| | | <div className="platform-qualification-modal__loading">加载中...</div> |
| | | ) : null} |
| | | </Modal> |
| | | </div> |
| | | ); |
| | | }; |
| | | |
| | | export default PlatformQualificationPage; |
| New file |
| | |
| | | import { request } from '@umijs/max'; |
| | | |
| | | export async function getPlatformQualificationPage(params?: Record<string, any>) { |
| | | return request('/platform-qualifications', { |
| | | method: 'GET', |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | export async function getPlatformQualificationDetail(id: number | string) { |
| | | return request(`/platform-qualifications/${id}`, { |
| | | method: 'GET', |
| | | }); |
| | | } |
| | | |
| | | export async function createPlatformQualification(data: Record<string, any>) { |
| | | return request('/platform-qualifications', { |
| | | method: 'POST', |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export async function updatePlatformQualification(id: number | string, data: Record<string, any>) { |
| | | return request(`/platform-qualifications/${id}`, { |
| | | method: 'PUT', |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export async function deletePlatformQualification(id: number | string) { |
| | | return request(`/platform-qualifications/${id}`, { |
| | | method: 'DELETE', |
| | | }); |
| | | } |
| | | |
| | | export async function updatePlatformQualificationPublishStatus( |
| | | id: number | string, |
| | | publishEnabled: boolean, |
| | | ) { |
| | | return request(`/platform-qualifications/${id}/publish-status`, { |
| | | method: 'PATCH', |
| | | data: { publishEnabled }, |
| | | }); |
| | | } |
| New file |
| | |
| | | .platform-qualification-page { |
| | | min-height: calc(100vh - 48px); |
| | | padding: 18px 20px 24px; |
| | | background: #f2f5f9; |
| | | } |
| | | |
| | | .platform-qualification-card { |
| | | min-height: 700px; |
| | | padding: 8px 14px 18px; |
| | | background: #fff; |
| | | border-radius: 10px; |
| | | box-shadow: 0 2px 10px rgba(15, 23, 42, 0.04); |
| | | } |
| | | |
| | | .platform-qualification-card__title { |
| | | margin: 0 0 22px; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .platform-qualification-search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .platform-qualification-search-form { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 0 18px; |
| | | align-items: center; |
| | | } |
| | | |
| | | .platform-qualification-search-form .ant-form-item { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .platform-qualification-search-form .ant-input { |
| | | width: 170px; |
| | | } |
| | | |
| | | .platform-qualification-search-form__actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | align-items: center; |
| | | } |
| | | |
| | | .platform-qualification-toolbar { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-bottom: 14px; |
| | | } |
| | | |
| | | .platform-qualification-table-card { |
| | | overflow: hidden; |
| | | border: 1px solid #e6ebf2; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .platform-qualification-table-card .ant-table-thead > tr > th { |
| | | color: #303133; |
| | | font-weight: 500; |
| | | background: #f5f7fa; |
| | | } |
| | | |
| | | .platform-qualification-table-card .ant-table-tbody > tr > td { |
| | | color: #606266; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | .platform-qualification-pagination { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | padding: 16px 18px; |
| | | background: #fff; |
| | | } |
| | | |
| | | .platform-qualification-modal .ant-modal-content { |
| | | padding: 18px 24px 20px; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .platform-qualification-modal .ant-modal-title { |
| | | color: #303133; |
| | | font-weight: 500; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .platform-qualification-modal .ant-modal-body { |
| | | padding-top: 12px; |
| | | } |
| | | |
| | | .platform-qualification-form { |
| | | position: relative; |
| | | } |
| | | |
| | | .platform-qualification-form__body { |
| | | padding-right: 6px; |
| | | } |
| | | |
| | | .platform-qualification-form .ant-form-item { |
| | | margin-bottom: 18px; |
| | | } |
| | | |
| | | .platform-qualification-form .ant-form-item-label > label { |
| | | color: #303133; |
| | | } |
| | | |
| | | .platform-qualification-form__editor { |
| | | border: 1px solid #dcdfe6; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .platform-qualification-modal__loading { |
| | | position: absolute; |
| | | top: 66px; |
| | | right: 32px; |
| | | color: #909399; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | @media (max-width: 900px) { |
| | | .platform-qualification-page { |
| | | padding: 12px; |
| | | } |
| | | |
| | | .platform-qualification-card { |
| | | min-height: auto; |
| | | padding: 16px; |
| | | } |
| | | |
| | | .platform-qualification-search-form { |
| | | flex-direction: column; |
| | | align-items: stretch; |
| | | } |
| | | |
| | | .platform-qualification-search-form .ant-input { |
| | | width: 100%; |
| | | } |
| | | |
| | | .platform-qualification-search-form__actions { |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .platform-qualification-modal { |
| | | width: calc(100vw - 24px) !important; |
| | | max-width: calc(100vw - 24px); |
| | | } |
| | | |
| | | .platform-qualification-modal .ant-modal-content { |
| | | padding: 16px; |
| | | } |
| | | } |
| | |
| | | history.replace('/system/settings/banner'); |
| | | return; |
| | | } |
| | | if (access['system:activity-notice:view']) { |
| | | history.replace('/system/settings/activity-notice'); |
| | | return; |
| | | } |
| | | if (access['system:platform-qualification:view']) { |
| | | history.replace('/system/settings/platform-qualification'); |
| | | return; |
| | | } |
| | | if (access['system:message-push:view']) { |
| | | history.replace('/system/settings/message-push'); |
| | | return; |