Browse Source

feat: enhance tree structure implementation with new entity, service methods, and UI adjustments

PIG AI 1 week ago
parent
commit
727e366679
6 changed files with 150 additions and 91 deletions
  1. 1 1
      config.json
  2. 2 1
      tree/树形Service.java
  3. 47 30
      tree/树形ServiceImpl.java
  4. 90 0
      tree/树形实体.java
  5. 4 0
      tree/树形表单.vue
  6. 6 59
      tree/树形表格.vue

+ 1 - 1
config.json

@@ -132,7 +132,7 @@
     {
       "templateName": "实体",
       "generatorPath": "${backendPath}/src/main/java/${packagePath}/${moduleName}/entity/${ClassName}Entity.java",
-      "templateFile": "single/实体.java"
+      "templateFile": "tree/树形实体.java"
     },
     {
       "templateName": "Mapper",

+ 2 - 1
tree/树形Service.java

@@ -1,5 +1,6 @@
 package ${package}.${moduleName}.service;
 
+import cn.hutool.core.lang.tree.Tree;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.IService;
 import ${package}.${moduleName}.entity.${ClassName}Entity;
@@ -19,7 +20,7 @@ public interface ${ClassName}Service extends IService<${ClassName}Entity> {
      * @param wrapper 查询条件
      * @return 树形结构数据
      */
-    List<${ClassName}Entity> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);
+    List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);
 
     /**
      * 获取所有父级节点

+ 47 - 30
tree/树形ServiceImpl.java

@@ -1,20 +1,25 @@
 package ${package}.${moduleName}.service.impl;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.lang.tree.Tree;
+import cn.hutool.core.lang.tree.TreeNode;
+import cn.hutool.core.lang.tree.TreeUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import ${package}.${moduleName}.entity.${ClassName}Entity;
 import ${package}.${moduleName}.mapper.${ClassName}Mapper;
 import ${package}.${moduleName}.service.${ClassName}Service;
+import jakarta.validation.constraints.NotNull;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -33,7 +38,7 @@ public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${C
      * @return 树形结构数据
      */
     @Override
-    public List<${ClassName}Entity> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {
+    public List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {
         // 查询所有数据
         List<${ClassName}Entity> allList = list(wrapper);
         
@@ -41,36 +46,48 @@ public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${C
             return new ArrayList<>();
         }
 
-        // 按父ID分组
-        Map<${pk.attrType}, List<${ClassName}Entity>> groupByParent = allList.stream()
-                .collect(Collectors.groupingBy(item -> {
-                    // 假设有parentId字段,如果没有请根据实际情况调整
-                    return item.getParentId() != null ? item.getParentId() : 0L;
-                }));
+        // 转换为TreeNode
+        List<TreeNode<${pk.attrType}>> collect = allList.stream().map(getNodeFunction()).toList();
 
-        // 构建树形结构
-        return buildTreeRecursive(groupByParent, 0L);
+        // 使用TreeUtil构建树形结构,根节点ID为0
+#if($pk.attrType == 'Long')
+        return TreeUtil.build(collect, 0L);
+#else
+        return TreeUtil.build(collect, 0);
+#end
     }
 
     /**
-     * 递归构建树形结构
-     * @param groupByParent 按父ID分组的数据
-     * @param parentId 父ID
-     * @return 树形结构数据
+     * 获取TreeNode转换函数
+     * @return TreeNode转换函数
      */
-    private List<${ClassName}Entity> buildTreeRecursive(Map<${pk.attrType}, List<${ClassName}Entity>> groupByParent, ${pk.attrType} parentId) {
-        List<${ClassName}Entity> children = groupByParent.get(parentId);
-        if (CollUtil.isEmpty(children)) {
-            return new ArrayList<>();
-        }
-
-        children.forEach(child -> {
-            List<${ClassName}Entity> subChildren = buildTreeRecursive(groupByParent, child.get${str.capitalizeFirst($pk.attrName)}());
-            child.setChildren(subChildren);
-            child.setHasChildren(!CollUtil.isEmpty(subChildren));
-        });
-
-        return children;
+    @NotNull
+    private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {
+        return entity -> {
+            TreeNode<${pk.attrType}> node = new TreeNode<>();
+            node.setId(entity.get${str.capitalizeFirst($pk.attrName)}());
+#foreach($field in $fieldList)
+#if($field.attrName == 'name' || $field.fieldComment.contains('名称'))
+            node.setName(entity.get${str.capitalizeFirst($field.attrName)}());
+#end
+#end
+#if($pk.attrType == 'Long')
+            node.setParentId(entity.getParentId() != null ? entity.getParentId() : 0L);
+#else
+            node.setParentId(entity.getParentId() != null ? entity.getParentId() : 0);
+#end
+
+            // 扩展属性
+            Map<String, Object> extra = new HashMap<>();
+#foreach($field in $fieldList)
+#if(!$field.primaryPk && $field.attrName != 'parentId')
+            extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.get${str.capitalizeFirst($field.attrName)}());
+#end
+#end
+            
+            node.setExtra(extra);
+            return node;
+        };
     }
 
     /**
@@ -82,7 +99,7 @@ public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${C
         LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();
         // 查询所有父级节点(parentId为null或0的节点)
         wrapper.and(w -> w.isNull(${ClassName}Entity::getParentId).or().eq(${ClassName}Entity::getParentId, 0));
-        wrapper.orderByAsc(${ClassName}Entity::getSort); // 假设有sort字段用于排序
+        wrapper.orderByAsc(${ClassName}Entity::$str.getProperty($pk.attrName)); // 按主键排序
         return list(wrapper);
     }
 
@@ -95,7 +112,7 @@ public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${C
     public List<${ClassName}Entity> getChildrenByParentId(${pk.attrType} parentId) {
         LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();
         wrapper.eq(${ClassName}Entity::getParentId, parentId);
-        wrapper.orderByAsc(${ClassName}Entity::getSort); // 假设有sort字段用于排序
+        wrapper.orderByAsc(${ClassName}Entity::$str.getProperty($pk.attrName)); // 按主键排序
         return list(wrapper);
     }
 
@@ -136,7 +153,7 @@ public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${C
         
         LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();
         wrapper.eq(${ClassName}Entity::getParentId, parentId);
-        wrapper.select(${ClassName}Entity::get${str.capitalizeFirst($pk.attrName)});
+        wrapper.select(${ClassName}Entity::$str.getProperty($pk.attrName));
         
         List<${ClassName}Entity> children = list(wrapper);
         

+ 90 - 0
tree/树形实体.java

@@ -0,0 +1,90 @@
+package ${package}.${moduleName}.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.FieldNameConstants;
+
+#if($isTenant)
+import com.pig4cloud.pigx.common.data.tenant.TenantEntity;
+#end
+
+#foreach($pkg in $importList)
+import $pkg;
+#end
+
+import java.time.LocalDateTime;
+
+/**
+ * ${tableComment}
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@Data
+@TableName("${tableName}")
+@EqualsAndHashCode(callSuper = true)
+@FieldNameConstants
+@Schema(description = "${tableComment}")
+#if($isTenant)
+public class ${ClassName}Entity extends TenantEntity {
+#else
+public class ${ClassName}Entity extends Model<${ClassName}Entity> {
+#end
+
+#foreach ($field in $fieldList)
+#if($field.primaryPk)
+	/**
+	 * $field.fieldComment
+	 */
+	@TableId(type = IdType.ASSIGN_ID)
+	@Schema(description = "$field.fieldComment")
+	private $field.attrType $field.attrName;
+
+#end
+#end
+#foreach ($field in $fieldList)
+#if(!$field.primaryPk && !$field.baseField)
+	/**
+	 * $field.fieldComment
+	 */
+#if($field.fieldComment == '父级ID')
+	@Schema(description = "$field.fieldComment")
+	private $field.attrType parentId;
+
+#elseif($field.attrName == 'sort')
+	@Schema(description = "排序")
+	private Integer sort;
+
+#else
+#if($field.autoFill == 'INSERT')
+	@TableField(fill = FieldFill.INSERT)
+#elseif($field.autoFill == 'INSERT_UPDATE')
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+#elseif($field.autoFill == 'UPDATE')
+	@TableField(fill = FieldFill.UPDATE)
+#end
+	@Schema(description = "$field.fieldComment"#if($field.hidden), hidden = $field.hidden#end)
+	private $field.attrType $field.attrName;
+
+#end
+#end
+#end
+
+	/**
+	 * 子节点列表
+	 */
+	@TableField(exist = false)
+	@Schema(description = "子节点列表", hidden = true)
+	private java.util.List<${ClassName}Entity> children;
+
+	/**
+	 * 是否有子节点
+	 */
+	@TableField(exist = false)
+	@Schema(description = "是否有子节点", hidden = true)
+	private Boolean hasChildren;
+
+} 

+ 4 - 0
tree/树形表单.vue

@@ -4,7 +4,11 @@
     <el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
       <el-row :gutter="24">
         <!-- 父级节点选择 -->
+#if($formLayout == 1)
         <el-col :span="24" class="mb20">
+#elseif($formLayout == 2)
+        <el-col :span="12" class="mb20">
+#end
           <el-form-item label="父级节点" prop="parentId">
             <el-tree-select
               v-model="form.parentId"

+ 6 - 59
tree/树形表格.vue

@@ -97,16 +97,7 @@
           >
             展开/折叠
           </el-button>
-          <el-button 
-            plain 
-            icon="upload-filled" 
-            type="primary" 
-            class="ml10" 
-            @click="excelUploadRef.show()" 
-            v-auth="'${moduleName}_${functionName}_add'"
-          >
-            导入
-          </el-button>
+
           <el-button 
             plain 
             :disabled="multiple" 
@@ -119,8 +110,6 @@
           </el-button>
           <right-toolbar 
             v-model:showSearch="showSearch" 
-            :export="'${moduleName}_${functionName}_export'"
-            @exportExcel="exportExcel" 
             class="ml10 mr20" 
             style="float: right;"
             @queryTable="getDataList"
@@ -197,14 +186,7 @@
     <!-- 编辑、新增弹窗 -->
     <form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
 
-    <!-- 导入excel弹窗 (需要在 upms-biz/resources/file 下维护模板) -->
-    <upload-excel
-      ref="excelUploadRef"
-      title="导入"
-      url="/${moduleName}/${functionName}/import"
-      temp-url="/admin/sys-file/local/file/${functionName}.xlsx"
-      @refreshDataList="getDataList"
-    />
+
   </div>
 </template>
 
@@ -238,7 +220,6 @@ const { $dict.format($fieldDict) } = useDict($dict.quotation($fieldDict));
 
 // ========== 组件引用 ==========
 const formDialogRef = ref();          // 表单弹窗引用
-const excelUploadRef = ref();         // Excel上传弹窗引用
 const queryRef = ref();               // 查询表单引用
 
 // ========== 响应式数据 ==========
@@ -249,17 +230,18 @@ const isExpandAll = ref(false);       // 是否展开所有节点
 
 // ========== 表格状态 ==========
 const state: BasicTableProps = reactive<BasicTableProps>({
+  isPage: false,  // 是否分页
   queryForm: {},      // 查询参数
   pageList: fetchTreeList, // 树形数据查询方法(不分页)
   loading: false,     // 加载状态
   dataList: []        // 数据列表
 });
 
+
 // ========== Hook引用 ==========
 // 表格相关Hook (树形表格不使用分页)
 const {
   getDataList,
-  downBlobFile,
   tableStyle
 } = useTable(state);
 
@@ -283,16 +265,7 @@ const expandAll = () => {
   isExpandAll.value = !isExpandAll.value;
 };
 
-/**
- * 导出Excel文件
- */
-const exportExcel = () => {
-  downBlobFile(
-    '/${moduleName}/${functionName}/export',
-    Object.assign(state.queryForm, { ids: selectObjs }),
-    '${functionName}.xlsx'
-  );
-};
+
 
 /**
  * 表格多选事件处理
@@ -328,30 +301,4 @@ const handleDelete = async (ids: string[]) => {
 onMounted(() => {
   getDataList();
 });
-</script>
-
-<style scoped>
-.layout-padding {
-  padding: 15px;
-}
-
-.layout-padding-auto {
-  margin: 0 auto;
-}
-
-.layout-padding-view {
-  min-height: calc(100vh - 50px);
-}
-
-.mb8 {
-  margin-bottom: 8px;
-}
-
-.ml10 {
-  margin-left: 10px;
-}
-
-.mr20 {
-  margin-right: 20px;
-}
-</style> 
+</script>