ソースを参照

移动端菜单优化 + 静态表单国际化 + 拦截器(sql/国际化)

晴为镜 2 ヶ月 前
コミット
0b618d8129
22 ファイル変更1142 行追加53 行削除
  1. 229 0
      zkqy-admin/src/main/java/com/zkqy/web/controller/api/ApiMobilePageDesignDataController.java
  2. 5 4
      zkqy-admin/src/main/java/com/zkqy/web/controller/homepagestatistics/StatisticsController.java
  3. 3 1
      zkqy-admin/src/main/java/com/zkqy/web/controller/mobilepage/MobilePageDesignDataController.java
  4. 5 0
      zkqy-admin/src/main/java/com/zkqy/web/controller/system/DataSourceController.java
  5. 1 0
      zkqy-admin/src/main/resources/i18n/messages.properties
  6. 5 0
      zkqy-admin/src/main/resources/i18n/messages_en_US.properties
  7. 8 0
      zkqy-admin/src/main/resources/i18n/messages_zh_CN.properties
  8. 79 0
      zkqy-common/src/main/java/com/zkqy/common/core/domain/TreeSelectStrId.java
  9. 64 0
      zkqy-common/src/main/java/com/zkqy/common/core/domain/TreeSelectValueLabel.java
  10. 19 0
      zkqy-common/src/main/java/com/zkqy/common/core/domain/TreeSelectWithParentId.java
  11. 66 0
      zkqy-common/src/main/java/com/zkqy/common/entity/TreeComponentDTO.java
  12. 54 0
      zkqy-common/src/main/java/com/zkqy/common/enums/LocaleEnum.java
  13. 164 0
      zkqy-common/src/main/java/com/zkqy/common/utils/TreeBuilder.java
  14. 24 1
      zkqy-framework/src/main/java/com/zkqy/framework/aspectj/SqlInterceptor.java
  15. 15 0
      zkqy-framework/src/main/java/com/zkqy/framework/config/LocaleConfig.java
  16. 2 1
      zkqy-framework/src/main/java/com/zkqy/framework/config/SecurityConfig.java
  17. 54 0
      zkqy-framework/src/main/java/com/zkqy/framework/interceptor/LocaleInterceptor.java
  18. 32 0
      zkqy-system/src/main/java/com/zkqy/system/entity/MobilePageDesignData.java
  19. 19 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/MobilePageDesignDataMapper.java
  20. 10 1
      zkqy-system/src/main/java/com/zkqy/system/service/IMobilePageDesignDataService.java
  21. 225 44
      zkqy-system/src/main/java/com/zkqy/system/service/impl/MobilePageDesignDataServiceImpl.java
  22. 59 1
      zkqy-system/src/main/resources/mapper/mobile/MobilePageDesignDataMapper.xml

+ 229 - 0
zkqy-admin/src/main/java/com/zkqy/web/controller/api/ApiMobilePageDesignDataController.java

@@ -0,0 +1,229 @@
+package com.zkqy.web.controller.api;
+
+import com.github.pagehelper.PageInfo;
+import com.zkqy.common.config.ZkqyConfig;
+import com.zkqy.common.core.controller.BaseController;
+import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.core.domain.TreeSelect;
+import com.zkqy.common.core.domain.TreeSelectStrId;
+import com.zkqy.common.core.domain.TreeSelectValueLabel;
+import com.zkqy.common.core.page.TableDataInfo;
+import com.zkqy.common.utils.CollectionUtil;
+import com.zkqy.common.utils.StringUtils;
+import com.zkqy.common.utils.TreeBuilder;
+import com.zkqy.common.utils.file.FileUploadUtils;
+import com.zkqy.common.utils.file.FileUtils;
+import com.zkqy.framework.config.ServerConfig;
+import com.zkqy.system.entity.CommonEntity;
+import com.zkqy.system.entity.MobilePageDesignData;
+import com.zkqy.system.entity.dto.MobilePageDesignDataNormalDTO;
+import com.zkqy.common.entity.TreeComponentDTO;
+import com.zkqy.system.mapper.CommonMapper;
+import com.zkqy.system.service.IMobilePageDesignDataService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.zkqy.common.core.domain.AjaxResult.success;
+
+/**
+ * 通用controller层,免授权使用。
+ * 需要在请求头上增加dbname用来指定查询库名。
+ */
+@RestController
+@RequestMapping("/api/mobilePageDesignData")
+@Api(value = "/api", description = "")
+public class ApiMobilePageDesignDataController extends BaseController {
+
+    @Autowired
+    private IMobilePageDesignDataService mobilePageDesignDataService;
+
+    @Autowired
+    private ServerConfig serverConfig;
+
+    /**
+     * 移动端 获取表格页面的数据(不对结果封装),界面预览中使用
+     * 入参 表名 需要分页的参数
+     */
+    @GetMapping(value = "/muti/tableLimitInfo")
+    @ApiOperation(value = "获取表格页面的数据")
+    public TableDataInfo getMutiInfoLimit(Long pageId){
+        if (pageId == 0l){
+            return new TableDataInfo();
+        }
+        MobilePageDesignData mobilePageDesignData = mobilePageDesignDataService.selectMobilePageDesignDataById(pageId);
+        startPage();
+        String sql = mobilePageDesignDataService.mapToQuerySql(mobilePageDesignData.getPageJson());
+        List<Map<String,Object>> list = mobilePageDesignDataService.executeQuerySql(sql);
+        List<Map<String, Object>> underlineList = CollectionUtil.copyListMapWithCamelToUnderline(list);
+        int total = new Long(new PageInfo(list).getTotal()).intValue();
+        return new TableDataInfo(underlineList,total,200,"获取成功");
+    }
+    /**
+     * 移动端 点击新增跳转的接口
+     * 入参 需要跳转的页面的页面id
+     * 返回值 需要跳转页面的html数据
+     */
+    @GetMapping("/queryMobileClickAddData/{formId}")
+    public AjaxResult queryMobileClickAddData(@PathVariable("formId") Long id){
+        MobilePageDesignData mobilePageDesignData = mobilePageDesignDataService.selectMobilePageDesignDataById(id);
+        return success(mobilePageDesignData);
+    }
+
+    /**
+     * 移动端 点击修改跳转的接口
+     * 入参 表单页面的id,需要跳转到的页面id,主键id(这一行数据的主键id)
+     * 返回值 需要跳转页面的html数据(查询数据并保存在html中)
+     */
+    @GetMapping("/queryMobileClickUpdateData/{pageId}/{formId}/{searchId}")
+    public AjaxResult queryMobileClickUpdateData(@PathVariable("pageId") Long pageId,@PathVariable("formId") Long formId,@PathVariable("searchId") Long searchId){
+        MobilePageDesignData fromDesignData = mobilePageDesignDataService.selectMobilePageDesignDataById(pageId);
+        MobilePageDesignData toDesignData = mobilePageDesignDataService.selectMobilePageDesignDataById(formId);
+        String htmlData = mobilePageDesignDataService.fillUpdateJsonPageData(fromDesignData,toDesignData,searchId);
+        toDesignData.setHtmlData(htmlData);
+        return success(toDesignData);
+    }
+    /**
+     * 移动端 新增数据的接口
+     * 入参 需要新增的该行数据 页面id
+     * 返回值 判断是否新增成功
+     */
+    @PostMapping("/normal/insertData")
+    public AjaxResult normalInsertData(@RequestBody MobilePageDesignDataNormalDTO mobilePageDesignDataInsertDTO){
+        // 根据id获取表名或者是直接传表名 后期选一个
+        Long pageId = mobilePageDesignDataInsertDTO.getPageId();
+        String sql = mobilePageDesignDataService.mapToInsertSql(mobilePageDesignDataInsertDTO.getDataMap(),pageId);
+        if (StringUtils.isBlank(sql)){
+            return error("新增失败,请联系管理员");
+        }
+        return success(mobilePageDesignDataService.executeInsertSql(sql));
+    }
+
+    /**
+     * 移动端 保存数据的接口
+     * 入参 需要新增的该行数据
+     * 返回值 判断是否保存成功
+     */
+    @PostMapping("/normal/updateData")
+    public AjaxResult normalUpdateData(@RequestBody MobilePageDesignDataNormalDTO mobilePageDesignDataInsertDTO){
+        Long pageId = mobilePageDesignDataInsertDTO.getPageId();
+        Map<String, Object> dataMap = mobilePageDesignDataInsertDTO.getDataMap();
+        String sql = mobilePageDesignDataService.mapToUpdateSql2(dataMap,pageId);
+        return success(mobilePageDesignDataService.executeUpdateSql(sql));
+    }
+
+    /**
+     * 移动端 通用删除接口
+     * 入参 页面id 该行的id
+     * 返回值 判断是否删除成功
+     * 整体逻辑和修改差不多,因为是逻辑删除
+     */
+    @PostMapping("/normal/deleteData")
+    public AjaxResult normalDeleteData(@RequestBody MobilePageDesignDataNormalDTO mobilePageDesignDataInsertDTO){
+        // 根据id获取表名或者是直接传表名 后期选一个
+        Long pageId = mobilePageDesignDataInsertDTO.getPageId();
+        String sql = mobilePageDesignDataService.mapToDeleteSql(mobilePageDesignDataInsertDTO.getLineId(),pageId);
+        if (StringUtils.isBlank(sql)){
+            return error("删除失败,请联系管理员");
+        }
+        return success(mobilePageDesignDataService.executeUpdateSql(sql));
+    }
+
+    /**
+     * 树形节点数据的返回
+     * 树形因为要区分上下级关系,所以所有的value都要化成string类型的。id值的中间用@符号进行分隔,区分联层
+     * @return
+     */
+    @PostMapping("/tree")
+    public AjaxResult get(@RequestBody TreeComponentDTO treeComponentDTO){
+        String treeType = treeComponentDTO.getTreeType();
+        String componentType = treeComponentDTO.getComponentType();
+        Boolean paramJudge = StringUtils.isNotBlank(treeType) && StringUtils.isNotBlank(componentType);
+        List<TreeSelectStrId> ts = new ArrayList<>();
+        // 同表父子节点树 没有上下级的关系,所以肯定没有@
+        if (paramJudge && treeType.equals("singleTable")){
+            ts = mobilePageDesignDataService.getSingleTableTree(treeComponentDTO);
+        // 主子表节点树
+        }else if(StringUtils.isNotBlank(treeType) && treeType.equals("multiTable")){
+            ts = mobilePageDesignDataService.getMultiTableTree(treeComponentDTO);
+        }
+        // 树形直接返回
+        if (componentType.equals("tree")){
+            return AjaxResult.success(ts);
+        //如果是连接需要转换成label和value的形式
+        }else{
+            List<TreeSelectValueLabel> treeSelectValueLabels = mobilePageDesignDataService.convertTreeStrIdToValueLabel(ts);
+            return AjaxResult.success(treeSelectValueLabels);
+        }
+
+    }
+
+    /**
+     * 下拉框数据返回
+     * @return
+     */
+    @GetMapping("/dropdown")
+    public AjaxResult getTableValue(String tableName,String valueName,String labelName,String type){
+        if (StringUtils.isNotBlank(tableName) && StringUtils.isNotBlank(valueName) && StringUtils.isNotBlank(labelName) && StringUtils.isNotBlank(type)){
+            // 穿梭框
+            String columnName = "`value`";
+            if (type.equals("elTransfer")){
+                columnName = "`key`";
+            }
+            String sql = "select " + valueName + " as " + columnName + ", " + labelName + " as label " + " from " + "{DBNAME}."+tableName + " where del_flag = '0'";
+            List<Map<String, Object>> list = mobilePageDesignDataService.executeQuerySql(sql);
+            list = list.stream().filter(stringObjectMap -> stringObjectMap != null).collect(Collectors.toList());
+            List<Map<String, Object>> underlineList = CollectionUtil.copyListMapWithCamelToUnderline(list);
+//            for (int i = 0; i < underlineList.size(); i++) {
+//                Map<String, Object> stringObjectMap = underlineList.get(i);
+//                if (stringObjectMap.get("value") != null){
+//                    stringObjectMap.put("value",stringObjectMap.get("value").toString());
+//                }
+//            }
+            return AjaxResult.success(underlineList);
+        }
+        return null;
+    }
+    /**
+     * 通用上传请求(单个)
+     */
+    @PostMapping("/upload")
+    public AjaxResult uploadFile(MultipartFile file) throws Exception {
+        try {
+            // 上传文件路径
+            String filePath = ZkqyConfig.getUploadPath();
+            // 上传并返回新文件名称
+            String fileName = FileUploadUtils.upload(filePath, file);
+            String url = serverConfig.getUrl() + fileName;
+            AjaxResult ajax = success();
+            ajax.put("url", url);
+            ajax.put("fileName", fileName);
+            ajax.put("newFileName", FileUtils.getName(fileName));
+            ajax.put("originalFilename", file.getOriginalFilename());
+            return ajax;
+        } catch (Exception e) {
+            return AjaxResult.error(e.getMessage());
+        }
+    }
+    @PostMapping("/getTableId")
+    public AjaxResult getTableId(Long pageId){
+        Long tableId = 0l;
+        MobilePageDesignData mobilePageDesignData = mobilePageDesignDataService.selectMobilePageDesignDataById(pageId);
+        if (mobilePageDesignData != null && mobilePageDesignData.getTableId() != null){
+            tableId = mobilePageDesignData.getTableId();
+        }
+        return AjaxResult.success(tableId);
+    }
+
+}

+ 5 - 4
zkqy-admin/src/main/java/com/zkqy/web/controller/homepagestatistics/StatisticsController.java

@@ -2,6 +2,7 @@ package com.zkqy.web.controller.homepagestatistics;
 
 import com.zkqy.common.config.datasource.constant.DataSourceType;
 import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.utils.MessageUtils;
 import com.zkqy.common.utils.SecurityUtils;
 import com.zkqy.system.entity.DragForm;
 import com.zkqy.system.entity.DragFormGroup;
@@ -64,22 +65,22 @@ public class StatisticsController {
 
         List<Map> list=new ArrayList<>();
         HashMap dragFormsMap=new HashMap();
-        dragFormsMap.put("name","表单");
+        dragFormsMap.put("name",MessageUtils.message("form"));
         dragFormsMap.put("value",dragFormsCount);
         list.add(dragFormsMap);
 
         HashMap dragTableSMap=new HashMap();
-        dragTableSMap.put("name","表格");
+        dragTableSMap.put("name",MessageUtils.message("table"));
         dragTableSMap.put("value",dragTablesCount);
         list.add(dragTableSMap);
 
         HashMap dragTableGroupsSMap=new HashMap();
-        dragTableGroupsSMap.put("name","三级联动");
+        dragTableGroupsSMap.put("name",MessageUtils.message("threeLevelLinkage"));
         dragTableGroupsSMap.put("value",dragTableGroupsCount);
         list.add(dragTableGroupsSMap);
 
         HashMap dragFromGroupMap=new HashMap();
-        dragFromGroupMap.put("name","表单组");
+        dragFromGroupMap.put("name",MessageUtils.message("formGroup"));
         dragFromGroupMap.put("value",dragFromGroupCount);
         list.add(dragFromGroupMap);
         String aa="11";

+ 3 - 1
zkqy-admin/src/main/java/com/zkqy/web/controller/mobilepage/MobilePageDesignDataController.java

@@ -101,10 +101,12 @@ public class MobilePageDesignDataController extends BaseController
     public AjaxResult add(@RequestBody MobilePageDesignData mobilePageDesignData)
     {
         int i = mobilePageDesignDataService.insertMobilePageDesignData(mobilePageDesignData);
-        //在pageOption和pageHtml中需要把id放进去
+        //在pageOption和pageHtml中需要把id放进去,为新增和修改的表单的tableId放进去
         if (i > 0){
             mobilePageDesignDataService.fillPageIdToHtmlData(mobilePageDesignData);
             mobilePageDesignDataService.updateMobilePageDesignData(mobilePageDesignData);
+            List<Long> tableIds = mobilePageDesignData.getTableIds();
+            mobilePageDesignDataService.fillTableIdToForm(i,tableIds);
         }
         return toAjax(i);
     }

+ 5 - 0
zkqy-admin/src/main/java/com/zkqy/web/controller/system/DataSourceController.java

@@ -9,6 +9,7 @@ import com.zkqy.system.service.IDataSourceService;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -43,6 +44,10 @@ public class DataSourceController {
      */
     @PostMapping("/getInfoTable")
     public List<TableInfo> queryTableInfo(@RequestBody Map<String, Object> map) {
+        if (map.get("tableName") == null){
+            return new ArrayList<TableInfo>();
+        }
         return dataSourceService.tableFieldInfo(map);
     }
+
 }

+ 1 - 0
zkqy-admin/src/main/resources/i18n/messages.properties

@@ -9,6 +9,7 @@ user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分
 user.password.delete=对不起,您的账号已被删除
 user.blocked=用户已封禁,请联系管理员
 role.blocked=角色已封禁,请联系管理员
+login.blocked=很遗憾,访问IP已被列入系统黑名单
 user.logout.success=退出成功
 
 length.not.valid=长度必须在{min}到{max}个字符之间

+ 5 - 0
zkqy-admin/src/main/resources/i18n/messages_en_US.properties

@@ -0,0 +1,5 @@
+form=form
+table=table
+threeLevelLinkage=threeLevelLinkage
+formGroup=formGroup
+multiTableQuantity=multiTableQuantity

+ 8 - 0
zkqy-admin/src/main/resources/i18n/messages_zh_CN.properties

@@ -0,0 +1,8 @@
+# messages_zh_CN.properties
+form=\u8868\u5355
+table=\u8868\u683C
+threeLevelLinkage=\u4E09\u7EA7\u8054\u52A8
+formGroup=\u8868\u5355\u7EC4
+multiTableQuantity=\u591A\u8868\u6570\u91CF
+client.side=\u5BA2\u6237\u7AEF
+tool.side=\u5DE5\u5177\u7AEF

+ 79 - 0
zkqy-common/src/main/java/com/zkqy/common/core/domain/TreeSelectStrId.java

@@ -0,0 +1,79 @@
+package com.zkqy.common.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.zkqy.common.core.domain.entity.SysDept;
+import com.zkqy.common.core.domain.entity.SysMenu;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Treeselect树结构实体类
+ * 
+ * @author zkqy
+ */
+public class TreeSelectStrId implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 节点ID */
+    private String id;
+
+    /** 节点名称 */
+    private String label;
+
+
+    /** 子节点 */
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    private List<TreeSelectStrId> children;
+
+    public TreeSelectStrId()
+    {
+
+    }
+
+    public TreeSelectStrId(SysDept dept)
+    {
+        this.id = dept.getDeptId().toString();
+        this.label = dept.getDeptName();
+        this.children = dept.getChildren().stream().map(TreeSelectStrId::new).collect(Collectors.toList());
+    }
+
+    public TreeSelectStrId(SysMenu menu)
+    {
+        this.id = menu.getMenuId().toString();
+        this.label = menu.getMenuName();
+        this.children = menu.getChildren().stream().map(TreeSelectStrId::new).collect(Collectors.toList());
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public void setId(String id)
+    {
+        this.id = id;
+    }
+
+    public String getLabel()
+    {
+        return label;
+    }
+
+    public void setLabel(String label)
+    {
+        this.label = label;
+    }
+
+    public List<TreeSelectStrId> getChildren()
+    {
+        return children;
+    }
+
+    public void setChildren(List<TreeSelectStrId> children)
+    {
+        this.children = children;
+    }
+}

+ 64 - 0
zkqy-common/src/main/java/com/zkqy/common/core/domain/TreeSelectValueLabel.java

@@ -0,0 +1,64 @@
+package com.zkqy.common.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.zkqy.common.core.domain.entity.SysDept;
+import com.zkqy.common.core.domain.entity.SysMenu;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Treeselect树结构实体类
+ * 
+ * @author zkqy
+ */
+public class TreeSelectValueLabel implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 节点ID */
+    private String value;
+
+    /** 节点名称 */
+    private String label;
+
+
+    /** 子节点 */
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    private List<TreeSelectValueLabel> children;
+
+    public TreeSelectValueLabel()
+    {
+
+    }
+
+
+    public String getLabel()
+    {
+        return label;
+    }
+
+    public void setLabel(String label)
+    {
+        this.label = label;
+    }
+
+    public List<TreeSelectValueLabel> getChildren()
+    {
+        return children;
+    }
+
+    public void setChildren(List<TreeSelectValueLabel> children)
+    {
+        this.children = children;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}

+ 19 - 0
zkqy-common/src/main/java/com/zkqy/common/core/domain/TreeSelectWithParentId.java

@@ -0,0 +1,19 @@
+package com.zkqy.common.core.domain;
+
+/**
+ * Treeselect树结构实体类 继承了树,增加了parentId
+ * 
+ * @author zkqy
+ */
+public class TreeSelectWithParentId  extends TreeSelectStrId
+{
+    private String parentId;
+
+    public String getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(String parentId) {
+        this.parentId = parentId;
+    }
+}

+ 66 - 0
zkqy-common/src/main/java/com/zkqy/common/entity/TreeComponentDTO.java

@@ -0,0 +1,66 @@
+package com.zkqy.common.entity;
+
+public class TreeComponentDTO {
+    private String treeType;
+    private String componentType;
+    private String tableName;
+    private String showValue;
+    private String primaryIdField;
+    private String parentIdField;
+    private TreeComponentDTO childrenTree;
+    public String getTreeType() {
+        return treeType;
+    }
+
+    public void setTreeType(String treeType) {
+        this.treeType = treeType;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+    public String getShowValue() {
+        return showValue;
+    }
+
+    public void setShowValue(String showValue) {
+        this.showValue = showValue;
+    }
+
+    public String getPrimaryIdField() {
+        return primaryIdField;
+    }
+
+    public void setPrimaryIdField(String primaryIdField) {
+        this.primaryIdField = primaryIdField;
+    }
+
+    public String getParentIdField() {
+        return parentIdField;
+    }
+
+    public void setParentIdField(String parentIdField) {
+        this.parentIdField = parentIdField;
+    }
+
+    public TreeComponentDTO getChildrenTree() {
+        return childrenTree;
+    }
+
+    public void setChildrenTree(TreeComponentDTO childrenTree) {
+        this.childrenTree = childrenTree;
+    }
+
+    public String getComponentType() {
+        return componentType;
+    }
+
+    public void setComponentType(String componentType) {
+        this.componentType = componentType;
+    }
+}

+ 54 - 0
zkqy-common/src/main/java/com/zkqy/common/enums/LocaleEnum.java

@@ -0,0 +1,54 @@
+package com.zkqy.common.enums;
+
+public enum LocaleEnum {
+
+    Chinese("中国", "zh_CN"),
+    English("美国", "en_US");
+
+    private final String chineseName;
+    private final String localeCode;
+
+    LocaleEnum(String chineseName, String localeCode) {
+        this.chineseName = chineseName;
+        this.localeCode = localeCode;
+    }
+
+    /**
+     * 获取中文名称
+     */
+    public String getChineseName() {
+        return chineseName;
+    }
+
+    /**
+     * 获取地区代码
+     */
+    public String getLocaleCode() {
+        return localeCode;
+    }
+
+
+    /**
+     * 通过地区代码查找枚举
+     */
+    public static LocaleEnum fromLocaleCode(String localeCode) {
+        for (LocaleEnum locale : values()) {
+            if (locale.localeCode.equalsIgnoreCase(localeCode)) {
+                return locale;
+            }
+        }
+        throw new IllegalArgumentException("Unknown locale code: " + localeCode);
+    }
+
+    /**
+     * 通过中文名称查找枚举
+     */
+    public static LocaleEnum fromChineseName(String chineseName) {
+        for (LocaleEnum locale : values()) {
+            if (locale.chineseName.equals(chineseName)) {
+                return locale;
+            }
+        }
+        throw new IllegalArgumentException("Unknown Chinese name: " + chineseName);
+    }
+}

+ 164 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/TreeBuilder.java

@@ -0,0 +1,164 @@
+package com.zkqy.common.utils;
+
+import com.zkqy.common.core.domain.TreeSelect;
+import com.zkqy.common.core.domain.TreeSelectStrId;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class TreeBuilder {
+
+    // 递归地找到所有子节点并构建树形结构
+    public static List<Map<String, ?>> buildTree(List<Map<String, Object>> allNodes, String parentId,String tableId,String treeTableDgl) {
+        return allNodes.stream()
+                .filter(node ->Objects.equals(convertToString(parentId), convertToString(node.get(treeTableDgl))))
+                .map(parentNode -> {
+                     // 确保 tableId 对应的值被正确地转换为字符串
+                    Object tableIdObj = parentNode.get(tableId);
+                    String tableIdStr = convertToString(tableIdObj);
+                    List<Map<String, ?>> children = buildTree(allNodes, tableIdStr, tableId,treeTableDgl);
+                    if (!children.isEmpty()) {
+                        // 将子节点列表添加到当前节点
+                        Map<String, Object> enrichedParentNode = new HashMap<>(parentNode);
+                        enrichedParentNode.put("children", children);
+                        return enrichedParentNode;
+                    }
+                    return parentNode;
+                })
+                .collect(Collectors.toList());
+    }
+    public static List<TreeSelectStrId> convert(List<Map<String, ?>> maps) {
+        List<TreeSelectStrId> treeSelects = new ArrayList<>();
+        if (maps == null) {
+            return treeSelects;
+        }
+
+        for (Map<String, ?> map : maps) {
+            TreeSelectStrId treeSelect = convertMapToTreeSelect(map);
+            if (treeSelect != null) {
+                treeSelects.add(treeSelect);
+            }
+        }
+
+        return treeSelects;
+    }
+
+    private static TreeSelectStrId convertMapToTreeSelect(Map<String, ?> map) {
+        if (map == null) {
+            return null;
+        }
+
+        TreeSelectStrId treeSelect = new TreeSelectStrId();
+
+        // 设置id
+        Object idObj = map.get("id");
+        treeSelect.setId(idObj.toString());
+        // 设置label
+        Object labelObj = map.get("label");
+        if (labelObj != null) {
+            treeSelect.setLabel(labelObj.toString());
+        }
+
+        // 递归处理children
+        Object childrenObj = map.get("children");
+        if (childrenObj instanceof List) {
+            List<Map<String, ?>> childrenMaps = (List<Map<String, ?>>) childrenObj;
+            List<TreeSelectStrId> children = convert(childrenMaps);
+            treeSelect.setChildren(children);
+        }
+
+        return treeSelect;
+    }
+
+    // 递归的找到所有本节点和子节点的id
+    public static List<String> getTreeIdList(List<Map<String, String>> allNodes, String parentId,String tableId,String treeTableDgl,List<String> resIds) {
+        resIds.add(parentId);
+         allNodes.stream()
+                .filter(node ->Objects.equals(convertToString(parentId), convertToString(node.get(treeTableDgl))))
+                .map(parentNode -> {
+                    // 确保 tableId 对应的值被正确地转换为字符串
+                    Object tableIdObj = parentNode.get(tableId);
+                    String tableIdStr = convertToString(tableIdObj);
+                    System.out.println(tableIdStr);
+                    getTreeIdList(allNodes, tableIdStr, tableId,treeTableDgl,resIds);
+                    return parentNode;
+                })
+                .collect(Collectors.toList());
+        List<String> resIdList = resIds.stream().distinct().collect(Collectors.toList());
+        return resIdList;
+    }
+
+    private static String convertToString(Object obj) {
+        if (obj instanceof Integer) {
+            return ((Integer) obj).toString();
+        } else if (obj instanceof Long) {
+            return ((Long) obj).toString();
+        } else if (obj instanceof String) {
+            return (String) obj;
+        } else {
+            throw new IllegalArgumentException("Unsupported type: " + obj.getClass().getName());
+        }
+    }
+
+
+    private static TreeSelect convertToTreeSelect(Map<String, Object> node) {
+        TreeSelect treeNode = new TreeSelect();
+        treeNode.setId(Long.parseLong(node.get("id").toString()));
+        treeNode.setLabel(node.get("label").toString());
+        return treeNode;
+    }
+
+
+    public static void main(String[] args) {
+        // 示例数据: 假设这是从数据库获取的数据
+        // 示例数据: 假设这是从数据库获取的数据
+        List<Map<String, String>> mapList = Arrays.asList(
+                new HashMap<String, String>() {{
+                    put("id", "1");
+                    put("name", "根节点");
+                    put("parentId", "0"); // 根节点
+                }},
+                new HashMap<String, String>() {{
+                    put("id", "2");
+                    put("name", "二级节点");
+                    put("parentId", "1"); // 子节点
+                }},
+                new HashMap<String, String>() {{
+                    put("id", "3");
+                    put("name", "二级节点");
+                    put("parentId", "1"); // 子节点
+                }},
+                new HashMap<String, String>() {{
+                    put("id", "4");
+                    put("name", "三级节点");
+                    put("parentId", "2"); // 子节点的子节点
+                }});
+
+        // 构建树形结构
+        ArrayList<String> strings = new ArrayList<>();
+//        List<String> treeIdList = getTreeIdList(mapList, "2", "id", "parentId", strings);
+//        List<Map<String, ?>> tree = buildTree(mapList, "1","id","parentId");
+        System.out.println();
+//        String jsonString = JSON.toJSONString(tree);
+//        System.out.println(jsonString);
+        // 打印结果
+//        printTree(tree);
+    }
+
+    private static void printTree(List<Map<String, ?>> tree) {
+        for (Map<String, ?> node : tree) {
+            System.out.println(node.get("id")+"---"+node.get("name"));
+            if (node.containsKey("children")) {
+                //noinspection unchecked
+                printTree((List<Map<String, ?>>) node.get("children"));
+            }
+        }
+        Integer a=new Integer(1);
+        String string = a.toString();
+
+    }
+
+    /**
+     * [{"name":"根节点","id":"1","children":[{"name":"二级节点","id":"2","children":[{"name":"三级节点","id":"4","parentId":"2"}],"parentId":"1"},{"name":"二级节点","id":"3","parentId":"1"}]}]
+     */
+}

+ 24 - 1
zkqy-framework/src/main/java/com/zkqy/framework/aspectj/SqlInterceptor.java

@@ -4,10 +4,14 @@ package com.zkqy.framework.aspectj;
 import com.zkqy.common.core.domain.AjaxResult;
 import com.zkqy.common.exception.tenantdatassource.TenantDataSource;
 import com.zkqy.common.utils.SecurityUtils;
+import com.zkqy.common.utils.StringUtils;
 import com.zkqy.common.utils.reflect.ReflectUtils;
 import org.apache.ibatis.executor.statement.StatementHandler;
 import org.apache.ibatis.plugin.*;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
 
+import javax.servlet.http.HttpServletRequest;
 import java.sql.Connection;
 import java.util.Properties;
 
@@ -50,11 +54,30 @@ public class SqlInterceptor implements Interceptor {
 
     // 根据类型设置不同的选择数据源格式
     private String modifySql(String sql) {
+        // 优先级别为 请求头包含权限认证 > 请求头里面包含dbname作为免登录的库名使用
+        String headerValue = null;
         try {
             SecurityUtils.getDatabaseType();
         } catch (Exception e) {
-            return "error";
+            // 查询请求头中是否有dbname
+            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            if (attributes != null) {
+                HttpServletRequest request = attributes.getRequest();
+                headerValue = request.getHeader("dbname");
+            }
+            if (StringUtils.isBlank(headerValue)){
+                return "error";
+            }
         }
+        if (StringUtils.isNotBlank(headerValue)){
+            if (sql.contains("{DBNAME}.")) {
+                return sql.replace("{DBNAME}.", "`" + headerValue + "`.");
+            }else{
+                //如果带请求头dbname的,必须包含{DBNAME}.
+                return "error";
+            }
+        }
+
         if (SecurityUtils.getDatabaseType().equals("sqlserver"))
             return "USE `" + SecurityUtils.getDatabaseName() + "`; " + sql;
         if (SecurityUtils.getDatabaseType().equals("dm"))

+ 15 - 0
zkqy-framework/src/main/java/com/zkqy/framework/config/LocaleConfig.java

@@ -0,0 +1,15 @@
+package com.zkqy.framework.config;
+
+import com.zkqy.framework.interceptor.LocaleInterceptor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class LocaleConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(new LocaleInterceptor()).addPathPatterns("/**");
+    }
+}

+ 2 - 1
zkqy-framework/src/main/java/com/zkqy/framework/config/SecurityConfig.java

@@ -113,7 +113,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/login", "/register", "/captchaImage").permitAll()
                 // 静态资源,可匿名访问
                 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
-                .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**","/dataSource/**").permitAll()
+                .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**","/dataSource/**",
+                "/api/**").permitAll()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
                 .and()

+ 54 - 0
zkqy-framework/src/main/java/com/zkqy/framework/interceptor/LocaleInterceptor.java

@@ -0,0 +1,54 @@
+package com.zkqy.framework.interceptor;
+
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Locale;
+
+public class LocaleInterceptor implements HandlerInterceptor {
+
+    private static final String DEFAULT_LANGUAGE = "zh_CN";
+    private static final String LANGUAGE_COOKIE_NAME = "language";
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+        // 1. 优先从Header中获取language
+        String language = getLanguageFromHeader(request);
+
+        // 2. 如果没有,使用默认值
+        if (language == null || language.isEmpty()) {
+            language = DEFAULT_LANGUAGE;
+        }
+
+        // 4. 解析语言和国家代码
+        Locale locale = parseLocale(language);
+
+        // 5. 设置到LocaleContextHolder
+        LocaleContextHolder.setLocale(locale);
+
+        return true;
+    }
+
+    private String getLanguageFromHeader(HttpServletRequest request) {
+        return request.getHeader("language"); // 直接从Header获取language字段
+    }
+
+    private Locale parseLocale(String language) {
+        if (language == null || language.isEmpty()) {
+            return Locale.SIMPLIFIED_CHINESE; // 默认简体中文
+        }
+
+        // 示例:解析 "en_US" 或 "zh_CN"
+        String[] parts = language.replace("-", "_").split("_");
+        if (parts.length == 1) {
+            return new Locale(parts[0]); // 只有语言代码(如 "en")
+        } else if (parts.length == 2) {
+            return new Locale(parts[0], parts[1]); // 语言 + 国家(如 "en_US")
+        } else {
+            return Locale.SIMPLIFIED_CHINESE; // 默认
+        }
+    }
+}

+ 32 - 0
zkqy-system/src/main/java/com/zkqy/system/entity/MobilePageDesignData.java

@@ -5,6 +5,8 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.zkqy.common.annotation.Excel;
 
+import java.util.List;
+
 /**
  * 新新页面设计对象 mobile_page_design_data
  * 
@@ -41,6 +43,9 @@ public class MobilePageDesignData extends BaseEntity
     @Excel(name = "html数据")
     private String htmlData;
 
+    @Excel(name = "节点数存储字段")
+    private String multipleTreeParam;
+
     /** 创建者id */
     @Excel(name = "创建者id")
     private Long createById;
@@ -68,6 +73,10 @@ public class MobilePageDesignData extends BaseEntity
     @Excel(name = "任务节点编码")
     private String taskNodeKey;
 
+    private Long tableId;
+
+    private List<Long> tableIds;
+
     public void setId(Long id) 
     {
         this.id = id;
@@ -216,4 +225,27 @@ public class MobilePageDesignData extends BaseEntity
         this.htmlData = htmlData;
     }
 
+    public String getMultipleTreeParam() {
+        return multipleTreeParam;
+    }
+
+    public void setMultipleTreeParam(String multipleTreeParam) {
+        this.multipleTreeParam = multipleTreeParam;
+    }
+
+    public List<Long> getTableIds() {
+        return tableIds;
+    }
+
+    public void setTableIds(List<Long> tableIds) {
+        this.tableIds = tableIds;
+    }
+
+    public Long getTableId() {
+        return tableId;
+    }
+
+    public void setTableId(Long tableId) {
+        this.tableId = tableId;
+    }
 }

+ 19 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/MobilePageDesignDataMapper.java

@@ -3,6 +3,9 @@ package com.zkqy.system.mapper;
 import java.util.List;
 import java.util.Map;
 
+import com.zkqy.common.core.domain.TreeSelectStrId;
+import com.zkqy.common.core.domain.TreeSelectWithParentId;
+import com.zkqy.common.entity.TreeComponentDTO;
 import com.zkqy.system.entity.MobilePageDesignData;
 import org.apache.ibatis.annotations.Param;
 
@@ -66,4 +69,20 @@ public interface MobilePageDesignDataMapper
     int executeUpdateSql(@Param("executeSql") String executeSql);
     int executeInsertSql(@Param("executeSql") String executeSql);
     int executeDeleteSql(@Param("executeSql") String executeSql);
+
+    List<Map<String, Object>> selectDynamicTreeData(@Param("tableName") String tableName, @Param("showValue") String showValue, @Param("parentIdField") String parentIdField, @Param("primaryIdField") String primaryIdField);
+
+    List<Map<String, Object>> getSingleTableTree(@Param("tableName") String tableName, @Param("primaryIdField") String primaryIdField, @Param("parentIdField") String parentIdField, @Param("showValue") String showValue);
+
+    /**
+     * 查询主表节点
+     */
+    List<TreeSelectStrId> selectRootNodes(@Param("dto") TreeComponentDTO dto);
+
+    /**
+     * 查询子节点
+     */
+    List<TreeSelectWithParentId> selectChildNodes(@Param("dto") TreeComponentDTO dto, @Param("parentIds") List<String> parentIds);
+
+    void fillTableIdToForm(@Param("id") int id, @Param("tableIds") List<Long> tableIds);
 }

+ 10 - 1
zkqy-system/src/main/java/com/zkqy/system/service/IMobilePageDesignDataService.java

@@ -3,8 +3,10 @@ package com.zkqy.system.service;
 import java.util.List;
 import java.util.Map;
 
+import com.zkqy.common.core.domain.TreeSelectStrId;
+import com.zkqy.common.core.domain.TreeSelectValueLabel;
 import com.zkqy.system.entity.MobilePageDesignData;
-import com.zkqy.system.entity.MobilePageNavigationBar;
+import com.zkqy.common.entity.TreeComponentDTO;
 
 /**
  * 新新页面设计Service接口
@@ -84,4 +86,11 @@ public interface IMobilePageDesignDataService
 
     void fillPageIdToHtmlData(MobilePageDesignData mobilePageDesignData);
 
+    List<TreeSelectStrId> getSingleTableTree(TreeComponentDTO treeComponentDTO);
+
+    List<TreeSelectStrId> getMultiTableTree(TreeComponentDTO treeComponentDTO);
+
+    List<TreeSelectValueLabel> convertTreeStrIdToValueLabel(List<TreeSelectStrId> originalTree);
+
+    void fillTableIdToForm(int i, List<Long> tableIds);
 }

+ 225 - 44
zkqy-system/src/main/java/com/zkqy/system/service/impl/MobilePageDesignDataServiceImpl.java

@@ -4,17 +4,25 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
 import java.util.*;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONException;
 import com.alibaba.fastjson2.JSONObject;
+import com.zkqy.common.core.domain.TreeSelectStrId;
+import com.zkqy.common.core.domain.TreeSelectValueLabel;
+import com.zkqy.common.core.domain.TreeSelectWithParentId;
 import com.zkqy.common.utils.CollectionUtil;
 import com.zkqy.common.utils.DateUtils;
 import com.zkqy.common.utils.StringUtils;
+import com.zkqy.common.entity.TreeComponentDTO;
+import com.zkqy.common.utils.TreeBuilder;
 import com.zkqy.system.entity.vo.MobilePageDesignDataSubTableVo;
+import org.apache.commons.lang3.StringEscapeUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -152,15 +160,21 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
     public String fillUpdateJsonPageData(MobilePageDesignData fromDesignData,MobilePageDesignData toDesignData, Long searchId) {
         //得到列名 + 表名
         String primaryTable = findPrimaryTableByPageJson(fromDesignData.getPageJson());
-        String querySqlWhere = String.format(" and %s.id = %d",primaryTable,searchId);
         String pageJson = toDesignData.getPageJson();
+        String pageJsonFrom = fromDesignData.getPageJson();
+        JSONArray columnsFrom = JSONArray.parseArray(pageJsonFrom);
+        JSONArray columnsFromRes = null;
+        for (JSONObject pageConfig : columnsFrom.toJavaList(JSONObject.class)) {
+            columnsFromRes = pageConfig.getJSONArray("columns");
+        }
         JSONArray columns = JSONArray.parseArray(pageJson);
+
         // 构建SELECT部分
         String selectSql = buildSelectClause(columns);
         // 构建FROM和JOIN部分
-        String fromSql = buildFromClause(columns);
+        String fromSql = buildFromClause(columnsFromRes);
         // 构建where部分
-        String whereSql = " where " + primaryTable +".del_flag='0'";
+        String whereSql = " where " + primaryTable + ".del_flag='0' and "+ primaryTable + ".id =" + searchId;
         String querySql = selectSql + " " + fromSql + whereSql ;
 
         if (StringUtils.isBlank(querySql)){
@@ -196,18 +210,28 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
                     if (field == null || field.equals("")){
                         continue;
                     }
-                    //从结果的map中遍历,得到结果
-//                    for (Map<String, String> map : maps) {
                     Object fieldValue = maps.get(field);
                     if (fieldValue != null && !String.valueOf(fieldValue).equals("")&& StringUtils.isNotBlank(String.valueOf(fieldValue))){
-                        jsonObject.put("value",fieldValue);
+                        String strValue = String.valueOf(fieldValue);
+                        try {
+                            // 尝试解析成 JSONArray
+                            if (strValue.startsWith("[") && strValue.endsWith("]")) {
+                                JSONArray array = JSON.parseArray(strValue);            // 按逗号分割
+                                jsonObject.put("value", array);
+                            } else {
+                                jsonObject.put("value", fieldValue);
+                            }
+                        } catch (JSONException e) {
+                            // 如果不是合法的 JSON 数组,直接存原值
+                            jsonObject.put("value", fieldValue);
+                        }
                     }
-//                    }
                 }
                 //替换html的rule数据
+
                 htmlDecode = htmlDecode.replace(
                         "rule: formCreate.parseJson('" + oldRuleJson + "')",
-                        "rule: formCreate.parseJson('" + jsonArray + "')"
+                        "rule: formCreate.parseJson('" + StringEscapeUtils.unescapeJava(jsonArray.toString()) + "')"
                 );
             }
         }
@@ -255,6 +279,7 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
             //构建 构建insert
             StringBuilder sqlBuilder = new StringBuilder();
             Map<String, Object> primaryMap = resultMap.get(primaryTableName);
+            primaryMap.put("del_flag","0");
             resultMap.remove(primaryTableName);
             List<String> extraList = new ArrayList<>();
             for (Map.Entry<String, Map<String, Object>> entry : resultMap.entrySet()) {
@@ -271,6 +296,8 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
                     fields.add(fieldName);
                     values.add(processFieldValue(fieldValue));
                 }
+                fields.add("del_flag");
+                values.add("0");
                 // 构建 INSERT SQL
                 sqlBuilder.append("INSERT INTO ")
                         .append("{DBNAME}.").append(tableName)
@@ -301,26 +328,37 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
             String primaryTable = findPrimaryTable(columns);
             Map<String, MobilePageDesignDataSubTableVo> subTableMap = findSubTableMap(columns);
             // 分三段拼接出 update set 和 where 语法
-            List<String> collect = subTableMap.keySet().stream().collect(Collectors.toList());
-            StringBuilder update = new StringBuilder("UPDATE ")
-                    .append("{DBNAME}.").append(primaryTable);
-            collect.forEach(tableName -> update.append(",").append("{DBNAME}.").append(tableName));
-            String updateSql = update.toString();
+            StringBuilder updateSql = getUpdateSql(primaryTable, subTableMap);
             //从datamap中提取出 where判断的id
             Number id = (Number) dataMap.get(primaryTable + "@id");
             dataMap.remove(primaryTable + "@id");
             Map<String, Object> stringObjectMap = convertKeysFromFirstUnderLineToDot(dataMap);
             String setSql = " SET " + stringObjectMap.entrySet().stream()
-                    .map(entry -> entry.getKey() + "='" + entry.getValue()+"'")
-                    .collect(Collectors.joining(", ")); // 自动处理逗号分隔
+                    .map(entry -> {
+                        Object value = entry.getValue();
+                        String valueStr;
+                        // 处理数组或集合类型
+                        if (value instanceof Collection || value != null && value.getClass().isArray()) {
+                            // 将数组或集合转为 Stream,再拼接成无空格的字符串
+                            List<?> list = (List<?>) value;
+                             valueStr = "[" + list.stream().filter(str -> str!= null && !str.equals(""))
+                                    .map(str -> {
+                                        if (str instanceof String) {
+                                            return "\"" + str + "\"";  // 如果是String类型,加上双引号
+                                        }
+                                        return str.toString();
+                                    })
+                                    .collect(Collectors.joining(",")) + "]" ;
+                        } else {
+                            // 普通类型直接转字符串
+                            valueStr = value.toString();
+                        }
+                        return entry.getKey() + "='" + valueStr + "'";
+                    })
+                    .collect(Collectors.joining(","));
             // 拼接 where条件 (关联的表 + 主键该行的id)
-            String idSql = primaryTable +".id" +"=" + id;
-            String whereSql = subTableMap.entrySet().stream()
-                    .map(entry -> primaryTable + "." + entry.getValue().getPrimaryKey() +
-                            "=" + entry.getKey() + "." + entry.getValue().getSubKey())
-                    .collect(Collectors.joining(" and ")) ;
-            whereSql = " where " + (StringUtils.isBlank(whereSql) ? idSql : whereSql + " and " + idSql);
-            fullUpdateSql = updateSql + setSql + whereSql;
+            String idSql = " where " + primaryTable +".id" +"=" + id;
+            fullUpdateSql = updateSql + setSql + idSql;
         }
         return fullUpdateSql;
     }
@@ -337,11 +375,7 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
             String primaryTable = findPrimaryTable(columns);
             Map<String, MobilePageDesignDataSubTableVo> subTableMap = findSubTableMap(columns);
             // 分三段拼接出 update set 和 where 语法
-            List<String> collect = subTableMap.keySet().stream().collect(Collectors.toList());
-            StringBuilder update = new StringBuilder("UPDATE ")
-                    .append("{DBNAME}.").append(primaryTable);
-            collect.forEach(tableName -> update.append(",").append("{DBNAME}.").append(tableName));
-            String updateSql = update.toString();
+            StringBuilder updateSql = getUpdateSql(primaryTable, subTableMap);
             String DELFLAG = ".del_flag = '2' ";
             String setSQL = " SET " + primaryTable + DELFLAG + " ";
             subTableMap.entrySet().stream()
@@ -349,16 +383,13 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
                     .collect(Collectors.joining(", "));
             // 拼接 where条件 (关联的表 + 主键该行的id)
             String idSql = primaryTable +".id" +"=" + lineId;
-            String whereSql = subTableMap.entrySet().stream()
-                    .map(entry -> primaryTable + "." + entry.getValue().getPrimaryKey() +
-                            "=" + entry.getKey() + "." + entry.getValue().getSubKey())
-                    .collect(Collectors.joining(" and "));
-            whereSql = " where " + (StringUtils.isBlank(whereSql) ? idSql : whereSql + " and " + idSql);
+            String whereSql = " where "+ idSql;
             fullUpdateSql = updateSql + setSQL + whereSql;
         }
         return fullUpdateSql;
     }
 
+
     @Override
     public String mapToQuerySql(String pageJson) {
         JSONArray pageConfigs = JSON.parseArray(pageJson);
@@ -386,7 +417,7 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
         Long id = mobilePageDesignData.getId();
         String htmlData = mobilePageDesignData.getHtmlData();
         htmlData = URLDecoder.decode(htmlData);
-        Pattern pattern = Pattern.compile("options:\\s*formCreate\\.parseJson\\('(.*?)'\\)");
+        Pattern pattern = Pattern.compile("rule:\\s*formCreate\\.parseJson\\('(.*?)'\\)");
         Matcher matcher = pattern.matcher(htmlData);
 
         if (!matcher.find()) {
@@ -394,11 +425,19 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
         }
 
         String oldJsonStr = matcher.group(1);
-        JSONObject jsonObject = JSONObject.parseObject(oldJsonStr);
-        JSONObject form = jsonObject.getJSONObject("form");
-        form.put("pageId",id);
-        jsonObject.put("form",form);
-        String newJsonStr = jsonObject.toString();
+        JSONArray jsonArray = JSON.parseArray(oldJsonStr);
+        for (int i = 0; i < jsonArray.size(); i++) {
+            JSONObject jsonObject = jsonArray.getJSONObject(i);
+            if (jsonObject.get("type") != null && jsonObject.get("type").equals("zkqyTable") && jsonObject.getJSONObject("props") != null){
+                JSONObject jsonObject1 = jsonObject.getJSONObject("props");
+                jsonObject1.put("pageId",id);
+            }
+        }
+//        JSONObject jsonObject = JSONObject.parseObject(oldJsonStr);
+//        JSONObject form = jsonObject.getJSONObject("form");
+//        form.put("pageId",id);
+//        jsonObject.put("form",form);
+        String newJsonStr = jsonArray.toString();
         String newHtmlData = htmlData.replace(oldJsonStr,newJsonStr);
         String encodeHtml = "";
         try {
@@ -409,7 +448,7 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
         }
         mobilePageDesignData.setHtmlData(encodeHtml);
         // 给pageOption里面也加上
-        mobilePageDesignData.setPageOptions(newJsonStr);
+        mobilePageDesignData.setPageJson(newJsonStr);
     }
 
     private  String buildSelectClause(JSONArray columns) {
@@ -433,19 +472,21 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
         if (primaryTable == null) {
             return ""; // 或者抛出异常,根据业务需求决定
         }
-
         StringBuilder fromBuilder = new StringBuilder(" from ").append("{DBNAME}.").append(primaryTable);
-
+        HashSet<String> set = new HashSet<>();
         for (JSONObject column : columns.toJavaList(JSONObject.class)) {
             String tableType = column.getString("tableType");
             if (!"sub".equals(tableType)) {
                 continue;
             }
-
             String tableName = column.getString("tableName");
             String subKey = column.getString("subKey");
             String primaryKey = column.getString("primaryKey");
-
+            if (set.contains(tableName)){
+                continue;
+            }else{
+                set.add(tableName);
+            }
             fromBuilder.append(" left join ")
                     .append("{DBNAME}.").append(tableName)
                     .append(" on ")
@@ -512,7 +553,6 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
             String tableName,
             Map<String, Object> dataMap,
             List<String> extraFields) {
-
         // 合并所有字段名(dataMap 的 key + extraFields)
         List<String> allFields = new ArrayList<>(dataMap.keySet());
         allFields.addAll(extraFields);
@@ -545,6 +585,17 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
             return "'" + escapeSqlString(value.toString()) + "'";
         } else if (value instanceof Number || value instanceof Boolean) {
             return value.toString();
+        } else if (value instanceof List) {
+            List<?> list = (List<?>) value;
+            String collect = "'[" + list.stream().filter(str -> str!= null && !str.equals(""))
+                    .map(str -> {
+                        if (str instanceof String) {
+                            return "\"" + str + "\"";  // 如果是String类型,加上双引号
+                        }
+                        return str.toString();
+                    })
+                    .collect(Collectors.joining(",")) + "]'" ;
+            return collect;
         }
         return "NULL";
     }
@@ -567,4 +618,134 @@ public class MobilePageDesignDataServiceImpl implements IMobilePageDesignDataSer
         }
         return key; // 如果没有下划线,保持原样
     }
+
+    @Override
+    public List<TreeSelectStrId> getSingleTableTree(TreeComponentDTO treeComponentDTO) {
+        // 构建查询的sql
+        String tableName = treeComponentDTO.getTableName();
+        String primaryIdField = treeComponentDTO.getPrimaryIdField();
+        String parentIdField = treeComponentDTO.getParentIdField();
+        String showValue = treeComponentDTO.getShowValue();
+        if (StringUtils.isNotBlank(tableName) && StringUtils.isNotBlank(primaryIdField) && StringUtils.isNotBlank(parentIdField) && StringUtils.isNotBlank(showValue)){
+            List<Map<String,Object>> dataList = mobilePageDesignDataMapper.getSingleTableTree(tableName,primaryIdField,parentIdField,showValue);
+            // 前面查询的时候已经将列转换过了,所以这里可以指定列名
+            List<Map<String, ?>> maps = TreeBuilder.buildTree(dataList, "0", "id", "parentId");
+            List<TreeSelectStrId> convert = TreeBuilder.convert(maps);
+            return convert;
+        }else{
+            return Collections.emptyList();
+        }
+    }
+
+    public List<TreeSelectStrId> getMultiTableTree(TreeComponentDTO dto) {
+        // 1. 查询根节点
+        List<TreeSelectStrId> rootNodes = mobilePageDesignDataMapper.selectRootNodes(dto);
+        // 2. 递归构建子节点
+        if (dto.getChildrenTree() != null) {
+            buildChildNodes(rootNodes, dto.getChildrenTree());
+        }
+        return rootNodes;
+    }
+
+    private void buildChildNodes(List<TreeSelectStrId> parentNodes, TreeComponentDTO childDto) {
+        // 1. 提取父节点的原始ID(去掉@前缀部分)用于查询
+        List<String> parentOriginalIds = parentNodes.stream()
+                .map(node -> {
+                    // 获取最后一个@后的部分(原始ID)
+                    String idWithPath = node.getId();
+                    return idWithPath.contains("@")
+                            ? idWithPath.substring(idWithPath.lastIndexOf("@") + 1)
+                            : idWithPath;
+                })
+                .collect(Collectors.toList());
+
+        // 2. 查询子节点数据(用原始parentId匹配数据库)
+        List<TreeSelectWithParentId> childNodes = mobilePageDesignDataMapper.selectChildNodes(childDto, parentOriginalIds);
+
+        // 3. 建立父子关系
+        Map<String, TreeSelectStrId> parentNodeMap = parentNodes.stream()
+                .collect(Collectors.toMap(
+                        node -> {
+                            // 用同样的逻辑提取原始ID作为Map的Key
+                            String idWithPath = node.getId();
+                            return idWithPath.contains("@")
+                                    ? idWithPath.substring(idWithPath.lastIndexOf("@") + 1)
+                                    : idWithPath;
+                        },
+                        Function.identity()
+                ));
+
+        for (TreeSelectWithParentId child : childNodes) {
+            // 根据child的parentId(原始ID)找到父节点
+            TreeSelectStrId parent = parentNodeMap.get(child.getParentId());
+            if (parent != null) {
+                if (parent.getChildren() == null) {
+                    parent.setChildren(new ArrayList<>());
+                }
+                // 生成带路径的子节点ID:父节点路径 + @ + 子节点原始ID
+                String childIdWithPath = parent.getId() + "@" + child.getId();
+                child.setId(childIdWithPath); // 修改为带路径的ID
+                parent.getChildren().add(child);
+            }
+        }
+
+        // 4. 递归处理子节点
+        if (childDto.getChildrenTree() != null) {
+            List<TreeSelectStrId> nodesWithChildren = parentNodes.stream()
+                    .filter(node -> node.getChildren() != null && !node.getChildren().isEmpty())
+                    .collect(Collectors.toList());
+            for (TreeSelectStrId parent : nodesWithChildren) {
+                buildChildNodes(parent.getChildren(), childDto.getChildrenTree());
+            }
+        }
+    }
+
+    public List<TreeSelectValueLabel> convertTreeStrIdToValueLabel(List<TreeSelectStrId> originalTree) {
+        return originalTree.stream()
+                .map(node -> convertNodeStrId(node))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public void fillTableIdToForm(int i, List<Long> tableIds) {
+        if (tableIds != null && !tableIds.isEmpty()){
+            mobilePageDesignDataMapper.fillTableIdToForm(i,tableIds);
+        }
+    }
+
+    private static TreeSelectValueLabel convertNodeStrId(TreeSelectStrId originalNode) {
+        TreeSelectValueLabel converted = new TreeSelectValueLabel();
+        converted.setValue(originalNode.getId());
+        converted.setLabel(originalNode.getLabel());
+
+        if (originalNode.getChildren() != null) {
+            List<TreeSelectValueLabel> children = originalNode.getChildren().stream()
+                    .map(node -> convertNodeStrId(node))
+                    .collect(Collectors.toList());
+            converted.setChildren(children);
+        }
+
+        return converted;
+    }
+
+    /**
+     *
+     * @param primaryTable 主表
+     * @param subTableMap 子表集合
+     * @return
+     */
+    public StringBuilder getUpdateSql(String primaryTable,Map<String,MobilePageDesignDataSubTableVo> subTableMap){
+        StringBuilder updateSql = new StringBuilder("UPDATE ")
+                .append("{DBNAME}.").append(primaryTable);
+        // 添加LEFT JOIN部分
+        for (Map.Entry<String, MobilePageDesignDataSubTableVo> entry : subTableMap.entrySet()) {
+            String subTableName = entry.getKey();
+            MobilePageDesignDataSubTableVo subTableVo = entry.getValue();
+            updateSql.append(" LEFT JOIN {DBNAME}.").append(subTableName)
+                    .append(" ON ").append(primaryTable).append(".").append(subTableVo.getPrimaryKey())
+                    .append(" = ").append(subTableName).append(".").append(subTableVo.getSubKey());
+        }
+        return updateSql;
+    }
+
 }

+ 59 - 1
zkqy-system/src/main/resources/mapper/mobile/MobilePageDesignDataMapper.xml

@@ -12,6 +12,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="pageLink"    column="page_link"    />
         <result property="componentData"    column="component_data"    />
         <result property="htmlData"    column="html_data"    />
+        <result property="multipleTreeParam"    column="multiple_tree_param"    />
+        <result property="tableId"    column="table_id"    />
         <result property="remark"    column="remark"    />
         <result property="createById"    column="create_by_id"    />
         <result property="createBy"    column="create_by"    />
@@ -28,7 +30,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectMobilePageDesignDataVo">
-        select id, name, page_json, page_options, page_link,component_data,html_data, remark, create_by_id, create_by, create_time, update_by_id, update_by, update_time, del_flag, data_approval_status, process_key, task_process_key, task_node_key from mobile_page_design_data
+        select id, name, page_json, page_options, page_link,component_data,html_data,multiple_tree_param,table_id, remark, create_by_id, create_by, create_time, update_by_id, update_by, update_time, del_flag, data_approval_status, process_key, task_process_key, task_node_key from {DBNAME}.mobile_page_design_data
     </sql>
 
     <select id="selectMobilePageDesignDataList" parameterType="com.zkqy.system.entity.MobilePageDesignData" resultMap="MobilePageDesignDataResult">
@@ -40,6 +42,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="pageLink != null  and pageLink != ''"> and page_link = #{pageLink}</if>
             <if test="componentData != null  and componentData != ''"> and component_data = #{componentData}</if>
             <if test="htmlData != null  and htmlData != ''"> and html_data = #{htmlData}</if>
+            <if test="multipleTreeParam != null  and multipleTreeParam != ''"> and multiple_tree_param = #{multipleTreeParam}</if>
+            <if test="tableId != null  and tableId != ''"> and table_id = #{tableId}</if>
             <if test="createById != null "> and create_by_id = #{createById}</if>
             <if test="updateById != null "> and update_by_id = #{updateById}</if>
             <if test="dataApprovalStatus != null  and dataApprovalStatus != ''"> and data_approval_status = #{dataApprovalStatus}</if>
@@ -64,6 +68,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="pageLink != null">page_link,</if>
             <if test="componentData != null">component_data,</if>
             <if test="htmlData != null">html_data,</if>
+            <if test="multipleTreeParam != null">multiple_tree_param,</if>
             <if test="remark != null">remark,</if>
             <if test="createById != null">create_by_id,</if>
             <if test="createBy != null">create_by,</if>
@@ -84,6 +89,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="pageLink != null">#{pageLink},</if>
             <if test="componentData != null">#{componentData},</if>
             <if test="htmlData != null">#{htmlData},</if>
+            <if test="multipleTreeParam != null">#{multipleTreeParam},</if>
             <if test="remark != null">#{remark},</if>
             <if test="createById != null">#{createById},</if>
             <if test="createBy != null">#{createBy},</if>
@@ -111,6 +117,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="pageLink != null">page_link = #{pageLink},</if>
             <if test="componentData != null">component_data = #{componentData},</if>
             <if test="htmlData != null">html_data = #{htmlData},</if>
+            <if test="multipleTreeParam != null">multiple_tree_param = #{multipleTreeParam},</if>
             <if test="remark != null">remark = #{remark},</if>
             <if test="createById != null">create_by_id = #{createById},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
@@ -129,10 +136,34 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <update id="executeUpdateSql">
         ${executeSql}
     </update>
+    <update id="fillTableIdToForm">
+        update mobile_page_design_data
+        set table_id = #{id}
+        WHERE id IN
+        <foreach item="tableId" collection="tableIds" open="(" separator="," close=")">
+            #{tableId}
+        </foreach>
+    </update>
 
     <select id="executeQuerySql"  resultType="java.util.HashMap">
         ${executeSql}
     </select>
+    <select id="selectDynamicTreeData" resultType="java.util.Map">
+        SELECT
+        ${primaryIdField} as id,
+        ${showValue} as label
+        <if test="parentIdField != null and parentIdField != ''">
+            ,${parentIdField} as parentId
+        </if>
+        FROM {DBNAME}.${tableName}
+    </select>
+    <select id="getSingleTableTree" resultType="java.util.Map">
+       select
+            ${primaryIdField} as id,
+            ${showValue} as label,
+            ${parentIdField} as parentId
+        from {DBNAME}.${tableName}
+    </select>
 
     <delete id="deleteMobilePageDesignDataById" parameterType="Long">
         delete from mobile_page_design_data where id = #{id}
@@ -147,4 +178,31 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <delete id="executeDeleteSql">
         ${executeSql}
     </delete>
+
+    <!-- 查询根节点 -->
+    <select id="selectRootNodes" resultType="com.zkqy.common.core.domain.TreeSelectStrId">
+        SELECT
+        ${dto.primaryIdField} as id,
+        ${dto.showValue} as label
+        FROM {DBNAME}.${dto.tableName}
+        <where>
+            <!-- 主表通常没有parentId或parentId为0 -->
+            <if test="dto.parentIdField != null and dto.parentIdField != ''">
+                AND ${dto.parentIdField} = 0
+            </if>
+        </where>
+    </select>
+
+    <!-- 查询子节点 -->
+    <select id="selectChildNodes" resultType="com.zkqy.common.core.domain.TreeSelectWithParentId">
+        SELECT
+        ${dto.primaryIdField} as id,
+        ${dto.showValue} as label,
+        ${dto.parentIdField} as parentId
+        FROM {DBNAME}.${dto.tableName}
+        WHERE ${dto.parentIdField} IN
+        <foreach item="id" collection="parentIds" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
 </mapper>