@liujitcn/kratos-admin-system 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +137 -0
  2. package/dist/package/declarations/index.d.ts +1 -0
  3. package/dist/package/declarations/src/ai.d.ts +10 -0
  4. package/dist/package/declarations/src/api/base/ai_message.d.ts +27 -0
  5. package/dist/package/declarations/src/api/base/ai_session.d.ts +17 -0
  6. package/dist/package/declarations/src/api/base/ai_tool.d.ts +7 -0
  7. package/dist/package/declarations/src/api/base/oauth.d.ts +13 -0
  8. package/dist/package/declarations/src/api/base/sse.d.ts +31 -0
  9. package/dist/package/declarations/src/api/system/auth.d.ts +17 -0
  10. package/dist/package/declarations/src/api/system/base_api.d.ts +22 -0
  11. package/dist/package/declarations/src/api/system/base_area.d.ts +19 -0
  12. package/dist/package/declarations/src/api/system/base_config.d.ts +20 -0
  13. package/dist/package/declarations/src/api/system/base_dept.d.ts +21 -0
  14. package/dist/package/declarations/src/api/system/base_dict.d.ts +32 -0
  15. package/dist/package/declarations/src/api/system/base_job.d.ts +28 -0
  16. package/dist/package/declarations/src/api/system/base_log.d.ts +9 -0
  17. package/dist/package/declarations/src/api/system/base_menu.d.ts +21 -0
  18. package/dist/package/declarations/src/api/system/base_migration.d.ts +10 -0
  19. package/dist/package/declarations/src/api/system/base_post.d.ts +21 -0
  20. package/dist/package/declarations/src/api/system/base_role.d.ts +23 -0
  21. package/dist/package/declarations/src/api/system/base_tenant.d.ts +21 -0
  22. package/dist/package/declarations/src/api/system/base_user.d.ts +23 -0
  23. package/dist/package/declarations/src/api/system/code_gen.d.ts +14 -0
  24. package/dist/package/declarations/src/api/system/code_gen_column.d.ts +12 -0
  25. package/dist/package/declarations/src/api/system/code_gen_proto.d.ts +10 -0
  26. package/dist/package/declarations/src/api/system/code_gen_sse.d.ts +8 -0
  27. package/dist/package/declarations/src/api/system/code_gen_table.d.ts +18 -0
  28. package/dist/package/declarations/src/api/system/project_document.d.ts +9 -0
  29. package/dist/package/declarations/src/components/Ai.vue.d.ts +3 -0
  30. package/dist/package/declarations/src/index.d.ts +3 -0
  31. package/dist/package/declarations/src/module.d.ts +2 -0
  32. package/dist/package/declarations/src/rpc/base/v1/ai_message.d.ts +82 -0
  33. package/dist/package/declarations/src/rpc/base/v1/ai_session.d.ts +176 -0
  34. package/dist/package/declarations/src/rpc/base/v1/ai_tool.d.ts +59 -0
  35. package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +167 -0
  36. package/dist/package/declarations/src/rpc/base/v1/sse.d.ts +13 -0
  37. package/dist/package/declarations/src/rpc/common/v1/common.d.ts +57 -0
  38. package/dist/package/declarations/src/rpc/common/v1/enum.d.ts +58 -0
  39. package/dist/package/declarations/src/rpc/common/v1/types.d.ts +60 -0
  40. package/dist/package/declarations/src/rpc/google/protobuf/empty.d.ts +11 -0
  41. package/dist/package/declarations/src/rpc/google/protobuf/timestamp.d.ts +107 -0
  42. package/dist/package/declarations/src/rpc/system/admin/v1/auth.d.ts +156 -0
  43. package/dist/package/declarations/src/rpc/system/admin/v1/base_api.d.ts +193 -0
  44. package/dist/package/declarations/src/rpc/system/admin/v1/base_area.d.ts +82 -0
  45. package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +112 -0
  46. package/dist/package/declarations/src/rpc/system/admin/v1/base_dept.d.ts +112 -0
  47. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +215 -0
  48. package/dist/package/declarations/src/rpc/system/admin/v1/base_job.d.ts +177 -0
  49. package/dist/package/declarations/src/rpc/system/admin/v1/base_log.d.ts +87 -0
  50. package/dist/package/declarations/src/rpc/system/admin/v1/base_menu.d.ts +151 -0
  51. package/dist/package/declarations/src/rpc/system/admin/v1/base_migration.d.ts +64 -0
  52. package/dist/package/declarations/src/rpc/system/admin/v1/base_post.d.ts +112 -0
  53. package/dist/package/declarations/src/rpc/system/admin/v1/base_role.d.ts +128 -0
  54. package/dist/package/declarations/src/rpc/system/admin/v1/base_tenant.d.ts +112 -0
  55. package/dist/package/declarations/src/rpc/system/admin/v1/base_user.d.ts +161 -0
  56. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen.d.ts +155 -0
  57. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_column.d.ts +143 -0
  58. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_proto.d.ts +86 -0
  59. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_table.d.ts +149 -0
  60. package/dist/package/declarations/src/rpc/system/admin/v1/project_document.d.ts +64 -0
  61. package/dist/package/declarations/src/rpc/system/common/v1/enum.d.ts +67 -0
  62. package/dist/package/declarations/src/views/ai/chat/attachment.d.ts +14 -0
  63. package/dist/package/declarations/src/views/ai/chat/components/AiMarkdown.vue.d.ts +13 -0
  64. package/dist/package/declarations/src/views/ai/chat/components/ChatPanel.vue.d.ts +45 -0
  65. package/dist/package/declarations/src/views/ai/chat/components/SessionPanel.vue.d.ts +34 -0
  66. package/dist/package/declarations/src/views/ai/chat/components/XSender.vue.d.ts +12 -0
  67. package/dist/package/declarations/src/views/ai/chat/index.vue.d.ts +4 -0
  68. package/dist/package/declarations/src/views/ai/chat/message.d.ts +59 -0
  69. package/dist/package/declarations/src/views/ai/chat/stream.d.ts +10 -0
  70. package/dist/package/declarations/src/views/ai/chat/types.d.ts +115 -0
  71. package/dist/package/declarations/src/views/base/api/index.vue.d.ts +3 -0
  72. package/dist/package/declarations/src/views/base/area/index.vue.d.ts +3 -0
  73. package/dist/package/declarations/src/views/base/config/index.vue.d.ts +3 -0
  74. package/dist/package/declarations/src/views/base/dept/index.vue.d.ts +3 -0
  75. package/dist/package/declarations/src/views/base/dict/index.vue.d.ts +3 -0
  76. package/dist/package/declarations/src/views/base/dict/item.vue.d.ts +3 -0
  77. package/dist/package/declarations/src/views/base/job/index.vue.d.ts +3 -0
  78. package/dist/package/declarations/src/views/base/job/log.vue.d.ts +3 -0
  79. package/dist/package/declarations/src/views/base/log/index.vue.d.ts +3 -0
  80. package/dist/package/declarations/src/views/base/menu/index.vue.d.ts +3 -0
  81. package/dist/package/declarations/src/views/base/migration/index.vue.d.ts +4 -0
  82. package/dist/package/declarations/src/views/base/post/index.vue.d.ts +3 -0
  83. package/dist/package/declarations/src/views/base/role/index.vue.d.ts +3 -0
  84. package/dist/package/declarations/src/views/base/tenant/index.vue.d.ts +3 -0
  85. package/dist/package/declarations/src/views/base/user/components/dept-tree.vue.d.ts +19 -0
  86. package/dist/package/declarations/src/views/base/user/index.vue.d.ts +3 -0
  87. package/dist/package/declarations/src/views/profile/components/base.vue.d.ts +13 -0
  88. package/dist/package/declarations/src/views/profile/components/password.vue.d.ts +3 -0
  89. package/dist/package/declarations/src/views/profile/components/security.vue.d.ts +15 -0
  90. package/dist/package/declarations/src/views/profile/index.vue.d.ts +3 -0
  91. package/dist/package/declarations/src/views/tool/api-doc/index.vue.d.ts +4 -0
  92. package/dist/package/declarations/src/views/tool/code-gen/code-preview/index.vue.d.ts +3 -0
  93. package/dist/package/declarations/src/views/tool/code-gen/columns/index.vue.d.ts +3 -0
  94. package/dist/package/declarations/src/views/tool/code-gen/columns/option-copy.d.ts +25 -0
  95. package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenProgressDialog.vue.d.ts +16 -0
  96. package/dist/package/declarations/src/views/tool/code-gen/components/CodePreviewPane.vue.d.ts +9 -0
  97. package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +32 -0
  98. package/dist/package/declarations/src/views/tool/code-gen/preview/capabilities.d.ts +20 -0
  99. package/dist/package/declarations/src/views/tool/code-gen/preview/data.d.ts +42 -0
  100. package/dist/package/declarations/src/views/tool/code-gen/preview/index.vue.d.ts +3 -0
  101. package/dist/package/declarations/src/views/tool/code-gen/proto/index.vue.d.ts +3 -0
  102. package/dist/package/declarations/src/views/tool/code-gen/table/index.vue.d.ts +3 -0
  103. package/dist/package/declarations/src/views/tool/project-doc/index.vue.d.ts +4 -0
  104. package/dist/package/src/ai.ts +16 -0
  105. package/dist/package/src/api/base/ai_message.ts +139 -0
  106. package/dist/package/src/api/base/ai_session.ts +77 -0
  107. package/dist/package/src/api/base/ai_tool.ts +22 -0
  108. package/dist/package/src/api/base/oauth.ts +47 -0
  109. package/dist/package/src/api/base/sse.ts +192 -0
  110. package/dist/package/src/api/system/auth.ts +65 -0
  111. package/dist/package/src/api/system/base_api.ts +95 -0
  112. package/dist/package/src/api/system/base_area.ts +73 -0
  113. package/dist/package/src/api/system/base_config.ts +82 -0
  114. package/dist/package/src/api/system/base_dept.ts +83 -0
  115. package/dist/package/src/api/system/base_dict.ts +144 -0
  116. package/dist/package/src/api/system/base_job.ts +125 -0
  117. package/dist/package/src/api/system/base_log.ts +32 -0
  118. package/dist/package/src/api/system/base_menu.ts +83 -0
  119. package/dist/package/src/api/system/base_migration.ts +35 -0
  120. package/dist/package/src/api/system/base_post.ts +83 -0
  121. package/dist/package/src/api/system/base_role.ts +93 -0
  122. package/dist/package/src/api/system/base_tenant.ts +83 -0
  123. package/dist/package/src/api/system/base_user.ts +93 -0
  124. package/dist/package/src/api/system/code_gen.ts +56 -0
  125. package/dist/package/src/api/system/code_gen_column.ts +43 -0
  126. package/dist/package/src/api/system/code_gen_proto.ts +32 -0
  127. package/dist/package/src/api/system/code_gen_sse.ts +33 -0
  128. package/dist/package/src/api/system/code_gen_table.ts +74 -0
  129. package/dist/package/src/api/system/project_document.ts +33 -0
  130. package/dist/package/src/components/Ai.vue +57 -0
  131. package/dist/package/src/index.ts +3 -0
  132. package/dist/package/src/module.ts +17 -0
  133. package/dist/package/src/rpc/base/v1/ai_message.ts +98 -0
  134. package/dist/package/src/rpc/base/v1/ai_session.ts +214 -0
  135. package/dist/package/src/rpc/base/v1/ai_tool.ts +74 -0
  136. package/dist/package/src/rpc/base/v1/oauth.ts +199 -0
  137. package/dist/package/src/rpc/base/v1/sse.ts +22 -0
  138. package/dist/package/src/rpc/common/v1/common.ts +71 -0
  139. package/dist/package/src/rpc/common/v1/enum.ts +71 -0
  140. package/dist/package/src/rpc/common/v1/types.ts +77 -0
  141. package/dist/package/src/rpc/google/protobuf/empty.ts +19 -0
  142. package/dist/package/src/rpc/google/protobuf/timestamp.ts +115 -0
  143. package/dist/package/src/rpc/system/admin/v1/auth.ts +208 -0
  144. package/dist/package/src/rpc/system/admin/v1/base_api.ts +241 -0
  145. package/dist/package/src/rpc/system/admin/v1/base_area.ts +105 -0
  146. package/dist/package/src/rpc/system/admin/v1/base_config.ts +140 -0
  147. package/dist/package/src/rpc/system/admin/v1/base_dept.ts +140 -0
  148. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +249 -0
  149. package/dist/package/src/rpc/system/admin/v1/base_job.ts +206 -0
  150. package/dist/package/src/rpc/system/admin/v1/base_log.ts +101 -0
  151. package/dist/package/src/rpc/system/admin/v1/base_menu.ts +195 -0
  152. package/dist/package/src/rpc/system/admin/v1/base_migration.ts +81 -0
  153. package/dist/package/src/rpc/system/admin/v1/base_post.ts +134 -0
  154. package/dist/package/src/rpc/system/admin/v1/base_role.ts +151 -0
  155. package/dist/package/src/rpc/system/admin/v1/base_tenant.ts +134 -0
  156. package/dist/package/src/rpc/system/admin/v1/base_user.ts +190 -0
  157. package/dist/package/src/rpc/system/admin/v1/code_gen.ts +176 -0
  158. package/dist/package/src/rpc/system/admin/v1/code_gen_column.ts +170 -0
  159. package/dist/package/src/rpc/system/admin/v1/code_gen_proto.ts +104 -0
  160. package/dist/package/src/rpc/system/admin/v1/code_gen_table.ts +179 -0
  161. package/dist/package/src/rpc/system/admin/v1/project_document.ts +79 -0
  162. package/dist/package/src/rpc/system/common/v1/enum.ts +80 -0
  163. package/dist/package/src/typings/swagger-ui-dist.d.ts +28 -0
  164. package/dist/package/src/typings/x-markdown-vue.d.ts +1 -0
  165. package/dist/package/src/views/ai/chat/attachment.ts +65 -0
  166. package/dist/package/src/views/ai/chat/components/AiMarkdown.vue +162 -0
  167. package/dist/package/src/views/ai/chat/components/ChatPanel.vue +1861 -0
  168. package/dist/package/src/views/ai/chat/components/SessionPanel.vue +316 -0
  169. package/dist/package/src/views/ai/chat/components/XSender.vue +554 -0
  170. package/dist/package/src/views/ai/chat/index.vue +848 -0
  171. package/dist/package/src/views/ai/chat/message.ts +416 -0
  172. package/dist/package/src/views/ai/chat/stream.ts +115 -0
  173. package/dist/package/src/views/ai/chat/types.ts +126 -0
  174. package/dist/package/src/views/base/api/index.vue +455 -0
  175. package/dist/package/src/views/base/area/index.vue +269 -0
  176. package/dist/package/src/views/base/config/index.vue +530 -0
  177. package/dist/package/src/views/base/dept/index.vue +461 -0
  178. package/dist/package/src/views/base/dict/index.vue +304 -0
  179. package/dist/package/src/views/base/dict/item.vue +373 -0
  180. package/dist/package/src/views/base/job/index.vue +535 -0
  181. package/dist/package/src/views/base/job/log.vue +202 -0
  182. package/dist/package/src/views/base/log/index.vue +317 -0
  183. package/dist/package/src/views/base/menu/index.vue +944 -0
  184. package/dist/package/src/views/base/migration/index.vue +791 -0
  185. package/dist/package/src/views/base/post/index.vue +324 -0
  186. package/dist/package/src/views/base/role/index.vue +684 -0
  187. package/dist/package/src/views/base/tenant/index.vue +328 -0
  188. package/dist/package/src/views/base/user/components/dept-tree.vue +75 -0
  189. package/dist/package/src/views/base/user/index.vue +783 -0
  190. package/dist/package/src/views/profile/components/base.vue +320 -0
  191. package/dist/package/src/views/profile/components/password.vue +269 -0
  192. package/dist/package/src/views/profile/components/security.vue +443 -0
  193. package/dist/package/src/views/profile/index.vue +199 -0
  194. package/dist/package/src/views/tool/api-doc/index.vue +275 -0
  195. package/dist/package/src/views/tool/code-gen/code-preview/index.vue +233 -0
  196. package/dist/package/src/views/tool/code-gen/columns/index.vue +1456 -0
  197. package/dist/package/src/views/tool/code-gen/columns/option-copy.ts +125 -0
  198. package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +328 -0
  199. package/dist/package/src/views/tool/code-gen/components/CodePreviewPane.vue +42 -0
  200. package/dist/package/src/views/tool/code-gen/config.ts +175 -0
  201. package/dist/package/src/views/tool/code-gen/preview/capabilities.ts +34 -0
  202. package/dist/package/src/views/tool/code-gen/preview/data.ts +331 -0
  203. package/dist/package/src/views/tool/code-gen/preview/index.vue +831 -0
  204. package/dist/package/src/views/tool/code-gen/proto/index.vue +703 -0
  205. package/dist/package/src/views/tool/code-gen/table/index.vue +864 -0
  206. package/dist/package/src/views/tool/project-doc/index.vue +561 -0
  207. package/package.json +60 -0
  208. package/tsconfig.json +13 -0
@@ -0,0 +1,831 @@
1
+ <!-- 代码生成完整页面效果预览 -->
2
+ <template>
3
+ <div v-loading="loading" class="app-container code-gen-page-preview-page">
4
+ <template v-if="snapshot">
5
+ <section v-if="pageType === 'left_tree'" class="main-box code-gen-left-tree-preview">
6
+ <TreeFilter
7
+ id="value"
8
+ label="label"
9
+ :title="leftTreeTitle"
10
+ :data="leftTreeLazy ? undefined : leftTreeOptions"
11
+ :request-api="leftTreeLazy ? requestPreviewLeftTree : undefined"
12
+ :lazy="leftTreeLazy"
13
+ :parent-key="leftTreeParentColumn"
14
+ :show-all="false"
15
+ @change="handleLeftTreeChange"
16
+ />
17
+ <div class="code-gen-left-tree-preview__table table-box">
18
+ <ProTable
19
+ :key="previewTableKey"
20
+ ref="proTable"
21
+ :row-key="primaryColumn"
22
+ :columns="tableColumns"
23
+ :header-actions="headerActions"
24
+ :request-api="requestPreviewTable"
25
+ scrollbar-always-on
26
+ class="code-gen-page-preview-table"
27
+ />
28
+ </div>
29
+ </section>
30
+
31
+ <div v-else class="code-gen-page-preview__table table-box">
32
+ <ProTable
33
+ :key="previewTableKey"
34
+ ref="proTable"
35
+ :row-key="primaryColumn"
36
+ :columns="tableColumns"
37
+ :header-actions="headerActions"
38
+ :request-api="requestPreviewTable"
39
+ :pagination="pageType !== 'tree' && pageType !== 'tree_lazy'"
40
+ :indent="20"
41
+ :lazy="pageType === 'tree_lazy'"
42
+ :load="pageType === 'tree_lazy' ? loadPreviewTreeChildren : undefined"
43
+ :tree-props="
44
+ pageType === 'tree' || pageType === 'tree_lazy'
45
+ ? { children: 'children', hasChildren: pageType === 'tree_lazy' ? 'has_children' : 'hasChildren' }
46
+ : undefined
47
+ "
48
+ scrollbar-always-on
49
+ class="code-gen-page-preview-table"
50
+ />
51
+ </div>
52
+
53
+ <FormDialog
54
+ v-model="dialog.visible"
55
+ ref="formDialogRef"
56
+ :title="dialog.title"
57
+ width="min(920px, calc(100vw - 32px))"
58
+ top="4vh"
59
+ :model="previewFormModel"
60
+ :fields="formFields"
61
+ label-width="116px"
62
+ :gutter="20"
63
+ :col-span="12"
64
+ @confirm="handleSubmit"
65
+ @close="handleCloseDialog"
66
+ >
67
+ <template #passwordStrength>
68
+ <PasswordStrength :password="String(previewFormModel[passwordFieldName])" />
69
+ </template>
70
+ <template #codeGenPreviewSlot="{ field }">
71
+ <el-input v-model="previewFormModel[field.prop]" placeholder="自定义插槽内容">
72
+ <template #append>自定义</template>
73
+ </el-input>
74
+ </template>
75
+ </FormDialog>
76
+ </template>
77
+ <el-empty v-else-if="!loading" description="暂无页面配置" />
78
+ </div>
79
+ </template>
80
+
81
+ <script setup lang="ts">
82
+ import { computed, nextTick, onMounted, reactive, ref, watch } from "vue";
83
+ import { ElMessage, ElMessageBox } from "element-plus";
84
+ import { CirclePlus, Delete, EditPen } from "@element-plus/icons-vue";
85
+ import { useRoute } from "vue-router";
86
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
87
+ import type { ColumnProps, HeaderActionProps, ProTableInstance, RenderScope } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
88
+ import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
89
+ import PasswordStrength from "@liujitcn/kratos-admin-core/components/PasswordStrength/index.vue";
90
+ import type { ProFormComponentType, ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
91
+ import TreeFilter from "@liujitcn/kratos-admin-core/components/TreeFilter/index.vue";
92
+ import { useTabsStore } from "@liujitcn/kratos-admin-core/stores/runtime";
93
+ import { defBaseDictService } from "@liujitcn/kratos-admin-system/api/system/base_dict";
94
+ import { defCodeGenColumnService } from "@liujitcn/kratos-admin-system/api/system/code_gen_column";
95
+ import { defCodeGenProtoService } from "@liujitcn/kratos-admin-system/api/system/code_gen_proto";
96
+ import { defCodeGenTableService } from "@liujitcn/kratos-admin-system/api/system/code_gen_table";
97
+ import type { OptionBaseDictResponse_BaseDict } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_dict";
98
+ import type { CodeGenColumn, CodeGenColumnOptionConfig } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen_column";
99
+ import type { CodeGenProtoCheck } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen_proto";
100
+ import type { CodeGenDatabaseTable } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen_table";
101
+ import { codeGenFormComponentOptions } from "../config";
102
+ import { resolveCodeGenPreviewCapabilities } from "./capabilities";
103
+ import {
104
+ buildCodeGenPreviewTree,
105
+ createCodeGenLeftTreeOptions,
106
+ createCodeGenPreviewOptionMap,
107
+ createCodeGenPreviewRows,
108
+ filterCodeGenPreviewRows,
109
+ flattenCodeGenPreviewOptions,
110
+ isNumericColumn,
111
+ resolveCodeGenPreviewOptions,
112
+ resolveCodeGenPrimaryColumn,
113
+ type CodeGenPagePreviewSnapshot,
114
+ type CodeGenPreviewRow
115
+ } from "./data";
116
+
117
+ defineOptions({
118
+ name: "CodeGenPreview",
119
+ inheritAttrs: false
120
+ });
121
+
122
+ const route = useRoute();
123
+ const tabsStore = useTabsStore();
124
+ const proTable = ref<ProTableInstance>();
125
+ const formDialogRef = ref<InstanceType<typeof FormDialog>>();
126
+ const loading = ref(false);
127
+ const snapshot = ref<CodeGenPagePreviewSnapshot | null>(null);
128
+ const protoChecks = ref<CodeGenProtoCheck[]>([]);
129
+ const dictionaries = ref<OptionBaseDictResponse_BaseDict[]>([]);
130
+ const databaseTables = ref<CodeGenDatabaseTable[]>([]);
131
+ const mockRows = ref<CodeGenPreviewRow[]>([]);
132
+ const previewFormModel = reactive<Record<string, any>>({});
133
+ const editingRowKey = ref<string | number>();
134
+ const selectedLeftTreeValues = ref<Array<string | number | boolean>>([]);
135
+ const passwordFieldName = computed(() => snapshot.value?.columns.find(column => column.form_config?.component === "password")?.name ?? "pwd");
136
+ const supportedFormComponents = new Set(codeGenFormComponentOptions.map(item => String(item.value)));
137
+
138
+ const dialog = reactive({
139
+ title: "",
140
+ visible: false
141
+ });
142
+
143
+ /** 当前代码生成表配置 ID。 */
144
+ const tableId = computed(() => {
145
+ const value = route.params.tableId;
146
+ const id = Number(Array.isArray(value) ? value[0] : value);
147
+ return Number.isFinite(id) && id > 0 ? id : 0;
148
+ });
149
+
150
+ /** 当前页面类型。 */
151
+ const pageType = computed(() => snapshot.value?.table.page_type || "normal");
152
+
153
+ /** 页面预览表格重建键。 */
154
+ const previewTableKey = computed(() => `${tableId.value}:${pageType.value}`);
155
+
156
+ /** 当前真实主键字段。 */
157
+ const primaryColumn = computed(() => resolveCodeGenPrimaryColumn(snapshot.value?.columns ?? []));
158
+
159
+ /** 当前字段配置对应的全部模拟选项。 */
160
+ const optionMap = computed(() => createCodeGenPreviewOptionMap(snapshot.value?.columns ?? [], dictionaries.value));
161
+
162
+ /** 左树右表页面的模拟节点。 */
163
+ const leftTreeOptions = computed(() => createCodeGenLeftTreeOptions(snapshot.value?.table.left_tree_config));
164
+
165
+ /** 左树是否启用懒加载。 */
166
+ const leftTreeLazy = computed(() => pageType.value === "left_tree" && Boolean(snapshot.value?.table.left_tree_config?.lazy));
167
+
168
+ /** 左树懒加载请求使用的父节点字段。 */
169
+ const leftTreeParentColumn = computed(() => snapshot.value?.table.left_tree_config?.parent_column || "parent_id");
170
+
171
+ /** 旧配置缺少左树描述时,从数据库表元数据中读取描述。 */
172
+ const leftTreeTableComment = computed(() => {
173
+ const tableName = snapshot.value?.table.left_tree_config?.table_name;
174
+ return databaseTables.value.find(item => item.name === tableName)?.comment || "";
175
+ });
176
+
177
+ /** 左树标题优先使用可编辑描述,旧配置使用数据表描述补齐。 */
178
+ const leftTreeTitle = computed(
179
+ () =>
180
+ snapshot.value?.table.left_tree_config?.comment ||
181
+ leftTreeTableComment.value ||
182
+ snapshot.value?.table.left_tree_config?.table_name ||
183
+ "分类列表"
184
+ );
185
+
186
+ /** 当前实体已经存在或已经选择生成的 Proto 维护能力。 */
187
+ const protoCapabilities = computed(() =>
188
+ resolveCodeGenPreviewCapabilities(toPascalCase(snapshot.value?.table.name ?? ""), protoChecks.value)
189
+ );
190
+
191
+ /** 根据字段配置生成最终页面的查询项和列表列。 */
192
+ const tableColumns = computed<ColumnProps[]>(() => {
193
+ const columns = snapshot.value?.columns ?? [];
194
+ const treeParentColumn = pageType.value === "tree" || pageType.value === "tree_lazy" ? snapshot.value?.table.parent_column : "";
195
+ const treeLabelColumn = pageType.value === "tree" || pageType.value === "tree_lazy" ? snapshot.value?.table.tree_label_column : "";
196
+ const configuredColumns = columns
197
+ .filter(
198
+ column =>
199
+ column.name !== "deleted_at" &&
200
+ (column.list_config?.enabled ||
201
+ column.query_config?.enabled ||
202
+ column.name === treeLabelColumn ||
203
+ column.name === treeParentColumn)
204
+ )
205
+ .sort((left, right) => left.sort - right.sort)
206
+ .sort((left, right) => {
207
+ if (left.name === treeLabelColumn) return -1;
208
+ if (right.name === treeLabelColumn) return 1;
209
+ return 0;
210
+ })
211
+ .map(column => {
212
+ const isTreeLabel = column.name === treeLabelColumn;
213
+ const isTreeParent = column.name === treeParentColumn && treeParentColumn !== treeLabelColumn;
214
+ const listConfig = column.list_config ?? { enabled: false, component: "input", option: undefined };
215
+ const previewColumn = isTreeLabel && !listConfig.enabled
216
+ ? { ...column, list_config: { ...listConfig, enabled: true } }
217
+ : column;
218
+ const result = createPreviewTableColumn(previewColumn);
219
+ if (isTreeLabel) result.align = "left";
220
+ if (isTreeParent) {
221
+ result.isShow = false;
222
+ result.isSetting = false;
223
+ }
224
+ return result;
225
+ });
226
+ const result: ColumnProps[] = [...configuredColumns];
227
+ if (protoCapabilities.value.delete) result.unshift({ type: "selection", width: 55, fixed: "left" });
228
+ const actions: NonNullable<ColumnProps["actions"]> = [];
229
+ if (protoCapabilities.value.update) {
230
+ actions.push({
231
+ label: "编辑",
232
+ type: "primary",
233
+ link: true,
234
+ icon: EditPen,
235
+ onClick: scope => handleOpenDialog(scope.row as CodeGenPreviewRow)
236
+ });
237
+ }
238
+ if (protoCapabilities.value.delete) {
239
+ actions.push({
240
+ label: "删除",
241
+ type: "danger",
242
+ link: true,
243
+ icon: Delete,
244
+ onClick: scope => handleDelete([scope.row as CodeGenPreviewRow])
245
+ });
246
+ }
247
+ if (actions.length) {
248
+ result.push({
249
+ prop: "operation",
250
+ label: "操作",
251
+ width: actions.length === 1 ? 100 : 180,
252
+ fixed: "right",
253
+ cellType: "actions",
254
+ actions
255
+ });
256
+ }
257
+ return result;
258
+ });
259
+
260
+ /** 页面预览表格顶部操作。 */
261
+ const headerActions = computed<HeaderActionProps[]>(() => {
262
+ const actions: HeaderActionProps[] = [];
263
+ if (protoCapabilities.value.create && formFields.value.length) {
264
+ actions.push({
265
+ label: "新增",
266
+ type: "success",
267
+ icon: CirclePlus,
268
+ onClick: () => handleOpenDialog()
269
+ });
270
+ }
271
+ if (protoCapabilities.value.delete) {
272
+ actions.push({
273
+ label: "删除",
274
+ type: "danger",
275
+ icon: Delete,
276
+ disabled: scope => !scope.selectedList.length,
277
+ onClick: scope => handleDelete(scope.selectedList as CodeGenPreviewRow[])
278
+ });
279
+ }
280
+ return actions;
281
+ });
282
+
283
+ /** 根据真实表单字段配置生成新增、编辑弹窗。 */
284
+ const formFields = computed<ProFormField[]>(() => {
285
+ return (snapshot.value?.columns ?? [])
286
+ .filter(
287
+ column =>
288
+ !column.is_primary && !column.is_auto_increment && column.name !== "deleted_at" && column.form_config?.enabled
289
+ )
290
+ .sort((left, right) => left.sort - right.sort)
291
+ .flatMap(column => {
292
+ const label = column.comment || column.name;
293
+ const isTreeParent =
294
+ (pageType.value === "tree" || pageType.value === "tree_lazy") && column.name === snapshot.value?.table.parent_column;
295
+ const component = isTreeParent ? "tree-select" : resolvePreviewFormComponent(column.form_config?.component);
296
+ const isMultipleTreeSelect = component === "tree-select" && Boolean(column.form_config?.multiple);
297
+ const options = isTreeParent
298
+ ? treeParentOptions.value
299
+ : resolveCodeGenPreviewOptions(optionMap.value, column.name, "form");
300
+ const field: ProFormField = {
301
+ prop: column.name,
302
+ label,
303
+ component,
304
+ props: {
305
+ ...createPreviewFormProps(component, label, column.form_config?.option, isMultipleTreeSelect),
306
+ ...(isTreeParent ? { disabled: Boolean(previewFormModel[primaryColumn.value]) } : {})
307
+ },
308
+ options,
309
+ checkboxLabel: component === "checkbox" ? `启用${label}` : undefined,
310
+ slotName: component === "slot" ? "codeGenPreviewSlot" : undefined,
311
+ visible: component === "password" ? model => !model[primaryColumn.value] : undefined,
312
+ rules: column.form_config?.required ? [{ required: true, message: `${label}不能为空` }] : undefined,
313
+ colSpan: resolvePreviewColSpan(component)
314
+ };
315
+ if (component !== "password") return [field];
316
+ return [
317
+ field,
318
+ {
319
+ prop: "passwordStrength",
320
+ label: "强度提示",
321
+ component: "slot",
322
+ slotName: "passwordStrength",
323
+ visible: model => !model[primaryColumn.value]
324
+ }
325
+ ];
326
+ });
327
+ });
328
+
329
+ /** 树形表格新增弹窗中的父节点选项。 */
330
+ const treeParentOptions = computed<ProFormOption[]>(() => {
331
+ if (!snapshot.value?.table.parent_column || (pageType.value !== "tree" && pageType.value !== "tree_lazy")) return [];
332
+ const treeRows = buildCodeGenPreviewTree(mockRows.value, primaryColumn.value, snapshot.value.table.parent_column);
333
+ return [{ label: "顶级节点", value: 0 }, ...mapPreviewRowsToOptions(treeRows)];
334
+ });
335
+
336
+ // 路由生成对象变化时重新载入对应预览。
337
+ watch(tableId, () => {
338
+ void loadPreview();
339
+ });
340
+
341
+ /** 请求左树预览节点,懒加载时只返回当前父节点的直接子节点。 */
342
+ async function requestPreviewLeftTree(params?: Record<string, any>) {
343
+ const parentValue = params?.[leftTreeParentColumn.value] ?? 0;
344
+ return { data: findPreviewTreeChildren(leftTreeOptions.value, parentValue) };
345
+ }
346
+
347
+ /** 查找模拟树中指定父节点的直接子节点。 */
348
+ function findPreviewTreeChildren(options: ProFormOption[], parentValue: unknown): ProFormOption[] {
349
+ if (String(parentValue) === "0") {
350
+ return options.map(({ children: _children, ...option }) => option);
351
+ }
352
+ for (const option of options) {
353
+ if (String(option.value) === String(parentValue)) {
354
+ return (option.children ?? []).map(({ children: _children, ...child }) => child);
355
+ }
356
+ const nested = findPreviewTreeChildren(option.children ?? [], parentValue);
357
+ if (nested.length) return nested;
358
+ }
359
+ return [];
360
+ }
361
+
362
+ /** 请求前端模拟列表,并复用最终页面的查询与分页交互。 */
363
+ async function requestPreviewTable(params: Record<string, any>) {
364
+ const columns = snapshot.value?.columns ?? [];
365
+ let rows = filterCodeGenPreviewRows(mockRows.value, columns, params);
366
+ if (
367
+ pageType.value === "left_tree" &&
368
+ snapshot.value?.table.left_tree_config?.filter_column &&
369
+ selectedLeftTreeValues.value.length
370
+ ) {
371
+ const filterColumn = snapshot.value.table.left_tree_config.filter_column;
372
+ rows = rows.filter(row => selectedLeftTreeValues.value.some(value => String(value) === String(row[filterColumn])));
373
+ }
374
+ if (pageType.value === "tree_lazy" && snapshot.value?.table.parent_column) {
375
+ const parentColumn = snapshot.value.table.parent_column;
376
+ const parentValue = params[parentColumn] ?? 0;
377
+ const allRows = mockRows.value;
378
+ rows = rows
379
+ .filter(row => String(row[parentColumn] ?? 0) === String(parentValue))
380
+ .map(row => ({
381
+ ...row,
382
+ has_children: allRows.some(child => String(child[parentColumn] ?? 0) === String(row[primaryColumn.value]))
383
+ }));
384
+ return { data: rows };
385
+ }
386
+ if (pageType.value === "tree" && snapshot.value?.table.parent_column) {
387
+ return { data: buildCodeGenPreviewTree(rows, primaryColumn.value, snapshot.value.table.parent_column) };
388
+ }
389
+ const pageNum = Number(params.pageNum ?? 1);
390
+ const pageSize = Number(params.pageSize ?? 10);
391
+ const start = (pageNum - 1) * pageSize;
392
+ return { data: { list: rows.slice(start, start + pageSize), total: rows.length } };
393
+ }
394
+
395
+ /** 请求懒加载树表格的子节点。 */
396
+ async function loadPreviewTreeChildren(
397
+ row: CodeGenPreviewRow,
398
+ _treeNode: unknown,
399
+ resolve: (data: CodeGenPreviewRow[]) => void
400
+ ) {
401
+ const parentColumn = snapshot.value?.table.parent_column;
402
+ if (!parentColumn) {
403
+ resolve([]);
404
+ return;
405
+ }
406
+ const response = await requestPreviewTable({ [parentColumn]: row[primaryColumn.value], lazy: true });
407
+ resolve(Array.isArray(response.data) ? response.data : []);
408
+ }
409
+
410
+ /** 刷新预览表格。 */
411
+ function refreshTable() {
412
+ proTable.value?.getTableList();
413
+ }
414
+
415
+ /** 加载当前表、字段和 Proto 配置并创建页面预览。 */
416
+ async function loadPreview() {
417
+ loading.value = true;
418
+ try {
419
+ snapshot.value = null;
420
+ protoChecks.value = [];
421
+ dictionaries.value = [];
422
+ databaseTables.value = [];
423
+ if (!tableId.value) return;
424
+ const [table, columnResponse, protoResponse, dictionaryResponse, databaseTableResponse] = await Promise.all([
425
+ defCodeGenTableService.GetCodeGenTable({ id: tableId.value }),
426
+ defCodeGenColumnService.ListCodeGenColumn({ table_id: tableId.value }),
427
+ defCodeGenProtoService.ListCodeGenProto({ table_id: tableId.value }),
428
+ defBaseDictService.OptionBaseDict({}),
429
+ defCodeGenTableService.ListCodeGenDatabaseTable({})
430
+ ]);
431
+ snapshot.value = { table, columns: columnResponse.code_gen_columns ?? [] };
432
+ protoChecks.value = protoResponse.code_gen_protos ?? [];
433
+ dictionaries.value = dictionaryResponse.base_dicts ?? [];
434
+ databaseTables.value = databaseTableResponse.tables ?? [];
435
+ createMockRows();
436
+ syncWorkspaceTitle();
437
+ } finally {
438
+ loading.value = false;
439
+ }
440
+ }
441
+
442
+ /** 根据 TreeFilter 当前节点筛选该节点及其全部子节点。 */
443
+ function handleLeftTreeChange(value: string | number | boolean | undefined) {
444
+ const selectedNode = flattenCodeGenPreviewOptions(leftTreeOptions.value).find(option => String(option.value) === String(value));
445
+ selectedLeftTreeValues.value = selectedNode
446
+ ? flattenCodeGenPreviewOptions([selectedNode]).map(option => option.value)
447
+ : [];
448
+ proTable.value?.search();
449
+ }
450
+
451
+ /** 打开新增或编辑模拟记录弹窗。 */
452
+ function handleOpenDialog(row?: CodeGenPreviewRow) {
453
+ resetPreviewForm(row);
454
+ editingRowKey.value = row?.[primaryColumn.value];
455
+ dialog.title = row ? `编辑${snapshot.value?.table.comment || "数据"}` : `新增${snapshot.value?.table.comment || "数据"}`;
456
+ dialog.visible = true;
457
+ }
458
+
459
+ /** 关闭模拟表单并清理编辑上下文。 */
460
+ function handleCloseDialog() {
461
+ dialog.visible = false;
462
+ editingRowKey.value = undefined;
463
+ resetPreviewForm();
464
+ }
465
+
466
+ /** 重置预览表单并按组件类型写入结构正确的初始值。 */
467
+ function resetPreviewForm(row?: CodeGenPreviewRow) {
468
+ formDialogRef.value?.resetFields();
469
+ formDialogRef.value?.clearValidate();
470
+ Object.keys(previewFormModel).forEach(key => delete previewFormModel[key]);
471
+ formFields.value.forEach(field => {
472
+ previewFormModel[field.prop] = row ? resolvePreviewFormValue(field, row[field.prop]) : createPreviewFormValue(field);
473
+ });
474
+ }
475
+
476
+ /** 提交模拟新增或编辑,并刷新当前列表布局。 */
477
+ function handleSubmit() {
478
+ formDialogRef.value?.validate()?.then(async valid => {
479
+ if (!valid || !snapshot.value) return;
480
+ if (editingRowKey.value !== undefined) {
481
+ const rowIndex = mockRows.value.findIndex(row => String(row[primaryColumn.value]) === String(editingRowKey.value));
482
+ if (rowIndex >= 0) mockRows.value[rowIndex] = { ...mockRows.value[rowIndex], ...clonePreviewValue(previewFormModel) };
483
+ } else {
484
+ const template = createCodeGenPreviewRows(snapshot.value, optionMap.value, leftTreeOptions.value)[0] ?? {};
485
+ const nextPrimaryValue = createNextPrimaryValue();
486
+ const newRow = { ...template, ...clonePreviewValue(previewFormModel), [primaryColumn.value]: nextPrimaryValue };
487
+ if (
488
+ pageType.value === "left_tree" &&
489
+ snapshot.value.table.left_tree_config?.filter_column &&
490
+ selectedLeftTreeValues.value.length
491
+ ) {
492
+ newRow[snapshot.value.table.left_tree_config.filter_column] = selectedLeftTreeValues.value[0];
493
+ }
494
+ mockRows.value.unshift(newRow);
495
+ }
496
+ const successMessage = editingRowKey.value !== undefined ? "修改成功" : "新增成功";
497
+ handleCloseDialog();
498
+ await nextTick();
499
+ refreshTable();
500
+ ElMessage.success(successMessage);
501
+ });
502
+ }
503
+
504
+ /** 删除一条或多条模拟记录。 */
505
+ async function handleDelete(rows: CodeGenPreviewRow[]) {
506
+ if (!rows.length) {
507
+ ElMessage.warning("请勾选删除项");
508
+ return;
509
+ }
510
+ try {
511
+ await ElMessageBox.confirm(`确认删除选中的 ${rows.length} 条数据吗?`, "删除确认", {
512
+ confirmButtonText: "确定",
513
+ cancelButtonText: "取消",
514
+ type: "warning"
515
+ });
516
+ } catch {
517
+ return;
518
+ }
519
+ const keys = new Set(rows.map(row => String(row[primaryColumn.value])));
520
+ mockRows.value = mockRows.value.filter(row => !keys.has(String(row[primaryColumn.value])));
521
+ await nextTick();
522
+ refreshTable();
523
+ ElMessage.success("删除成功");
524
+ }
525
+
526
+ /** 创建当前页面类型使用的完整模拟数据。 */
527
+ function createMockRows() {
528
+ mockRows.value = snapshot.value ? createCodeGenPreviewRows(snapshot.value, optionMap.value, leftTreeOptions.value) : [];
529
+ selectedLeftTreeValues.value = [];
530
+ }
531
+
532
+ /** 同步预览页签和浏览器标题。 */
533
+ function syncWorkspaceTitle() {
534
+ const title = snapshot.value?.table.comment || snapshot.value?.table.name || "数据列表";
535
+ tabsStore.setTabsTitle(title);
536
+ document.title = `${title} - ${import.meta.env.VITE_GLOB_APP_TITLE}`;
537
+ }
538
+
539
+ /** 将数据库表名转换为生成器使用的实体名。 */
540
+ function toPascalCase(value: string) {
541
+ return value
542
+ .split("_")
543
+ .filter(Boolean)
544
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
545
+ .join("");
546
+ }
547
+
548
+ /** 创建最终 ProTable 单列配置。 */
549
+ function createPreviewTableColumn(column: CodeGenColumn): ColumnProps {
550
+ const label = column.comment || column.name;
551
+ const listOptions = resolveCodeGenPreviewOptions(optionMap.value, column.name, "list");
552
+ const queryOptions = resolveCodeGenPreviewOptions(optionMap.value, column.name, "query");
553
+ const result: ColumnProps = {
554
+ prop: column.name,
555
+ label,
556
+ minWidth: resolvePreviewColumnWidth(column),
557
+ isShow: Boolean(column.list_config?.enabled),
558
+ isSetting: Boolean(column.list_config?.enabled),
559
+ align: resolvePreviewColumnAlign(column)
560
+ };
561
+ if (column.query_config?.enabled) {
562
+ result.search = {
563
+ el: resolvePreviewSearchComponent(column.query_config.component),
564
+ props: createPreviewSearchProps(column)
565
+ };
566
+ if (queryOptions.length) result.enum = queryOptions;
567
+ }
568
+ applyPreviewListComponent(result, column, listOptions);
569
+ return result;
570
+ }
571
+
572
+ /** 根据字段类型和选项数据源解析预览表格列对齐方式。 */
573
+ function resolvePreviewColumnAlign(column: CodeGenColumn): NonNullable<ColumnProps["align"]> {
574
+ const listComponent = column.list_config?.component;
575
+ if (listComponent === "money") return "right";
576
+ if (listComponent === "image") return "center";
577
+
578
+ const optionSources = [column.query_config?.option?.source_type, column.list_config?.option?.source_type].filter(Boolean);
579
+ if (optionSources.includes("table")) return "left";
580
+ if (optionSources.includes("dict") || optionSources.includes("static")) return "center";
581
+ if (isNumericColumn(column)) return "right";
582
+ return "left";
583
+ }
584
+
585
+ /** 将列表展示组件映射为 ProTable 列能力。 */
586
+ function applyPreviewListComponent(result: ColumnProps, column: CodeGenColumn, options: ProFormOption[]) {
587
+ const component = column.list_config?.component;
588
+ if (component === "image") {
589
+ result.cellType = "image";
590
+ result.width = 120;
591
+ result.imageProps = {
592
+ width: 52,
593
+ height: 52,
594
+ src: scope => {
595
+ const value = scope.row[column.name];
596
+ return Array.isArray(value) ? String(value[0] ?? "") : String(value ?? "");
597
+ }
598
+ };
599
+ return;
600
+ }
601
+ if (component === "money") {
602
+ result.cellType = "money";
603
+ result.align = "right";
604
+ return;
605
+ }
606
+ if (component === "switch") {
607
+ result.width = 110;
608
+ result.render = scope => renderPreviewOptionValue(scope, column.name, options);
609
+ return;
610
+ }
611
+ if (options.length) {
612
+ result.render = scope => renderPreviewOptionValue(scope, column.name, options);
613
+ }
614
+ }
615
+
616
+ /** 渲染列表选择值,树形子选项同样可以正确匹配。 */
617
+ function renderPreviewOptionValue(scope: RenderScope, columnName: string, options: ProFormOption[]) {
618
+ const flatOptions = flattenCodeGenPreviewOptions(options);
619
+ const value = scope.row[columnName];
620
+ const matched = flatOptions.find(option => String(option.value) === String(value));
621
+ return matched?.label || String(value ?? "--");
622
+ }
623
+
624
+ /** 还原编辑态字段值,保证多选树形选择始终接收数组。 */
625
+ function resolvePreviewFormValue(field: ProFormField, value: unknown) {
626
+ const props = field.props && typeof field.props !== "function" ? field.props : {};
627
+ if (field.component !== "tree-select" || !props.multiple || Array.isArray(value)) return clonePreviewValue(value);
628
+ if (typeof value !== "string") return [];
629
+ try {
630
+ const parsed = JSON.parse(value);
631
+ return Array.isArray(parsed) ? parsed : [];
632
+ } catch {
633
+ return [];
634
+ }
635
+ }
636
+
637
+ /** 创建新增记录不与现有主键冲突的模拟编号。 */
638
+ function createNextPrimaryValue() {
639
+ const values = mockRows.value.map(row => Number(row[primaryColumn.value])).filter(Number.isFinite);
640
+ if (values.length === mockRows.value.length) return Math.max(0, ...values) + 1;
641
+ return `record-${Date.now()}`;
642
+ }
643
+
644
+ /** 将树形模拟记录转换成父节点选择项。 */
645
+ function mapPreviewRowsToOptions(rows: CodeGenPreviewRow[], parentPath = ""): ProFormOption[] {
646
+ const labelColumn = snapshot.value?.table.tree_label_column || primaryColumn.value;
647
+ return rows.map(row => {
648
+ const label = String(row[labelColumn] ?? row[primaryColumn.value]);
649
+ const fullPath = [parentPath, label].filter(Boolean).join("/");
650
+ return {
651
+ label: fullPath,
652
+ value: row[primaryColumn.value],
653
+ children: row.children?.length ? mapPreviewRowsToOptions(row.children, fullPath) : undefined
654
+ };
655
+ });
656
+ }
657
+
658
+ /** 将配置中的组件字符串收敛为 ProForm 支持类型,字典预览使用模拟下拉避免接口请求。 */
659
+ function resolvePreviewFormComponent(component?: string): ProFormComponentType {
660
+ if (component === "dict") return "select";
661
+ return component && supportedFormComponents.has(component) ? (component as ProFormComponentType) : "input";
662
+ }
663
+
664
+ /** 创建不同 ProForm 组件在最终新增弹窗中的参数。 */
665
+ function createPreviewFormProps(
666
+ component: ProFormComponentType,
667
+ label: string,
668
+ option?: CodeGenColumnOptionConfig,
669
+ isMultipleTreeSelect = false
670
+ ) {
671
+ const fullWidthStyle = { width: "100%" };
672
+ switch (component) {
673
+ case "input":
674
+ return { placeholder: `请输入${label}`, clearable: true, style: fullWidthStyle };
675
+ case "password":
676
+ return { placeholder: `请输入${label}`, clearable: true, showPassword: true, style: fullWidthStyle };
677
+ case "textarea":
678
+ return { placeholder: `请输入${label}`, rows: 4 };
679
+ case "input-number":
680
+ return { min: 0, controlsPosition: "right", style: fullWidthStyle };
681
+ case "segmented":
682
+ return { block: true };
683
+ case "select":
684
+ return { placeholder: `请选择${label}`, clearable: true, filterable: true, checkStrictly: true, style: fullWidthStyle };
685
+ case "tree-select":
686
+ return {
687
+ placeholder: `请选择${label}`,
688
+ clearable: true,
689
+ filterable: true,
690
+ checkStrictly: true,
691
+ ...(isMultipleTreeSelect ? { multiple: true, showCheckbox: true, nodeKey: "value" } : {}),
692
+ style: fullWidthStyle
693
+ };
694
+ case "date-picker":
695
+ return { type: "datetime", placeholder: `请选择${label}`, style: fullWidthStyle };
696
+ case "transfer":
697
+ return { titles: ["可选项", "已选项"] };
698
+ case "image-upload":
699
+ case "images-upload":
700
+ case "file-upload":
701
+ case "files-upload":
702
+ return { disabled: true };
703
+ case "dynamic-list":
704
+ return { inputProps: { placeholder: `请输入${label}` } };
705
+ case "kv-list":
706
+ return { keyInputProps: { placeholder: "键" }, valueInputProps: { placeholder: "值" } };
707
+ default:
708
+ return option?.source_value ? { placeholder: option.source_value } : {};
709
+ }
710
+ }
711
+
712
+ /** 将查询组件映射为 SearchForm 支持类型。 */
713
+ function resolvePreviewSearchComponent(component?: string) {
714
+ if (["input", "input-number", "select", "tree-select", "date-picker"].includes(component || "")) return component as any;
715
+ return "input";
716
+ }
717
+
718
+ /** 创建查询组件参数,区间查询使用日期范围。 */
719
+ function createPreviewSearchProps(column: CodeGenColumn) {
720
+ const props: Record<string, any> = { clearable: true, style: { width: "100%" } };
721
+ if (column.query_config?.component === "date-picker") {
722
+ props.type = column.query_config.operator === "between" ? "datetimerange" : "datetime";
723
+ props.rangeSeparator = "至";
724
+ props.startPlaceholder = "开始时间";
725
+ props.endPlaceholder = "结束时间";
726
+ }
727
+ if (column.query_config?.component === "tree-select") {
728
+ props.checkStrictly = true;
729
+ props.renderAfterExpand = false;
730
+ }
731
+ return props;
732
+ }
733
+
734
+ /** 创建不同组件的空白新增表单初始值。 */
735
+ function createPreviewFormValue(field: ProFormField) {
736
+ switch (field.component) {
737
+ case "input-number":
738
+ case "segmented":
739
+ case "select":
740
+ case "radio-group":
741
+ case "date-picker":
742
+ return undefined;
743
+ case "tree-select":
744
+ return field.props && typeof field.props !== "function" && field.props.multiple ? [] : undefined;
745
+ case "switch":
746
+ case "checkbox":
747
+ return false;
748
+ case "checkbox-group":
749
+ case "transfer":
750
+ case "images-upload":
751
+ case "files-upload":
752
+ case "dynamic-list":
753
+ case "kv-list":
754
+ return [];
755
+ default:
756
+ return "";
757
+ }
758
+ }
759
+
760
+ /** 宽内容组件占满整行,其余组件在桌面端双列展示。 */
761
+ function resolvePreviewColSpan(component: ProFormComponentType) {
762
+ return new Set([
763
+ "textarea",
764
+ "checkbox-group",
765
+ "transfer",
766
+ "image-upload",
767
+ "images-upload",
768
+ "file-upload",
769
+ "files-upload",
770
+ "rich-text",
771
+ "dynamic-list",
772
+ "kv-list",
773
+ "slot"
774
+ ]).has(component)
775
+ ? 24
776
+ : 12;
777
+ }
778
+
779
+ /** 根据列表组件和字段名称分配稳定列宽。 */
780
+ function resolvePreviewColumnWidth(column: CodeGenColumn) {
781
+ if (["created_at", "updated_at"].includes(column.name) || column.list_config?.component === "date") return 180;
782
+ if (column.list_config?.component === "image") return 120;
783
+ if (column.list_config?.component === "switch") return 110;
784
+ return 150;
785
+ }
786
+
787
+ /** 深拷贝模拟表单和行数据,避免编辑时直接污染列表。 */
788
+ function clonePreviewValue<T>(value: T): T {
789
+ if (value === undefined || value === null) return value;
790
+ return JSON.parse(JSON.stringify(value)) as T;
791
+ }
792
+
793
+ onMounted(() => {
794
+ void loadPreview();
795
+ });
796
+ </script>
797
+
798
+ <style scoped lang="scss">
799
+ .code-gen-page-preview-page {
800
+ display: flex;
801
+ flex-direction: column;
802
+ width: 100%;
803
+ height: 100%;
804
+ min-width: 0;
805
+ min-height: 0;
806
+ overflow: hidden;
807
+ }
808
+
809
+ .code-gen-page-preview__table,
810
+ .code-gen-left-tree-preview {
811
+ min-width: 0;
812
+ min-height: 0;
813
+ overflow: hidden;
814
+ }
815
+
816
+ .code-gen-left-tree-preview__table {
817
+ min-width: 0;
818
+ min-height: 0;
819
+ }
820
+
821
+ :deep(.code-gen-page-preview-table) {
822
+ --el-table-header-bg-color: var(--el-fill-color-light);
823
+ }
824
+
825
+ @media (width <= 768px) {
826
+ :deep(.el-dialog .el-col-12) {
827
+ max-width: 100%;
828
+ flex: 0 0 100%;
829
+ }
830
+ }
831
+ </style>