@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,684 @@
1
+ <template>
2
+ <div class="table-box">
3
+ <ProTable
4
+ ref="proTable"
5
+ :key="isDefaultTenant ? 'default-tenant' : 'normal-tenant'"
6
+ row-key="id"
7
+ :columns="columns"
8
+ :header-actions="headerActions"
9
+ :request-api="requestBaseRoleTable"
10
+ />
11
+
12
+ <FormDialog
13
+ v-model="dialog.visible"
14
+ ref="formDialogRef"
15
+ :title="dialog.title"
16
+ width="500px"
17
+ :model="formData"
18
+ :fields="formFields"
19
+ :rules="rules"
20
+ label-width="100px"
21
+ @confirm="handleSubmit"
22
+ @close="handleCloseDialog"
23
+ />
24
+
25
+ <el-drawer v-model="assignPermDialogVisible" :title="`【${checkedBaseRole.name}】权限分配`" size="500">
26
+ <div class="perm-toolbar">
27
+ <el-input v-model="permKeywords" clearable class="perm-search" placeholder="菜单权限名称">
28
+ <template #prefix>
29
+ <Search />
30
+ </template>
31
+ </el-input>
32
+
33
+ <div class="perm-toolbar__actions">
34
+ <div class="perm-toolbar__group">
35
+ <span class="perm-toolbar__label">树操作</span>
36
+ <el-button type="primary" size="small" plain class="perm-toolbar__button" @click="togglePermTree">
37
+ <template #icon>
38
+ <Switch />
39
+ </template>
40
+ {{ isExpanded ? "收缩节点" : "展开节点" }}
41
+ </el-button>
42
+ </div>
43
+ <div class="perm-toolbar__group perm-toolbar__group--linkage">
44
+ <span class="perm-toolbar__label">勾选模式</span>
45
+ <el-checkbox v-model="parentChildLinked" @change="handelParentChildLinkedChange">父子联动</el-checkbox>
46
+ <el-tooltip placement="bottom">
47
+ <template #content>如果只需勾选菜单权限,不需要勾选子菜单或者按钮权限,请关闭父子联动</template>
48
+ <el-icon class="perm-linkage__icon">
49
+ <QuestionFilled />
50
+ </el-icon>
51
+ </el-tooltip>
52
+ </div>
53
+ </div>
54
+ </div>
55
+
56
+ <el-tree
57
+ ref="permTreeRef"
58
+ node-key="value"
59
+ show-checkbox
60
+ :data="menuPermOptions"
61
+ :filter-node-method="handlePermFilter"
62
+ :default-expand-all="true"
63
+ :check-strictly="!parentChildLinked"
64
+ class="mt-5"
65
+ >
66
+ <template #default="{ data }">
67
+ {{ data.label }}
68
+ </template>
69
+ </el-tree>
70
+
71
+ <template #footer>
72
+ <div class="dialog-footer">
73
+ <el-button type="primary" @click="handleAssignPermSubmit">确 定</el-button>
74
+ <el-button @click="assignPermDialogVisible = false">取 消</el-button>
75
+ </div>
76
+ </template>
77
+ </el-drawer>
78
+ </div>
79
+ </template>
80
+
81
+ <script setup lang="ts">
82
+ import { computed, nextTick, reactive, ref, watch } from "vue";
83
+ import { ElMessage, ElMessageBox, ElTree } from "element-plus";
84
+ import type { CheckboxValueType } from "element-plus";
85
+ import { CirclePlus, Delete, EditPen, Position, QuestionFilled, Search, Switch } from "@element-plus/icons-vue";
86
+ import type { ColumnProps, HeaderActionProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
87
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
88
+ import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
89
+ import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
90
+ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
91
+ import { defBaseRoleService } from "@liujitcn/kratos-admin-system/api/system/base_role";
92
+ import type { BaseRole, BaseRoleForm, PageBaseRoleRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_role";
93
+ import { defBaseMenuService } from "@liujitcn/kratos-admin-system/api/system/base_menu";
94
+ import { defBaseTenantService } from "@liujitcn/kratos-admin-system/api/system/base_tenant";
95
+ import type { SelectOptionResponse_Option, TreeOptionResponse_Option } from "@liujitcn/kratos-admin-system/rpc/common/v1/common";
96
+ import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
97
+ import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
98
+ import { useUserStore } from "@liujitcn/kratos-admin-core/stores/runtime";
99
+ import { DEFAULT_TENANT_CODE, requestTenantOptions } from "@liujitcn/kratos-admin-core/tenant";
100
+
101
+ defineOptions({
102
+ name: "BaseRole",
103
+ inheritAttrs: false
104
+ });
105
+
106
+ /** 当前正在分配权限的角色摘要。 */
107
+ interface CheckedBaseRole {
108
+ id?: number;
109
+ name?: string;
110
+ }
111
+
112
+ /** 角色表单状态,新增时租户必须由默认租户管理员显式选择。 */
113
+ type BaseRoleFormState = Omit<BaseRoleForm, "tenant_id"> & {
114
+ /** 租户ID。 */
115
+ tenant_id?: number;
116
+ };
117
+
118
+ const { BUTTONS } = useAuthButtons();
119
+ const userStore = useUserStore();
120
+ const proTable = ref<ProTableInstance>();
121
+ const formDialogRef = ref<InstanceType<typeof FormDialog>>();
122
+ const permTreeRef = ref<InstanceType<typeof ElTree>>();
123
+
124
+ const dialog = reactive({
125
+ title: "",
126
+ visible: false
127
+ });
128
+
129
+ const menuPermOptions = ref<TreeOptionResponse_Option[]>([]);
130
+ const tenantOptions = ref<SelectOptionResponse_Option[]>([]);
131
+ const statusOptions: ProFormOption[] = [
132
+ { label: "启用", value: Status.ENABLE },
133
+ { label: "禁用", value: Status.DISABLE }
134
+ ];
135
+ const protectedRoleCodes = new Set(["admin", "authuser", "user"]);
136
+
137
+ const formData = reactive<BaseRoleFormState>({
138
+ /** 角色ID */
139
+ id: 0,
140
+ /** 租户ID */
141
+ tenant_id: undefined,
142
+ /** 角色名称 */
143
+ name: "",
144
+ /** 角色值 */
145
+ code: "",
146
+ /** 数据权限:0全部数据1部门及子部门数据2本部门数据3本人数据 */
147
+ data_scope: 1,
148
+ /** 分配的菜单列表 */
149
+ menus: [],
150
+ /** 状态 */
151
+ status: Status.ENABLE,
152
+ /** 备注 */
153
+ remark: ""
154
+ });
155
+
156
+ const rules = reactive({
157
+ tenant_id: [{ required: true, message: "请选择所属租户", trigger: "change" }],
158
+ name: [
159
+ { required: true, message: "请输入角色名称", trigger: "blur" },
160
+ { max: 30, message: "角色名称不能超过 30 个字符", trigger: "blur" }
161
+ ],
162
+ code: [
163
+ { required: true, message: "请输入角色编码", trigger: "blur" },
164
+ { max: 20, message: "角色编号不能超过 20 个字符", trigger: "blur" }
165
+ ],
166
+ data_scope: [{ required: true, message: "请选择数据权限", trigger: "change" }],
167
+ menus: [
168
+ { required: true, message: "请选择菜单权限", trigger: "change" },
169
+ {
170
+ validator: (_rule: unknown, value: unknown, callback: (error?: Error) => void) => {
171
+ const menuIds = Array.isArray(value) ? value : [];
172
+ if (new Set(menuIds).size !== menuIds.length) {
173
+ callback(new Error("菜单权限不能重复"));
174
+ return;
175
+ }
176
+ callback();
177
+ },
178
+ trigger: "change"
179
+ }
180
+ ],
181
+ status: [{ required: true, message: "请选择状态", trigger: "change" }],
182
+ remark: [{ max: 500, message: "备注不能超过 500 个字符", trigger: "blur" }]
183
+ });
184
+
185
+ const checkedBaseRole = ref<CheckedBaseRole>({});
186
+ const assignPermDialogVisible = ref(false);
187
+ const permKeywords = ref("");
188
+ const isExpanded = ref(true);
189
+ const parentChildLinked = ref(true);
190
+
191
+ /** 当前登录账号是否默认租户。 */
192
+ const isDefaultTenant = computed(() => userStore.userInfo.tenant_code === DEFAULT_TENANT_CODE);
193
+
194
+ /** 角色表单字段配置。 */
195
+ const formFields = computed<ProFormField[]>(() => [
196
+ {
197
+ prop: "tenant_id",
198
+ label: "所属租户",
199
+ component: "select",
200
+ props: {
201
+ placeholder: "请选择所属租户",
202
+ filterable: true,
203
+ disabled: Boolean(formData.id),
204
+ onChange: handleFormTenantChange
205
+ },
206
+ visible: () => isDefaultTenant.value,
207
+ options: tenantOptions.value
208
+ },
209
+ { prop: "name", label: "角色名称", component: "input", props: { placeholder: "请输入角色名称" } },
210
+ {
211
+ prop: "code",
212
+ label: "角色编码",
213
+ component: "input",
214
+ props: { placeholder: "请输入角色编码", disabled: Boolean(formData.id && formData.code === "tenant") }
215
+ },
216
+ { prop: "data_scope", label: "数据权限", component: "dict", props: { code: "base_role_data_scope" } },
217
+ {
218
+ prop: "menus",
219
+ label: "菜单权限",
220
+ component: "tree-select",
221
+ options: menuPermOptions.value as unknown as ProFormOption[],
222
+ props: {
223
+ nodeKey: "value",
224
+ props: { label: "label", children: "children" },
225
+ multiple: true,
226
+ showCheckbox: true,
227
+ checkStrictly: true,
228
+ style: { width: "100%" },
229
+ onCheck: handleCheck
230
+ }
231
+ },
232
+ { prop: "remark", label: "备注", component: "textarea", props: { placeholder: "请输入备注" } },
233
+ {
234
+ prop: "status",
235
+ label: "状态",
236
+ component: "radio-group",
237
+ options: statusOptions,
238
+ props: { disabled: isRoleProtected(formData.code) }
239
+ }
240
+ ]);
241
+
242
+ /** 角色表格列配置。 */
243
+ const columns = computed<ColumnProps[]>(() => [
244
+ { type: "selection", width: 55, selectable: row => canDeleteRole(row as BaseRole) },
245
+ ...(isDefaultTenant.value
246
+ ? ([
247
+ {
248
+ prop: "tenant_id",
249
+ label: "租户",
250
+ minWidth: 140,
251
+ showOverflowTooltip: true,
252
+ search: { el: "select", key: "tenant_id", props: { filterable: true }, order: 1 },
253
+ enum: requestTenantOptions
254
+ }
255
+ ] satisfies ColumnProps[])
256
+ : []),
257
+ { prop: "name", label: "角色名称", minWidth: 140, search: { el: "input" } },
258
+ { prop: "code", label: "角色编码", minWidth: 160, search: { el: "input" } },
259
+ { prop: "data_scope", label: "数据权限", minWidth: 120, dictCode: "base_role_data_scope", search: { el: "select" } },
260
+ { prop: "remark", label: "备注", minWidth: 160 },
261
+ {
262
+ prop: "status",
263
+ label: "状态",
264
+ minWidth: 100,
265
+ search: { el: "select" },
266
+ cellType: "status",
267
+ statusProps: {
268
+ activeValue: Status.ENABLE,
269
+ inactiveValue: Status.DISABLE,
270
+ activeText: "启用",
271
+ inactiveText: "禁用",
272
+ disabled: scope => !canChangeRoleStatus(scope.row as BaseRole) || !BUTTONS.value["base:role:status"],
273
+ beforeChange: scope => handleBeforeSetStatus(scope.row as BaseRole)
274
+ }
275
+ },
276
+ { prop: "created_at", label: "创建时间", minWidth: 180 },
277
+ { prop: "updated_at", label: "更新时间", minWidth: 180 },
278
+ {
279
+ prop: "operation",
280
+ label: "操作",
281
+ width: 280,
282
+ fixed: "right",
283
+ cellType: "actions",
284
+ actions: [
285
+ {
286
+ label: "分配权限",
287
+ type: "primary",
288
+ link: true,
289
+ icon: Position,
290
+ hidden: scope => !canManageRole(scope.row as BaseRole) || !BUTTONS.value["base:role:menus"],
291
+ onClick: scope => handleOpenAssignPermDialog(scope.row as BaseRole)
292
+ },
293
+ {
294
+ label: "编辑",
295
+ type: "primary",
296
+ link: true,
297
+ icon: EditPen,
298
+ hidden: scope => !canManageRole(scope.row as BaseRole) || !BUTTONS.value["base:role:update"],
299
+ params: scope => ({ roleId: scope.row.id }),
300
+ onClick: (scope, params) => handleOpenDialog((params?.roleId as number | undefined) ?? (scope.row as BaseRole).id)
301
+ },
302
+ {
303
+ label: "删除",
304
+ type: "danger",
305
+ link: true,
306
+ icon: Delete,
307
+ hidden: scope => !canDeleteRole(scope.row as BaseRole) || !BUTTONS.value["base:role:delete"],
308
+ onClick: scope => handleDelete(scope.row as BaseRole)
309
+ }
310
+ ]
311
+ }
312
+ ]);
313
+
314
+ /** 角色顶部按钮配置。 */
315
+ const headerActions: HeaderActionProps[] = [
316
+ {
317
+ label: "新增",
318
+ type: "success",
319
+ icon: CirclePlus,
320
+ hidden: () => !BUTTONS.value["base:role:create"],
321
+ onClick: () => handleOpenDialog()
322
+ },
323
+ {
324
+ label: "删除",
325
+ type: "danger",
326
+ icon: Delete,
327
+ hidden: () => !BUTTONS.value["base:role:delete"],
328
+ disabled: scope => !scope.selectedList.length,
329
+ onClick: scope => handleDelete(scope.selectedList as BaseRole[])
330
+ }
331
+ ];
332
+
333
+ /**
334
+ * 请求角色列表,并由 ProTable 统一维护分页与搜索参数。
335
+ */
336
+ async function requestBaseRoleTable(params: PageBaseRoleRequest) {
337
+ const data = await defBaseRoleService.PageBaseRole({
338
+ ...buildPageRequest(params),
339
+ tenant_id: isDefaultTenant.value ? params.tenant_id : undefined,
340
+ });
341
+ return { data: { list: data.base_roles ?? [], total: data.total } };
342
+ }
343
+
344
+ /**
345
+ * 刷新角色表格。
346
+ */
347
+ function refreshTable() {
348
+ proTable.value?.getTableList();
349
+ }
350
+
351
+ /**
352
+ * 加载租户下拉选项。
353
+ */
354
+ async function loadTenantOptions() {
355
+ if (!isDefaultTenant.value || tenantOptions.value.length) return;
356
+ const response = await defBaseTenantService.OptionBaseTenant({ keyword: "" });
357
+ tenantOptions.value = response.list ?? [];
358
+ }
359
+
360
+ /**
361
+ * 按目标角色加载可分配的菜单权限树数据。
362
+ */
363
+ async function loadMenuPermOptions(roleId?: number) {
364
+ const optionBaseMenuRes = await defBaseMenuService.OptionBaseMenu({
365
+ role_id: roleId
366
+ });
367
+ menuPermOptions.value = optionBaseMenuRes.list ?? [];
368
+ }
369
+
370
+ /**
371
+ * 切换角色所属租户时,清空已选菜单并重新加载当前可分配权限。
372
+ */
373
+ async function handleFormTenantChange() {
374
+ formData.menus = [];
375
+ await loadMenuPermOptions();
376
+ }
377
+
378
+ /**
379
+ * 打开角色弹窗。
380
+ */
381
+ async function handleOpenDialog(roleId?: number) {
382
+ resetForm();
383
+ await loadTenantOptions();
384
+ dialog.title = roleId ? "修改角色" : "新增角色";
385
+ dialog.visible = true;
386
+ if (!roleId) {
387
+ await loadMenuPermOptions();
388
+ return;
389
+ }
390
+
391
+ const data = await defBaseRoleService.GetBaseRole({ id: roleId });
392
+ Object.assign(formData, data);
393
+ await loadMenuPermOptions(roleId);
394
+ }
395
+
396
+ /**
397
+ * 关闭角色弹窗并恢复默认表单值。
398
+ */
399
+ function handleCloseDialog() {
400
+ dialog.visible = false;
401
+ resetForm();
402
+ }
403
+
404
+ /**
405
+ * 重置角色表单,避免新增与编辑之间互相污染。
406
+ */
407
+ function resetForm() {
408
+ formDialogRef.value?.resetFields();
409
+ formDialogRef.value?.clearValidate();
410
+ formData.id = 0;
411
+ formData.tenant_id = undefined;
412
+ formData.name = "";
413
+ formData.code = "";
414
+ formData.data_scope = 1;
415
+ formData.menus = [];
416
+ formData.status = Status.ENABLE;
417
+ formData.remark = "";
418
+ menuPermOptions.value = [];
419
+ }
420
+
421
+ /**
422
+ * 同步树选择组件已勾选菜单到表单值。
423
+ */
424
+ function handleCheck(currentNode: unknown, { checkedNodes }: { checkedNodes: Array<{ value: number }> }) {
425
+ formData.menus = checkedNodes.map(node => node.value);
426
+ }
427
+
428
+ /**
429
+ * 提交角色表单。
430
+ */
431
+ function handleSubmit() {
432
+ formDialogRef.value?.validate()?.then(valid => {
433
+ if (!valid) return;
434
+
435
+ const submitData = JSON.parse(JSON.stringify(formData)) as BaseRoleForm;
436
+ const request = submitData.id
437
+ ? defBaseRoleService.UpdateBaseRole({ base_role: submitData })
438
+ : defBaseRoleService.CreateBaseRole({ base_role: submitData });
439
+ request.then(() => {
440
+ ElMessage.success(submitData.id ? "修改角色成功" : "新增角色成功");
441
+ handleCloseDialog();
442
+ refreshTable();
443
+ });
444
+ });
445
+ }
446
+
447
+ /**
448
+ * 根据后端保护标记判断当前账号是否允许操作目标角色。
449
+ */
450
+ function canManageRole(row?: BaseRole) {
451
+ return Boolean(row?.code && !row.is_protected);
452
+ }
453
+
454
+ /** 判断角色是否禁止切换状态和删除。 */
455
+ function isRoleProtected(code?: string) {
456
+ return Boolean(code && protectedRoleCodes.has(code));
457
+ }
458
+
459
+ /** 判断当前账号是否允许切换目标角色状态。 */
460
+ function canChangeRoleStatus(row?: BaseRole) {
461
+ return canManageRole(row) && !isRoleProtected(row?.code);
462
+ }
463
+
464
+ /** 判断当前账号是否允许删除目标角色。 */
465
+ function canDeleteRole(row?: BaseRole) {
466
+ return canManageRole(row) && !isRoleProtected(row?.code);
467
+ }
468
+
469
+ /**
470
+ * 在角色状态切换前先完成确认与接口调用,避免首屏渲染触发误操作。
471
+ */
472
+ async function handleBeforeSetStatus(row: BaseRole) {
473
+ if (!canChangeRoleStatus(row)) {
474
+ ElMessage.warning("默认角色不允许修改状态");
475
+ return false;
476
+ }
477
+
478
+ const nextStatus = row.status === Status.ENABLE ? Status.DISABLE : Status.ENABLE;
479
+ const text = nextStatus === Status.ENABLE ? "启用" : "禁用";
480
+ const roleName = row.name || row.code || `ID:${row.id}`;
481
+ try {
482
+ await ElMessageBox.confirm(`是否确定${text}角色?\n角色名称:${roleName}`, "提示", {
483
+ confirmButtonText: "确认",
484
+ cancelButtonText: "取消",
485
+ type: "warning"
486
+ });
487
+ await defBaseRoleService.SetBaseRoleStatus({ id: row.id, status: nextStatus });
488
+ ElMessage.success(`${text}成功`);
489
+ refreshTable();
490
+ return true;
491
+ } catch {
492
+ return false;
493
+ }
494
+ }
495
+
496
+ /**
497
+ * 删除角色,兼容单条删除与批量删除。
498
+ */
499
+ function handleDelete(selected?: number | string | Array<number | string> | BaseRole | BaseRole[]) {
500
+ const roleList = Array.isArray(selected)
501
+ ? (selected.filter(item => typeof item === "object") as BaseRole[])
502
+ : selected && typeof selected === "object"
503
+ ? [selected as BaseRole]
504
+ : [];
505
+ if (roleList.some(role => !canDeleteRole(role))) {
506
+ ElMessage.warning("默认角色不允许删除");
507
+ return;
508
+ }
509
+
510
+ const roleIds = (
511
+ roleList.length ? roleList.map(item => item.id) : normalizeSelectedIds(selected as number | string | Array<number | string>)
512
+ ).join(",");
513
+ if (!roleIds) {
514
+ ElMessage.warning("请勾选删除项");
515
+ return;
516
+ }
517
+
518
+ const confirmMessage = roleList.length
519
+ ? roleList.length === 1
520
+ ? `是否确定删除角色?\n角色名称:${roleList[0].name || roleList[0].code || `ID:${roleList[0].id}`}`
521
+ : `确认删除已选中的 ${roleList.length} 个角色吗?`
522
+ : "确认删除已选中的角色吗?";
523
+
524
+ ElMessageBox.confirm(confirmMessage, "警告", {
525
+ confirmButtonText: "确定",
526
+ cancelButtonText: "取消",
527
+ type: "warning"
528
+ }).then(
529
+ () => {
530
+ defBaseRoleService.DeleteBaseRole({ id: roleIds }).then(() => {
531
+ ElMessage.success("删除角色成功");
532
+ refreshTable();
533
+ });
534
+ },
535
+ () => {
536
+ ElMessage.info("已取消删除角色");
537
+ }
538
+ );
539
+ }
540
+
541
+ /**
542
+ * 打开分配菜单权限抽屉,并回显当前角色已拥有的菜单。
543
+ */
544
+ async function handleOpenAssignPermDialog(row: BaseRole) {
545
+ if (!row.id) return;
546
+ if (!canManageRole(row)) {
547
+ ElMessage.warning("当前租户不能分配该内置角色权限");
548
+ return;
549
+ }
550
+ checkedBaseRole.value = { id: row.id, name: row.name };
551
+ await loadMenuPermOptions(row.id);
552
+ assignPermDialogVisible.value = true;
553
+ nextTick(() => {
554
+ permTreeRef.value?.setCheckedKeys(row.menus, false);
555
+ });
556
+ }
557
+
558
+ /**
559
+ * 提交角色菜单权限分配。
560
+ */
561
+ function handleAssignPermSubmit() {
562
+ const roleId = checkedBaseRole.value.id;
563
+ if (!roleId) return;
564
+
565
+ const checkedNodes = (permTreeRef.value?.getCheckedNodes(false, true) as Array<{ value: number }> | undefined) ?? [];
566
+ const checkedMenuIds = checkedNodes.map(node => Number(node.value));
567
+ if (!checkedMenuIds.length) {
568
+ ElMessage.warning("请选择菜单权限");
569
+ return;
570
+ }
571
+ if (new Set(checkedMenuIds).size !== checkedMenuIds.length) {
572
+ ElMessage.warning("菜单权限不能重复");
573
+ return;
574
+ }
575
+ defBaseRoleService.SetBaseRoleMenu({ id: roleId, menus: checkedMenuIds }).then(() => {
576
+ ElMessage.success("分配权限成功");
577
+ assignPermDialogVisible.value = false;
578
+ refreshTable();
579
+ });
580
+ }
581
+
582
+ /**
583
+ * 展开或收缩权限树。
584
+ */
585
+ function togglePermTree() {
586
+ isExpanded.value = !isExpanded.value;
587
+ if (!permTreeRef.value) return;
588
+
589
+ Object.values(permTreeRef.value.store.nodesMap).forEach((node: any) => {
590
+ if (isExpanded.value) node.expand();
591
+ else node.collapse();
592
+ });
593
+ }
594
+
595
+ watch(permKeywords, val => {
596
+ permTreeRef.value?.filter(val);
597
+ });
598
+
599
+ /**
600
+ * 按关键字过滤菜单权限树节点。
601
+ */
602
+ function handlePermFilter(value: string, data: Record<string, any>) {
603
+ if (!value) return true;
604
+ return data.label.includes(value);
605
+ }
606
+
607
+ /**
608
+ * 切换父子联动配置。
609
+ */
610
+ function handelParentChildLinkedChange(val: CheckboxValueType) {
611
+ parentChildLinked.value = Boolean(val);
612
+ }
613
+ </script>
614
+
615
+ <style scoped>
616
+ .perm-toolbar {
617
+ display: flex;
618
+ flex-direction: column;
619
+ gap: 10px;
620
+ padding: 14px;
621
+ background: linear-gradient(180deg, #f8fafc 0%, #f3f6fb 100%);
622
+ border: 1px solid #e4eaf3;
623
+ border-radius: 12px;
624
+ }
625
+ .perm-search {
626
+ width: 100%;
627
+ }
628
+ .perm-toolbar__actions {
629
+ display: flex;
630
+ flex-wrap: wrap;
631
+ gap: 12px;
632
+ align-items: center;
633
+ justify-content: space-between;
634
+ }
635
+ .perm-toolbar__group {
636
+ display: inline-flex;
637
+ gap: 10px;
638
+ align-items: center;
639
+ min-height: 38px;
640
+ padding: 6px 10px;
641
+ background: rgb(255 255 255 / 94%);
642
+ border: 1px solid #e4eaf3;
643
+ border-radius: 10px;
644
+ }
645
+ .perm-toolbar__group--linkage {
646
+ margin-left: auto;
647
+ }
648
+ .perm-toolbar__label {
649
+ font-size: 12px;
650
+ font-weight: 600;
651
+ color: #6b7280;
652
+ letter-spacing: 0.02em;
653
+ white-space: nowrap;
654
+ }
655
+ .perm-toolbar__button {
656
+ min-width: 98px;
657
+ background: #ffffff;
658
+ border-color: var(--el-color-primary-light-5);
659
+ }
660
+ .perm-toolbar__button:hover,
661
+ .perm-toolbar__button:focus-visible {
662
+ color: var(--el-color-primary);
663
+ background: var(--el-color-primary-light-9);
664
+ border-color: var(--el-color-primary-light-5);
665
+ }
666
+ .perm-linkage__icon {
667
+ font-size: 14px;
668
+ color: var(--el-color-primary);
669
+ cursor: pointer;
670
+ }
671
+
672
+ @media (width <= 768px) {
673
+ .perm-toolbar__actions {
674
+ flex-direction: column;
675
+ align-items: stretch;
676
+ }
677
+ .perm-toolbar__group,
678
+ .perm-toolbar__group--linkage {
679
+ justify-content: space-between;
680
+ width: 100%;
681
+ margin-left: 0;
682
+ }
683
+ }
684
+ </style>