Commit 015565cc9a577f6e2174f08d8a683f2574d41d53

Authored by puhui999
1 parent 69486939

【功能新增】INFRA: vben next 树表代码生成

yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -153,6 +153,15 @@ public class CodegenEngine { @@ -153,6 +153,15 @@ public class CodegenEngine {
153 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) 153 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
154 .put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("api/api.ts"), 154 .put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("api/api.ts"),
155 vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) 155 vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
  156 + // 主子表模板配置 - Vue3 vben5 schema 模版
  157 + //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/master_slave_data.ts"),
  158 + // vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
  159 + //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/master_slave_index.vue"),
  160 + // vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
  161 + //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/modules/master_slave_form.vue"),
  162 + // vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
  163 + //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/modules/sub_table.vue"),
  164 + // vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/sub_table.vue"))
156 .build(); 165 .build();
157 166
158 @Resource 167 @Resource
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/api/api.ts.vm
@@ -17,6 +17,9 @@ export namespace ${simpleClassName}Api { @@ -17,6 +17,9 @@ export namespace ${simpleClassName}Api {
17 #end 17 #end
18 #end 18 #end
19 #end 19 #end
  20 +#if ( $table.templateType == 2 )
  21 + children?: ${simpleClassName}[];
  22 +#end
20 } 23 }
21 } 24 }
22 25
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm
  1 +import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
1 import type { VbenFormSchema } from '#/adapter/form'; 2 import type { VbenFormSchema } from '#/adapter/form';
2 -import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; 3 +import type { OnActionClickFn } from '#/adapter/vxe-table';
3 import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 4 import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
4 5
5 import { z } from '#/adapter/form'; 6 import { z } from '#/adapter/form';
6 -import { CommonStatusEnum } from '#/utils/constants'; 7 +#if(${table.templateType} == 2)## 树表需要导入这些
  8 +import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
  9 +import { handleTree } from '#/utils/tree';
  10 +#end
7 import { DICT_TYPE, getDictOptions } from '#/utils/dict'; 11 import { DICT_TYPE, getDictOptions } from '#/utils/dict';
8 -import { getRangePickerDefaultProps } from '#/utils/date'; 12 +import { CommonStatusEnum } from '#/utils/constants';
9 import { useAccess } from '@vben/access'; 13 import { useAccess } from '@vben/access';
10 14
11 const { hasAccessByCodes } = useAccess(); 15 const { hasAccessByCodes } = useAccess();
@@ -21,9 +25,34 @@ export function useFormSchema(): VbenFormSchema[] { @@ -21,9 +25,34 @@ export function useFormSchema(): VbenFormSchema[] {
21 show: () => false, 25 show: () => false,
22 }, 26 },
23 }, 27 },
  28 +#if(${table.templateType} == 2)## 树表特有字段:上级
  29 + {
  30 + fieldName: '${treeParentColumn.javaField}',
  31 + label: '上级${table.classComment}',
  32 + component: 'ApiTreeSelect',
  33 + componentProps: {
  34 + allowClear: true,
  35 + api: async () => {
  36 + const data = await get${simpleClassName}List({});
  37 + data.unshift({
  38 + id: 0,
  39 + ${treeNameColumn.javaField}: '顶级${table.classComment}',
  40 + });
  41 + return handleTree(data);
  42 + },
  43 + class: 'w-full',
  44 + labelField: '${treeNameColumn.javaField}',
  45 + valueField: 'id',
  46 + childrenField: 'children',
  47 + placeholder: '请选择上级${table.classComment}',
  48 + treeDefaultExpandAll: true,
  49 + },
  50 + rules: 'selectRequired',
  51 + },
  52 +#end
24 #foreach($column in $columns) 53 #foreach($column in $columns)
25 #if ($column.createOperation || $column.updateOperation) 54 #if ($column.createOperation || $column.updateOperation)
26 -#if (!$column.primaryKey)## 忽略主键,不用在表单里 55 +#if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段,这里排除
27 #set ($dictType = $column.dictType) 56 #set ($dictType = $column.dictType)
28 #set ($javaType = $column.javaType) 57 #set ($javaType = $column.javaType)
29 #set ($javaField = $column.javaField) 58 #set ($javaField = $column.javaField)
@@ -69,6 +98,7 @@ export function useFormSchema(): VbenFormSchema[] { @@ -69,6 +98,7 @@ export function useFormSchema(): VbenFormSchema[] {
69 options: [], 98 options: [],
70 #end 99 #end
71 placeholder: '请选择${comment}', 100 placeholder: '请选择${comment}',
  101 + class: 'w-full',
72 }, 102 },
73 #elseif($column.htmlType == "checkbox")## 多选框 103 #elseif($column.htmlType == "checkbox")## 多选框
74 component: 'Checkbox', 104 component: 'Checkbox',
@@ -102,6 +132,14 @@ export function useFormSchema(): VbenFormSchema[] { @@ -102,6 +132,14 @@ export function useFormSchema(): VbenFormSchema[] {
102 componentProps: { 132 componentProps: {
103 placeholder: '请输入${comment}', 133 placeholder: '请输入${comment}',
104 }, 134 },
  135 + #elseif($column.htmlType == "inputNumber")## 数字输入框
  136 + component: 'InputNumber',
  137 + componentProps: {
  138 + min: 0,
  139 + class: 'w-full',
  140 + controlsPosition: 'right',
  141 + placeholder: '请输入${comment}',
  142 + },
105 #end 143 #end
106 }, 144 },
107 #end 145 #end
@@ -159,7 +197,6 @@ export function useGridFormSchema(): VbenFormSchema[] { @@ -159,7 +197,6 @@ export function useGridFormSchema(): VbenFormSchema[] {
159 #elseif($column.htmlType == "datetime") 197 #elseif($column.htmlType == "datetime")
160 component: 'RangePicker', 198 component: 'RangePicker',
161 componentProps: { 199 componentProps: {
162 - ...getRangePickerDefaultProps(),  
163 allowClear: true, 200 allowClear: true,
164 }, 201 },
165 #end 202 #end
@@ -170,9 +207,9 @@ export function useGridFormSchema(): VbenFormSchema[] { @@ -170,9 +207,9 @@ export function useGridFormSchema(): VbenFormSchema[] {
170 } 207 }
171 208
172 /** 列表的字段 */ 209 /** 列表的字段 */
173 -export function useGridColumns<T = ${simpleClassName}Api.${simpleClassName}>(  
174 - onActionClick: OnActionClickFn<T>,  
175 -): VxeTableGridOptions['columns'] { 210 +export function useGridColumns(
  211 + onActionClick?: OnActionClickFn<${simpleClassName}Api.${simpleClassName}>,
  212 +): VxeTableGridOptions<${simpleClassName}Api.${simpleClassName}>['columns'] {
176 return [ 213 return [
177 #foreach($column in $columns) 214 #foreach($column in $columns)
178 #if ($column.listOperationResult) 215 #if ($column.listOperationResult)
@@ -191,15 +228,20 @@ export function useGridColumns&lt;T = ${simpleClassName}Api.${simpleClassName}&gt;( @@ -191,15 +228,20 @@ export function useGridColumns&lt;T = ${simpleClassName}Api.${simpleClassName}&gt;(
191 props: { type: DICT_TYPE.$dictType.toUpperCase() }, 228 props: { type: DICT_TYPE.$dictType.toUpperCase() },
192 }, 229 },
193 #end 230 #end
  231 + #if (${table.templateType} == 2 && $column.id == $treeNameColumn.id)## 树表特有:标记树节点列
  232 + treeNode: true,
  233 + #end
194 }, 234 },
195 #end 235 #end
196 #end 236 #end
197 { 237 {
198 field: 'operation', 238 field: 'operation',
199 title: '操作', 239 title: '操作',
200 - minWidth: 180,  
201 - align: 'center', 240 + minWidth: 200,
  241 + align: 'right',
202 fixed: 'right', 242 fixed: 'right',
  243 + headerAlign: 'center',
  244 + showOverflow: false,
203 cellRender: { 245 cellRender: {
204 attrs: { 246 attrs: {
205 nameField: '${columns[0].javaField}', 247 nameField: '${columns[0].javaField}',
@@ -208,6 +250,13 @@ export function useGridColumns&lt;T = ${simpleClassName}Api.${simpleClassName}&gt;( @@ -208,6 +250,13 @@ export function useGridColumns&lt;T = ${simpleClassName}Api.${simpleClassName}&gt;(
208 }, 250 },
209 name: 'CellOperation', 251 name: 'CellOperation',
210 options: [ 252 options: [
  253 +#if (${table.templateType} == 2)## 树表特有操作
  254 + {
  255 + code: 'add_child',
  256 + text: '新增下级',
  257 + show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:create']),
  258 + },
  259 +#end
211 { 260 {
212 code: 'edit', 261 code: 'edit',
213 show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:update']), 262 show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:update']),
@@ -215,7 +264,12 @@ export function useGridColumns&lt;T = ${simpleClassName}Api.${simpleClassName}&gt;( @@ -215,7 +264,12 @@ export function useGridColumns&lt;T = ${simpleClassName}Api.${simpleClassName}&gt;(
215 { 264 {
216 code: 'delete', 265 code: 'delete',
217 show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']), 266 show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']),
218 - } 267 +#if (${table.templateType} == 2)## 树表禁止删除带有子节点的数据
  268 + disabled: (row: ${simpleClassName}Api.${simpleClassName}) => {
  269 + return !!(row.children && row.children.length > 0);
  270 + },
  271 +#end
  272 + },
219 ], 273 ],
220 }, 274 },
221 }, 275 },
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm
@@ -4,8 +4,8 @@ import type { ${simpleClassName}Api } from &#39;#/api/${table.moduleName}/${simpleCl @@ -4,8 +4,8 @@ import type { ${simpleClassName}Api } from &#39;#/api/${table.moduleName}/${simpleCl
4 import { useVbenModal } from '@vben/common-ui'; 4 import { useVbenModal } from '@vben/common-ui';
5 import { message } from 'ant-design-vue'; 5 import { message } from 'ant-design-vue';
6 6
7 -import { $t } from '#/locales';  
8 import { computed, ref } from 'vue'; 7 import { computed, ref } from 'vue';
  8 +import { $t } from '#/locales';
9 import { useVbenForm } from '#/adapter/form'; 9 import { useVbenForm } from '#/adapter/form';
10 import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 10 import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
11 11
@@ -13,11 +13,25 @@ import { useFormSchema } from &#39;../data&#39;; @@ -13,11 +13,25 @@ import { useFormSchema } from &#39;../data&#39;;
13 13
14 const emit = defineEmits(['success']); 14 const emit = defineEmits(['success']);
15 const formData = ref<${simpleClassName}Api.${simpleClassName}>(); 15 const formData = ref<${simpleClassName}Api.${simpleClassName}>();
  16 +#if (${table.templateType} == 2)## 树表特有:父ID处理
  17 +// 新增下级时的父级ID
  18 +const parentId = ref<number>();
  19 +
  20 +const getTitle = computed(() => {
  21 + if (formData.value?.id) {
  22 + return $t('ui.actionTitle.edit', ['${table.classComment}']);
  23 + }
  24 + return parentId.value
  25 + ? $t('ui.actionTitle.create', ['下级${table.classComment}'])
  26 + : $t('ui.actionTitle.create', ['${table.classComment}']);
  27 +});
  28 +#else## 标准表标题
16 const getTitle = computed(() => { 29 const getTitle = computed(() => {
17 return formData.value?.id 30 return formData.value?.id
18 ? $t('ui.actionTitle.edit', ['${table.classComment}']) 31 ? $t('ui.actionTitle.edit', ['${table.classComment}'])
19 : $t('ui.actionTitle.create', ['${table.classComment}']); 32 : $t('ui.actionTitle.create', ['${table.classComment}']);
20 }); 33 });
  34 +#end
21 35
22 const [Form, formApi] = useVbenForm({ 36 const [Form, formApi] = useVbenForm({
23 layout: 'horizontal', 37 layout: 'horizontal',
@@ -55,17 +69,47 @@ const [Modal, modalApi] = useVbenModal({ @@ -55,17 +69,47 @@ const [Modal, modalApi] = useVbenModal({
55 return; 69 return;
56 } 70 }
57 // 加载数据 71 // 加载数据
  72 +#if (${table.templateType} == 2)## 树表处理传入的父ID
  73 + let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();
  74 +#else## 标准表直接获取
58 const data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>(); 75 const data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();
59 - if (!data || !data.id) { 76 +#end
  77 + if (!data) {
60 return; 78 return;
61 } 79 }
62 - modalApi.lock();  
63 - try {  
64 - formData.value = await get${simpleClassName}(data.id as number);  
65 - // 设置到 values 80 +
  81 +#if (${table.templateType} == 2)## 树表特有:处理新增下级的情况
  82 + // 处理新增下级的情况
  83 + if (!data.id && data.${treeParentColumn.javaField}) {
  84 + parentId.value = data.${treeParentColumn.javaField};
  85 + formData.value = { ${treeParentColumn.javaField}: parentId.value } as ${simpleClassName}Api.${simpleClassName};
66 await formApi.setValues(formData.value); 86 await formApi.setValues(formData.value);
67 - } finally {  
68 - modalApi.lock(false); 87 + return;
  88 + }
  89 +#end
  90 +
  91 + if (data.id) {
  92 + // 编辑
  93 + modalApi.lock();
  94 + try {
  95 +#if (${table.templateType} == 2)## 树表获取数据后重新赋值
  96 + data = await get${simpleClassName}(data.id);
  97 + formData.value = data;
  98 +#else## 标准表设置表单数据
  99 + formData.value = await get${simpleClassName}(data.id as number);
  100 +#end
  101 + await formApi.setValues(formData.value);
  102 + } finally {
  103 + modalApi.lock(false);
  104 + }
  105 + } else {
  106 + // 新增
  107 +#if (${table.templateType} == 2)## 树表特有:设置顶级ID
  108 + formData.value = { ${treeParentColumn.javaField}: 0 } as ${simpleClassName}Api.${simpleClassName};
  109 +#else## 标准表:设置空值
  110 + formData.value = data;
  111 +#end
  112 + await formApi.setValues(formData.value || {});
69 } 113 }
70 }, 114 },
71 }); 115 });
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm
@@ -8,9 +8,15 @@ import { Download, Plus } from &#39;@vben/icons&#39;; @@ -8,9 +8,15 @@ import { Download, Plus } from &#39;@vben/icons&#39;;
8 import Form from './modules/form.vue'; 8 import Form from './modules/form.vue';
9 import { DocAlert } from '#/components/doc-alert'; 9 import { DocAlert } from '#/components/doc-alert';
10 10
  11 +import { ref } from 'vue';
11 import { $t } from '#/locales'; 12 import { $t } from '#/locales';
12 import { useVbenVxeGrid } from '#/adapter/vxe-table'; 13 import { useVbenVxeGrid } from '#/adapter/vxe-table';
  14 +#if (${table.templateType} == 2)## 树表接口
  15 +import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
  16 +import { handleTree } from '#/utils/tree';
  17 +#else## 标准表接口
13 import { get${simpleClassName}Page, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; 18 import { get${simpleClassName}Page, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}';
  19 +#end
14 import { downloadByData } from '#/utils/download'; 20 import { downloadByData } from '#/utils/download';
15 21
16 import { useGridColumns, useGridFormSchema } from './data'; 22 import { useGridColumns, useGridFormSchema } from './data';
@@ -20,6 +26,15 @@ const [FormModal, formModalApi] = useVbenModal({ @@ -20,6 +26,15 @@ const [FormModal, formModalApi] = useVbenModal({
20 destroyOnClose: true, 26 destroyOnClose: true,
21 }); 27 });
22 28
  29 +#if (${table.templateType} == 2)## 树表特有:控制表格展开收缩
  30 +/** 切换树形展开/收缩状态 */
  31 +const isExpanded = ref(true);
  32 +function toggleExpand() {
  33 + isExpanded.value = !isExpanded.value;
  34 + gridApi.grid.setAllTreeExpand(isExpanded.value);
  35 +}
  36 +#end
  37 +
23 /** 刷新表格 */ 38 /** 刷新表格 */
24 function onRefresh() { 39 function onRefresh() {
25 gridApi.query(); 40 gridApi.query();
@@ -41,17 +56,24 @@ function onEdit(row: ${simpleClassName}Api.${simpleClassName}) { @@ -41,17 +56,24 @@ function onEdit(row: ${simpleClassName}Api.${simpleClassName}) {
41 formModalApi.setData(row).open(); 56 formModalApi.setData(row).open();
42 } 57 }
43 58
  59 +#if (${table.templateType} == 2)## 树表特有:新增下级
  60 +/** 新增下级${table.classComment} */
  61 +function onAddChild(row: ${simpleClassName}Api.${simpleClassName}) {
  62 + formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open();
  63 +}
  64 +#end
  65 +
44 /** 删除${table.classComment} */ 66 /** 删除${table.classComment} */
45 async function onDelete(row: ${simpleClassName}Api.${simpleClassName}) { 67 async function onDelete(row: ${simpleClassName}Api.${simpleClassName}) {
46 const hideLoading = message.loading({ 68 const hideLoading = message.loading({
47 - content: $t('ui.actionMessage.deleting', [row.${columns[0].javaField}]), 69 + content: $t('ui.actionMessage.deleting', [row.id]),
48 duration: 0, 70 duration: 0,
49 key: 'action_process_msg', 71 key: 'action_process_msg',
50 }); 72 });
51 try { 73 try {
52 await delete${simpleClassName}(row.id as number); 74 await delete${simpleClassName}(row.id as number);
53 message.success({ 75 message.success({
54 - content: $t('ui.actionMessage.deleteSuccess', [row.${columns[0].javaField}]), 76 + content: $t('ui.actionMessage.deleteSuccess', [row.id]),
55 key: 'action_process_msg', 77 key: 'action_process_msg',
56 }); 78 });
57 onRefresh(); 79 onRefresh();
@@ -74,6 +96,12 @@ function onActionClick({ @@ -74,6 +96,12 @@ function onActionClick({
74 onDelete(row); 96 onDelete(row);
75 break; 97 break;
76 } 98 }
  99 +#if (${table.templateType} == 2)## 树表特有:新增下级
  100 + case 'add_child': {
  101 + onAddChild(row);
  102 + break;
  103 + }
  104 +#end
77 } 105 }
78 } 106 }
79 107
@@ -84,20 +112,40 @@ const [Grid, gridApi] = useVbenVxeGrid({ @@ -84,20 +112,40 @@ const [Grid, gridApi] = useVbenVxeGrid({
84 gridOptions: { 112 gridOptions: {
85 columns: useGridColumns(onActionClick), 113 columns: useGridColumns(onActionClick),
86 height: 'auto', 114 height: 'auto',
87 - keepSource: true, 115 +#if (${table.templateType} == 2)## 树表设置
  116 + treeConfig: {
  117 + parentField: '${treeParentColumn.javaField}',
  118 + rowField: 'id',
  119 + transform: true,
  120 + expandAll: true,
  121 + reserve: true,
  122 + },
  123 +#else## 标准表设置
  124 + pagerConfig: {
  125 + enabled: true,
  126 + },
  127 +#end
88 proxyConfig: { 128 proxyConfig: {
89 ajax: { 129 ajax: {
  130 +#if (${table.templateType} == 2)## 树表数据加载
  131 + query: async (_, formValues) => {
  132 + return await get${simpleClassName}List(formValues);
  133 + },
  134 +#else## 标准表数据加载
90 query: async ({ page }, formValues) => { 135 query: async ({ page }, formValues) => {
91 - return await get${simpleClassName}Page({ 136 + const { items, total } = await get${simpleClassName}Page({
92 pageNo: page.currentPage, 137 pageNo: page.currentPage,
93 pageSize: page.pageSize, 138 pageSize: page.pageSize,
94 ...formValues, 139 ...formValues,
95 }); 140 });
  141 + return { items, total };
96 }, 142 },
  143 +#end
97 }, 144 },
98 }, 145 },
99 rowConfig: { 146 rowConfig: {
100 keyField: 'id', 147 keyField: 'id',
  148 + isHover: true,
101 }, 149 },
102 toolbarConfig: { 150 toolbarConfig: {
103 refresh: { code: 'query' }, 151 refresh: { code: 'query' },
@@ -112,8 +160,14 @@ const [Grid, gridApi] = useVbenVxeGrid({ @@ -112,8 +160,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
112 <DocAlert title="${table.classComment}" url="https://doc.iocoder.cn/${table.moduleName}/" /> 160 <DocAlert title="${table.classComment}" url="https://doc.iocoder.cn/${table.moduleName}/" />
113 161
114 <FormModal @success="onRefresh" /> 162 <FormModal @success="onRefresh" />
  163 +
115 <Grid table-title="${table.classComment}列表"> 164 <Grid table-title="${table.classComment}列表">
116 <template #toolbar-tools> 165 <template #toolbar-tools>
  166 +#if (${table.templateType} == 2)## 树表特有:展开/收缩按钮
  167 + <Button @click="toggleExpand" class="mr-2">
  168 + {{ isExpanded ? '收缩' : '展开' }}
  169 + </Button>
  170 +#end
117 <Button type="primary" @click="onCreate" v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:create']"> 171 <Button type="primary" @click="onCreate" v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:create']">
118 <Plus class="size-5" /> 172 <Plus class="size-5" />
119 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }} 173 {{ $t('ui.actionTitle.create', ['${table.classComment}']) }}