Skip to content

Commit a6b73c2

Browse files
pixelmaxQmpiexlMax(奇淼
andauthored
beta:2.7.8-a 增加自动化创建树形结构 (#1941)
* feat: 支持创建树形结构 --------- Co-authored-by: piexlMax(奇淼 <[email protected]>
1 parent c909d32 commit a6b73c2

File tree

14 files changed

+584
-132
lines changed

14 files changed

+584
-132
lines changed

server/model/common/basetypes.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,10 @@ func (m *JSONMap) Scan(value interface{}) error {
3434
}
3535
return nil
3636
}
37+
38+
type TreeNode[T any] interface {
39+
GetChildren() []T
40+
SetChildren(children T)
41+
GetID() int
42+
GetParentID() int
43+
}

server/model/system/request/sys_auto_code.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type AutoCode struct {
2727
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql" example:"false"` // 是否自动创建menu
2828
AutoCreateBtnAuth bool `json:"autoCreateBtnAuth" example:"false"` // 是否自动创建按钮权限
2929
OnlyTemplate bool `json:"onlyTemplate" example:"false"` // 是否只生成模板
30+
IsTree bool `json:"isTree" example:"false"` // 是否树形结构
31+
TreeJson string `json:"treeJson" example:"展示的树json字段"` // 展示的树json字段
3032
IsAdd bool `json:"isAdd" example:"false"` // 是否新增
3133
Fields []*AutoCodeField `json:"fields"`
3234
Module string `json:"-"`

server/resource/package/server/api/api.go.tpl

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"{{.Module}}/global"
66
"{{.Module}}/model/common/response"
77
"{{.Module}}/model/{{.Package}}"
8+
{{- if not .IsTree}}
89
{{.Package}}Req "{{.Module}}/model/{{.Package}}/request"
10+
{{- end }}
911
"github.com/gin-gonic/gin"
1012
"go.uber.org/zap"
1113
{{- if .AutoCreateResource}}
@@ -142,6 +144,25 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Find{{.StructName}}(c *gin.Conte
142144
response.OkWithData(re{{.Abbreviation}}, c)
143145
}
144146

147+
{{- if .IsTree }}
148+
// Get{{.StructName}}List 分页获取{{.Description}}列表,Tree模式下不接受参数
149+
// @Tags {{.StructName}}
150+
// @Summary 分页获取{{.Description}}列表
151+
// @Security ApiKeyAuth
152+
// @accept application/json
153+
// @Produce application/json
154+
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
155+
// @Router /{{.Abbreviation}}/get{{.StructName}}List [get]
156+
func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}List(c *gin.Context) {
157+
list, err := {{.Abbreviation}}Service.Get{{.StructName}}InfoList()
158+
if err != nil {
159+
global.GVA_LOG.Error("获取失败!", zap.Error(err))
160+
response.FailWithMessage("获取失败:" + err.Error(), c)
161+
return
162+
}
163+
response.OkWithDetailed(list, "获取成功", c)
164+
}
165+
{{- else }}
145166
// Get{{.StructName}}List 分页获取{{.Description}}列表
146167
// @Tags {{.StructName}}
147168
// @Summary 分页获取{{.Description}}列表
@@ -171,6 +192,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}List(c *gin.Co
171192
PageSize: pageInfo.PageSize,
172193
}, "获取成功", c)
173194
}
195+
{{- end }}
174196

175197
{{- if .HasDataSource }}
176198
// Get{{.StructName}}DataSource 获取{{.StructName}}的数据源
@@ -199,7 +221,6 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}DataSource(c *
199221
// @Summary 不需要鉴权的{{.Description}}接口
200222
// @accept application/json
201223
// @Produce application/json
202-
// @Param data query {{.Package}}Req.{{.StructName}}Search true "分页获取{{.Description}}列表"
203224
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
204225
// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get]
205226
func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}Public(c *gin.Context) {

server/resource/package/server/model/model.go.tpl

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ type {{.StructName}} struct {
7272
UpdatedBy uint `gorm:"column:updated_by;comment:更新者"`
7373
DeletedBy uint `gorm:"column:deleted_by;comment:删除者"`
7474
{{- end }}
75+
{{- if .IsTree }}
76+
Children []*{{.StructName}} `json:"children" gorm:"-"` //子节点
77+
ParentID int `json:"parentID" gorm:"column:parent_id;comment:父节点"`
78+
{{- end }}
7579
{{- end }}
7680
}
7781

@@ -82,5 +86,26 @@ func ({{.StructName}}) TableName() string {
8286
}
8387
{{ end }}
8488

89+
{{if .IsTree }}
90+
// GetChildren 实现TreeNode接口
91+
func (s *{{.StructName}}) GetChildren() []*{{.StructName}} {
92+
return s.Children
93+
}
94+
95+
// SetChildren 实现TreeNode接口
96+
func (s *{{.StructName}}) SetChildren(children *{{.StructName}}) {
97+
s.Children = append(s.Children, children)
98+
}
99+
100+
// GetID 实现TreeNode接口
101+
func (s *{{.StructName}}) GetID() int {
102+
return int({{if not .GvaModel}}*{{- end }}s.{{.PrimaryField.FieldName}})
103+
}
85104

86-
{{ end }}
105+
// GetParentID 实现TreeNode接口
106+
func (s *{{.StructName}}) GetParentID() int {
107+
return s.ParentID
108+
}
109+
{{ end }}
110+
111+
{{ end }}

server/resource/package/server/service/service.go.tpl

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,12 @@ import (
6060
{{- if not .OnlyTemplate }}
6161
"{{.Module}}/global"
6262
"{{.Module}}/model/{{.Package}}"
63+
{{- if not .IsTree}}
6364
{{.Package}}Req "{{.Module}}/model/{{.Package}}/request"
65+
{{- else }}
66+
"{{.Module}}/utils"
67+
"errors"
68+
{{- end }}
6469
{{- if .AutoCreateResource }}
6570
"gorm.io/gorm"
6671
{{- end}}
@@ -80,6 +85,17 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service) Create{{.StructName}}({{
8085
// Delete{{.StructName}} 删除{{.Description}}记录
8186
// Author [yourname](https://github.com/yourname)
8287
func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}({{.PrimaryField.FieldJson}} string{{- if .AutoCreateResource -}},userID uint{{- end -}}) (err error) {
88+
{{- if .IsTree }}
89+
var count int64
90+
err = {{$db}}.Find(&{{.Package}}.{{.StructName}}{},"parent_id = ?",{{.PrimaryField.FieldJson}}).Count(&count).Error
91+
if count > 0 {
92+
return errors.New("此节点存在子节点不允许删除")
93+
}
94+
if err != nil {
95+
return err
96+
}
97+
{{- end }}
98+
8399
{{- if .AutoCreateResource }}
84100
err = {{$db}}.Transaction(func(tx *gorm.DB) error {
85101
if err := tx.Model(&{{.Package}}.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?", {{.PrimaryField.FieldJson}}).Update("deleted_by", userID).Error; err != nil {
@@ -129,6 +145,20 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}({{.Pri
129145
return
130146
}
131147

148+
149+
{{- if .IsTree }}
150+
// Get{{.StructName}}InfoList 分页获取{{.Description}}记录,Tree模式下不添加分页和搜索
151+
// Author [yourname](https://github.com/yourname)
152+
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoList() (list []*{{.Package}}.{{.StructName}},err error) {
153+
// 创建db
154+
db := {{$db}}.Model(&{{.Package}}.{{.StructName}}{})
155+
var {{.Abbreviation}}s []*{{.Package}}.{{.StructName}}
156+
157+
err = db.Find(&{{.Abbreviation}}s).Error
158+
159+
return utils.BuildTree({{.Abbreviation}}s), err
160+
}
161+
{{- else }}
132162
// Get{{.StructName}}InfoList 分页获取{{.Description}}记录
133163
// Author [yourname](https://github.com/yourname)
134164
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoList(info {{.Package}}Req.{{.StructName}}Search) (list []{{.Package}}.{{.StructName}}, total int64, err error) {
@@ -193,6 +223,8 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
193223
return {{.Abbreviation}}s, total, err
194224
}
195225

226+
{{- end }}
227+
196228
{{- if .HasDataSource }}
197229
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}DataSource() (res map[string][]map[string]any, err error) {
198230
res = make(map[string][]map[string]any)
@@ -215,4 +247,4 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}Public(
215247
// 此方法为获取数据源定义的数据
216248
// 请自行实现
217249
}
218-
{{- end }}
250+
{{- end }}

server/resource/package/web/view/form.vue.tpl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,20 @@ getDataSourceFunc()
165165
<div>
166166
<div class="gva-form-box">
167167
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
168+
{{- if .IsTree }}
169+
<el-form-item label="父节点:" prop="parentID" >
170+
<el-tree-select
171+
v-model="formData.parentID"
172+
:data="[rootNode,...tableData]"
173+
check-strictly
174+
:render-after-expand="false"
175+
:props="defaultProps"
176+
clearable
177+
style="width: 240px"
178+
placeholder="根节点"
179+
/>
180+
</el-form-item>
181+
{{- end }}
168182
{{- range .Fields}}
169183
{{- if .Form }}
170184
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}">
@@ -239,6 +253,9 @@ import {
239253
{{- if .HasDataSource }}
240254
get{{.StructName}}DataSource,
241255
{{- end }}
256+
{{- if .IsTree }}
257+
get{{.StructName}}List,
258+
{{- end }}
242259
create{{.StructName}},
243260
update{{.StructName}},
244261
find{{.StructName}}
@@ -277,6 +294,32 @@ import ArrayCtrl from '@/components/arrayCtrl/arrayCtrl.vue'
277294
const route = useRoute()
278295
const router = useRouter()
279296
297+
{{- if .IsTree }}
298+
const tableData = ref([])
299+
300+
const defaultProps = {
301+
children: "children",
302+
label: "{{ .TreeJson }}",
303+
value: "{{ .PrimaryField.FieldJson }}"
304+
}
305+
306+
const rootNode = {
307+
{{ .PrimaryField.FieldJson }}: 0,
308+
{{ .TreeJson }}: '根节点',
309+
children: []
310+
}
311+
312+
const getTableData = async() => {
313+
const table = await get{{.StructName}}List()
314+
if (table.code === 0) {
315+
tableData.value = table.data || []
316+
}
317+
}
318+
319+
getTableData()
320+
321+
{{- end }}
322+
280323
// 提交按钮loading
281324
const btnLoading = ref(false)
282325
@@ -285,6 +328,9 @@ const type = ref('')
285328
const {{ $element }}Options = ref([])
286329
{{- end }}
287330
const formData = ref({
331+
{{- if .IsTree }}
332+
parentID: undefined,
333+
{{- end }}
288334
{{- range .Fields}}
289335
{{- if .Form }}
290336
{{- if eq .FieldType "bool" }}

server/resource/package/web/view/table.vue.tpl

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ getDataSourceFunc()
363363
{{- if not .OnlyTemplate}}
364364
<template>
365365
<div>
366+
{{- if not .IsTree }}
366367
<div class="gva-search-box">
367368
<el-form ref="elSearchFormRef" :inline="true" :model="searchInfo" class="demo-form-inline" :rules="searchRule" @keyup.enter="onSubmit">
368369
{{- if .GvaModel }}
@@ -515,9 +516,10 @@ getDataSourceFunc()
515516
</el-form-item>
516517
</el-form>
517518
</div>
519+
{{- end }}
518520
<div class="gva-table-box">
519521
<div class="gva-btn-list">
520-
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.add"{{ end }} type="primary" icon="plus" @click="openDialog">新增</el-button>
522+
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.add"{{ end }} type="primary" icon="plus" @click="openDialog()">新增</el-button>
521523
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.batchDelete"{{ end }} icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="onDelete">删除</el-button>
522524
{{ if .HasExcel -}}
523525
<ExportTemplate {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.exportTemplate"{{ end }} template-id="{{$templateID}}" />
@@ -538,7 +540,7 @@ getDataSourceFunc()
538540
>
539541
<el-table-column type="selection" width="55" />
540542
{{ if .GvaModel }}
541-
<el-table-column align="left" label="日期" prop="createdAt" width="180">
543+
<el-table-column align="left" label="日期" prop="createdAt" {{- if .IsTree }} min-{{- end }}width="180">
542544
<template #default="scope">{{ "{{ formatDate(scope.row.CreatedAt) }}" }}</template>
543545
</el-table-column>
544546
{{ end }}
@@ -629,9 +631,12 @@ getDataSourceFunc()
629631
{{- end }}
630632
<el-table-column align="left" label="操作" fixed="right" min-width="240">
631633
<template #default="scope">
634+
{{- if .IsTree }}
635+
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.add"{{ end }} type="primary" link class="table-button" @click="openDialog(scope.row)"><el-icon style="margin-right: 5px"><InfoFilled /></el-icon>新增子节点</el-button>
636+
{{- end }}
632637
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.info"{{ end }} type="primary" link class="table-button" @click="getDetails(scope.row)"><el-icon style="margin-right: 5px"><InfoFilled /></el-icon>查看</el-button>
633638
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.edit"{{ end }} type="primary" link icon="edit" class="table-button" @click="update{{.StructName}}Func(scope.row)">编辑</el-button>
634-
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.delete"{{ end }} type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
639+
<el-button {{ if .IsTree }}v-if="!scope.row.children?.length" {{ end }} {{if $global.AutoCreateBtnAuth }}v-auth="btnAuth.delete"{{ end }} type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
635640
</template>
636641
</el-table-column>
637642
</el-table>
@@ -659,6 +664,20 @@ getDataSourceFunc()
659664
</template>
660665

661666
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
667+
{{- if .IsTree }}
668+
<el-form-item label="父节点:" prop="parentID" >
669+
<el-tree-select
670+
v-model="formData.parentID"
671+
:data="[rootNode,...tableData]"
672+
check-strictly
673+
:render-after-expand="false"
674+
:props="defaultProps"
675+
clearable
676+
style="width: 240px"
677+
placeholder="根节点"
678+
/>
679+
</el-form-item>
680+
{{- end }}
662681
{{- range .Fields}}
663682
{{- if .Form}}
664683
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}" >
@@ -734,6 +753,21 @@ getDataSourceFunc()
734753

735754
<el-drawer destroy-on-close size="800" v-model="detailShow" :show-close="true" :before-close="closeDetailShow" title="查看">
736755
<el-descriptions :column="1" border>
756+
{{- if .IsTree }}
757+
<el-descriptions-item label="父节点">
758+
<el-tree-select
759+
v-model="detailFrom.parentID"
760+
:data="[rootNode,...tableData]"
761+
check-strictly
762+
disabled
763+
:render-after-expand="false"
764+
:props="defaultProps"
765+
clearable
766+
style="width: 240px"
767+
placeholder="根节点"
768+
/>
769+
</el-descriptions-item>
770+
{{- end }}
737771
{{- range .Fields}}
738772
{{- if .Desc }}
739773
<el-descriptions-item label="{{ .FieldDesc }}">
@@ -852,6 +886,9 @@ const showAllQuery = ref(false)
852886
const {{ $element }}Options = ref([])
853887
{{- end }}
854888
const formData = ref({
889+
{{- if .IsTree }}
890+
parentID:undefined,
891+
{{- end }}
855892
{{- range .Fields}}
856893
{{- if .Form}}
857894
{{- if eq .FieldType "bool" }}
@@ -999,6 +1036,7 @@ const sortChange = ({ prop, order }) => {
9991036
}
10001037
{{- end}}
10011038
1039+
{{- if not .IsTree }}
10021040
// 重置
10031041
const onReset = () => {
10041042
searchInfo.value = {}
@@ -1040,6 +1078,28 @@ const getTableData = async() => {
10401078
pageSize.value = table.data.pageSize
10411079
}
10421080
}
1081+
{{- else }}
1082+
// 树选择器配置
1083+
const defaultProps = {
1084+
children: "children",
1085+
label: "{{ .TreeJson }}",
1086+
value: "{{ .PrimaryField.FieldJson }}"
1087+
}
1088+
1089+
const rootNode = {
1090+
{{ .PrimaryField.FieldJson }}: 0,
1091+
{{ .TreeJson }}: '根节点',
1092+
children: []
1093+
}
1094+
1095+
// 查询
1096+
const getTableData = async() => {
1097+
const table = await get{{.StructName}}List()
1098+
if (table.code === 0) {
1099+
tableData.value = table.data || []
1100+
}
1101+
}
1102+
{{- end }}
10431103
10441104
getTableData()
10451105
@@ -1140,8 +1200,11 @@ const delete{{.StructName}}Func = async (row) => {
11401200
const dialogFormVisible = ref(false)
11411201
11421202
// 打开弹窗
1143-
const openDialog = () => {
1203+
const openDialog = ({{- if .IsTree -}}row{{- end -}}) => {
11441204
type.value = 'create'
1205+
{{- if .IsTree }}
1206+
formData.value.parentID = row ? row.{{.PrimaryField.FieldJson}} : undefined
1207+
{{- end }}
11451208
dialogFormVisible.value = true
11461209
}
11471210

0 commit comments

Comments
 (0)