@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,1456 @@
1
+ <!-- 代码生成字段配置 -->
2
+ <template>
3
+ <div v-loading="loading" class="app-container code-gen-sub-page">
4
+ <el-card class="code-gen-sub-card" shadow="never">
5
+ <div v-if="formData.id" class="code-gen-column-pane">
6
+ <div class="code-gen-toolbar code-gen-column-toolbar">
7
+ <div class="code-gen-column-toolbar__meta">
8
+ <strong>数据库字段</strong>
9
+ <!-- 展示当前字段配置对应的业务表。 -->
10
+ <span class="code-gen-column-toolbar__table-name" :title="formData.name">表名:{{ formData.name }}</span>
11
+ <span class="code-gen-column-toolbar__table-comment" :title="formData.comment || '--'">
12
+ 表注释:{{ formData.comment || "--" }}
13
+ </span>
14
+ <span>查询 {{ enabledSummary.query }} 项</span>
15
+ <span>列表 {{ enabledSummary.list }} 项</span>
16
+ <span>表单 {{ enabledSummary.form }} 项</span>
17
+ </div>
18
+ <div class="code-gen-column-toolbar__actions">
19
+ <el-button type="primary" :icon="Document" :disabled="!canEdit" @click="handleSaveColumns()">保存</el-button>
20
+ </div>
21
+ </div>
22
+
23
+ <div ref="columnTableRef" class="code-gen-column-table">
24
+ <el-table :data="columns" row-key="name" border stripe table-layout="fixed" empty-text="暂无字段配置">
25
+ <el-table-column label="数据库字段" min-width="320" fixed="left">
26
+ <template #default="{ row, $index }">
27
+ <div class="code-gen-field-cell">
28
+ <el-popover trigger="hover" placement="right-start" :width="320" :show-after="250">
29
+ <template #reference>
30
+ <div class="code-gen-field-trigger">
31
+ <span class="code-gen-field-trigger__name">{{ row.name }}</span>
32
+ </div>
33
+ </template>
34
+ <div class="code-gen-field-popover">
35
+ <div class="code-gen-field-popover__header">
36
+ <strong>{{ row.name }}</strong>
37
+ <span>{{ row.comment || row.name }}</span>
38
+ </div>
39
+ <div class="code-gen-field-popover__types">
40
+ <div>
41
+ <span>数据库</span><b>{{ row.db_type || "--" }}</b>
42
+ </div>
43
+ <div>
44
+ <span>Go</span><b>{{ row.go_type || "--" }}</b>
45
+ </div>
46
+ <div>
47
+ <span>Proto</span><b>{{ row.proto_type || "--" }}</b>
48
+ </div>
49
+ <div>
50
+ <span>TS</span><b>{{ row.ts_type || "--" }}</b>
51
+ </div>
52
+ </div>
53
+ <div class="code-gen-field-popover__flags">
54
+ <el-tag v-if="row.is_primary" size="small" type="danger" effect="plain">主键</el-tag>
55
+ <el-tag v-if="row.is_auto_increment" size="small" type="warning" effect="plain">自增</el-tag>
56
+ <el-tag size="small" :type="row.is_nullable ? 'info' : 'success'" effect="plain">
57
+ {{ row.is_nullable ? "可空" : "必填" }}
58
+ </el-tag>
59
+ </div>
60
+ </div>
61
+ </el-popover>
62
+ <div class="code-gen-field-order">
63
+ <span class="code-gen-field-order__index">{{ $index + 1 }}</span>
64
+ <el-tooltip content="拖拽排序" placement="top">
65
+ <el-button
66
+ text
67
+ size="small"
68
+ :icon="List"
69
+ :disabled="!canEdit"
70
+ class="code-gen-field-order__drag"
71
+ aria-label="拖拽调整字段顺序"
72
+ />
73
+ </el-tooltip>
74
+ </div>
75
+ </div>
76
+ </template>
77
+ </el-table-column>
78
+
79
+ <el-table-column label="字段描述" min-width="280">
80
+ <template #default="{ row }">
81
+ <el-input v-model="row.comment" :disabled="!canEdit" maxlength="255" placeholder="请输入字段描述" />
82
+ </template>
83
+ </el-table-column>
84
+
85
+ <el-table-column label="查询" min-width="440">
86
+ <template #default="{ row }">
87
+ <div class="code-gen-config-cell">
88
+ <el-switch
89
+ v-model="row.query_config.enabled"
90
+ :disabled="!canEdit"
91
+ inline-prompt
92
+ active-text="开"
93
+ inactive-text="关"
94
+ />
95
+ <el-select
96
+ v-model="row.query_config.operator"
97
+ :disabled="!canEdit || !row.query_config.enabled"
98
+ placeholder="查询方式"
99
+ >
100
+ <el-option
101
+ v-for="item in queryOperatorOptions"
102
+ :key="String(item.value)"
103
+ :label="item.label"
104
+ :value="item.value"
105
+ />
106
+ </el-select>
107
+ <el-select
108
+ v-model="row.query_config.component"
109
+ :disabled="!canEdit || !row.query_config.enabled"
110
+ placeholder="查询组件"
111
+ @change="handleComponentChange(row, 'query')"
112
+ >
113
+ <el-option
114
+ v-for="item in queryComponentOptions"
115
+ :key="String(item.value)"
116
+ :label="item.label"
117
+ :value="item.value"
118
+ />
119
+ </el-select>
120
+ <el-tooltip
121
+ v-if="shouldShowOptionEntry(row.query_config, 'query')"
122
+ :content="optionEntryTip(row.query_config.option)"
123
+ placement="top"
124
+ >
125
+ <el-button
126
+ size="small"
127
+ :type="hasOptionConfig(row.query_config.option) ? 'primary' : 'default'"
128
+ :icon="Setting"
129
+ :disabled="!canEdit"
130
+ @click="openOptionDialog(row, 'query')"
131
+ >
132
+ 选项
133
+ </el-button>
134
+ </el-tooltip>
135
+ </div>
136
+ </template>
137
+ </el-table-column>
138
+
139
+ <el-table-column label="列表" min-width="420">
140
+ <template #default="{ row }">
141
+ <div class="code-gen-config-cell">
142
+ <el-switch
143
+ v-model="row.list_config.enabled"
144
+ :disabled="!canEdit"
145
+ inline-prompt
146
+ active-text="开"
147
+ inactive-text="关"
148
+ />
149
+ <el-select
150
+ v-model="row.list_config.component"
151
+ :disabled="!canEdit || !row.list_config.enabled"
152
+ placeholder="展示组件"
153
+ @change="handleComponentChange(row, 'list')"
154
+ >
155
+ <el-option
156
+ v-for="item in listComponentOptions"
157
+ :key="String(item.value)"
158
+ :label="item.label"
159
+ :value="item.value"
160
+ />
161
+ </el-select>
162
+ <el-tooltip
163
+ v-if="shouldShowOptionEntry(row.list_config, 'list')"
164
+ :content="optionEntryTip(row.list_config.option)"
165
+ placement="top"
166
+ >
167
+ <el-button
168
+ size="small"
169
+ :type="hasOptionConfig(row.list_config.option) ? 'primary' : 'default'"
170
+ :icon="Setting"
171
+ :disabled="!canEdit"
172
+ @click="openOptionDialog(row, 'list')"
173
+ >
174
+ 选项
175
+ </el-button>
176
+ </el-tooltip>
177
+ </div>
178
+ </template>
179
+ </el-table-column>
180
+
181
+ <el-table-column label="表单" min-width="440">
182
+ <template #default="{ row }">
183
+ <div class="code-gen-config-cell">
184
+ <el-switch
185
+ v-model="row.form_config.enabled"
186
+ :disabled="!canEdit"
187
+ inline-prompt
188
+ active-text="开"
189
+ inactive-text="关"
190
+ @change="handleFormEnabledChange(row)"
191
+ />
192
+ <el-select
193
+ v-model="row.form_config.component"
194
+ :disabled="!canEdit || !row.form_config.enabled"
195
+ placeholder="录入组件"
196
+ @change="handleComponentChange(row, 'form')"
197
+ >
198
+ <el-option
199
+ v-for="item in formComponentOptions"
200
+ :key="String(item.value)"
201
+ :label="item.label"
202
+ :value="item.value"
203
+ />
204
+ </el-select>
205
+ <el-checkbox v-model="row.form_config.required" :disabled="!canEdit || !row.form_config.enabled"
206
+ >必填</el-checkbox
207
+ >
208
+ <el-tooltip
209
+ v-if="shouldShowOptionEntry(row.form_config, 'form')"
210
+ :content="optionEntryTip(row.form_config.option)"
211
+ placement="top"
212
+ >
213
+ <el-button
214
+ size="small"
215
+ :type="hasOptionConfig(row.form_config.option) ? 'primary' : 'default'"
216
+ :icon="Setting"
217
+ :disabled="!canEdit"
218
+ @click="openOptionDialog(row, 'form')"
219
+ >
220
+ 选项
221
+ </el-button>
222
+ </el-tooltip>
223
+ </div>
224
+ </template>
225
+ </el-table-column>
226
+ </el-table>
227
+ </div>
228
+ </div>
229
+ <el-empty v-else description="请先选择生成记录" />
230
+ </el-card>
231
+
232
+ <ProDialog
233
+ v-model="optionDialog.visible"
234
+ :title="`${optionDialog.scopeLabel}选项 - ${optionDialog.columnName}`"
235
+ width="min(560px, calc(100vw - 32px))"
236
+ destroy-on-close
237
+ :show-close="false"
238
+ :close-on-click-modal="false"
239
+ :close-on-press-escape="false"
240
+ :show-footer="false"
241
+ @closed="handleOptionDialogClosed"
242
+ >
243
+ <template #header="{ titleId, titleClass }">
244
+ <div class="code-gen-config-dialog__header">
245
+ <span :id="titleId" :class="titleClass">{{ `${optionDialog.scopeLabel}选项 - ${optionDialog.columnName}` }}</span>
246
+ <el-button
247
+ type="primary"
248
+ :icon="Document"
249
+ :disabled="!canEdit"
250
+ aria-label="保存并关闭"
251
+ @click="handleSaveOptionDialog"
252
+ >
253
+ 保存
254
+ </el-button>
255
+ </div>
256
+ </template>
257
+
258
+ <div v-if="optionDialog.option" class="code-gen-option-dialog">
259
+ <div v-if="optionDialog.formConfig" class="code-gen-popover-form__row">
260
+ <span class="code-gen-popover-form__label">选择模式</span>
261
+ <el-radio-group v-model="optionDialog.formConfig.multiple" :disabled="!canEdit">
262
+ <el-radio-button :value="false">单选</el-radio-button>
263
+ <el-tooltip content="多选仅支持 JSON 字段" :disabled="optionDialog.isJSONColumn" placement="top">
264
+ <el-radio-button :value="true" :disabled="!optionDialog.isJSONColumn">多选</el-radio-button>
265
+ </el-tooltip>
266
+ </el-radio-group>
267
+ </div>
268
+ <div
269
+ v-if="
270
+ !['tree', 'switch'].includes(optionDialog.option.kind) &&
271
+ !(optionDialog.scope === 'form' && optionDialog.component === 'dict')
272
+ "
273
+ class="code-gen-popover-form__row"
274
+ >
275
+ <span class="code-gen-popover-form__label">来源</span>
276
+ <el-select
277
+ v-model="optionDialog.option.source_type"
278
+ :disabled="!canEdit"
279
+ clearable
280
+ placeholder="选择来源"
281
+ @change="handleOptionSourceTypeChange"
282
+ >
283
+ <el-option
284
+ v-for="item in sourceTypeOptions"
285
+ :key="String(item.value)"
286
+ :label="item.label"
287
+ :value="item.value"
288
+ />
289
+ </el-select>
290
+ </div>
291
+ <div v-if="optionDialog.option.source_type === 'dict'" class="code-gen-popover-form__row">
292
+ <span class="code-gen-popover-form__label">字典</span>
293
+ <el-select
294
+ v-model="optionDialog.option.source_value"
295
+ :disabled="!canEdit"
296
+ :loading="loadingDictionaries"
297
+ filterable
298
+ clearable
299
+ placeholder="选择字典"
300
+ @change="handleOptionSourceValueChange"
301
+ >
302
+ <el-option
303
+ v-for="item in dictionaries"
304
+ :key="item.code"
305
+ :label="item.name ? `${item.name}(${item.code})` : item.code"
306
+ :value="item.code"
307
+ />
308
+ </el-select>
309
+ </div>
310
+ <template v-if="optionDialog.option.kind === 'switch' && optionDialog.option.source_value">
311
+ <div class="code-gen-popover-form__row">
312
+ <span class="code-gen-popover-form__label">开启值</span>
313
+ <el-select
314
+ v-model="optionDialog.option.active_value"
315
+ :disabled="!canEdit"
316
+ filterable
317
+ placeholder="选择开启值"
318
+ >
319
+ <el-option
320
+ v-for="item in dictionaryItemsForEditor"
321
+ :key="item.value"
322
+ :label="`${item.label}(${item.value})`"
323
+ :value="item.value"
324
+ :disabled="item.value === optionDialog.option.inactive_value"
325
+ />
326
+ </el-select>
327
+ </div>
328
+ <div class="code-gen-popover-form__row">
329
+ <span class="code-gen-popover-form__label">关闭值</span>
330
+ <el-select
331
+ v-model="optionDialog.option.inactive_value"
332
+ :disabled="!canEdit"
333
+ filterable
334
+ placeholder="选择关闭值"
335
+ >
336
+ <el-option
337
+ v-for="item in dictionaryItemsForEditor"
338
+ :key="item.value"
339
+ :label="`${item.label}(${item.value})`"
340
+ :value="item.value"
341
+ :disabled="item.value === optionDialog.option.active_value"
342
+ />
343
+ </el-select>
344
+ </div>
345
+ </template>
346
+ <div v-else-if="optionDialog.option.source_type === 'table'" class="code-gen-popover-form__row">
347
+ <span class="code-gen-popover-form__label">数据表</span>
348
+ <el-select
349
+ v-model="optionDialog.option.source_value"
350
+ :disabled="!canEdit"
351
+ :loading="loadingDatabaseTables"
352
+ filterable
353
+ clearable
354
+ placeholder="选择数据表"
355
+ @change="handleOptionSourceValueChange"
356
+ >
357
+ <el-option
358
+ v-for="item in databaseTables"
359
+ :key="item.name"
360
+ :label="item.comment ? `${item.name}(${item.comment})` : item.name"
361
+ :value="item.name"
362
+ />
363
+ </el-select>
364
+ </div>
365
+ <template v-if="optionDialog.option.source_type === 'table'">
366
+ <div v-if="optionDialog.option.kind === 'tree'" class="code-gen-popover-form__row">
367
+ <span class="code-gen-popover-form__label">树父字段</span>
368
+ <el-select
369
+ v-model="optionDialog.option.parent_field"
370
+ :disabled="!canEdit || !optionDialog.option.source_value"
371
+ :loading="loadingDatabaseColumns.has(optionDialog.option.source_value)"
372
+ filterable
373
+ clearable
374
+ placeholder="选择树父字段"
375
+ >
376
+ <el-option
377
+ v-for="item in databaseColumnsForEditor"
378
+ :key="item.name"
379
+ :label="formatDatabaseColumn(item)"
380
+ :value="item.name"
381
+ />
382
+ </el-select>
383
+ </div>
384
+ <div v-if="optionDialog.option.kind === 'tree'" class="code-gen-popover-form__row">
385
+ <span class="code-gen-popover-form__label">加载方式</span>
386
+ <el-checkbox v-model="optionDialog.option.lazy" :disabled="!canEdit">
387
+ 懒加载子节点
388
+ </el-checkbox>
389
+ </div>
390
+ <div class="code-gen-popover-form__row">
391
+ <span class="code-gen-popover-form__label">
392
+ {{ optionDialog.option.kind === "tree" ? "树显示字段" : "Label 字段" }}
393
+ </span>
394
+ <el-select
395
+ v-model="optionDialog.option.label_field"
396
+ :disabled="!canEdit || !optionDialog.option.source_value"
397
+ :loading="loadingDatabaseColumns.has(optionDialog.option.source_value)"
398
+ filterable
399
+ clearable
400
+ :placeholder="optionDialog.option.kind === 'tree' ? '选择树显示字段' : '选择显示字段'"
401
+ >
402
+ <el-option
403
+ v-for="item in databaseColumnsForEditor"
404
+ :key="item.name"
405
+ :label="formatDatabaseColumn(item)"
406
+ :value="item.name"
407
+ />
408
+ </el-select>
409
+ </div>
410
+ <div class="code-gen-popover-form__row">
411
+ <span class="code-gen-popover-form__label">
412
+ {{ optionDialog.option.kind === "tree" ? "树值字段" : "Value 字段" }}
413
+ </span>
414
+ <el-select
415
+ v-model="optionDialog.option.value_field"
416
+ :disabled="!canEdit || !optionDialog.option.source_value"
417
+ :loading="loadingDatabaseColumns.has(optionDialog.option.source_value)"
418
+ filterable
419
+ clearable
420
+ :placeholder="optionDialog.option.kind === 'tree' ? '选择树值字段' : '选择值字段'"
421
+ >
422
+ <el-option
423
+ v-for="item in databaseColumnsForEditor"
424
+ :key="item.name"
425
+ :label="formatDatabaseColumn(item)"
426
+ :value="item.name"
427
+ />
428
+ </el-select>
429
+ </div>
430
+ </template>
431
+ <div v-if="optionDialog.option.source_type === 'static'" class="code-gen-static-options">
432
+ <div class="code-gen-static-options__header">
433
+ <span class="code-gen-popover-form__label">静态数据</span>
434
+ <el-button size="small" :icon="Plus" :disabled="!canEdit" @click="addStaticOption">添加</el-button>
435
+ </div>
436
+ <div v-if="staticOptionsForEditor.length" class="code-gen-static-options__list">
437
+ <div v-for="(item, index) in staticOptionsForEditor" :key="index" class="code-gen-static-options__item">
438
+ <el-input v-model="item.label" :disabled="!canEdit" placeholder="Label" @input="syncStaticOptions" />
439
+ <el-input
440
+ :model-value="String(item.value)"
441
+ :disabled="!canEdit"
442
+ placeholder="Value"
443
+ @update:model-value="updateStaticOptionValue(item, $event)"
444
+ />
445
+ <el-tooltip content="删除静态数据" placement="top">
446
+ <el-button
447
+ :icon="Delete"
448
+ :disabled="!canEdit"
449
+ circle
450
+ text
451
+ aria-label="删除静态数据"
452
+ @click="removeStaticOption(index)"
453
+ />
454
+ </el-tooltip>
455
+ </div>
456
+ </div>
457
+ <el-empty v-else :image-size="48" description="暂无静态数据" />
458
+ </div>
459
+ </div>
460
+ </ProDialog>
461
+ </div>
462
+ </template>
463
+
464
+ <script setup lang="ts">
465
+ import Sortable from "sortablejs";
466
+ import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
467
+ import { Delete, Document, List, Plus, Setting } from "@element-plus/icons-vue";
468
+ import { useRoute, useRouter } from "vue-router";
469
+ import ProDialog from "@liujitcn/kratos-admin-core/components/Dialog/ProDialog.vue";
470
+ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
471
+ import { useTabsStore } from "@liujitcn/kratos-admin-core/stores/runtime";
472
+ import { defBaseDictService } from "@liujitcn/kratos-admin-system/api/system/base_dict";
473
+ import { defCodeGenColumnService } from "@liujitcn/kratos-admin-system/api/system/code_gen_column";
474
+ import { defCodeGenTableService } from "@liujitcn/kratos-admin-system/api/system/code_gen_table";
475
+ import type { OptionBaseDictResponse_BaseDict } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_dict";
476
+ import type {
477
+ CodeGenColumn as CodeGenColumnDTO,
478
+ CodeGenColumnFormConfig,
479
+ CodeGenColumnListConfig,
480
+ CodeGenColumnOptionConfig,
481
+ CodeGenColumnQueryConfig,
482
+ CodeGenDatabaseColumn
483
+ } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen_column";
484
+ import type { CodeGenDatabaseTable, CodeGenTableForm } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen_table";
485
+ import {
486
+ copyCodeGenOptionToEmptyMatches,
487
+ copyFirstMatchingCodeGenOption,
488
+ fillMissingCodeGenOptionConfigs,
489
+ getCodeGenOptionContainer,
490
+ isCompleteCodeGenOptionConfig
491
+ } from "./option-copy";
492
+ import type { CodeGenOptionContainer, CodeGenOptionScope } from "./option-copy";
493
+ import {
494
+ codeGenFormComponentOptions as formComponentOptions,
495
+ codeGenListComponentOptions as listComponentOptions,
496
+ codeGenQueryComponentOptions as queryComponentOptions,
497
+ codeGenQueryOperatorOptions as queryOperatorOptions,
498
+ codeGenSourceTypeOptions as sourceTypeOptions,
499
+ createDefaultCodeGenFormConfig,
500
+ createDefaultCodeGenListConfig,
501
+ createDefaultCodeGenOptionConfig,
502
+ createDefaultCodeGenQueryConfig,
503
+ createDefaultCodeGenTableForm
504
+ } from "../config";
505
+
506
+ defineOptions({
507
+ name: "CodeGenColumn",
508
+ inheritAttrs: false
509
+ });
510
+
511
+ const route = useRoute();
512
+ const router = useRouter();
513
+ const tabsStore = useTabsStore();
514
+ const { BUTTONS } = useAuthButtons();
515
+
516
+ const loading = ref(false);
517
+ const columns = ref<CodeGenColumnView[]>([]);
518
+ const columnTableRef = ref<HTMLElement>();
519
+ const formData = reactive<CodeGenTableForm>(createDefaultCodeGenTableForm());
520
+ const dictionaries = ref<OptionBaseDictResponse_BaseDict[]>([]);
521
+ const databaseTables = ref<CodeGenDatabaseTable[]>([]);
522
+ const databaseColumns = reactive<Record<string, CodeGenDatabaseColumn[]>>({});
523
+ const staticOptions = reactive(new Map<string, CodeGenStaticOption[]>());
524
+ const loadingDictionaries = ref(false);
525
+ const loadingDatabaseTables = ref(false);
526
+ const loadingDatabaseColumns = reactive(new Set<string>());
527
+ let dictionariesLoaded = false;
528
+ let databaseTablesLoaded = false;
529
+ let columnSortable: Sortable | undefined;
530
+
531
+ /** 开关组件默认使用的状态字典和值。 */
532
+ const codeGenDefaultSwitchOption = {
533
+ sourceValue: "status",
534
+ activeValue: "1",
535
+ inactiveValue: "2"
536
+ };
537
+
538
+ /** 字段配置页面使用的完整结构化编辑模型。 */
539
+ type CodeGenColumnView = Omit<CodeGenColumnDTO, "query_config" | "list_config" | "form_config"> & {
540
+ query_config: CodeGenColumnQueryConfig & { option: CodeGenColumnOptionConfig };
541
+ list_config: CodeGenColumnListConfig & { option: CodeGenColumnOptionConfig };
542
+ form_config: CodeGenColumnFormConfig & { option: CodeGenColumnOptionConfig };
543
+ };
544
+
545
+ /** 静态选项编辑项,保留字符串、数字和布尔值类型。 */
546
+ interface CodeGenStaticOption {
547
+ label: string;
548
+ value: string | number | boolean;
549
+ }
550
+
551
+ /** 单个选项弹窗的编辑上下文。 */
552
+ interface CodeGenOptionDialog {
553
+ visible: boolean;
554
+ scope: CodeGenOptionScope;
555
+ scopeLabel: string;
556
+ columnName: string;
557
+ component: string;
558
+ isJSONColumn: boolean;
559
+ cacheKey: string;
560
+ option: CodeGenColumnOptionConfig | null;
561
+ formConfig: CodeGenColumnView["form_config"] | null;
562
+ }
563
+
564
+ /** 保存前需要打开选项编辑器的字段配置问题。 */
565
+ interface CodeGenColumnOptionIssue {
566
+ row: CodeGenColumnView;
567
+ scope: CodeGenOptionScope;
568
+ message: string;
569
+ }
570
+
571
+ /** 保存前需要确认选项来源不一致的字段配置问题。 */
572
+ interface CodeGenColumnOptionConsistencyIssue {
573
+ columnName: string;
574
+ scopes: string;
575
+ }
576
+
577
+ const optionDialog = reactive<CodeGenOptionDialog>({
578
+ visible: false,
579
+ scope: "query",
580
+ scopeLabel: "查询",
581
+ columnName: "",
582
+ component: "",
583
+ isJSONColumn: false,
584
+ cacheKey: "",
585
+ option: null,
586
+ formConfig: null
587
+ });
588
+ /** 当前生成对象 ID。 */
589
+ const tableId = computed(() => {
590
+ const value = route.params.tableId ?? route.query.tableId;
591
+ const id = Number(Array.isArray(value) ? value[0] : value);
592
+ return Number.isFinite(id) && id > 0 ? id : 0;
593
+ });
594
+
595
+ /** 是否可以维护字段配置。 */
596
+ const canEdit = computed(() => !!BUTTONS.value["tool:code-gen-table:column"]);
597
+
598
+ /** 已启用字段配置统计。 */
599
+ const enabledSummary = computed(() => ({
600
+ query: columns.value.filter(item => item.query_config.enabled).length,
601
+ list: columns.value.filter(item => item.list_config.enabled).length,
602
+ form: columns.value.filter(item => item.form_config.enabled).length
603
+ }));
604
+
605
+ /** 当前选项编辑器可用的数据库字段。 */
606
+ const databaseColumnsForEditor = computed(() => {
607
+ const tableName = optionDialog.option?.source_value ?? "";
608
+ return databaseColumns[tableName] ?? [];
609
+ });
610
+
611
+ /** 当前开关选中字典内的可用字典项。 */
612
+ const dictionaryItemsForEditor = computed(
613
+ () => dictionaries.value.find(item => item.code === optionDialog.option?.source_value)?.items ?? []
614
+ );
615
+
616
+ /** 当前选项编辑器中的静态数据。 */
617
+ const staticOptionsForEditor = computed(() => staticOptions.get(optionDialog.cacheKey) ?? []);
618
+
619
+ // 路由生成对象变化时重新加载字段配置。
620
+ watch(tableId, () => {
621
+ void handleQuery();
622
+ });
623
+
624
+ // 权限数据加载完成后再同步拖拽能力,避免只读用户创建可拖拽实例。
625
+ watch(canEdit, () => {
626
+ void nextTick(initColumnSortable);
627
+ });
628
+
629
+ /** 查询生成对象字段配置。 */
630
+ async function handleQuery() {
631
+ loading.value = true;
632
+ try {
633
+ destroyColumnSortable();
634
+ Object.assign(formData, createDefaultCodeGenTableForm());
635
+ columns.value = [];
636
+ staticOptions.clear();
637
+ if (!tableId.value) return;
638
+ const [table, response] = await Promise.all([
639
+ defCodeGenTableService.GetCodeGenTable({ id: tableId.value }),
640
+ defCodeGenColumnService.ListCodeGenColumn({ table_id: tableId.value })
641
+ ]);
642
+ Object.assign(formData, table);
643
+ // 字段配置和预览都保留数据库完整字段快照,由用户决定是否调整默认配置。
644
+ columns.value = (response.code_gen_columns ?? []).map(normalizeColumn);
645
+ syncWorkspaceTitle();
646
+ await nextTick();
647
+ initColumnSortable();
648
+ } finally {
649
+ loading.value = false;
650
+ }
651
+ }
652
+
653
+ /** 同步当前页签和浏览器标题。 */
654
+ function syncWorkspaceTitle() {
655
+ const tableTitle = formData.comment || formData.name;
656
+ const title = tableTitle ? `${tableTitle}字段配置` : "字段配置";
657
+ tabsStore.setTabsTitle(title);
658
+ document.title = `${title} - ${import.meta.env.VITE_GLOB_APP_TITLE}`;
659
+ }
660
+
661
+ /** 保存字段配置。 */
662
+ async function handleSaveColumns(showMessage = true) {
663
+ if (!formData.id) return false;
664
+ syncColumnSorts();
665
+ columns.value.forEach(syncColumnOptionKinds);
666
+ if (columns.value.some(item => !item.name || !item.db_type)) {
667
+ ElMessage.warning("字段名和数据库类型不能为空");
668
+ return false;
669
+ }
670
+ const optionIssue = findCodeGenColumnOptionIssue(columns.value);
671
+ if (optionIssue) {
672
+ ElMessage.warning(optionIssue.message);
673
+ await openOptionDialog(optionIssue.row, optionIssue.scope);
674
+ return false;
675
+ }
676
+ const consistencyIssue = findCodeGenColumnOptionConsistencyIssue(columns.value);
677
+ if (consistencyIssue) {
678
+ try {
679
+ await ElMessageBox.confirm(
680
+ `字段 ${consistencyIssue.columnName} 的${consistencyIssue.scopes}选项来源不一致,是否继续保存?`,
681
+ "选项配置警告",
682
+ {
683
+ confirmButtonText: "继续保存",
684
+ cancelButtonText: "返回修改",
685
+ type: "warning"
686
+ }
687
+ );
688
+ } catch {
689
+ return false;
690
+ }
691
+ }
692
+ await defCodeGenColumnService.SaveCodeGenColumn({
693
+ table_id: formData.id,
694
+ code_gen_columns: columns.value.map((item, index) => ({
695
+ ...item,
696
+ table_id: formData.id,
697
+ sort: index + 1
698
+ }))
699
+ });
700
+ if (showMessage) ElMessage.success("保存成功");
701
+ // 路由切换前保留当前页签地址,避免误删目标页签。
702
+ const currentPath = route.fullPath;
703
+ await router.push("/code/gen/table");
704
+ await tabsStore.removeTabs(currentPath, false);
705
+ return true;
706
+ }
707
+
708
+ /** 初始化字段表格拖拽排序,并限制仅通过排序手柄触发。 */
709
+ function initColumnSortable() {
710
+ destroyColumnSortable();
711
+ if (!canEdit.value) return;
712
+ const tbody =
713
+ columnTableRef.value?.querySelector<HTMLElement>(".el-table__fixed-body-wrapper tbody") ??
714
+ columnTableRef.value?.querySelector<HTMLElement>(".el-table__body-wrapper tbody");
715
+ if (!tbody) return;
716
+ columnSortable = Sortable.create(tbody, {
717
+ handle: ".code-gen-field-order__drag",
718
+ animation: 150,
719
+ ghostClass: "code-gen-column-sortable-ghost",
720
+ chosenClass: "code-gen-column-sortable-chosen",
721
+ onEnd({ newIndex, oldIndex }) {
722
+ if (oldIndex === undefined || newIndex === undefined || oldIndex === newIndex) return;
723
+ const column = columns.value.splice(oldIndex, 1)[0];
724
+ if (!column) return;
725
+ columns.value.splice(newIndex, 0, column);
726
+ syncColumnSorts();
727
+ }
728
+ });
729
+ }
730
+
731
+ /** 销毁字段表格拖拽实例,避免路由切换后保留 DOM 事件。 */
732
+ function destroyColumnSortable() {
733
+ columnSortable?.destroy();
734
+ columnSortable = undefined;
735
+ }
736
+
737
+ /** 按当前字段表格行顺序更新持久化排序值。 */
738
+ function syncColumnSorts() {
739
+ columns.value.forEach((item, index) => {
740
+ item.sort = index + 1;
741
+ });
742
+ }
743
+
744
+ /** 将接口字段配置补齐为三份互不共享的选项对象。 */
745
+ function normalizeColumn(column: CodeGenColumnDTO): CodeGenColumnView {
746
+ const query = column.query_config ?? createDefaultCodeGenQueryConfig();
747
+ const list = column.list_config ?? createDefaultCodeGenListConfig();
748
+ const form = column.form_config ?? createDefaultCodeGenFormConfig();
749
+ const normalizedColumn: CodeGenColumnView = {
750
+ ...column,
751
+ query_config: {
752
+ ...query,
753
+ option: { ...(query.option ?? createDefaultCodeGenOptionConfig()) }
754
+ },
755
+ list_config: {
756
+ ...list,
757
+ option: { ...(list.option ?? createDefaultCodeGenOptionConfig()) }
758
+ },
759
+ form_config: {
760
+ ...form,
761
+ option: { ...(form.option ?? createDefaultCodeGenOptionConfig()) }
762
+ }
763
+ };
764
+ syncColumnOptionKinds(normalizedColumn);
765
+ fillMissingCodeGenOptionConfigs(normalizedColumn);
766
+ return normalizedColumn;
767
+ }
768
+
769
+ /** 组件变化时清空旧类型配置,并从相同组件范围重新复刻。 */
770
+ function handleComponentChange(row: CodeGenColumnView, scope: CodeGenOptionScope) {
771
+ const config = getCodeGenOptionContainer(row, scope);
772
+ Object.assign(config.option, createDefaultCodeGenOptionConfig());
773
+ syncOptionKind(config, scope);
774
+ if (scope === "form" && row.form_config.component !== "tree-select") row.form_config.multiple = false;
775
+ copyFirstMatchingCodeGenOption(row, scope);
776
+ }
777
+
778
+ /** 关闭表单展示时同步关闭必填约束。 */
779
+ function handleFormEnabledChange(row: CodeGenColumnView) {
780
+ if (!row.form_config.enabled) {
781
+ row.form_config.required = false;
782
+ row.form_config.multiple = false;
783
+ }
784
+ }
785
+
786
+ /** 打开查询、列表或表单自己的选项编辑弹窗。 */
787
+ async function openOptionDialog(row: CodeGenColumnView, scope: CodeGenOptionScope) {
788
+ const config = getCodeGenOptionContainer(row, scope);
789
+ syncOptionKind(config, scope);
790
+ optionDialog.scope = scope;
791
+ optionDialog.scopeLabel = scope === "query" ? "查询" : scope === "list" ? "列表" : "表单";
792
+ optionDialog.columnName = row.name;
793
+ optionDialog.component = config.component;
794
+ optionDialog.isJSONColumn = row.db_type.trim().toLowerCase() === "json";
795
+ optionDialog.cacheKey = `${row.table_id}:${row.name}:${scope}`;
796
+ optionDialog.option = config.option;
797
+ optionDialog.formConfig = scope === "form" && config.component === "tree-select" ? row.form_config : null;
798
+ optionDialog.visible = true;
799
+ await prepareOptionEditor();
800
+ }
801
+
802
+ /** 保存选项配置并关闭弹窗。 */
803
+ function handleSaveOptionDialog() {
804
+ const row = columns.value.find(item => item.name === optionDialog.columnName);
805
+ if (row) copyCodeGenOptionToEmptyMatches(row, optionDialog.scope);
806
+ optionDialog.visible = false;
807
+ }
808
+
809
+ /** 清理选项配置弹窗上下文。 */
810
+ function handleOptionDialogClosed() {
811
+ optionDialog.option = null;
812
+ optionDialog.formConfig = null;
813
+ }
814
+
815
+ /** 按当前选项来源准备弹窗所需数据。 */
816
+ async function prepareOptionEditor() {
817
+ const option = optionDialog.option;
818
+ if (!option) return;
819
+ if (option.source_type === "static") {
820
+ if (!staticOptions.has(optionDialog.cacheKey)) {
821
+ staticOptions.set(optionDialog.cacheKey, parseCodeGenStaticOptions(option.source_value));
822
+ }
823
+ return;
824
+ }
825
+ if (option.source_type === "dict") {
826
+ option.label_field = "label";
827
+ option.value_field = "value";
828
+ await loadDictionaries();
829
+ return;
830
+ }
831
+ if (option.source_type === "table") {
832
+ await loadDatabaseTables();
833
+ await loadDatabaseColumns(option.source_value);
834
+ applyTableOptionDefaultFields(option, optionDialog.component);
835
+ }
836
+ }
837
+
838
+ /** 切换选项来源时清理当前范围的旧来源字段。 */
839
+ async function handleOptionSourceTypeChange() {
840
+ const option = optionDialog.option;
841
+ if (!option) return;
842
+ option.source_value = "";
843
+ option.label_field = "";
844
+ option.value_field = "";
845
+ option.parent_field = "";
846
+ option.active_value = "";
847
+ option.inactive_value = "";
848
+ option.lazy = false;
849
+ staticOptions.delete(optionDialog.cacheKey);
850
+ if (option.source_type === "static") {
851
+ staticOptions.set(optionDialog.cacheKey, []);
852
+ option.source_value = serializeCodeGenStaticOptions([]);
853
+ option.label_field = "label";
854
+ option.value_field = "value";
855
+ return;
856
+ }
857
+ if (option.source_type === "dict") {
858
+ option.label_field = "label";
859
+ option.value_field = "value";
860
+ await loadDictionaries();
861
+ return;
862
+ }
863
+ if (option.source_type === "table") await loadDatabaseTables();
864
+ }
865
+
866
+ /** 选择字典或数据表后同步当前范围的字段配置。 */
867
+ async function handleOptionSourceValueChange() {
868
+ const option = optionDialog.option;
869
+ if (!option) return;
870
+ option.label_field = option.source_type === "dict" ? "label" : "";
871
+ option.value_field = option.source_type === "dict" ? "value" : "";
872
+ option.parent_field = "";
873
+ option.active_value = "";
874
+ option.inactive_value = "";
875
+ option.lazy = false;
876
+ if (option.source_type === "table") {
877
+ await loadDatabaseColumns(option.source_value);
878
+ applyTableOptionDefaultFields(option, optionDialog.component);
879
+ }
880
+ }
881
+
882
+ /** 加载可用字典列表。 */
883
+ async function loadDictionaries() {
884
+ if (dictionariesLoaded || loadingDictionaries.value) return;
885
+ loadingDictionaries.value = true;
886
+ try {
887
+ const data = await defBaseDictService.OptionBaseDict({});
888
+ dictionaries.value = data.base_dicts ?? [];
889
+ dictionariesLoaded = true;
890
+ } finally {
891
+ loadingDictionaries.value = false;
892
+ }
893
+ }
894
+
895
+ /** 加载可用数据库表列表。 */
896
+ async function loadDatabaseTables() {
897
+ if (databaseTablesLoaded || loadingDatabaseTables.value) return;
898
+ loadingDatabaseTables.value = true;
899
+ try {
900
+ const data = await defCodeGenTableService.ListCodeGenDatabaseTable({});
901
+ databaseTables.value = data.tables ?? [];
902
+ databaseTablesLoaded = true;
903
+ } finally {
904
+ loadingDatabaseTables.value = false;
905
+ }
906
+ }
907
+
908
+ /** 按数据表加载字段并缓存。 */
909
+ async function loadDatabaseColumns(tableName: string) {
910
+ if (!tableName || databaseColumns[tableName] || loadingDatabaseColumns.has(tableName)) return;
911
+ loadingDatabaseColumns.add(tableName);
912
+ try {
913
+ const data = await defCodeGenColumnService.ListCodeGenDatabaseColumn({ table_name: tableName });
914
+ databaseColumns[tableName] = data.columns ?? [];
915
+ } finally {
916
+ loadingDatabaseColumns.delete(tableName);
917
+ }
918
+ }
919
+
920
+ /** 为下拉或树形选项补充所选数据表中存在的常用字段。 */
921
+ function applyTableOptionDefaultFields(option: CodeGenColumnOptionConfig, component: string) {
922
+ if (!option.source_value) return;
923
+ const columnNames = new Set((databaseColumns[option.source_value] ?? []).map(item => item.name));
924
+ if (option.kind === "tree") {
925
+ if (!option.parent_field && columnNames.has("parent_id")) option.parent_field = "parent_id";
926
+ if (!option.label_field && columnNames.has("name")) option.label_field = "name";
927
+ if (!option.value_field && columnNames.has("id")) option.value_field = "id";
928
+ return;
929
+ }
930
+ if (component !== "select") return;
931
+ if (!option.label_field && columnNames.has("name")) option.label_field = "name";
932
+ if (!option.value_field && columnNames.has("id")) option.value_field = "id";
933
+ }
934
+
935
+ /** 格式化数据表字段选项。 */
936
+ function formatDatabaseColumn(column: CodeGenDatabaseColumn) {
937
+ const columnType = column.column_type || column.db_type;
938
+ return column.comment
939
+ ? `${column.name}(${column.comment} / ${columnType})`
940
+ : `${column.name}(${columnType})`;
941
+ }
942
+
943
+ /** 添加一条空白静态选项。 */
944
+ function addStaticOption() {
945
+ const items = staticOptions.get(optionDialog.cacheKey) ?? [];
946
+ items.push({ label: "", value: "" });
947
+ staticOptions.set(optionDialog.cacheKey, items);
948
+ syncStaticOptions();
949
+ }
950
+
951
+ /** 删除指定静态选项。 */
952
+ function removeStaticOption(index: number) {
953
+ const items = staticOptions.get(optionDialog.cacheKey) ?? [];
954
+ items.splice(index, 1);
955
+ syncStaticOptions();
956
+ }
957
+
958
+ /** 保留已有静态值类型,并将无法按原类型解析的编辑值回退为字符串。 */
959
+ function updateStaticOptionValue(option: CodeGenStaticOption, value: string) {
960
+ if (typeof option.value === "number") {
961
+ const parsedValue = Number(value);
962
+ option.value = value !== "" && Number.isFinite(parsedValue) ? parsedValue : value;
963
+ } else if (typeof option.value === "boolean" && (value === "true" || value === "false")) {
964
+ option.value = value === "true";
965
+ } else {
966
+ option.value = value;
967
+ }
968
+ syncStaticOptions();
969
+ }
970
+
971
+ /** 将当前范围的静态选项同步回字段配置。 */
972
+ function syncStaticOptions() {
973
+ if (!optionDialog.option) return;
974
+ optionDialog.option.source_value = serializeCodeGenStaticOptions(staticOptions.get(optionDialog.cacheKey) ?? []);
975
+ }
976
+
977
+ /** 解析静态选项 JSON,并保留数字和布尔值类型。 */
978
+ function parseCodeGenStaticOptions(value: string): CodeGenStaticOption[] {
979
+ if (!value) return [];
980
+ try {
981
+ const items = JSON.parse(value) as unknown;
982
+ if (!Array.isArray(items)) return [];
983
+ return items.flatMap(item => {
984
+ if (!item || typeof item !== "object") return [];
985
+ const label = Reflect.get(item, "label");
986
+ const optionValue = Reflect.get(item, "value");
987
+ if (
988
+ (typeof label !== "string" && typeof label !== "number") ||
989
+ (typeof optionValue !== "string" && typeof optionValue !== "number" && typeof optionValue !== "boolean")
990
+ ) {
991
+ return [];
992
+ }
993
+ return [{ label: String(label), value: optionValue }];
994
+ });
995
+ } catch {
996
+ return [];
997
+ }
998
+ }
999
+
1000
+ /** 将静态选项序列化到数据源值字段。 */
1001
+ function serializeCodeGenStaticOptions(options: CodeGenStaticOption[]) {
1002
+ return JSON.stringify(options);
1003
+ }
1004
+
1005
+ /** 返回保存前首个需要补齐来源配置的字段选项。 */
1006
+ function findCodeGenColumnOptionIssue(items: CodeGenColumnView[]): CodeGenColumnOptionIssue | undefined {
1007
+ for (const column of items) {
1008
+ const optionConfigs: Array<[CodeGenOptionScope, CodeGenColumnOptionConfig]> = [
1009
+ ["query", column.query_config.option],
1010
+ ["list", column.list_config.option],
1011
+ ["form", column.form_config.option]
1012
+ ];
1013
+ for (const [scope, option] of optionConfigs) {
1014
+ const scopeLabel = scope === "query" ? "查询" : scope === "list" ? "列表" : "表单";
1015
+ const message = getCodeGenOptionValidationMessage(column.name, scopeLabel, option);
1016
+ if (message) return { row: column, scope, message };
1017
+ }
1018
+ }
1019
+ }
1020
+
1021
+ /** 检查同一字段查询、列表和表单选项来源是否一致。 */
1022
+ function findCodeGenColumnOptionConsistencyIssue(items: CodeGenColumnView[]): CodeGenColumnOptionConsistencyIssue | undefined {
1023
+ for (const column of items) {
1024
+ const entries = (
1025
+ [
1026
+ ["查询", column.query_config, "query"],
1027
+ ["列表", column.list_config, "list"],
1028
+ ["表单", column.form_config, "form"]
1029
+ ] as const
1030
+ ).filter(([, config, scope]) => config.enabled && hasOptionComponent(config.component, scope) && isCompleteCodeGenOptionConfig(config.option));
1031
+ if (entries.length < 2) continue;
1032
+ const firstSignature = codeGenOptionSourceSignature(entries[0][1].option);
1033
+ if (entries.every(([, config]) => codeGenOptionSourceSignature(config.option) === firstSignature)) continue;
1034
+ return {
1035
+ columnName: column.name,
1036
+ scopes: entries.map(([label]) => label).join("、")
1037
+ };
1038
+ }
1039
+ }
1040
+
1041
+ /** 返回用于比较字段选项数据源的稳定签名。 */
1042
+ function codeGenOptionSourceSignature(option: CodeGenColumnOptionConfig) {
1043
+ return JSON.stringify({
1044
+ source_type: option.source_type,
1045
+ source_value: option.source_value,
1046
+ label_field: option.label_field,
1047
+ value_field: option.value_field,
1048
+ parent_field: option.parent_field,
1049
+ lazy: option.lazy
1050
+ });
1051
+ }
1052
+
1053
+ /** 返回单个范围内不完整选项配置的提示文案。 */
1054
+ function getCodeGenOptionValidationMessage(columnName: string, scope: string, option: CodeGenColumnOptionConfig) {
1055
+ const hasSourceFields = !!(
1056
+ option.source_type ||
1057
+ option.source_value ||
1058
+ option.label_field ||
1059
+ option.value_field ||
1060
+ option.parent_field ||
1061
+ option.active_value ||
1062
+ option.inactive_value ||
1063
+ option.lazy
1064
+ );
1065
+ if (!option.kind) return hasSourceFields ? `字段 ${columnName} 的${scope}选项配置无对应组件` : "";
1066
+ if (option.kind === "switch") {
1067
+ if (option.source_type !== "dict" || !option.source_value || !option.active_value || !option.inactive_value) {
1068
+ return `字段 ${columnName} 的${scope}开关配置不完整`;
1069
+ }
1070
+ if (option.active_value === option.inactive_value) return `字段 ${columnName} 的${scope}开启值和关闭值不能相同`;
1071
+ return "";
1072
+ }
1073
+ if (option.active_value || option.inactive_value) return `字段 ${columnName} 的${scope}选项不能配置开关值`;
1074
+ if (option.lazy && option.kind !== "tree") return `字段 ${columnName} 的${scope}仅树形选项支持懒加载`;
1075
+ if (option.kind === "tree" && option.source_type !== "table") {
1076
+ return `字段 ${columnName} 的${scope}树形选项只能使用数据表来源`;
1077
+ }
1078
+ if (!new Set(["static", "dict", "table"]).has(option.source_type) || !option.source_value) {
1079
+ return `字段 ${columnName} 的${scope}选项来源配置不完整`;
1080
+ }
1081
+ if (option.source_type === "static") {
1082
+ const options = parseCodeGenStaticOptions(option.source_value);
1083
+ if (!options.length || options.some(item => item.label === "" || item.value === "")) {
1084
+ return `字段 ${columnName} 的${scope}选项至少需要一条完整的静态数据`;
1085
+ }
1086
+ }
1087
+ if (
1088
+ option.source_type === "table" &&
1089
+ (!option.label_field || !option.value_field || (option.kind === "tree" && !option.parent_field))
1090
+ ) {
1091
+ return `字段 ${columnName} 的${scope}数据表选项字段配置不完整`;
1092
+ }
1093
+ return "";
1094
+ }
1095
+
1096
+ /** 判断当前配置是否需要展示选项入口。 */
1097
+ function shouldShowOptionEntry(config: CodeGenOptionContainer, scope: CodeGenOptionScope) {
1098
+ return config.enabled && hasOptionComponent(config.component, scope);
1099
+ }
1100
+
1101
+ /** 判断当前范围是否已经填写选项配置。 */
1102
+ function hasOptionConfig(option: CodeGenColumnOptionConfig) {
1103
+ return isCompleteCodeGenOptionConfig(option);
1104
+ }
1105
+
1106
+ /** 返回选项入口的当前状态提示。 */
1107
+ function optionEntryTip(option: CodeGenColumnOptionConfig) {
1108
+ return hasOptionConfig(option) ? "已配置,可直接保存;需要覆盖时可打开修改" : "配置选项";
1109
+ }
1110
+
1111
+ /** 判断组件是否依赖选择数据源。 */
1112
+ function hasOptionComponent(component: string, scope: CodeGenOptionScope) {
1113
+ return (
1114
+ (scope !== "query" && component === "switch") ||
1115
+ ["segmented", "select", "dict", "radio-group", "checkbox-group", "tree-select", "transfer"].includes(component)
1116
+ );
1117
+ }
1118
+
1119
+ /** 同步字段在查询、列表和表单范围内由组件决定的选项形态。 */
1120
+ function syncColumnOptionKinds(column: CodeGenColumnView) {
1121
+ syncOptionKind(column.query_config, "query");
1122
+ syncOptionKind(column.list_config, "list");
1123
+ syncOptionKind(column.form_config, "form");
1124
+ if (!column.form_config.enabled || column.form_config.component !== "tree-select") column.form_config.multiple = false;
1125
+ }
1126
+
1127
+ /** 根据当前组件自动确定选项形态,并移除不再适用的选项配置。 */
1128
+ function syncOptionKind(config: CodeGenOptionContainer, scope: CodeGenOptionScope) {
1129
+ if (!config.enabled || !hasOptionComponent(config.component, scope)) {
1130
+ Object.assign(config.option, createDefaultCodeGenOptionConfig());
1131
+ return;
1132
+ }
1133
+ const kind =
1134
+ config.component === "tree-select"
1135
+ ? "tree"
1136
+ : scope !== "query" && config.component === "switch"
1137
+ ? "switch"
1138
+ : "option";
1139
+ if (kind === "tree" && config.option.source_type !== "table") {
1140
+ Object.assign(config.option, createDefaultCodeGenOptionConfig());
1141
+ config.option.source_type = "table";
1142
+ }
1143
+ if (kind === "switch") {
1144
+ if (config.option.source_type !== "dict") {
1145
+ Object.assign(config.option, createDefaultCodeGenOptionConfig());
1146
+ config.option.source_type = "dict";
1147
+ }
1148
+ config.option.label_field = "label";
1149
+ config.option.value_field = "value";
1150
+ if (!config.option.source_value) config.option.source_value = codeGenDefaultSwitchOption.sourceValue;
1151
+ if (!config.option.active_value) config.option.active_value = codeGenDefaultSwitchOption.activeValue;
1152
+ if (!config.option.inactive_value) config.option.inactive_value = codeGenDefaultSwitchOption.inactiveValue;
1153
+ }
1154
+ if (scope === "form" && config.component === "dict") {
1155
+ if (config.option.source_type !== "dict") {
1156
+ Object.assign(config.option, createDefaultCodeGenOptionConfig());
1157
+ config.option.source_type = "dict";
1158
+ }
1159
+ config.option.label_field = "label";
1160
+ config.option.value_field = "value";
1161
+ }
1162
+ config.option.kind = kind;
1163
+ if (config.option.kind !== "tree") {
1164
+ config.option.parent_field = "";
1165
+ config.option.lazy = false;
1166
+ }
1167
+ if (config.option.kind !== "switch") {
1168
+ config.option.active_value = "";
1169
+ config.option.inactive_value = "";
1170
+ }
1171
+ }
1172
+
1173
+ onMounted(() => {
1174
+ syncWorkspaceTitle();
1175
+ void handleQuery();
1176
+ });
1177
+
1178
+ onBeforeUnmount(() => {
1179
+ destroyColumnSortable();
1180
+ });
1181
+ </script>
1182
+
1183
+ <style scoped lang="scss">
1184
+ .code-gen-sub-card {
1185
+ background: var(--admin-page-card-bg);
1186
+ border: 1px solid var(--admin-page-card-border);
1187
+ border-radius: var(--admin-page-radius);
1188
+ box-shadow: var(--admin-page-shadow);
1189
+ }
1190
+
1191
+ :deep(.code-gen-toolbar) {
1192
+ display: flex;
1193
+ flex-wrap: wrap;
1194
+ gap: 12px;
1195
+ align-items: center;
1196
+ justify-content: flex-end;
1197
+ margin-bottom: 14px;
1198
+ }
1199
+
1200
+ .code-gen-column-pane {
1201
+ display: flex;
1202
+ flex-direction: column;
1203
+ gap: 14px;
1204
+ }
1205
+
1206
+ .code-gen-column-toolbar {
1207
+ justify-content: space-between;
1208
+ padding: 12px 14px;
1209
+ background: var(--admin-page-card-bg-soft);
1210
+ border: 1px solid var(--admin-page-card-border-soft);
1211
+ border-radius: var(--admin-page-radius);
1212
+ }
1213
+
1214
+ .code-gen-column-toolbar__meta {
1215
+ display: flex;
1216
+ flex-wrap: wrap;
1217
+ gap: 8px;
1218
+ align-items: center;
1219
+ min-width: 0;
1220
+ color: var(--admin-page-text-secondary);
1221
+ }
1222
+
1223
+ .code-gen-column-toolbar__meta strong {
1224
+ font-size: 14px;
1225
+ color: var(--admin-page-text-primary);
1226
+ }
1227
+
1228
+ .code-gen-column-toolbar__meta span {
1229
+ padding: 3px 8px;
1230
+ overflow: hidden;
1231
+ font-size: 12px;
1232
+ line-height: 18px;
1233
+ text-overflow: ellipsis;
1234
+ white-space: nowrap;
1235
+ background: var(--admin-page-card-bg);
1236
+ border: 1px solid var(--admin-page-card-border-soft);
1237
+ border-radius: 999px;
1238
+ }
1239
+
1240
+ .code-gen-column-toolbar__table-name {
1241
+ max-width: 220px;
1242
+ }
1243
+
1244
+ .code-gen-column-toolbar__table-comment {
1245
+ max-width: 280px;
1246
+ }
1247
+
1248
+ .code-gen-column-toolbar__actions {
1249
+ display: flex;
1250
+ gap: 8px;
1251
+ flex-shrink: 0;
1252
+ margin-left: auto;
1253
+ }
1254
+
1255
+ .code-gen-column-table {
1256
+ overflow: hidden;
1257
+ border-radius: var(--admin-page-radius);
1258
+ }
1259
+
1260
+ :deep(.code-gen-column-table .el-table__header th) {
1261
+ color: var(--admin-page-text-secondary);
1262
+ background: var(--admin-page-card-bg-soft);
1263
+ }
1264
+
1265
+ :deep(.code-gen-column-table .el-table__cell) {
1266
+ padding: 8px 0;
1267
+ vertical-align: middle;
1268
+ }
1269
+
1270
+ .code-gen-field-cell,
1271
+ .code-gen-field-trigger,
1272
+ .code-gen-field-order {
1273
+ display: flex;
1274
+ gap: 8px;
1275
+ align-items: center;
1276
+ min-width: 0;
1277
+ }
1278
+
1279
+ .code-gen-field-cell {
1280
+ justify-content: space-between;
1281
+ }
1282
+
1283
+ .code-gen-field-trigger {
1284
+ flex: 1;
1285
+ min-height: 28px;
1286
+ cursor: help;
1287
+ }
1288
+
1289
+ .code-gen-field-order {
1290
+ gap: 4px;
1291
+ flex-shrink: 0;
1292
+ }
1293
+
1294
+ .code-gen-field-order__index {
1295
+ min-width: 18px;
1296
+ font-size: 12px;
1297
+ color: var(--admin-page-text-secondary);
1298
+ text-align: right;
1299
+ }
1300
+
1301
+ .code-gen-field-order__drag {
1302
+ width: 24px;
1303
+ height: 24px;
1304
+ padding: 0;
1305
+ cursor: grab;
1306
+ }
1307
+
1308
+ .code-gen-field-order__drag:active {
1309
+ cursor: grabbing;
1310
+ }
1311
+
1312
+ :deep(.code-gen-column-sortable-ghost > td) {
1313
+ background: var(--admin-page-card-bg-soft);
1314
+ }
1315
+
1316
+ :deep(.code-gen-column-sortable-chosen > td) {
1317
+ background: var(--admin-page-card-bg-soft);
1318
+ }
1319
+
1320
+ .code-gen-config-cell,
1321
+ .code-gen-static-options__header,
1322
+ .code-gen-static-options__item {
1323
+ display: flex;
1324
+ flex-wrap: wrap;
1325
+ gap: 8px;
1326
+ align-items: center;
1327
+ }
1328
+
1329
+ .code-gen-field-trigger__name {
1330
+ flex-shrink: 0;
1331
+ max-width: 135px;
1332
+ overflow: hidden;
1333
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
1334
+ font-size: 13px;
1335
+ font-weight: 700;
1336
+ color: var(--admin-page-text-primary);
1337
+ text-overflow: ellipsis;
1338
+ white-space: nowrap;
1339
+ }
1340
+
1341
+ .code-gen-field-popover {
1342
+ display: grid;
1343
+ gap: 12px;
1344
+ }
1345
+
1346
+ .code-gen-field-popover__header {
1347
+ display: grid;
1348
+ gap: 3px;
1349
+ }
1350
+
1351
+ .code-gen-field-popover__header strong,
1352
+ .code-gen-field-popover__types b {
1353
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
1354
+ color: var(--admin-page-text-primary);
1355
+ }
1356
+
1357
+ .code-gen-field-popover__header span {
1358
+ font-size: 12px;
1359
+ color: var(--admin-page-text-secondary);
1360
+ }
1361
+
1362
+ .code-gen-field-popover__types {
1363
+ display: grid;
1364
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1365
+ gap: 7px 12px;
1366
+ }
1367
+
1368
+ .code-gen-field-popover__types div {
1369
+ display: grid;
1370
+ grid-template-columns: 52px minmax(0, 1fr);
1371
+ gap: 6px;
1372
+ align-items: center;
1373
+ min-width: 0;
1374
+ font-size: 12px;
1375
+ }
1376
+
1377
+ .code-gen-field-popover__types span {
1378
+ color: var(--admin-page-text-secondary);
1379
+ }
1380
+
1381
+ .code-gen-field-popover__types b {
1382
+ overflow: hidden;
1383
+ font-size: 12px;
1384
+ text-overflow: ellipsis;
1385
+ white-space: nowrap;
1386
+ }
1387
+
1388
+ .code-gen-field-popover__flags {
1389
+ display: flex;
1390
+ gap: 6px;
1391
+ align-items: center;
1392
+ flex-wrap: wrap;
1393
+ }
1394
+
1395
+ .code-gen-config-cell .el-select {
1396
+ width: 124px;
1397
+ }
1398
+
1399
+ .code-gen-config-cell .el-checkbox {
1400
+ margin-right: 0;
1401
+ }
1402
+
1403
+ .code-gen-option-dialog,
1404
+ .code-gen-static-options,
1405
+ .code-gen-static-options__list {
1406
+ display: grid;
1407
+ gap: 10px;
1408
+ }
1409
+
1410
+ .code-gen-config-dialog__header {
1411
+ display: flex;
1412
+ gap: 12px;
1413
+ align-items: center;
1414
+ justify-content: space-between;
1415
+ width: 100%;
1416
+ }
1417
+
1418
+ .code-gen-popover-form__row {
1419
+ display: grid;
1420
+ grid-template-columns: 92px minmax(0, 1fr);
1421
+ gap: 10px;
1422
+ align-items: center;
1423
+ }
1424
+
1425
+ .code-gen-popover-form__label {
1426
+ font-size: 12px;
1427
+ color: var(--admin-page-text-secondary);
1428
+ }
1429
+
1430
+ .code-gen-static-options {
1431
+ padding-top: 2px;
1432
+ }
1433
+
1434
+ .code-gen-static-options__header {
1435
+ justify-content: space-between;
1436
+ }
1437
+
1438
+ .code-gen-static-options__item {
1439
+ flex-wrap: nowrap;
1440
+ }
1441
+
1442
+ .code-gen-static-options__item .el-input {
1443
+ min-width: 0;
1444
+ }
1445
+
1446
+ @media (width <= 900px) {
1447
+ .code-gen-column-toolbar {
1448
+ align-items: flex-start;
1449
+ flex-direction: column;
1450
+ }
1451
+
1452
+ .code-gen-column-toolbar__actions {
1453
+ margin-left: 0;
1454
+ }
1455
+ }
1456
+ </style>