From fa1f70a074ab87d1dc9876bb77ad841e391564c9 Mon Sep 17 00:00:00 2001
From: shenrongliang <1328040932@qq.com>
Date: 星期四, 26 六月 2025 10:11:43 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/main'

---
 ruoyi-modules/sc-page-designer/pom.xml                                                                                |   28 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerVo.java                        |   23 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerTemplateServiceImpl.java |   89 +
 ruoyi-ui/apps/web-antd/src/views/system/process/index.vue                                                             |  472 +++++++++
 ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/index.vue                                                   |   95 +
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerTemplateMapper.java            |   20 
 ruoyi-ui/apps/web-antd/src/views/tool/template/template-drawer.vue                                                    |  224 ++++
 ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue                                                                |   13 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerTemplateController.java    |   70 +
 ruoyi-modules/sc-page-designer/src/main/resources/mapper/PageDesignerMapper.xml                                       |   33 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerService.java                  |   29 
 ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue                                                                |   13 
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java                                      |   13 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplateVo.java                |   19 
 pom.xml                                                                                                               |   11 
 ruoyi-modules/pom.xml                                                                                                 |    2 
 ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/serivceDialog.vue                                           |   64 +
 ruoyi-ui/apps/web-antd/src/views/work/statistics/index.vue                                                            |   13 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplate.java                  |   28 
 ruoyi-ui/apps/web-antd/src/api/tool/template/model.d.ts                                                               |   14 
 ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/controller/system/SysUserController.java                    |   11 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerController.java            |   72 +
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java                 |    3 
 ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/data.ts                                                     |   44 
 ruoyi-ui/apps/web-antd/src/api/tool/page-designer/model.d.ts                                                          |   21 
 ruoyi-ui/apps/web-antd/src/services/flowableService.js                                                                |    0 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplateDTO.java               |   22 
 ruoyi-ui/apps/web-antd/src/views/tool/dynamicForm/index.vue                                                           |   10 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerTemplateService.java          |   27 
 ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue                                                         |  217 ++++
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerDTO.java                       |   27 
 ruoyi-ui/apps/web-antd/src/views/tool/page-designer/page-drawer.vue                                                   |  337 ++++++
 ruoyi-ui/apps/web-antd/src/views/tool/page-designer/data.tsx                                                          |  109 ++
 ruoyi-admin/pom.xml                                                                                                   |   10 
 ruoyi-modules/sc-page-designer/src/main/resources/mapper/PageDesignerTemplateMapper.xml                               |   27 
 ruoyi-ui/apps/web-antd/src/api/tool/page-designer/index.ts                                                            |   52 +
 ruoyi-ui/apps/web-antd/package.json                                                                                   |    4 
 ruoyi-modules/sc-services/pom.xml                                                                                     |   24 
 ruoyi-ui/apps/web-antd/src/api/tool/template/index.ts                                                                 |   45 
 ruoyi-ui/apps/web-antd/src/bootstrap.ts                                                                               |    4 
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerServiceImpl.java         |  133 ++
 ruoyi-ui/apps/web-antd/src/views/tool/template/data.tsx                                                               |   86 +
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesigner.java                          |   40 
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java                               |    3 
 ruoyi-ui/apps/web-antd/src/views/tool/template/index.vue                                                              |  397 +++++++
 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerMapper.java                    |   20 
 46 files changed, 3,009 insertions(+), 9 deletions(-)

diff --git a/pom.xml b/pom.xml
index 848c226..bcb9603 100644
--- a/pom.xml
+++ b/pom.xml
@@ -308,7 +308,16 @@
                 <artifactId>ruoyi-chat</artifactId>
                 <version>${revision}</version>
             </dependency>
-
+            <dependency>
+                <groupId>org.ruoyi</groupId>
+                <artifactId>sc-services</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.ruoyi</groupId>
+                <artifactId>sc-page-designer</artifactId>
+                <version>${revision}</version>
+            </dependency>
             <dependency>
                 <groupId>org.ruoyi</groupId>
                 <artifactId>ruoyi-knowledge-api</artifactId>
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 5e3267d..3b8d957 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -61,6 +61,16 @@
 
         <dependency>
             <groupId>org.ruoyi</groupId>
+            <artifactId>sc-services</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ruoyi</groupId>
+            <artifactId>sc-page-designer</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ruoyi</groupId>
             <artifactId>ruoyi-generator</artifactId>
         </dependency>
 
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java
index aa8967a..cffdc53 100644
--- a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java
@@ -1,5 +1,6 @@
 package org.ruoyi.domain;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
@@ -16,7 +17,7 @@
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
-@TableName("chat_session")
+@TableName("CHAT_SESSION")
 public class ChatSession extends BaseEntity {
 
     @Serial
@@ -25,28 +26,30 @@
     /**
      * 涓婚敭
      */
-    @TableId(value = "id")
+    @TableId(value = "ID")
     private Long id;
 
     /**
-     * 鐢ㄦ埛id
+     * 鐢ㄦ埛ID
      */
+    @TableField("USER_ID")
     private Long userId;
 
     /**
      * 浼氳瘽鏍囬
      */
+    @TableField("SESSION_TITLE")
     private String sessionTitle;
 
     /**
      * 浼氳瘽鍐呭
      */
+    @TableField("SESSION_CONTENT")
     private String sessionContent;
 
     /**
      * 澶囨敞
      */
+    @TableField("REMARK")
     private String remark;
-
-
 }
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java
index a924688..5cfb993 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java
@@ -17,7 +17,7 @@
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
-@TableName("knowledge_info")
+@TableName("KNOWLEDGE_INFO")
 public class KnowledgeInfo extends BaseEntity {
 
     @Serial
@@ -27,6 +27,7 @@
      * 涓婚敭ID
      */
     @TableId(value = "ID")
+    @TableField("ID")
     private Long id;
 
     /**
diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml
index 95230d7..cbd569e 100644
--- a/ruoyi-modules/pom.xml
+++ b/ruoyi-modules/pom.xml
@@ -22,6 +22,8 @@
         <module>ruoyi-system</module>
         <module>ruoyi-generator</module>
         <module>ruoyi-flowable</module>
+        <module>sc-services</module>
+        <module>sc-page-designer</module>
     </modules>
 
     <properties>
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java
index 4a04827..efb587d 100644
--- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java
@@ -177,7 +177,8 @@
   @Transactional(rollbackFor = Exception.class)
   public void removeKnowledge(String id) {
     Map<String,Object> map = new HashMap<>();
-    KnowledgeInfo knowledgeInfo = baseMapper.selectById(id);
+    KnowledgeInfo knowledgeInfo = baseMapper.selectOne(new LambdaQueryWrapper<KnowledgeInfo>()
+            .eq(KnowledgeInfo::getKid, id));
     check(knowledgeInfo);
     map.put("kid",knowledgeInfo.getKid());
     // 鍒犻櫎鍚戦噺鏁版嵁
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/controller/system/SysUserController.java
index 04e5c0d..30babe9 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/controller/system/SysUserController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/controller/system/SysUserController.java
@@ -322,4 +322,15 @@
         return R.ok(trees);
     }
 
+    /**
+     * 鑾峰彇閮ㄩ棬涓嬬殑鎵�鏈夌敤鎴蜂俊鎭�
+     */
+    @SaCheckPermission("system:user:list")
+    @GetMapping("/list/dept/{deptId}")
+    public R<List<SysUserVo>> listByDeptId(@PathVariable Long deptId) {
+        List<SysUserVo> sysUserVos = userService.selectUserList(new SysUserBo());
+        return R.ok(sysUserVos.stream()
+                .filter(user -> deptId.equals(user.getDeptId()))
+                .collect(Collectors.toList()));
+    }
 }
diff --git a/ruoyi-modules/sc-page-designer/pom.xml b/ruoyi-modules/sc-page-designer/pom.xml
new file mode 100644
index 0000000..400f53d
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/pom.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.ruoyi</groupId>
+        <artifactId>ruoyi-modules</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>sc-page-designer</artifactId>
+    <description>
+        school-ai椤甸潰璁捐鍣ㄦā鍧�
+    </description>
+
+    <dependencies>
+        <!-- 閫氱敤宸ュ叿-->
+        <dependency>
+            <groupId>org.ruoyi</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.ruoyi</groupId>
+            <artifactId>ruoyi-system-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerController.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerController.java
new file mode 100644
index 0000000..16fad07
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerController.java
@@ -0,0 +1,72 @@
+package org.ruoyi.pageDesigner.controller;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.ruoyi.common.core.domain.R;
+import org.ruoyi.common.web.core.BaseController;
+import org.ruoyi.core.page.PageQuery;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.pageDesigner.domain.PageDesigner;
+import org.ruoyi.pageDesigner.domain.PageDesignerDTO;
+import org.ruoyi.pageDesigner.domain.PageDesignerVo;
+import org.ruoyi.pageDesigner.service.PageDesignerService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 14:41:24
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/page-designer")
+public class PageDesignerController extends BaseController {
+
+    private final PageDesignerService pageDesignerService;
+
+    /**
+     * 鑾峰彇椤甸潰璁捐鍒楄〃
+     */
+    @GetMapping("/list")
+    public TableDataInfo<PageDesignerVo> list(PageDesignerDTO pageDesignerDTO, PageQuery pageQuery) {
+        return pageDesignerService.selectPagelistAll(pageDesignerDTO, pageQuery);
+    }
+
+    /**
+     * 鑾峰彇椤甸潰璁捐璇︽儏
+     */
+    @GetMapping("/{id}")
+    public R<PageDesigner> getInfo(@PathVariable Long id) {
+        return R.ok(pageDesignerService.getDetail(id));
+    }
+
+    /**
+     * 鏂板椤甸潰璁捐
+     */
+    @PostMapping
+    public R<Void> add(@Valid @RequestBody PageDesignerDTO dto) {
+        pageDesignerService.add(dto);
+        return R.ok("鏂板鎴愬姛");
+    }
+
+    /**
+     * 淇敼椤甸潰璁捐
+     */
+    @PutMapping
+    public R<Void> update(@Valid @RequestBody PageDesignerDTO dto) {
+        pageDesignerService.updatePage(dto);
+        return R.ok("淇敼鎴愬姛");
+    }
+
+    /**
+     * 鍒犻櫎椤甸潰璁捐
+     */
+    @DeleteMapping
+    public R<Void> remove(@RequestBody List<Long> ids) {
+        pageDesignerService.deleteByIds(ids);
+        return R.ok("鍒犻櫎鎴愬姛");
+    }
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerTemplateController.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerTemplateController.java
new file mode 100644
index 0000000..5af3c15
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerTemplateController.java
@@ -0,0 +1,70 @@
+package org.ruoyi.pageDesigner.controller;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.ruoyi.common.core.domain.R;
+import org.ruoyi.common.web.core.BaseController;
+import org.ruoyi.core.page.PageQuery;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.pageDesigner.domain.*;
+import org.ruoyi.pageDesigner.service.PageDesignerTemplateService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 14:41:24
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/page-designer-template")
+public class PageDesignerTemplateController extends BaseController {
+
+    private final PageDesignerTemplateService service;
+
+    /**
+     * 鑾峰彇椤甸潰璁捐鍒楄〃
+     */
+    @GetMapping("/list")
+    public TableDataInfo<PageDesignerTemplateVo> list(PageDesignerTemplateDTO dto, PageQuery pageQuery) {
+        return service.selectPagelistAll(dto, pageQuery);
+    }
+
+    /**
+     * 鑾峰彇椤甸潰璁捐璇︽儏
+     */
+    @GetMapping("/{id}")
+    public R<PageDesignerTemplate> getInfo(@PathVariable Long id) {
+        return R.ok(service.getDetail(id));
+    }
+
+    /**
+     * 鏂板椤甸潰璁捐
+     */
+    @PostMapping
+    public R<Void> add(@Valid @RequestBody PageDesignerTemplateDTO dto) {
+        service.add(dto);
+        return R.ok("鏂板鎴愬姛");
+    }
+
+    /**
+     * 淇敼椤甸潰璁捐
+     */
+    @PutMapping
+    public R<Void> update(@Valid @RequestBody PageDesignerTemplateDTO dto) {
+        service.updatePage(dto);
+        return R.ok("淇敼鎴愬姛");
+    }
+
+    /**
+     * 鍒犻櫎椤甸潰璁捐
+     */
+    @DeleteMapping
+    public R<Void> remove(@RequestBody List<Long> ids) {
+        service.deleteByIds(ids);
+        return R.ok("鍒犻櫎鎴愬姛");
+    }
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesigner.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesigner.java
new file mode 100644
index 0000000..19603e1
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesigner.java
@@ -0,0 +1,40 @@
+package org.ruoyi.pageDesigner.domain;
+
+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.Data;
+import lombok.EqualsAndHashCode;
+import org.ruoyi.core.domain.BaseEntity;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:24:05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("PAGE_DESIGNER")
+public class PageDesigner extends BaseEntity {
+    @TableId(value = "ID")
+    private Long id;
+    @TableField("NAME")
+    private String name;
+    @TableField("MENU_ID")
+    private String menuId; // 鍏宠仈鐨勮彍鍗旾D
+    @TableField("MENU_PARENT_ID")
+    private String menuParentId; // 鍏宠仈鐨勮彍鍗曠埗ID
+    @TableField("STATUS")
+    private String status;
+    @TableField(value = "DEL_FLAG")
+    @TableLogic
+    private String delFlag;
+    @TableField("REMARK")
+    private String remark;
+    @TableField("FORM_JSON")
+    private String formJson;
+    @TableField("SHOW_COLUMN")
+    private String showColumn; // 鍙互灏� selectedFields 瀛樺偍涓� JSON 瀛楃涓�
+    @TableField("ACTIONS_FUNC")
+    private String actionsFunc;  // 鍙互灏� enableActions 瀛樺偍涓� JSON 瀛楃涓�
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerDTO.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerDTO.java
new file mode 100644
index 0000000..bf81590
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerDTO.java
@@ -0,0 +1,27 @@
+package org.ruoyi.pageDesigner.domain;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.ruoyi.core.domain.BaseEntity;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:23:35
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = PageDesigner.class, reverseConvertGenerate = false)
+public class PageDesignerDTO extends BaseEntity {
+    private Long id; // 淇敼鏃朵紶鍏�
+    private String name;       // 椤甸潰鍚嶇О
+    private String menuId;     // 鍏宠仈鑿滃崟 ID
+    private String status;     // 鍚敤鐘舵��
+    private String remark;     // 澶囨敞
+    private String formJson;   // 琛ㄥ崟璁捐 JSON 瀛楃涓�
+    private String showColumn;   // 瀛楁鏄剧ず閰嶇疆
+    private String actionsFunc;  // 鍚敤鐨勬搷浣滈」锛堝鍒犳敼鏌ワ級
+    private String menuParentId;
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplate.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplate.java
new file mode 100644
index 0000000..338c97a
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplate.java
@@ -0,0 +1,28 @@
+package org.ruoyi.pageDesigner.domain;
+
+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.Data;
+import lombok.EqualsAndHashCode;
+import org.ruoyi.core.domain.BaseEntity;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:24:05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("PAGE_DESIGNER_TEMPLATE")
+public class PageDesignerTemplate extends BaseEntity {
+    @TableId(value = "ID")
+    private Long id;
+    @TableField(value = "PAGE_ID")
+    private Long pageId;
+    @TableField("FORM_DATA")
+    private String formData;
+    @TableField(value = "DEL_FLAG")
+    @TableLogic
+    private String delFlag;
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplateDTO.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplateDTO.java
new file mode 100644
index 0000000..f2be0b8
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplateDTO.java
@@ -0,0 +1,22 @@
+package org.ruoyi.pageDesigner.domain;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.ruoyi.core.domain.BaseEntity;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:23:35
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = PageDesignerTemplate.class, reverseConvertGenerate = false)
+public class PageDesignerTemplateDTO extends BaseEntity {
+    private Long id;
+    private Long pageId;
+    private String formData;
+    private String delFlag;
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplateVo.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplateVo.java
new file mode 100644
index 0000000..9974206
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerTemplateVo.java
@@ -0,0 +1,19 @@
+package org.ruoyi.pageDesigner.domain;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:23:35
+ */
+@Data
+@AutoMapper(target = PageDesignerTemplate.class)
+public class PageDesignerTemplateVo implements Serializable {
+    private Long id;
+    private Long pageId;
+    private String formData;
+    private String delFlag;
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerVo.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerVo.java
new file mode 100644
index 0000000..6d079fe
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerVo.java
@@ -0,0 +1,23 @@
+package org.ruoyi.pageDesigner.domain;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.ruoyi.system.domain.vo.SysMenuVo;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:23:35
+ */
+@Data
+@AutoMapper(target = PageDesigner.class)
+public class PageDesignerVo implements Serializable {
+    private Long id; // 淇敼鏃朵紶鍏�
+    private String name;       // 椤甸潰鍚嶇О
+    private String status;     // 鍚敤鐘舵��
+    private String remark;     // 澶囨敞
+    private String menuParentId; // 鑿滃崟鐖禝D
+    private Date createTime; // 鍒涘缓鏃堕棿
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerMapper.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerMapper.java
new file mode 100644
index 0000000..f3757c7
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerMapper.java
@@ -0,0 +1,20 @@
+package org.ruoyi.pageDesigner.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.ruoyi.pageDesigner.domain.PageDesigner;
+import org.ruoyi.pageDesigner.domain.PageDesignerVo;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:34:48
+ */
+@Mapper
+public interface PageDesignerMapper extends BaseMapper<PageDesigner> {
+    Page<PageDesignerVo> selectPagelistAll(@Param("page") Page<PageDesigner> page, @Param(Constants.WRAPPER) Wrapper<PageDesigner> queryWrapper);
+
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerTemplateMapper.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerTemplateMapper.java
new file mode 100644
index 0000000..c0a54d1
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerTemplateMapper.java
@@ -0,0 +1,20 @@
+package org.ruoyi.pageDesigner.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.ruoyi.pageDesigner.domain.PageDesignerTemplate;
+import org.ruoyi.pageDesigner.domain.PageDesignerTemplateVo;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:34:48
+ */
+@Mapper
+public interface PageDesignerTemplateMapper extends BaseMapper<PageDesignerTemplate> {
+    Page<PageDesignerTemplateVo> selectPagelistAll(@Param("page") Page<PageDesignerTemplate> page, @Param(Constants.WRAPPER) Wrapper<PageDesignerTemplate> queryWrapper);
+
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerService.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerService.java
new file mode 100644
index 0000000..2289ae9
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerService.java
@@ -0,0 +1,29 @@
+package org.ruoyi.pageDesigner.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.ruoyi.core.page.PageQuery;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.pageDesigner.domain.PageDesigner;
+import org.ruoyi.pageDesigner.domain.PageDesignerDTO;
+import org.ruoyi.pageDesigner.domain.PageDesignerVo;
+
+import java.util.List;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:35:56
+ */
+public interface PageDesignerService extends IService<PageDesigner> {
+
+    void add(PageDesignerDTO dto);
+
+    void updatePage(PageDesignerDTO dto);
+
+    List<PageDesigner> listAll(String keyword);
+
+    PageDesigner getDetail(Long id);
+
+    void deleteByIds(List<Long> ids);
+
+    TableDataInfo<PageDesignerVo> selectPagelistAll(PageDesignerDTO pageDesignerDTO, PageQuery pageQuery);
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerTemplateService.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerTemplateService.java
new file mode 100644
index 0000000..a0e2bdb
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerTemplateService.java
@@ -0,0 +1,27 @@
+package org.ruoyi.pageDesigner.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.ruoyi.core.page.PageQuery;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.pageDesigner.domain.*;
+
+import java.util.List;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:35:56
+ */
+public interface PageDesignerTemplateService extends IService<PageDesignerTemplate> {
+
+    void add(PageDesignerTemplateDTO dto);
+
+    void updatePage(PageDesignerTemplateDTO dto);
+
+    List<PageDesignerTemplate> listAll(String keyword);
+
+    PageDesignerTemplate getDetail(Long id);
+
+    void deleteByIds(List<Long> ids);
+
+    TableDataInfo<PageDesignerTemplateVo> selectPagelistAll(PageDesignerTemplateDTO dto, PageQuery pageQuery);
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerServiceImpl.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerServiceImpl.java
new file mode 100644
index 0000000..ee61662
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerServiceImpl.java
@@ -0,0 +1,133 @@
+package org.ruoyi.pageDesigner.service.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.amazonaws.util.json.Jackson;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import org.ruoyi.common.core.constant.UserConstants;
+import org.ruoyi.common.core.utils.StreamUtils;
+import org.ruoyi.common.core.utils.StringUtils;
+import org.ruoyi.core.page.PageQuery;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.helper.DataBaseHelper;
+import org.ruoyi.pageDesigner.domain.PageDesigner;
+import org.ruoyi.pageDesigner.domain.PageDesignerDTO;
+import org.ruoyi.pageDesigner.domain.PageDesignerVo;
+import org.ruoyi.pageDesigner.mapper.PageDesignerMapper;
+import org.ruoyi.pageDesigner.service.PageDesignerService;
+import org.ruoyi.system.domain.SysMenu;
+import org.ruoyi.system.mapper.SysMenuMapper;
+import org.ruoyi.system.mapper.SysUserMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:36:29
+ */
+@Service
+@RequiredArgsConstructor
+public class PageDesignerServiceImpl extends ServiceImpl<PageDesignerMapper, PageDesigner>
+        implements PageDesignerService {
+
+    private final SysMenuMapper menuMapper;
+
+    @Override
+    public void add(PageDesignerDTO dto) {
+        SysMenu menu = new SysMenu();
+        menu.setMenuName(dto.getName());
+        menu.setParentId(Long.valueOf(dto.getMenuParentId()));
+        menu.setOrderNum(999);
+        menu.setComponent("tool/template/index");
+        menu.setIsFrame("1");
+        menu.setIsCache("0");
+        menu.setMenuType("C"); // C:鐩綍, M:鑿滃崟, F:鎸夐挳
+        menu.setVisible("0");
+        menu.setStatus(dto.getStatus());
+        menuMapper.insert(menu);
+        PageDesigner entity = convertToEntity(dto);
+        entity.setMenuId(menu.getMenuId().toString());
+        this.save(entity);
+        menu.setPath(String.valueOf(entity.getId()));
+        menuMapper.updateById(menu);
+    }
+
+    @Override
+    public void updatePage(PageDesignerDTO dto) {
+        PageDesigner entity = convertToEntity(dto);
+        this.updateById(entity);
+        PageDesigner byId = this.getById(entity.getId());
+        SysMenu menu = new SysMenu();
+        menu.setMenuId(Long.valueOf(byId.getMenuId()));
+        menu.setStatus(entity.getStatus());
+        menu.setParentId(Long.valueOf(entity.getMenuParentId()));
+        menuMapper.updateById(menu);
+    }
+
+    @Override
+    public TableDataInfo<PageDesignerVo> selectPagelistAll(PageDesignerDTO pageDesignerDTO, PageQuery pageQuery) {
+        Page<PageDesignerVo> page = baseMapper.selectPagelistAll(pageQuery.build(), this.buildQueryWrapper(pageDesignerDTO));
+        return TableDataInfo.build(page);
+    }
+    private Wrapper<PageDesigner> buildQueryWrapper(PageDesignerDTO pageDesignerDTO) {
+        Map<String, Object> params = pageDesignerDTO.getParams();
+        QueryWrapper<PageDesigner> wrapper = Wrappers.query();
+        wrapper.eq("del_flag", UserConstants.USER_NORMAL)
+                .eq(ObjectUtil.isNotNull(pageDesignerDTO.getId()), "id", pageDesignerDTO.getId())
+                .like(StringUtils.isNotBlank(pageDesignerDTO.getName()), "name", pageDesignerDTO.getName())
+                .eq(StringUtils.isNotBlank(pageDesignerDTO.getStatus()), "status", pageDesignerDTO.getStatus())
+                .between(params.get("beginTime") != null && params.get("endTime") != null,
+                        "create_time", params.get("beginTime"), params.get("endTime"))
+        ;
+        return wrapper;
+    }
+    @Override
+    public List<PageDesigner> listAll(String keyword) {
+        LambdaQueryWrapper<PageDesigner> wrapper = new LambdaQueryWrapper<>();
+        wrapper.like(keyword != null, PageDesigner::getName, keyword);
+        return this.list(wrapper);
+    }
+
+    @Override
+    public PageDesigner getDetail(Long id) {
+        return this.getById(id);
+    }
+
+    @Override
+    public void deleteByIds(List<Long> ids) {
+        List<PageDesigner> designers = this.listByIds(ids);
+        for (PageDesigner designer : designers) {
+            Long id = designer.getId();
+            this.removeById(id);
+
+            Long menuId = Long.valueOf(designer.getMenuId());
+            if (menuId != null) {
+                menuMapper.deleteById(menuId);
+            }
+        }
+
+    }
+
+    private PageDesigner convertToEntity(PageDesignerDTO dto) {
+        PageDesigner entity = new PageDesigner();
+        entity.setId(dto.getId());
+        entity.setName(dto.getName());
+        entity.setMenuId(dto.getMenuId());
+        entity.setStatus(dto.getStatus());
+        entity.setRemark(dto.getRemark());
+        entity.setFormJson(dto.getFormJson());
+        entity.setShowColumn(dto.getShowColumn());
+        entity.setActionsFunc(dto.getActionsFunc());
+        entity.setMenuParentId(dto.getMenuParentId());
+        return entity;
+    }
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerTemplateServiceImpl.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerTemplateServiceImpl.java
new file mode 100644
index 0000000..8ed291e
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerTemplateServiceImpl.java
@@ -0,0 +1,89 @@
+package org.ruoyi.pageDesigner.service.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import org.ruoyi.common.core.constant.UserConstants;
+import org.ruoyi.core.page.PageQuery;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.pageDesigner.domain.*;
+import org.ruoyi.pageDesigner.mapper.PageDesignerMapper;
+import org.ruoyi.pageDesigner.mapper.PageDesignerTemplateMapper;
+import org.ruoyi.pageDesigner.service.PageDesignerService;
+import org.ruoyi.pageDesigner.service.PageDesignerTemplateService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author kanglujie
+ * @date 2025-06-23 17:36:29
+ */
+@Service
+@RequiredArgsConstructor
+public class PageDesignerTemplateServiceImpl extends ServiceImpl<PageDesignerTemplateMapper, PageDesignerTemplate>
+        implements PageDesignerTemplateService {
+
+    private final ObjectMapper objectMapper;
+
+    @Override
+    public void add(PageDesignerTemplateDTO dto) {
+        PageDesignerTemplate entity = convertToEntity(dto);
+        this.save(entity);
+    }
+
+    @Override
+    public void updatePage(PageDesignerTemplateDTO dto) {
+        PageDesignerTemplate entity = convertToEntity(dto);
+        this.updateById(entity);
+    }
+
+    @Override
+    public TableDataInfo<PageDesignerTemplateVo> selectPagelistAll(PageDesignerTemplateDTO dto, PageQuery pageQuery) {
+        Page<PageDesignerTemplateVo> page = baseMapper.selectPagelistAll(pageQuery.build(), this.buildQueryWrapper(dto));
+        return TableDataInfo.build(page);
+    }
+    private Wrapper<PageDesignerTemplate> buildQueryWrapper(PageDesignerTemplateDTO dto) {
+        //Map<String, Object> params = pageDesignerDTO.getParams();
+        QueryWrapper<PageDesignerTemplate> wrapper = Wrappers.query();
+        wrapper.eq("del_flag", UserConstants.USER_NORMAL)
+                .eq(ObjectUtil.isNotNull(dto.getPageId()), "page_id", dto.getPageId())
+        //        .like(StringUtils.isNotBlank(pageDesignerDTO.getName()), "name", pageDesignerDTO.getName())
+        //        .eq(StringUtils.isNotBlank(pageDesignerDTO.getStatus()), "status", pageDesignerDTO.getStatus())
+        //        .between(params.get("beginTime") != null && params.get("endTime") != null,
+        //                "create_time", params.get("beginTime"), params.get("endTime"))
+        ;
+        return wrapper;
+    }
+    @Override
+    public List<PageDesignerTemplate> listAll(String keyword) {
+        LambdaQueryWrapper<PageDesignerTemplate> wrapper = new LambdaQueryWrapper<>();
+        wrapper.like(keyword != null, PageDesignerTemplate::getPageId, keyword);
+        return this.list(wrapper);
+    }
+
+    @Override
+    public PageDesignerTemplate getDetail(Long id) {
+        return this.getById(id);
+    }
+
+    @Override
+    public void deleteByIds(List<Long> ids) {
+        this.removeByIds(ids);
+    }
+
+    private PageDesignerTemplate convertToEntity(PageDesignerTemplateDTO dto) {
+        PageDesignerTemplate entity = new PageDesignerTemplate();
+        entity.setId(dto.getId());
+        entity.setFormData(dto.getFormData());
+        entity.setDelFlag(dto.getDelFlag());
+        entity.setPageId(dto.getPageId());
+        return entity;
+    }
+}
diff --git a/ruoyi-modules/sc-page-designer/src/main/resources/mapper/PageDesignerMapper.xml b/ruoyi-modules/sc-page-designer/src/main/resources/mapper/PageDesignerMapper.xml
new file mode 100644
index 0000000..bd1d17f
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/resources/mapper/PageDesignerMapper.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.ruoyi.pageDesigner.mapper.PageDesignerMapper">
+
+    <!-- 澶氱粨鏋勫祵濂楄嚜鍔ㄦ槧灏勯渶甯︿笂姣忎釜瀹炰綋鐨勪富閿甶d 鍚﹀垯鏄犲皠浼氬け璐� -->
+    <resultMap type="org.ruoyi.pageDesigner.domain.PageDesignerVo" id="PageDesignerResult">
+        <id property="id" column="id"/>
+        <result property="name" column="name"/>
+        <result property="status" column="status"/>
+        <result property="createTime" column="create_time"/>
+        <result property="menuParentId" column="menu_parent_id"/>
+        <result property="remark" column="remark"/>
+    </resultMap>
+
+
+    <select id="selectPagelistAll" resultMap="PageDesignerResult">
+        select
+            id,
+            name,
+            status,
+            create_time,
+            menu_parent_id,
+            remark
+        from page_designer
+        ${ew.getCustomSqlSegment}
+    </select>
+
+
+
+
+</mapper>
diff --git a/ruoyi-modules/sc-page-designer/src/main/resources/mapper/PageDesignerTemplateMapper.xml b/ruoyi-modules/sc-page-designer/src/main/resources/mapper/PageDesignerTemplateMapper.xml
new file mode 100644
index 0000000..8c23389
--- /dev/null
+++ b/ruoyi-modules/sc-page-designer/src/main/resources/mapper/PageDesignerTemplateMapper.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.ruoyi.pageDesigner.mapper.PageDesignerTemplateMapper">
+
+    <!-- 缁撴灉鏄犲皠 -->
+    <resultMap type="org.ruoyi.pageDesigner.domain.PageDesignerTemplateVo" id="PageDesignerTemplateResult">
+        <id property="id" column="id"/>
+        <result property="pageId" column="page_id"/>
+        <result property="formData" column="form_data"/>
+        <result property="delFlag" column="del_flag"/>
+    </resultMap>
+
+    <select id="selectPagelistAll" resultMap="PageDesignerTemplateResult">
+        SELECT
+        id,
+        page_id,
+        form_data,
+        del_flag
+        FROM page_designer_template
+        <where>
+            ${ew.sqlSegment}
+        </where>
+    </select>
+
+</mapper>
diff --git a/ruoyi-modules/sc-services/pom.xml b/ruoyi-modules/sc-services/pom.xml
new file mode 100644
index 0000000..982daae
--- /dev/null
+++ b/ruoyi-modules/sc-services/pom.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.ruoyi</groupId>
+        <artifactId>ruoyi-modules</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>sc-services</artifactId>
+    <description>
+        school-ai涓氬姟妯″潡
+    </description>
+
+    <dependencies>
+    <!-- 閫氱敤宸ュ叿-->
+    <dependency>
+        <groupId>org.ruoyi</groupId>
+        <artifactId>ruoyi-common-core</artifactId>
+    </dependency>
+    </dependencies>
+</project>
diff --git a/ruoyi-ui/apps/web-antd/package.json b/ruoyi-ui/apps/web-antd/package.json
index 2138a2a..7336cf0 100644
--- a/ruoyi-ui/apps/web-antd/package.json
+++ b/ruoyi-ui/apps/web-antd/package.json
@@ -46,14 +46,18 @@
     "@vben/utils": "workspace:*",
     "@vueuse/core": "catalog:",
     "ant-design-vue": "catalog:",
+    "axios": "^1.10.0",
+    "bpmn-js": "^18.6.2",
     "cropperjs": "^1.6.2",
     "crypto-js": "^4.2.0",
     "dayjs": "catalog:",
+    "diagram-js": "^15.3.0",
     "echarts": "^5.5.1",
     "element-plus": "^2.10.2",
     "jsencrypt": "^3.3.2",
     "lodash-es": "^4.17.21",
     "pinia": "catalog:",
+    "qs": "^6.13.1",
     "tinymce": "^7.3.0",
     "unplugin-vue-components": "^0.27.3",
     "vue": "catalog:",
diff --git a/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/index.ts b/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/index.ts
new file mode 100644
index 0000000..79fa508
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/index.ts
@@ -0,0 +1,52 @@
+import { requestClient } from '#/api/request';
+import type {
+  PageDesigner
+} from './model';
+
+enum Api {
+  pageAdd = '/page-designer',
+  pageUpdate = '/page-designer',
+  pageList = '/page-designer/list',
+  pageInfo = '/page-designer',
+  pageRemove = '/page-designer',
+}
+
+/**
+ * 鏂板椤甸潰璁捐
+ */
+export function pageAdd(data: any) {
+  return requestClient.post(Api.pageAdd, data);
+}
+
+/**
+ * 淇敼椤甸潰璁捐
+ */
+export function pageUpdate(data: any) {
+  return requestClient.put(Api.pageUpdate, data);
+}
+
+/**
+ * 鑾峰彇椤甸潰璁捐鍒楄〃
+ */
+export function pageList(params?: any) {
+    return requestClient.get<{
+      total: number;
+      rows: PageDesigner[];
+      code: number;
+      msg: string;
+    }>(Api.pageList, { params });
+}
+
+/**
+ * 鑾峰彇椤甸潰璁捐璇︽儏
+ */
+export function pageInfo(id: string | number) {
+  return requestClient.get<PageDesigner>(`${Api.pageInfo}/${id}`);
+}
+
+/**
+ * 鍒犻櫎椤甸潰璁捐
+ */
+export function pageRemove(ids: string[] | number[]) {
+  return requestClient.delete(Api.pageRemove, { data: ids });
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/model.d.ts b/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/model.d.ts
new file mode 100644
index 0000000..88d885d
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/model.d.ts
@@ -0,0 +1,21 @@
+export interface PageDesigner {
+  id?: string | number;          // 涓婚敭锛岀紪杈戞椂鐢�
+  name: string;                  // 椤甸潰鍚嶇О
+  menuId: string | number | null; // 涓婄骇鐩綍锛堣彍鍗旾D锛�
+  parentId: string | number | null; // 鐖剁骇ID
+  status: string;                // 鐘舵��
+  remark?: string | null;        // 澶囨敞
+  formJson?: string | null;      // 琛ㄥ崟璁捐JSON
+  showColumn?: string | null;    // 鏄剧ず瀛楁(JSON瀛楃涓�)
+  actionsFunc?: string | null;   // 鍚敤鍔熻兘(JSON瀛楃涓�)
+  createTime?: string;           // 鍒涘缓鏃堕棿
+  updateTime?: string;           // 鏇存柊鏃堕棿
+  menu?: {                       // 鍏宠仈鐨勮彍鍗曚俊鎭�
+    menuId: number;
+    menuName: string;
+  } | null;
+  parent?: {                     // 鍏宠仈鐨勭埗绾т俊鎭�
+    id: string | number;
+    name: string;
+  } | null;
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/tool/template/index.ts b/ruoyi-ui/apps/web-antd/src/api/tool/template/index.ts
new file mode 100644
index 0000000..0b91788
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/tool/template/index.ts
@@ -0,0 +1,45 @@
+import { requestClient } from '#/api/request';
+import type { Template } from './model';
+
+enum Api {
+  templateAdd = '/page-designer-template',
+  templateUpdate = '/page-designer-template',
+  templateList = '/page-designer-template/list',
+  templateInfo = '/page-designer-template',
+  templateRemove = '/page-designer-template',
+}
+
+/**
+ * 鏂板椤甸潰璁捐
+ */
+export function templateAdd(data: any) {
+  return requestClient.post(Api.templateAdd, data);
+}
+
+/**
+ * 淇敼椤甸潰璁捐
+ */
+export function templateUpdate(data: any) {
+  return requestClient.put(Api.templateUpdate, data);
+}
+
+/**
+ * 鑾峰彇椤甸潰璁捐鍒楄〃
+ */
+export function templateList(params?: any) {
+  return requestClient.get(Api.templateList, { params });
+}
+
+/**
+ * 鑾峰彇椤甸潰璁捐璇︽儏
+ */
+export function templateInfo(id: string | number) {
+  return requestClient.get(`${Api.templateInfo}/${id}`);
+}
+
+/**
+ * 鍒犻櫎椤甸潰璁捐
+ */
+export function templateRemove(ids: string[] | number[]) {
+  return requestClient.delete(Api.templateRemove, { data: ids });
+} 
\ No newline at end of file
diff --git a/ruoyi-ui/apps/web-antd/src/api/tool/template/model.d.ts b/ruoyi-ui/apps/web-antd/src/api/tool/template/model.d.ts
new file mode 100644
index 0000000..ab9d827
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/tool/template/model.d.ts
@@ -0,0 +1,14 @@
+export interface Template {
+  id?: string | number;          // 涓婚敭
+  formData: string;                  // 妯$増鍚嶇О
+  pageDesignId: string | number; // 鍏宠仈鐨勯〉闈㈣璁D
+  createTime?: string;           // 鍒涘缓鏃堕棿
+  updateTime?: string;           // 鏇存柊鏃堕棿
+  pageDesign?: {                 // 鍏宠仈鐨勯〉闈㈣璁′俊鎭�
+    id: string | number;
+    name: string;
+    formJson?: string;
+    showColumn?: string;
+    actionsFunc?: string;
+  } | null;
+} 
\ No newline at end of file
diff --git a/ruoyi-ui/apps/web-antd/src/bootstrap.ts b/ruoyi-ui/apps/web-antd/src/bootstrap.ts
index f76df08..adf3f37 100644
--- a/ruoyi-ui/apps/web-antd/src/bootstrap.ts
+++ b/ruoyi-ui/apps/web-antd/src/bootstrap.ts
@@ -19,7 +19,10 @@
 import { router } from './router';
 import formCreate from '@form-create/element-ui';
 import FcDesigner from '@form-create/designer';
+import Antd from 'ant-design-vue';
+import 'ant-design-vue/dist/reset.css';
 async function bootstrap(namespace: string) {
+
   // 鍒濆鍖栫粍浠堕�傞厤鍣�
   await initComponentAdapter();
 
@@ -62,6 +65,7 @@
   app.use(ElementPlus);
   app.use(formCreate);
   app.use(FcDesigner);
+  app.use(Antd);
   // 鍔ㄦ�佹洿鏂版爣棰�
   watchEffect(() => {
     if (preferences.app.dynamicTitle) {
diff --git a/ruoyi-ui/apps/web-antd/src/services/flowableService.js b/ruoyi-ui/apps/web-antd/src/services/flowableService.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/services/flowableService.js
diff --git a/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/data.ts b/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/data.ts
new file mode 100644
index 0000000..eec5231
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/data.ts
@@ -0,0 +1,44 @@
+import type { VxeGridProps } from 'src/adapter/vxe-table';
+import type { FormSchemaGetter } from "src/adapter/form";
+
+
+export const querySchema: FormSchemaGetter = () => [
+  {
+    component: 'Input',
+    fieldName: 'roleName',
+    label: '鍚嶇О',
+  },
+
+  {
+    component: 'Select',
+    fieldName: 'roleSort',
+    label: '鍒涘缓浜�',
+  },
+  {
+    component: 'RangePicker',
+    fieldName: 'createTime',
+    label: '鍒涘缓鏃堕棿',
+  },
+];
+export const columns: VxeGridProps['columns'] = [
+  { type: 'checkbox', width: 60 },
+  {
+    title: '鍚嶇О',
+    field: 'roleName',
+  },
+  {
+    title: '鍒涘缓浜�',
+    field: 'roleSort',
+  },
+  {
+    title: '鍒涘缓鏃堕棿',
+    field: 'createTime',
+  },
+  {
+    field: 'action',
+    fixed: 'right',
+    slots: { default: 'action' },
+    title: '鎿嶄綔',
+    width: 180,
+  },
+];
diff --git a/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/index.vue b/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/index.vue
new file mode 100644
index 0000000..c6d7a38
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/index.vue
@@ -0,0 +1,95 @@
+<template>
+  <Page :auto-content-height="true">
+    <BasicTable table-title="宸ヤ綔鑰冩牳鎸囨爣鍒楄〃">
+      <template #toolbar-tools>
+        <Space>
+          <a-button
+            v-access:code="['system:role:export']"
+            @click="handleDownloadExcel"
+            style="margin-right: 10px"
+          >
+            {{ $t('pages.common.export') }}
+          </a-button>
+          <a-button
+            type="primary"
+            v-access:code="['system:role:add']"
+            @click="handleAdd"
+          >
+            {{ $t('pages.common.add') }}
+          </a-button>
+        </Space>
+      </template>
+    </BasicTable>
+  </Page>
+</template>
+
+<script setup lang="ts">
+import type { VxeGridProps } from "#/adapter/vxe-table";
+import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
+import type { VbenFormProps } from '@vben/common-ui';
+import type { roleList} from "#/api/system/role";
+import { columns, querySchema } from './data';
+import { Page, useVbenDrawer, useVbenModal } from '@vben/common-ui';
+const formOptions: VbenFormProps = {
+  commonConfig: {
+    labelWidth: 80,
+    componentProps: {
+      allowClear: true,
+    },
+  },
+  schema: querySchema(),
+  wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
+  // 鏃ユ湡閫夋嫨鏍煎紡鍖�
+  fieldMappingTime: [
+    [
+      'createTime',
+      ['params[beginTime]', 'params[endTime]'],
+      ['YYYY-MM-DD 00:00:00', 'YYYY-MM-DD 23:59:59'],
+    ],
+  ],
+};
+const gridOptions: VxeGridProps = {
+  checkboxConfig: {
+    // 楂樹寒
+    highlight: true,
+    // 缈婚〉鏃朵繚鐣欓�変腑鐘舵��
+    reserve: true,
+    // 鐐瑰嚮琛岄�変腑
+    // trigger: 'row',
+    checkMethod: ({ row }) => row.roleId !== 1,
+  },
+  columns,
+  height: 'auto',
+  keepSource: true,
+  pagerConfig: {},
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }, formValues = {}) => {
+        return await roleList({
+          pageNum: page.currentPage,
+          pageSize: page.pageSize,
+          ...formValues,
+        });
+      },
+    },
+  },
+  rowConfig: {
+    keyField: 'roleId',
+  },
+  id: 'system-role-index',
+};
+const [BasicTable,tableApi] = useVbenVxeGrid({
+  formOptions,
+  gridOptions,
+});
+const handleAdd = () => {
+  console.log('鏂板')
+}
+const handleDownloadExcel = () => {
+  console.log('瀵煎嚭')
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/serivceDialog.vue b/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/serivceDialog.vue
new file mode 100644
index 0000000..c79fc8a
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/assessment/serviceRating/serivceDialog.vue
@@ -0,0 +1,64 @@
+
+
+<script>
+import {modelInfo} from "#/api/system/model/index.js";
+import {pick} from "lodash-es";
+import {useVbenModal} from "@vben/common-ui";
+import {ref, computed} from "vue";
+import { $t } from '@vben/locales';
+const defaultValues = {
+  id: undefined,
+  name: undefined,
+}
+
+const isUpdate = ref(false);
+const title = computed(() => {
+  return isUpdate.value ? $t('缂栬緫') : $t('鏂板');
+});
+const { validate, validateInfos, resetFields } = Form.useForm(
+  formData,
+  formRules,
+);
+const [BasicModal, modalApi] = useVbenModal({
+  class: 'w-[550px]',
+  fullscreenButton: false,
+  closeOnClickModal: false,
+  onClosed: handleCancel,
+  onConfirm: handleConfirm,
+  onOpenChange: async (isOpen) => {
+    if (!isOpen) {
+      return null;
+    }
+    modalApi.modalLoading(true);
+
+    const { id } = modalApi.getData() as { id?: number | string };
+    isUpdate.value = !!id;
+
+    if (isUpdate.value && id) {
+      const record = await modelInfo(id);
+      // 鍙祴鍊煎瓨鍦ㄧ殑瀛楁
+      const filterRecord = pick(record, Object.keys(defaultValues));
+      formData.value = filterRecord;
+    }
+
+    modalApi.modalLoading(false);
+  },
+});
+
+
+</script>
+<template>
+  <BasicModal :title="title">
+    <Form :label-col="{ span: 4 }">
+      <FormItem label="鍚嶇О" v-bind="validateInfos.name">
+        <Input
+          v-model:value="formData.name"
+          :placeholder="$t('ui.formRules.required')"
+        />
+      </FormItem>
+    </Form>
+  </BasicModal>
+</template>
+<style scoped>
+
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/system/process/index.vue b/ruoyi-ui/apps/web-antd/src/views/system/process/index.vue
new file mode 100644
index 0000000..fd598fd
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/system/process/index.vue
@@ -0,0 +1,472 @@
+<template>
+  <div class="process-definition-container">
+    <a-card title="娴佺▼瀹氫箟鍒楄〃" :bordered="false">
+      <div class="table-actions">
+        <a-button type="primary" @click="showCreateModal">鏂板娴佺▼鍥�</a-button>
+      </div>
+      <a-table
+        :columns="columns"
+        :data-source="definitions"
+        :row-key="record => record.id"
+        :pagination="pagination"
+        :loading="loading"
+        @change="handleTableChange"
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'action'">
+            <a-space>
+              <a-button type="link" @click="showDiagram(record)">鏌ョ湅</a-button>
+              <a-button type="link" @click="editDiagram(record)">淇敼</a-button>
+              <a-button type="link" danger @click="deleteDefinition(record)">鍒犻櫎</a-button>
+            </a-space>
+          </template>
+        </template>
+      </a-table>
+    </a-card>
+
+    <!-- 娴佺▼鍥炬煡鐪嬫ā鎬佹 -->
+    <a-modal
+      v-model:visible="diagramVisible"
+      title="娴佺▼鍥炬煡鐪�"
+      width="80%"
+      :footer="null"
+      @cancel="handleDiagramCancel"
+    >
+      <div style="text-align: center">
+        <img
+          v-if="currentDiagramUrl"
+          :src="currentDiagramUrl"
+          alt="娴佺▼鍥�"
+          style="max-width: 100%"
+        />
+        <a-skeleton v-else active />
+      </div>
+    </a-modal>
+
+    <!-- 娴佺▼鍥剧紪杈戞ā鎬佹 -->
+    <a-modal
+      v-model:visible="editorVisible"
+      :title="editorTitle"
+      width="90%"
+      :maskClosable="false"
+      :okText="'淇濆瓨'"
+      :cancelText="'鍙栨秷'"
+      :confirmLoading="editorSaving"
+      @ok="handleEditorOk"
+      @cancel="handleEditorCancel"
+      :destroyOnClose="true"
+      :afterClose="handleEditorAfterClose"
+      :style="{ top: '20px' }"
+      :bodyStyle="{
+      padding: '0',
+      height: 'calc(100vh - 100px)',
+      overflow: 'hidden',
+      display: 'flex',
+      flexDirection: 'column'
+    }"
+    >
+      <div class="editor-container">
+        <div v-if="isCreateMode" class="create-form">
+          <a-form layout="vertical">
+            <a-form-item label="娴佺▼鍚嶇О" required>
+              <a-input v-model:value="newProcess.name" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" />
+            </a-form-item>
+            <a-form-item label="娴佺▼Key" required>
+              <a-input v-model:value="newProcess.key" placeholder="璇疯緭鍏ユ祦绋婯ey" />
+            </a-form-item>
+            <a-form-item label="娴佺▼鎻忚堪">
+              <a-textarea v-model:value="newProcess.description" placeholder="璇疯緭鍏ユ祦绋嬫弿杩�" />
+            </a-form-item>
+          </a-form>
+        </div>
+        <div class="bpmn-editor" ref="bpmnEditor"></div>
+      </div>
+    </a-modal>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, nextTick } from 'vue'
+import { message, Modal } from 'ant-design-vue'
+import BpmnModeler from 'bpmn-js/lib/Modeler'
+import 'bpmn-js/dist/assets/diagram-js.css'
+import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
+import 'bpmn-js/dist/assets/bpmn-js.css'
+
+// 琛ㄦ牸鍒楀畾涔�
+const columns = [
+  {
+    title: 'ID',
+    dataIndex: 'id',
+    key: 'id',
+    ellipsis: true
+  },
+  {
+    title: '鍚嶇О',
+    dataIndex: 'name',
+    key: 'name'
+  },
+  {
+    title: 'Key',
+    dataIndex: 'key',
+    key: 'key'
+  },
+  {
+    title: '鎿嶄綔',
+    key: 'action',
+  }
+]
+
+// 鏁版嵁鐘舵��
+const definitions = ref([])
+const loading = ref(false)
+const pagination = ref({
+  current: 1,
+  pageSize: 10,
+  total: 0,
+  showSizeChanger: true,
+  pageSizeOptions: ['10', '20', '50']
+})
+
+// 娴佺▼鍥炬煡鐪嬬浉鍏崇姸鎬�
+const diagramVisible = ref(false)
+const currentDiagramUrl = ref('')
+const currentProcessDefinition = ref(null)
+
+// 娴佺▼鍥剧紪杈戠浉鍏崇姸鎬�
+const editorVisible = ref(false)
+const editorSaving = ref(false)
+const editorTitle = ref('娴佺▼鍥剧紪杈�')
+const bpmnModeler = ref(null)
+const bpmnEditor = ref(null)
+const isCreateMode = ref(false)
+const newProcess = ref({
+  name: '',
+  key: '',
+  description: ''
+})
+
+// 鑾峰彇娴佺▼瀹氫箟鍒楄〃
+const fetchProcessDefinitions = async (params = {}) => {
+  loading.value = true
+  try {
+    // 妯℃嫙鏁版嵁
+    const mockData = [
+      { id: '1', name: '璇峰亣娴佺▼', key: 'leaveProcess'},
+      { id: '2', name: '鎶ラ攢娴佺▼', key: 'expenseProcess'},
+      { id: '3', name: '閲囪喘娴佺▼', key: 'purchaseProcess' }
+    ]
+    // 妯℃嫙鍒嗛〉
+    const start = (params.page - 1) * params.size
+    const end = start + params.size
+    definitions.value = mockData.slice(start, end)
+    pagination.value.total = mockData.length
+  } catch (error) {
+    message.error('鍔犺浇娴佺▼瀹氫箟澶辫触:'+ error.message)
+  } finally {
+    loading.value = false
+  }
+}
+
+// 琛ㄦ牸鍒嗛〉/鎺掑簭鍙樺寲澶勭悊
+const handleTableChange = (pag, filters, sorter) => {
+  const params = {
+    page: pag.current,
+    size: pag.pageSize
+  }
+
+  if (sorter.field) {
+    params.sort = sorter.field
+    params.order = sorter.order === 'ascend' ? 'asc' : 'desc'
+  }
+
+  fetchProcessDefinitions(params)
+}
+
+// 鏄剧ず娴佺▼鍥�
+const showDiagram = (record) => {
+  currentProcessDefinition.value = record
+  currentDiagramUrl.value = `https://via.placeholder.com/800x600?text=娴佺▼鍥�+${record.id}`
+  diagramVisible.value = true
+}
+
+// 閿�姣丅PMN缂栬緫鍣�
+const destroyBpmnEditor = () => {
+  if (bpmnModeler.value) {
+    bpmnModeler.value.destroy()
+    bpmnModeler.value = null
+  }
+}
+
+// 鍒濆鍖朆PMN缂栬緫鍣�
+const initBpmnEditor = async (xml) => {
+  await nextTick()
+
+  // 鍏堥攢姣佹棫鐨勭紪杈戝櫒
+  destroyBpmnEditor()
+
+  try {
+    // 鍒涘缓鏂扮殑缂栬緫鍣ㄥ疄渚�
+    bpmnModeler.value = new BpmnModeler({
+      container: bpmnEditor.value,
+    })
+
+    // 鍔犺浇XML鎴栭粯璁ゆ祦绋嬪浘
+    const diagram = xml || `
+      <?xml version="1.0" encoding="UTF-8"?>
+      <bpmn:definitions
+        xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
+        xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
+        xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
+        xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
+        id="Definitions_1"
+        targetNamespace="http://bpmn.io/schema/bpmn">
+        <bpmn:process id="Process_1" isExecutable="false">
+          <bpmn:startEvent id="StartEvent_1" />
+        </bpmn:process>
+        <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+          <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
+            <bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
+              <dc:Bounds x="173" y="102" width="36" height="36" />
+            </bpmndi:BPMNShape>
+          </bpmndi:BPMNPlane>
+        </bpmndi:BPMNDiagram>
+      </bpmn:definitions>
+    `
+
+    await bpmnModeler.value.importXML(diagram)
+  } catch (err) {
+    console.error('Error rendering diagram', err)
+    message.error('鍒濆鍖栨祦绋嬪浘缂栬緫鍣ㄥけ璐�: ' + err.message)
+  }
+}
+
+// 鏄剧ず鍒涘缓鏂版祦绋嬫ā鎬佹
+const showCreateModal = () => {
+  isCreateMode.value = true
+  editorTitle.value = '鍒涘缓鏂版祦绋嬪浘'
+  editorVisible.value = true
+
+  // 閲嶇疆琛ㄥ崟
+  newProcess.value = {
+    name: '',
+    key: '',
+    description: ''
+  }
+
+  // 鍒濆鍖栫紪杈戝櫒
+  initBpmnEditor()
+}
+
+// 缂栬緫娴佺▼鍥�
+const editDiagram = async (record) => {
+  isCreateMode.value = false
+  currentProcessDefinition.value = record
+  editorTitle.value = `缂栬緫娴佺▼ - ${record.name}`
+  editorVisible.value = true
+
+  // 妯℃嫙鍔犺浇娴佺▼瀹氫箟XML
+  const mockXml = `
+    <?xml version="1.0" encoding="UTF-8"?>
+    <bpmn:definitions
+      xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
+      xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
+      xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
+      xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
+      id="Definitions_1"
+      targetNamespace="http://bpmn.io/schema/bpmn">
+      <bpmn:process id="${record.id}" name="${record.name}" isExecutable="true">
+        <bpmn:startEvent id="StartEvent_1" name="寮�濮�" />
+        <bpmn:userTask id="UserTask_1" name="鎻愪氦鐢宠" />
+        <bpmn:sequenceFlow id="Flow_1" sourceRef="StartEvent_1" targetRef="UserTask_1" />
+        <bpmn:endEvent id="EndEvent_1" name="缁撴潫" />
+        <bpmn:sequenceFlow id="Flow_2" sourceRef="UserTask_1" targetRef="EndEvent_1" />
+      </bpmn:process>
+      <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+        <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="${record.id}">
+          <bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
+            <dc:Bounds x="173" y="102" width="36" height="36" />
+          </bpmndi:BPMNShape>
+          <bpmndi:BPMNShape id="UserTask_1_di" bpmnElement="UserTask_1">
+            <dc:Bounds x="280" y="80" width="100" height="80" />
+          </bpmndi:BPMNShape>
+          <bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1">
+            <dc:Bounds x="450" y="102" width="36" height="36" />
+          </bpmndi:BPMNShape>
+          <bpmndi:BPMNEdge id="Flow_1_di" bpmnElement="Flow_1">
+            <di:waypoint x="209" y="120" />
+            <di:waypoint x="280" y="120" />
+          </bpmndi:BPMNEdge>
+          <bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2">
+            <di:waypoint x="380" y="120" />
+            <di:waypoint x="450" y="120" />
+          </bpmndi:BPMNEdge>
+        </bpmndi:BPMNPlane>
+      </bpmndi:BPMNDiagram>
+    </bpmn:definitions>
+  `
+
+  // 鍒濆鍖栫紪杈戝櫒骞跺姞杞絏ML
+  initBpmnEditor(mockXml)
+}
+
+// 淇濆瓨娴佺▼鍥句慨鏀�
+const handleEditorOk = async () => {
+  editorSaving.value = true
+
+  try {
+    if (isCreateMode.value) {
+      // 楠岃瘉琛ㄥ崟
+      if (!newProcess.value.name || !newProcess.value.key) {
+        message.error('璇峰~鍐欐祦绋嬪悕绉板拰Key')
+        return
+      }
+
+      // 鑾峰彇XML
+      const { xml } = await bpmnModeler.value.saveXML({ format: true })
+      console.log('鏂版祦绋媂ML:', xml)
+
+      // 妯℃嫙鍒涘缓鏂版祦绋�
+      await new Promise(resolve => setTimeout(resolve, 1000))
+
+      // 娣诲姞鍒板垪琛�
+      const newId = Math.max(...definitions.value.map(d => parseInt(d.id))) + 1
+      definitions.value.unshift({
+        id: newId.toString(),
+        name: newProcess.value.name,
+        key: newProcess.value.key,
+        version: 1
+      })
+
+      message.success('鏂版祦绋嬪垱寤烘垚鍔�')
+    } else {
+      // 鑾峰彇淇敼鍚庣殑XML
+      const { xml } = await bpmnModeler.value.saveXML({ format: true })
+      console.log('淇敼鍚庣殑XML:', xml)
+
+      // 妯℃嫙淇濆瓨
+      await new Promise(resolve => setTimeout(resolve, 1000))
+
+      message.success('娴佺▼鍥句繚瀛樻垚鍔�')
+    }
+
+    editorVisible.value = false
+  } catch (error) {
+    console.error('Error saving BPMN diagram', error)
+    message.error('鎿嶄綔澶辫触: ' + error.message)
+  } finally {
+    editorSaving.value = false
+  }
+}
+
+// 鍒犻櫎娴佺▼瀹氫箟
+const deleteDefinition = (record) => {
+  Modal.confirm({
+    title: '纭鍒犻櫎娴佺▼?',
+    content: `纭畾瑕佸垹闄ゆ祦绋� "${record.name}" 鍚楋紵姝ゆ搷浣滀笉鍙仮澶嶃�俙,
+    okText: '纭',
+    okType: 'danger',
+    cancelText: '鍙栨秷',
+    onOk() {
+      // 妯℃嫙鍒犻櫎
+      definitions.value = definitions.value.filter(item => item.id !== record.id)
+      message.success('娴佺▼鍒犻櫎鎴愬姛')
+    }
+  })
+}
+
+// 妯℃�佹鍏抽棴鍚庢竻鐞�
+const handleEditorAfterClose = () => {
+  destroyBpmnEditor()
+}
+
+const handleEditorCancel = () => {
+  editorVisible.value = false
+}
+
+const handleDiagramCancel = () => {
+  diagramVisible.value = false
+}
+
+// 鍒濆鍖栧姞杞芥暟鎹�
+onMounted(() => {
+  const params = {
+    page: 1,
+    size: 10
+  }
+  fetchProcessDefinitions(params)
+})
+</script>
+
+<style scoped>
+.process-definition-container {
+  padding: 20px;
+  background: #fff;
+}
+
+.table-actions {
+  margin-bottom: 16px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+.editor-container {
+  height: 600px;
+  display: flex;
+  flex-direction: column;
+}
+.bpmn-editor {
+  flex: 1;
+  border: 1px solid #d9d9d9;
+  border-radius: 2px;
+  margin-top: 16px;
+  min-height: 500px; /* 纭繚缂栬緫鍣ㄦ湁瓒冲楂樺害 */
+}
+
+.create-form {
+  padding: 16px;
+  background: #fafafa;
+  border-radius: 2px;
+  border: 1px solid #d9d9d9;
+  margin-bottom: 16px;
+}
+
+/* 闅愯棌BPMN姘村嵃 */
+.bpmn-editor :deep(.bjs-powered-by) {
+  display: none !important;
+}
+.editor-container {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  overflow: hidden;
+}
+
+.bpmn-editor {
+  flex: 1;
+  min-height: 0; /* 閲嶈锛氬厑璁竑lex瀹瑰櫒鏀剁缉 */
+  border: 1px solid #d9d9d9;
+  border-radius: 2px;
+  margin-top: 16px;
+}
+
+.create-form {
+  padding: 16px;
+  background: #fafafa;
+  border-radius: 2px;
+  border: 1px solid #d9d9d9;
+  margin-bottom: 16px;
+}
+
+/* 闅愯棌BPMN姘村嵃 */
+.bpmn-editor :deep(.bjs-powered-by) {
+  display: none !important;
+}
+
+/* 纭繚BPMN宸ュ叿鏍忓彲瑙� */
+.bpmn-editor :deep(.djs-palette) {
+  top: 20px;
+  left: 20px;
+}
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/dynamicForm/index.vue b/ruoyi-ui/apps/web-antd/src/views/tool/dynamicForm/index.vue
index 0bd9f76..aba7965 100644
--- a/ruoyi-ui/apps/web-antd/src/views/tool/dynamicForm/index.vue
+++ b/ruoyi-ui/apps/web-antd/src/views/tool/dynamicForm/index.vue
@@ -1,5 +1,5 @@
 <template>
-  <fc-designer ref="designer" />
+  <fc-designer ref="designer" :config="config"  @save="handleSave" />
 </template>
 
 <script setup>
@@ -16,4 +16,12 @@
 const loadJson = (json) => {
   designer.value.setJson(json);
 };
+const config = ref({
+  showSaveBtn: true,
+  showPreviewBtn: true,
+});
+function handleSave(data) {
+  //淇濆瓨璁捐瑙勫垯
+  console.log(data);
+}
 </script>
diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/data.tsx b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/data.tsx
new file mode 100644
index 0000000..365557c
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/data.tsx
@@ -0,0 +1,109 @@
+import type { FormSchemaGetter } from '#/adapter/form';
+import type { VxeGridProps } from '#/adapter/vxe-table';
+import { h } from 'vue';
+import { FolderIcon, VbenIcon } from '@vben/icons';
+
+export const querySchema: FormSchemaGetter = () => [
+  {
+    component: 'Input',
+    fieldName: 'name',
+    label: '椤甸潰鍚嶇О',
+  },
+  {
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '姝e父', value: '0' },
+        { label: '鍋滅敤', value: '1' },
+      ],
+    },
+    fieldName: 'status',
+    label: '椤甸潰鐘舵��',
+  },
+];
+
+export const columns: VxeGridProps['columns'] = [
+  {
+    title: '椤甸潰鍚嶇О',
+    field: 'name',
+  },
+  {
+    title: '涓婄骇鐩綍',
+    field: 'menuParentName',
+
+  },
+  {
+    title: '鐘舵��',
+    field: 'status',
+    slots: {
+      default: ({ row }) => {
+        return row.status === '0' ? '姝e父' : '鍋滅敤';
+      },
+    },
+  },
+  {
+    title: '鍒涘缓鏃堕棿',
+    field: 'createTime',
+  },
+  {
+    field: 'action',
+    fixed: 'right',
+    slots: { default: 'action' },
+    title: '鎿嶄綔',
+  },
+];
+
+export const drawerSchema = () => [
+  {
+    component: 'Input',
+    fieldName: 'name',
+    label: '椤甸潰鍚嶇О',
+    rules: 'required',
+  },
+  {
+    component: 'TreeSelect',
+    fieldName: 'menuParentId',
+    label: '涓婄骇鐩綍',
+    componentProps: {
+      allowClear: true,
+      showSearch: true,
+    },
+    rules: 'selectRequired',
+  },
+  {
+    component: 'Select',
+    fieldName: 'status',
+    label: '鐘舵��',
+    componentProps: {
+      options: [
+        { label: '姝e父', value: '0' },
+        { label: '鍋滅敤', value: '1' },
+      ],
+    },
+    rules: 'selectRequired',
+  },
+  {
+    component: 'CheckboxGroup',
+    fieldName: 'actionsFunc',
+    label: '鍚敤鍔熻兘',
+    defaultValue: ['add', 'edit', 'delete', 'query'],
+    componentProps: {
+      options: [
+        { label: '鏂板', value: 'add' },
+        { label: '缂栬緫', value: 'edit' },
+        { label: '鍒犻櫎', value: 'delete' },
+        { label: '鏌ヨ', value: 'query' },
+      ],
+    },
+    rules: 'required',
+  },
+  {
+    component: 'Input',
+    fieldName: 'remark',
+    label: '澶囨敞',
+    componentProps: {
+      type: 'textarea',
+      rows: 2,
+    },
+  },
+];
diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue
new file mode 100644
index 0000000..aa8342d
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue
@@ -0,0 +1,217 @@
+<template>
+  <Page v-if="isAdmin" :auto-content-height="true">
+    <BasicTable table-title="椤甸潰璁捐鍣�" >
+      <template #toolbar-tools>
+        <Space>
+          <a-button type="primary" @click="handleAdd">鏂板</a-button>
+        </Space>
+      </template>
+      <template #action="{ row }">
+        <Space>
+          <ghost-button @click="handleEdit(row)">缂栬緫</ghost-button>
+          <Popconfirm :get-popup-container="getVxePopupContainer" placement="left" title="纭鍒犻櫎锛�" @confirm="handleDelete(row)">
+            <ghost-button danger @click.stop="">鍒犻櫎</ghost-button>
+          </Popconfirm>
+        </Space>
+      </template>
+    </BasicTable>
+    <PageDrawer ref="pageModalRef" @reload="tableApi.query()" :menu-array="menuArray" />
+  </Page>
+  <Fallback v-else description="鎮ㄦ病鏈夐〉闈㈢敓鎴愬櫒鐨勮闂潈闄�" status="403" />
+</template>
+
+<script setup lang="ts">
+import type { VbenFormProps } from '@vben/common-ui';
+import type { VxeGridProps } from '#/adapter/vxe-table';
+import { computed, ref, onMounted } from 'vue';
+import { useAccess } from '@vben/access';
+import { Fallback, Page, useVbenDrawer } from '@vben/common-ui';
+import { eachTree, getVxePopupContainer } from '@vben/utils';
+import { Popconfirm, Space } from 'ant-design-vue';
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { columns, querySchema } from './data';
+import PageDrawer from './page-drawer.vue';
+import FcDesigner from '@form-create/designer';
+import { pageList, pageRemove } from '#/api/tool/page-designer';
+import { menuList } from '../../../api/system/menu';
+import { listToTree} from '@vben/utils';
+// 绉婚櫎mock鏁版嵁
+// const pageList = async (params: any) => { ... };
+// const pageRemove = async (ids: number[]) => {};
+const menuArray = ref([]);
+const processedMenuTree = ref([]);
+onMounted(async () => {
+  try {
+    // 鑾峰彇鍘熷鑿滃崟鏁版嵁
+    const rawMenuData = await menuList();
+    menuArray.value = rawMenuData;
+
+    // 澶勭悊鑿滃崟鏁版嵁
+    processMenuData();
+  } catch (error) {
+    console.error('鑾峰彇鑿滃崟鏁版嵁澶辫触:', error);
+  }
+});
+// 澶勭悊鑿滃崟鏁版嵁鐨勫嚱鏁�
+const processMenuData = () => {
+  if (!menuArray.value || menuArray.value.length === 0) return;
+
+  // 1. 杩囨护鎺夋寜閽被鍨�(F)鍜岃彍鍗曠被鍨�(C)
+  const filteredList = menuArray.value.filter(item =>
+    item.menuType !== 'F' && item.menuType !== 'C'
+  );
+
+  // 2. 杞崲涓烘爲褰㈢粨鏋�
+  const treeData = listToTree(filteredList, {
+    id: 'menuId',
+    pid: 'parentId'
+  });
+
+  // 3. 娣诲姞鏍硅妭鐐�
+  processedMenuTree.value = [
+    {
+      menuId: 0,
+      parentId: 0,
+      menuName: '鏍圭洰褰�',
+      children: treeData
+    }
+  ];
+};
+const formOptions: VbenFormProps = {
+  commonConfig: {
+    labelWidth: 80,
+    componentProps: {
+      allowClear: true,
+    },
+  },
+  schema: querySchema(),
+  wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
+};
+const getFullMenuPath = (id: number) => {
+  if (!processedMenuTree.value || processedMenuTree.value.length === 0) return '';
+
+  // 閫掑綊鏌ユ壘鑿滃崟璺緞
+  const findPath = (tree, currentId, path = []): string[] | null => {
+    for (const item of tree) {
+      if (item.menuId === currentId) {
+        return [...path, item.menuName];
+      }
+      if (item.children && item.children.length > 0) {
+        const found = findPath(item.children, currentId, [...path, item.menuName]);
+        if (found) return found;
+      }
+    }
+    return null;
+  };
+
+  const path = findPath(processedMenuTree.value, id);
+  return path ? path.join(' / ') : '鏍圭洰褰�';
+};
+const gridOptions: VxeGridProps = {
+  columns,
+  height: 'auto',
+  keepSource: true,
+  pagerConfig: {
+    enabled: true,
+  },
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }, formValues = {}) => {
+        const resp = await pageList({
+          pageNum: page.currentPage,
+          pageSize: page.pageSize,
+          ...formValues,
+        });
+
+        // 澶勭悊杩斿洖鏁版嵁锛屾坊鍔爉enuParentName
+        const processedRows = resp.rows.map(row => {
+          return {
+            ...row,
+            menuParentName: getFullMenuPath(row.menuParentId) || '鏍圭洰褰�'
+          };
+        });
+        return {
+          rows: processedRows,  // 浣跨敤澶勭悊鍚庣殑鏁版嵁
+          total: resp.total,
+        };
+      },
+    },
+  },
+  rowConfig: {
+    keyField: 'id',
+  },
+  id: 'tool-page-designer-index',
+  columnConfig: { resizable: true },
+};
+
+const [BasicTable, tableApi] = useVbenVxeGrid({
+  formOptions,
+  gridOptions,
+});
+
+const designer = ref();
+const pageModalRef = ref();
+
+function getFormJson() {
+  // 鑾峰彇璁捐缁撴灉
+  const json = designer.value.getRule();
+  // 浣犲彲浠ュ皢 json 瀛樺埌鍚庣
+}
+
+function setFormJson(json) {
+  // 鍔犺浇宸叉湁璁捐
+  designer.value.setRule(json);
+}
+
+function handleAdd() {
+  pageModalRef.value.open({ update: false });
+}
+
+function handleEdit(record) {
+  pageModalRef.value.open({ id: record.id, update: true });
+}
+
+async function handleDelete(row: any) {
+  await pageRemove([row.id]);
+  await tableApi.query();
+}
+
+function handleSubAdd(row) {
+  pageModalRef.value.open({ id: row.id, update: false });
+}
+
+const { hasAccessByRoles } = useAccess();
+const isAdmin = computed(() => {
+  return hasAccessByRoles(['admin', 'superadmin']);
+});
+</script>
+
+<style scoped>
+.designer-page {
+  background: #f5f6fa;
+  padding: 16px;
+  min-height: 100vh;
+}
+.designer-query-form {
+  background: #fff;
+  padding: 16px 16px 0 16px;
+  border-radius: 6px;
+  margin-bottom: 12px;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+}
+.designer-toolbar {
+  background: #fff;
+  padding: 12px 16px;
+  border-radius: 6px;
+  margin-bottom: 12px;
+  display: flex;
+  gap: 8px;
+}
+.designer-table {
+  background: #fff;
+  border-radius: 6px;
+  padding: 0 0 16px 0;
+}
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/page-drawer.vue b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/page-drawer.vue
new file mode 100644
index 0000000..b8ac9be
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/page-drawer.vue
@@ -0,0 +1,337 @@
+<script setup lang="ts">
+import { computed, ref, watch, nextTick, onMounted } from 'vue';
+import { Modal, message, FormItem } from 'ant-design-vue';
+import { $t } from '@vben/locales';
+import { useVbenForm } from '#/adapter/form';
+import { drawerSchema } from './data';
+import FcDesigner from '@form-create/designer';
+
+import { listToTree, addFullName, getPopupContainer } from '@vben/utils';
+import { pageAdd, pageUpdate, pageInfo } from '#/api/tool/page-designer';
+
+interface ModalProps {
+  id?: number | string;
+  update: boolean;
+}
+
+
+const emit = defineEmits<{ reload: [] }>();
+
+const isUpdate = ref(false);
+const title = computed(() => {
+  return isUpdate.value ? $t('pages.common.edit') : $t('pages.common.add');
+});
+
+const [BasicForm, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  schema: drawerSchema(),
+  showDefaultActions: false,
+  wrapperClass: 'grid-cols-5',
+});
+
+const designer = ref();
+const selectedFields = ref([]); // 澶氶�夋閫変腑鐨勫瓧娈祂ey
+const fieldOptions = ref([]);   // 璁捐鍖烘墍鏈夊瓧娈�
+const modalVisible = ref(false);
+const modalLoading = ref(false);
+const currentEditId = ref<string | number>(''); // 褰撳墠缂栬緫鐨処D
+const fullMenuTree = ref([]);
+const props=defineProps({
+  menuArray: {
+    type: Array,
+    required: true,
+    default: () => []
+  }
+});
+// 鎵撳紑寮圭獥
+const open = async (params: ModalProps = { update: false }) => {
+  try {
+
+    modalVisible.value = true;
+    modalLoading.value = true;
+    isUpdate.value = params.update;
+    currentEditId.value = params.id || ''; // 淇濆瓨褰撳墠缂栬緫鐨処D
+    await setupPageSelect();
+
+    if (params.id) {
+      await formApi.setFieldValue('menuParentId', params.id);
+      if (params.update) {
+        // 鑾峰彇璇︽儏鏁版嵁
+        const record = await pageInfo(params.id);
+
+        // 璁剧疆鍩虹琛ㄥ崟鏁版嵁
+        const menuParentId = String(record.menuParentId || record.parentId || '');
+        await formApi.setValues({
+          name: record.name,
+          menuParentId: menuParentId,  // 鐢� menuParentId 瀛楁
+          status: record.status,
+          remark: record.remark,
+          actionsFunc: record.actionsFunc ? JSON.parse(record.actionsFunc) : ['add', 'edit', 'delete', 'query']
+        });
+
+        // 鍔犺浇琛ㄥ崟璁捐鏁版嵁
+        if (record.formJson) {
+          try {
+            const formRule = JSON.parse(record.formJson);
+            console.log('璁捐鍣ㄨ鍒�:', formRule);
+            designer.value.setRule(formRule);
+
+            // 鏇存柊瀛楁閫夐」
+            await nextTick();
+            updateFieldOptions();
+
+            // 鎭㈠閫変腑鐨勫瓧娈�
+            if (record.showColumn) {
+              selectedFields.value = JSON.parse(record.showColumn);
+            }
+          } catch (e) {
+            console.error('鍔犺浇琛ㄥ崟璁捐鏁版嵁澶辫触:', e);
+            message.error('鍔犺浇琛ㄥ崟璁捐鏁版嵁澶辫触');
+          }
+        }
+      }
+    } else {
+      // 鏂板鏃堕噸缃暟鎹�
+      designer.value?.setRule([]);
+      selectedFields.value = [];
+      await formApi.resetForm();
+    }
+  } catch (error) {
+    console.error('鎵撳紑寮圭獥澶辫触:', error);
+    message.error('鍔犺浇鏁版嵁澶辫触');
+  } finally {
+    modalLoading.value = false;
+  }
+};
+const close = () => {
+  modalVisible.value = false;
+};
+
+defineExpose({ open, close });
+
+async function setupPageSelect() {
+  // 鑾峰彇鑿滃崟鏁版嵁
+  if (!props.menuArray || props.menuArray.length === 0) {
+    await nextTick(); // 绛夊緟鍙兘鐨勫紓姝ュ姞杞�
+    if (!props.menuArray || props.menuArray.length === 0) {
+      console.warn('menuArray is empty');
+      return;
+    }
+  }
+  // 杩囨护鎺夋寜閽被鍨�
+  const filteredList = props.menuArray.filter(item => item.menuType !== 'F' && item.menuType !== 'C');
+  // 鏀寔i18n
+  filteredList.forEach(item => { item.menuName = $t(item.menuName); });
+  // 杞负鏍戠粨鏋�
+  const menuTree = listToTree(filteredList, { id: 'menuId', pid: 'parentId' });
+  // 閫掑綊鏄犲皠 menuId -> menuParentId
+  function mapMenuIdToParentId(list) {
+    return list.map(item => {
+      const newItem = { ...item, menuParentId: item.menuId };
+      if (item.children) {
+        newItem.children = mapMenuIdToParentId(item.children);
+      }
+      return newItem;
+    });
+  }
+  fullMenuTree.value = [
+    {
+      menuId: 0,
+      menuParentId: 0,
+      menuName: $t('menu.root'),
+      children: mapMenuIdToParentId(menuTree),
+    },
+  ];
+  // 鐢熸垚鍏ㄨ矾寰勫悕
+  addFullName(fullMenuTree.value, 'menuName', ' / ');
+
+  formApi.updateSchema([
+    {
+      componentProps: {
+        fieldNames: {
+          label: 'menuName',
+          value: 'menuId', // 鐢� menuId
+          children: 'children'
+        },
+        getPopupContainer,
+        listHeight: 300,
+        showSearch: true,
+        treeData: fullMenuTree.value,
+        treeDefaultExpandAll: false,
+        treeDefaultExpandedKeys: [0],
+        treeLine: { showLeafIcon: false },
+        treeNodeFilterProp: 'menuName',
+        treeNodeLabelProp: 'fullName',
+      },
+      fieldName: 'menuParentId', // 鐢� menuParentId
+    },
+  ]);
+}
+
+
+// 鍚屾鎵�鏈夊瓧娈靛埌閫変腑鐘舵��
+const syncAllFields = () => {
+  // 鑾峰彇琛ㄥ崟缁勪欢鐨勮鍒欐弿杩�
+  const formDesc = designer.value?.getFormDescription?.();
+
+  if (!formDesc || !Array.isArray(formDesc)) {
+    message.warning('鏆傛棤璁捐鏁版嵁');
+    return;
+  }
+
+  // 鎻愬彇瀛楁淇℃伅
+  const allFields = formDesc
+    .filter(item => item && item.field && item.title)
+    .map(item => ({
+      title: item.title,
+      field: item.field
+    }));
+
+
+  if (allFields.length === 0) {
+    message.warning('鏈壘鍒板彲鐢ㄥ瓧娈�');
+    return;
+  }
+
+  // 鏇存柊瀛楁閫夐」
+  fieldOptions.value = allFields.map(item => ({
+    label: item.title,
+    value: item.field
+  }));
+
+  // 閫変腑鎵�鏈夊瓧娈�
+  selectedFields.value = allFields.map(item => item.field);
+  message.success(`宸插悓姝� ${allFields.length} 涓瓧娈礰);
+};
+
+// 澶勭悊璁捐鍣ㄥ彉鍖�
+const handleDesignerChange = () => {
+  nextTick(() => {
+    updateFieldOptions();
+  });
+};
+
+// 褰撹璁″櫒鍐呭鍙樺寲鏃舵洿鏂板瓧娈甸�夐」
+const updateFieldOptions = () => {
+  // 鑾峰彇琛ㄥ崟缁勪欢鐨勮鍒欐弿杩�
+  const formDesc = designer.value?.getFormDescription?.();
+  if (!formDesc || !Array.isArray(formDesc)) return;
+
+  const fields = formDesc
+    .filter(item => item && item.field && item.title)
+    .map(item => ({
+      title: item.title,
+      field: item.field
+    }));
+
+  fieldOptions.value = fields.map(item => ({
+    label: item.title,
+    value: item.field
+  }));
+};
+
+// 鐩戝惉璁捐鍣ㄥ唴瀹瑰彉鍖�
+watch(() => modalVisible.value, (val) => {
+  if (val) {
+    nextTick(() => updateFieldOptions());
+  }
+});
+
+async function handleOk() {
+  try {
+    modalLoading.value = true;
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    const data = await formApi.getValues();
+
+    // 濡傛灉鏄紪杈戞ā寮忥紝娣诲姞id瀛楁
+    if (isUpdate.value) {
+      data.id = currentEditId.value;
+    }
+
+    // 鑾峰彇琛ㄥ崟璁捐 JSON
+    data.formJson = designer.value.getJson();
+    // 娣诲姞閫変腑鐨勫瓧娈�
+    data.showColumn = JSON.stringify(selectedFields.value);
+    // 杞崲鍚敤鍔熻兘涓篔SON瀛楃涓�
+    data.actionsFunc = JSON.stringify(data.actionsFunc);
+
+    // 鍚屾涓�娆″瓧娈靛閫�
+    updateFieldOptions();
+    await (isUpdate.value ? pageUpdate(data) : pageAdd(data));
+    emit('reload');
+    close();
+    message.success('淇濆瓨鎴愬姛');
+  } catch (error) {
+    console.error(error);
+  } finally {
+    modalLoading.value = false;
+  }
+}
+
+function handleCancel() {
+  close();
+}
+</script>
+
+<template>
+  <a-modal
+    v-model:open="modalVisible"
+    :title="title"
+    :width="'80vw'"
+    :confirm-loading="modalLoading"
+    @ok="handleOk"
+    @cancel="handleCancel"
+    :bodyStyle="{ padding: '24px', minHeight: '60vh' }"
+    destroyOnClose
+    wrapClassName="page-designer-modal"
+  >
+    <template #closeIcon>
+      <span></span>
+    </template>
+    <BasicForm />
+    <div style="margin-top: 16px;">
+      <FcDesigner
+        ref="designer"
+        @update="handleDesignerChange"
+        @change="handleDesignerChange"
+        @add-rule="handleDesignerChange"
+        @remove-rule="handleDesignerChange"
+
+      />
+      <div style="margin-top: 8px; display: flex; justify-content: flex-end;">
+        <a-button type="primary" ghost @click="syncAllFields">
+          鍚屾璁捐瀛楁鍒拌〃鏍�
+        </a-button>
+      </div>
+    </div>
+    <FormItem label="琛ㄦ牸瀛楁" style="margin-top: 24px;">
+      <a-checkbox-group
+        v-model:value="selectedFields"
+        :options="fieldOptions"
+        style="width:100%;display:flex;flex-wrap:wrap;gap:8px"
+      />
+    </FormItem>
+    <template #empty>
+      <div style="padding: 32px 0; color: #999; text-align: center;">
+        鏆傛棤鏁版嵁
+      </div>
+    </template>
+  </a-modal>
+</template>
+
+<style scoped>
+.page-designer-modal .ant-modal {
+  max-width: 1200px;
+}
+
+
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/template/data.tsx b/ruoyi-ui/apps/web-antd/src/views/tool/template/data.tsx
new file mode 100644
index 0000000..62d35c1
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/tool/template/data.tsx
@@ -0,0 +1,86 @@
+import type { FormSchemaGetter } from '#/adapter/form';
+import type { VxeGridProps } from '#/adapter/vxe-table';
+
+export const querySchema: FormSchemaGetter = () => [
+  {
+    component: 'Input',
+    fieldName: 'name',
+    label: '妯$増鍚嶇О',
+  },
+  {
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '姝e父', value: '1' },
+        { label: '鍋滅敤', value: '0' },
+      ],
+    },
+    fieldName: 'status',
+    label: '妯$増鐘舵��',
+  },
+];
+
+export const columns = [
+  {
+    title: '濮撳悕',
+    dataIndex: 'name',
+    auth: 'test', // 鏍规嵁鏉冮檺鎺у埗鏄惁鏄剧ず: 鏃犳潈闄愶紝涓嶆樉绀�
+  }
+];
+
+export const drawerSchema = () => [
+  {
+    component: 'Input',
+    fieldName: 'name',
+    label: '妯$増鍚嶇О',
+    rules: 'required',
+  },
+  {
+    component: 'Select',
+    fieldName: 'pageDesignId',
+    label: '椤甸潰璁捐',
+    componentProps: {
+      allowClear: true,
+      showSearch: true,
+      filterOption: (input: string, option: any) => {
+        return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+      },
+    },
+    rules: 'selectRequired',
+  },
+  {
+    component: 'Select',
+    fieldName: 'templateType',
+    label: '妯$増绫诲瀷',
+    componentProps: {
+      options: [
+        { label: '鍒楄〃椤�', value: 'list' },
+        { label: '琛ㄥ崟椤�', value: 'form' },
+        { label: '璇︽儏椤�', value: 'detail' },
+        { label: '浠〃鏉�', value: 'dashboard' },
+      ],
+    },
+    rules: 'selectRequired',
+  },
+  {
+    component: 'Select',
+    fieldName: 'status',
+    label: '鐘舵��',
+    componentProps: {
+      options: [
+        { label: '姝e父', value: '1' },
+        { label: '鍋滅敤', value: '0' },
+      ],
+    },
+    rules: 'selectRequired',
+  },
+  {
+    component: 'Input',
+    fieldName: 'remark',
+    label: '澶囨敞',
+    componentProps: {
+      type: 'textarea',
+      rows: 2,
+    },
+  },
+]; 
\ No newline at end of file
diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/template/index.vue b/ruoyi-ui/apps/web-antd/src/views/tool/template/index.vue
new file mode 100644
index 0000000..a37a390
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/tool/template/index.vue
@@ -0,0 +1,397 @@
+<template>
+  <Page v-if="isAdmin && pageId" :auto-content-height="true">
+    <BasicTable 
+      :key="tableKey"
+      :table-title="pageDesignDetail?.name || '妯℃澘鍒楄〃'"
+      :grid-options="gridOptions"
+    >
+      <template #toolbar-tools>
+        <Space>
+          <a-button v-if="showAction('add')" type="primary" @click="handleAdd">鏂板</a-button>
+        </Space>
+      </template>
+      <template #action="{ row }">
+        <Space>
+          <ghost-button v-if="showAction('edit')" @click="handleEdit(row)">缂栬緫</ghost-button>
+          <Popconfirm v-if="showAction('delete')" :get-popup-container="getVxePopupContainer" placement="left" title="纭鍒犻櫎锛�" @confirm="handleDelete(row)">
+            <ghost-button danger @click.stop="">鍒犻櫎</ghost-button>
+          </Popconfirm>
+        </Space>
+      </template>
+    </BasicTable>
+    <TemplateDrawer ref="templateModalRef" @reload="tableApi.query()" />
+  </Page>
+  <Fallback v-else description="鏈寚瀹� pageId锛屾棤娉曡闂椤甸潰" status="403" />
+</template>
+
+<script setup lang="ts">
+import type { VbenFormProps } from '@vben/common-ui';
+import type { VxeGridProps } from '#/adapter/vxe-table';
+import { computed, ref, onMounted, watch } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { useAccess } from '@vben/access';
+import { Fallback, Page } from '@vben/common-ui';
+import { getVxePopupContainer } from '@vben/utils';
+import { Popconfirm, Space, Spin as ASpin } from 'ant-design-vue';
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { columns as baseColumns, querySchema } from './data';
+import TemplateDrawer from './template-drawer.vue';
+import { templateList, templateRemove } from '#/api/tool/template';
+import { pageInfo } from '#/api/tool/page-designer';
+
+const route = useRoute();
+const router = useRouter();
+const pageId = ref<string | number>('');
+const pageDesignDetail = ref<any>(null); // 椤甸潰璁捐璇︽儏
+const loading = ref(true); // 鍔犺浇涓�
+
+// 鍔ㄦ�乧olumns
+const dynamicColumns = ref([
+  { field: 'id', title: 'ID', width: 100 },
+  { field: 'formData', title: '琛ㄥ崟鏁版嵁', minWidth: 160 },
+  { field: 'action', title: '鎿嶄綔', width: 160, slots: { default: 'action' } }
+]);
+
+// 鐢ㄤ簬琛ㄦ牸閲嶆柊娓叉煋鐨� key
+const tableKey = ref(0);
+
+// 鏇存柊鍔ㄦ�佸垪鐨勫嚱鏁�
+function updateDynamicColumns() {
+  if (!pageDesignDetail.value || !pageDesignDetail.value.showColumn || !pageDesignDetail.value.formJson) {
+    console.log('浣跨敤榛樿鍒�');
+    return;
+  }
+
+  try {
+    const showFields = JSON.parse(pageDesignDetail.value.showColumn);
+    const formFields = JSON.parse(pageDesignDetail.value.formJson);
+    const cols = showFields.map(field => {
+      const fieldDef = formFields.find(f => f.field === field);
+      return {
+        field: field,
+        title: fieldDef ? fieldDef.title : field,
+        minWidth: 120,
+        align: 'center',
+      };
+    });
+
+    cols.push({
+      field: 'action',
+      title: '鎿嶄綔',
+      width: 160,
+      slots: { default: 'action' },
+    });
+
+    dynamicColumns.value = cols;
+    gridOptions.value = { ...gridOptions.value, columns: cols };
+    tableKey.value++;
+  } catch (error) {
+    const fallbackCols = [
+      { field: 'id', title: 'ID', width: 100 },
+      { field: 'formData', title: '琛ㄥ崟鏁版嵁', minWidth: 160 },
+      { field: 'action', title: '鎿嶄綔', width: 160, slots: { default: 'action' } }
+    ];
+    dynamicColumns.value = fallbackCols;
+    gridOptions.value = { ...gridOptions.value, columns: fallbackCols };
+    tableKey.value++;
+  }
+}
+
+
+// 鍔ㄦ�佹寜閽�
+function showAction(action: string) {
+  if (!pageDesignDetail.value || !pageDesignDetail.value.actionsFunc) {
+    return true;
+  }
+  
+  try {
+    const actions = JSON.parse(pageDesignDetail.value.actionsFunc);
+    if (!Array.isArray(actions)) {
+      console.warn('actionsFunc 涓嶆槸鏁扮粍鏍煎紡:', pageDesignDetail.value.actionsFunc);
+      return true;
+    }
+    
+    return actions.includes(action);
+  } catch (error) {
+    console.error('瑙f瀽 actionsFunc 澶辫触:', error);
+    return true;
+  }
+}
+
+// 鑾峰彇 pageId锛屽彧鐢� params
+function getPageId() {
+  const segments = window.location.pathname.split('/');
+  return segments[segments.length - 1] || '';
+}
+
+onMounted(() => {
+  const initialPageId = getPageId();
+  console.log('鑾峰彇鍒扮殑 pageId:', initialPageId);
+  
+  if (initialPageId) {
+    pageId.value = initialPageId;
+    handlePageIdChange();
+  } else {
+    loading.value = false;
+  }
+});
+
+// 鐩戝惉璺敱鍙樺寲锛岃嚜鍔ㄦ洿鏂� pageId
+watch(
+  () => [route.meta.pageId, route.params.pageId, route.query.pageId],
+  (newValues, oldValues) => {
+    // 鍙湁褰撳�肩湡姝e彉鍖栨椂鎵嶅鐞�
+    if (JSON.stringify(newValues) !== JSON.stringify(oldValues)) {
+      const newPageId = getPageId();
+      console.log('璺敱鍙樺寲鍚� pageId:', newPageId);
+      
+      // 鍙湁褰� pageId 鐪熸鍙樺寲鏃舵墠鏇存柊
+      if (newPageId !== pageId.value) {
+        pageId.value = newPageId;
+        handlePageIdChange();
+        tableApi.query();
+      }
+    }
+  },
+  { deep: true }
+);
+
+// pageId鍙樺寲鏃惰嚜鍔ㄨ幏鍙栭〉闈㈣璁¤鎯�
+async function handlePageIdChange() {
+  loading.value = true;
+  console.log(`[handlePageIdChange] 寮�濮嬪鐞� pageId: ${pageId.value}`);
+  try {
+    if (pageId.value) {
+      const detail = await pageInfo(pageId.value);
+      console.log('[handlePageIdChange] 鑾峰彇鍒扮殑椤甸潰璁捐璇︽儏 (detail):', JSON.parse(JSON.stringify(detail)));
+      
+      // 澶勭悊鏁版嵁锛岄伩鍏嶅惊鐜紩鐢�
+      const safeDetail = {
+        id: detail.id,
+        name: detail.name,
+        menuParentId: detail.menuId,
+        status: detail.status,
+        remark: detail.remark,
+        formJson: detail.formJson,
+        showColumn: detail.showColumn,
+        actionsFunc: detail.actionsFunc,
+        createTime: detail.createTime,
+        updateTime: detail.updateTime,
+        createBy: detail.createBy,
+        updateBy: detail.updateBy,
+        createDept: detail.createDept
+      };
+      
+      pageDesignDetail.value = safeDetail;
+      console.log('[handlePageIdChange] 璁剧疆鐨� pageDesignDetail.value:', JSON.parse(JSON.stringify(pageDesignDetail.value)));
+      updateDynamicColumns();
+    } else {
+      pageDesignDetail.value = null;
+      updateDynamicColumns();
+    }
+  } catch (error) {
+    console.error('[handlePageIdChange] 鑾峰彇椤甸潰璁捐璇︽儏澶辫触:', error);
+    pageDesignDetail.value = null;
+    updateDynamicColumns();
+  } finally {
+    loading.value = false;
+  }
+}
+
+const formOptions: VbenFormProps = {
+  commonConfig: {
+    labelWidth: 80,
+    componentProps: {
+      allowClear: true,
+    },
+  },
+  schema: querySchema(),
+  wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
+};
+
+const gridOptions = ref<VxeGridProps>({
+  columns: dynamicColumns.value,
+  height: 'auto',
+  keepSource: true,
+  pagerConfig: {
+    enabled: true,
+  },
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }, formValues = {}) => {
+        try {
+          console.log('鏌ヨ鍙傛暟:', { page, formValues });
+          const queryParams = {
+            pageNum: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+          };
+          if (pageId.value) {
+            queryParams.pageId = pageId.value;
+          }
+          const resp = await templateList(queryParams);
+          // 澶勭悊姣忔潯 row 鐨� formData
+          const rows = (resp.rows || []).map(row => {
+            let formData = {};
+            try {
+              if (row.formData) {
+                formData = JSON.parse(row.formData);
+                console.log('瑙f瀽鍚庣殑 formData:', formData);
+                // 鍒犻櫎鍘熷鐨� formData 瀛楁锛屽洜涓烘垜浠凡缁忓睍寮�瀹冪殑鍐呭
+                const { formData: _, ...restRow } = row;
+                // 杩斿洖灞曞紑鍚庣殑鏁版嵁
+                return {
+                  ...restRow,
+                  ...formData
+                };
+              }
+            } catch (e) {
+              console.error('瑙f瀽 formData 澶辫触:', e);
+            }
+            return row;
+          });
+          console.log('澶勭悊鍚庣殑 rows:', rows);
+          return {
+            rows,
+            total: resp.total || 0,
+          };
+        } catch (error) {
+          console.error('鏌ヨ妯℃澘鍒楄〃澶辫触:', error);
+          return {
+            rows: [],
+            total: 0,
+          };
+        }
+      },
+    },
+  },
+  rowConfig: {
+    keyField: 'id',
+  },
+  id: 'tool-template-index',
+  columnConfig: { resizable: true },
+});
+
+const [BasicTable, tableApi] = useVbenVxeGrid({
+  formOptions,
+  gridOptions: computed(() => ({
+    ...gridOptions.value,
+    columns: dynamicColumns.value
+  })),
+});
+
+const templateModalRef = ref();
+const generateModalRef = ref();
+
+function handleAdd() {
+  // 濡傛灉鏈� pageId锛屼紶閫掔粰鏂板
+  const params: any = { update: false };
+  if (pageId.value) {
+    params.pageId = pageId.value;
+  }
+  // 鍔ㄦ�佷紶閫抐ormJson锛屽厛JSON.parse锛屼繚璇佹槸绾璞�
+  if (pageDesignDetail.value && pageDesignDetail.value.formJson) {
+    try {
+      const formJson = JSON.parse(pageDesignDetail.value.formJson);
+      params.formJson = formJson;
+      console.log('浼犻�掑姩鎬佽〃鍗曞瓧娈�:', formJson);
+    } catch (error) {
+      console.error('瑙f瀽 formJson 澶辫触:', error);
+      params.formJson = undefined;
+    }
+  }
+  templateModalRef.value.open(params);
+}
+
+function handleEdit(record) {
+  // 缂栬緫鏃朵篃浼犻�抐ormJson锛屽厛JSON.parse锛屼繚璇佹槸绾璞�
+  const params: any = { 
+    id: record.id, 
+    update: true,
+    pageId: pageId.value,  // 浼犻�掗〉闈㈣璁D
+    record: record  // 浼犻�掑畬鏁寸殑璁板綍鏁版嵁
+  };
+  
+  if (pageDesignDetail.value && pageDesignDetail.value.formJson) {
+    try {
+      const formJson = JSON.parse(pageDesignDetail.value.formJson);
+      params.formJson = formJson;
+      console.log('缂栬緫鏃朵紶閫掓暟鎹�:', { record, formJson, pageId: pageId.value });
+    } catch (error) {
+      console.error('瑙f瀽 formJson 澶辫触:', error);
+      params.formJson = undefined;
+    }
+  }
+  templateModalRef.value.open(params);
+}
+
+async function handleDelete(row: any) {
+  try {
+    await templateRemove([row.id]);
+    await tableApi.query();
+  } catch (error) {
+    console.error('鍒犻櫎妯℃澘澶辫触:', error);
+  }
+}
+
+function handleGenerate(row) {
+  try {
+    generateModalRef.value.open(row);
+  } catch (error) {
+    console.error('鎵撳紑鐢熸垚椤甸潰澶辫触:', error);
+  }
+}
+
+function handlePreview(row) {
+  try {
+    // 鎵撳紑棰勮绐楀彛
+    const url = `/tool/template/preview/${row.id}`;
+    window.open(url, '_blank');
+  } catch (error) {
+    console.error('鎵撳紑棰勮澶辫触:', error);
+  }
+}
+
+const { hasAccessByRoles } = useAccess();
+const isAdmin = computed(() => {
+  try {
+    return hasAccessByRoles(['admin', 'superadmin']);
+  } catch (error) {
+    console.error('妫�鏌ユ潈闄愬け璐�:', error);
+    return false;
+  }
+});
+
+const isReady = computed(() => {
+  try {
+    // 绠�鍖栧垽鏂�昏緫锛屽噺灏戜笉蹇呰鐨勮绠�
+    return !!(pageDesignDetail.value && !loading.value);
+  } catch (error) {
+    console.error('妫�鏌ラ〉闈㈠噯澶囩姸鎬佸け璐�:', error);
+    return false;
+  }
+});
+</script>
+
+<style scoped>
+.template-page {
+  background: #f5f6fa;
+  padding: 16px;
+  min-height: 100vh;
+  height: 100vh;
+  overflow: hidden;
+}
+
+/* 纭繚琛ㄦ牸瀹瑰櫒楂樺害绋冲畾 */
+:deep(.vxe-table--main-wrapper) {
+  height: 600px !important;
+}
+
+/* 纭繚鍒嗛〉鍣ㄤ綅缃浐瀹� */
+:deep(.vxe-pager) {
+  position: sticky;
+  bottom: 0;
+  background: white;
+  z-index: 10;
+}
+</style> 
\ No newline at end of file
diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/template/template-drawer.vue b/ruoyi-ui/apps/web-antd/src/views/tool/template/template-drawer.vue
new file mode 100644
index 0000000..a3d88e0
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/tool/template/template-drawer.vue
@@ -0,0 +1,224 @@
+<script setup lang="ts">
+import { computed, ref, watch, nextTick } from 'vue';
+import { Modal, message, FormItem } from 'ant-design-vue';
+import { $t } from '@vben/locales';
+import { templateAdd, templateUpdate, templateInfo } from '#/api/tool/template';
+import { pageList } from '#/api/tool/page-designer';
+import formCreate from '@form-create/element-ui';
+
+interface ModalProps {
+  id?: number | string;
+  update: boolean;
+  pageId?: string | number;
+  formJson?: any;
+}
+
+const emit = defineEmits<{ reload: [] }>();
+
+const isUpdate = ref(false);
+const title = computed(() => {
+  return isUpdate.value ? $t('pages.common.edit') : $t('pages.common.add');
+});
+
+const modalVisible = ref(false);
+const modalLoading = ref(false);
+const currentEditId = ref<string | number>('');
+const dynamicFormRef = ref();
+const dynamicFormRule = ref([]);
+const dynamicFormOption = ref({
+  submitBtn: false,
+  resetBtn: false
+});
+const showOkBtn = ref(true);
+const currentPageId = ref('');
+const formApi = ref();
+
+// 鍒ゆ柇鏄惁鏈夋彁浜ゆ寜閽�
+type RuleItem = { type?: string };
+const hasSubmitBtn = (rules: any[] = []) => {
+  return rules.some((item: RuleItem) => item.type === 'submit');
+};
+// 鏇村交搴曠殑閫掑綊绉婚櫎鎵�鏈� submit 鎸夐挳锛屾敮鎸� children銆乥ody銆乧olumns銆乴ist銆乷ptions銆乼abs 绛夊鍣�
+function removeSubmitBtn(rules = []) {
+  return rules
+    .filter(item => item.type !== 'submit')
+    .map(item => {
+      if (item.children) item.children = removeSubmitBtn(item.children);
+      if (item.body) item.body = removeSubmitBtn(item.body);
+      if (item.columns) item.columns = item.columns.map(col => ({
+        ...col,
+        list: removeSubmitBtn(col.list || [])
+      }));
+      if (item.list) item.list = removeSubmitBtn(item.list);
+      if (item.options && Array.isArray(item.options)) item.options = removeSubmitBtn(item.options);
+      if (item.tabs) item.tabs = item.tabs.map(tab => ({
+        ...tab,
+        list: removeSubmitBtn(tab.list || [])
+      }));
+      return item;
+    });
+}
+
+// 鎵撳紑寮圭獥
+const open = async (params: ModalProps = { update: false }) => {
+  try {
+    console.log('鎵撳紑寮圭獥锛屽弬鏁�:', params);
+    modalVisible.value = true;
+    modalLoading.value = true;
+    isUpdate.value = params.update;
+    currentEditId.value = params.id || '';
+    currentPageId.value = params.pageId || '';
+    
+    // 璁剧疆鍔ㄦ�佽〃鍗曞瓧娈碉紝濮嬬粓绉婚櫎鎵�鏈塻ubmit鎸夐挳
+    if (params.formJson) {
+      console.log('璁剧疆鍔ㄦ�佽〃鍗曡鍒�:', params.formJson);
+      dynamicFormRule.value = removeSubmitBtn(params.formJson);
+      showOkBtn.value = true; // 濮嬬粓鏄剧ず纭畾鎸夐挳
+    } else {
+      dynamicFormRule.value = [];
+      showOkBtn.value = true;
+    }
+
+    // 缂栬緫妯″紡
+    if (params.id && params.update) {
+      console.log('缂栬緫妯″紡锛岃幏鍙栬鎯呮暟鎹�');
+      try {
+        // 鑾峰彇璇︽儏鏁版嵁
+        const record = await templateInfo(params.id);
+        console.log('鑾峰彇鍒扮殑璁板綍鏁版嵁:', record);
+        
+        // 璁剧疆鍔ㄦ�佽〃鍗曟暟鎹�
+        if (record.formData) {
+          try {
+            const dynamicData = JSON.parse(record.formData);
+            console.log('瑙f瀽鍚庣殑琛ㄥ崟鏁版嵁:', dynamicData);
+            
+            await nextTick();
+            if (formApi.value) {
+              console.log('鍑嗗璁剧疆琛ㄥ崟鏁版嵁鍒扮粍浠�');
+              // 浣跨敤 form-create 鐨� API 璁剧疆鍊�
+              Object.keys(dynamicData).forEach(key => {
+                formApi.value.setValue(key, dynamicData[key]);
+              });
+              console.log('琛ㄥ崟鏁版嵁璁剧疆瀹屾垚');
+            } else {
+              console.warn('琛ㄥ崟API鏈氨缁�');
+              message.error('琛ㄥ崟鏈氨缁紝璇烽噸璇�');
+            }
+          } catch (error) {
+            console.error('瑙f瀽鍔ㄦ�佸瓧娈垫暟鎹け璐�:', error);
+            message.error('鍔犺浇琛ㄥ崟鏁版嵁澶辫触');
+          }
+        } else {
+          console.log('璁板綍涓病鏈� formData 鏁版嵁');
+        }
+      } catch (error) {
+        console.error('澶勭悊琛ㄥ崟鏁版嵁澶辫触:', error);
+        message.error('鍔犺浇琛ㄥ崟鏁版嵁澶辫触锛岃閲嶈瘯');
+      }
+    } else {
+      // 鏂板鏃堕噸缃暟鎹�
+      console.log('鏂板妯″紡锛岄噸缃〃鍗�');
+      await nextTick();
+      if (formApi.value) {
+        formApi.value.resetFields && formApi.value.resetFields();
+      }
+    }
+  } catch (error) {
+    console.error('鎵撳紑寮圭獥澶辫触:', error);
+    message.error('鍔犺浇鏁版嵁澶辫触');
+  } finally {
+    modalLoading.value = false;
+  }
+};
+
+const close = () => {
+  modalVisible.value = false;
+};
+
+defineExpose({ open, close });
+
+function onFormMounted(api) {
+  formApi.value = api;
+}
+
+// handleOk 鍙礋璐hЕ鍙戣〃鍗� submit
+async function handleOk() {
+  await nextTick();
+  console.log('handleOk formApi:', formApi.value);
+  if (!dynamicFormRule.value || dynamicFormRule.value.length === 0) {
+    message.error('琛ㄥ崟瑙勫垯鏈姞杞斤紝鏃犳硶鎻愪氦');
+    return;
+  }
+  if (!formApi.value) {
+    message.error('琛ㄥ崟鏈覆鏌撳畬鎴愶紝璇风◢鍚庨噸璇�');
+    return;
+  }
+  try {
+    modalLoading.value = true;
+    const valid = await formApi.value.validate();
+    if (!valid) {
+      modalLoading.value = false;
+      return;
+    }
+    const formData = formApi.value.formData();
+    await onFormSubmit(formData);
+  } catch (error) {
+    console.error('淇濆瓨澶辫触:', error);
+    message.error('淇濆瓨澶辫触');
+    modalLoading.value = false;
+  }
+}
+
+// form-create 鐨� submit 浜嬩欢澶勭悊
+async function onFormSubmit(dynamicData: any) {
+  try {
+    modalLoading.value = true;
+    // 鏋勫缓淇濆瓨鏁版嵁
+    const finalData = {
+      pageId: currentPageId.value,
+      formData: Object.keys(dynamicData).length > 0 ? JSON.stringify(dynamicData) : undefined
+    };
+    if (isUpdate.value) {
+      finalData.id = currentEditId.value;
+    }
+    console.log(finalData)
+    await (isUpdate.value ? templateUpdate(finalData) : templateAdd(finalData));
+    emit('reload');
+    close();
+    message.success('淇濆瓨鎴愬姛');
+  } catch (error) {
+    console.error('淇濆瓨澶辫触:', error);
+    message.error('淇濆瓨澶辫触');
+  } finally {
+    modalLoading.value = false;
+  }
+}
+
+function handleCancel() {
+  close();
+}
+</script>
+
+<template>
+  <a-modal
+    v-model:open="modalVisible"
+    :title="title"
+    :width="'80vw'"
+    :confirm-loading="modalLoading"
+    @ok="handleOk"
+    @cancel="handleCancel"
+    :bodyStyle="{ padding: '24px', minHeight: '60vh' }"
+    destroyOnClose
+    :ok-button-props="{ style: showOkBtn ? {} : { display: 'none' } }"
+  > 
+    <!-- 鍔ㄦ�佽〃鍗曞尯鍩� -->
+    <form-create 
+      ref="dynamicFormRef"
+      :rule="dynamicFormRule"
+      :option="dynamicFormOption"
+      @submit="onFormSubmit"
+      @mounted="onFormMounted"
+    />
+  </a-modal>
+</template> 
\ No newline at end of file
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue b/ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue
new file mode 100644
index 0000000..d884f8b
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue
@@ -0,0 +1,13 @@
+<template>
+  <div>宸ヤ綔涓嬪彂</div>
+</template>
+
+<script>
+export default {
+  name: "index"
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue
new file mode 100644
index 0000000..7dabebf
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue
@@ -0,0 +1,13 @@
+<template>
+  <div>鎴戠殑宸ヤ綔</div>
+</template>
+
+<script>
+export default {
+  name: "index"
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/statistics/index.vue b/ruoyi-ui/apps/web-antd/src/views/work/statistics/index.vue
new file mode 100644
index 0000000..f5bd867
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/statistics/index.vue
@@ -0,0 +1,13 @@
+<template>
+<div>宸ヤ綔缁熻</div>
+</template>
+
+<script>
+export default {
+  name: "index"
+}
+</script>
+
+<style scoped>
+
+</style>

--
Gitblit v1.9.3