@liujitcn/kratos-admin-system 0.0.49 → 0.0.51

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 (107) hide show
  1. package/README.md +44 -8
  2. package/dist/package/declarations/src/api/system/admin/v1/base_i18n_custom.d.ts +18 -0
  3. package/dist/package/declarations/src/api/system/admin/v1/base_redact_output_policy.d.ts +4 -1
  4. package/dist/package/declarations/src/api/system/admin/v1/base_redact_storage_policy.d.ts +3 -1
  5. package/dist/package/declarations/src/api/system/admin/v1/base_tenant.d.ts +2 -2
  6. package/dist/package/declarations/src/api/system/admin/v1/base_tenant_project.d.ts +23 -0
  7. package/dist/package/declarations/src/api/system/admin/v1/base_tenant_project_grant.d.ts +16 -0
  8. package/dist/package/declarations/src/components/tenant-project/TenantProjectManager.vue.d.ts +29 -0
  9. package/dist/package/declarations/src/components/tenant-project/TenantProjectSelect.vue.d.ts +31 -0
  10. package/dist/package/declarations/src/components/tenant-project/TenantProjectText.vue.d.ts +19 -0
  11. package/dist/package/declarations/src/components/tenant-project/tenant-project-manager-data.d.ts +14 -0
  12. package/dist/package/declarations/src/components/tenant-project/tenant-project-manager.d.ts +40 -0
  13. package/dist/package/declarations/src/components/tenant-project-grant/ProjectGrantDialog.vue.d.ts +8 -0
  14. package/dist/package/declarations/src/index.d.ts +2 -0
  15. package/dist/package/declarations/src/locales/generated.d.ts +196 -0
  16. package/dist/package/declarations/src/rpc/base/v1/config.d.ts +11 -0
  17. package/dist/package/declarations/src/rpc/system/admin/v1/base_i18n_custom.d.ts +105 -0
  18. package/dist/package/declarations/src/rpc/system/admin/v1/base_redact_output_policy.d.ts +8 -0
  19. package/dist/package/declarations/src/rpc/system/admin/v1/base_redact_storage_policy.d.ts +25 -0
  20. package/dist/package/declarations/src/rpc/system/admin/v1/base_tenant.d.ts +10 -1
  21. package/dist/package/declarations/src/rpc/system/admin/v1/base_tenant_project.d.ts +141 -0
  22. package/dist/package/declarations/src/rpc/system/admin/v1/base_tenant_project_grant.d.ts +105 -0
  23. package/dist/package/declarations/src/views/ai/chat/components/SessionPanel.vue.d.ts +2 -2
  24. package/dist/package/declarations/src/views/ai/chat/components/speech-recognition.d.ts +33 -0
  25. package/dist/package/declarations/src/views/base/i18n-custom/index.vue.d.ts +3 -0
  26. package/dist/package/declarations/src/views/base/tenant-project/index.vue.d.ts +3 -0
  27. package/dist/package/declarations/src/views/base/tenant-project-grant/index.vue.d.ts +3 -0
  28. package/dist/package/src/api/system/admin/v1/base_i18n_custom.ts +72 -0
  29. package/dist/package/src/api/system/admin/v1/base_redact_output_policy.ts +7 -0
  30. package/dist/package/src/api/system/admin/v1/base_redact_storage_policy.ts +7 -0
  31. package/dist/package/src/api/system/admin/v1/base_tenant.ts +3 -2
  32. package/dist/package/src/api/system/admin/v1/base_tenant_project.ts +94 -0
  33. package/dist/package/src/api/system/admin/v1/base_tenant_project_grant.ts +63 -0
  34. package/dist/package/src/components/tenant-project/TenantProjectManager.vue +431 -0
  35. package/dist/package/src/components/tenant-project/TenantProjectSelect.vue +83 -0
  36. package/dist/package/src/components/tenant-project/TenantProjectText.vue +37 -0
  37. package/dist/package/src/components/tenant-project/tenant-project-manager-data.ts +26 -0
  38. package/dist/package/src/components/tenant-project/tenant-project-manager.ts +48 -0
  39. package/dist/package/src/components/tenant-project-grant/ProjectGrantDialog.vue +108 -0
  40. package/dist/package/src/index.ts +12 -0
  41. package/dist/package/src/locales/README.md +1 -1
  42. package/dist/package/src/locales/en-US.json +51 -2
  43. package/dist/package/src/locales/ja-JP.json +51 -2
  44. package/dist/package/src/locales/zh-CN.json +51 -2
  45. package/dist/package/src/locales/zh-TW.json +51 -2
  46. package/dist/package/src/module.ts +3 -3
  47. package/dist/package/src/rpc/base/v1/config.ts +12 -0
  48. package/dist/package/src/rpc/system/admin/v1/base_i18n_custom.ts +126 -0
  49. package/dist/package/src/rpc/system/admin/v1/base_redact_output_policy.ts +9 -0
  50. package/dist/package/src/rpc/system/admin/v1/base_redact_storage_policy.ts +30 -0
  51. package/dist/package/src/rpc/system/admin/v1/base_tenant.ts +11 -1
  52. package/dist/package/src/rpc/system/admin/v1/base_tenant_project.ts +166 -0
  53. package/dist/package/src/rpc/system/admin/v1/base_tenant_project_grant.ts +128 -0
  54. package/dist/package/src/views/ai/chat/components/XSender.vue +21 -28
  55. package/dist/package/src/views/ai/chat/components/speech-recognition.ts +156 -0
  56. package/dist/package/src/views/base/api-log/index.vue +3 -3
  57. package/dist/package/src/views/base/config/index.vue +3 -3
  58. package/dist/package/src/views/base/data-access-log/index.vue +3 -3
  59. package/dist/package/src/views/base/dept/index.vue +10 -45
  60. package/dist/package/src/views/base/dict/index.vue +3 -3
  61. package/dist/package/src/views/base/dict/item.vue +3 -3
  62. package/dist/package/src/views/base/file/index.vue +12 -3
  63. package/dist/package/src/views/base/i18n-custom/index.vue +321 -0
  64. package/dist/package/src/views/base/job/index.vue +85 -161
  65. package/dist/package/src/views/base/login-log/index.vue +13 -4
  66. package/dist/package/src/views/base/login-policy/index.vue +109 -40
  67. package/dist/package/src/views/base/menu/index.vue +3 -3
  68. package/dist/package/src/views/base/message/index.vue +9 -32
  69. package/dist/package/src/views/base/message-category/index.vue +2 -4
  70. package/dist/package/src/views/base/oauth-client/index.vue +8 -42
  71. package/dist/package/src/views/base/online-session/index.vue +8 -27
  72. package/dist/package/src/views/base/operation-log/index.vue +3 -3
  73. package/dist/package/src/views/base/permission-log/index.vue +3 -3
  74. package/dist/package/src/views/base/policy-evaluation-log/index.vue +3 -3
  75. package/dist/package/src/views/base/post/index.vue +9 -42
  76. package/dist/package/src/views/base/redact-output-policy/index.vue +19 -7
  77. package/dist/package/src/views/base/redact-rule/index.vue +70 -53
  78. package/dist/package/src/views/base/redact-storage-policy/index.vue +18 -7
  79. package/dist/package/src/views/base/role/index.vue +14 -48
  80. package/dist/package/src/views/base/tenant/index.vue +163 -19
  81. package/dist/package/src/views/base/tenant-project/index.vue +12 -0
  82. package/dist/package/src/views/base/tenant-project-grant/index.vue +338 -0
  83. package/dist/package/src/views/base/user/index.vue +30 -51
  84. package/dist/package/src/views/profile/components/login-log.vue +15 -2
  85. package/dist/package/src/views/profile/components/password.vue +1 -1
  86. package/dist/package/src/views/profile/components/security.vue +2 -2
  87. package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +2 -2
  88. package/dist/package/src/views/tool/code-gen/config.ts +2 -1
  89. package/package.json +2 -2
  90. /package/dist/package/declarations/src/components/{ChangePasswordForm.vue.d.ts → account/ChangePasswordForm.vue.d.ts} +0 -0
  91. /package/dist/package/declarations/src/components/{ForcedPasswordDialog.vue.d.ts → account/ForcedPasswordDialog.vue.d.ts} +0 -0
  92. /package/dist/package/declarations/src/components/{Ai.vue.d.ts → ai/Ai.vue.d.ts} +0 -0
  93. /package/dist/package/declarations/src/components/{DynamicI18nCell.vue.d.ts → i18n/DynamicI18nCell.vue.d.ts} +0 -0
  94. /package/dist/package/declarations/src/components/{DynamicI18nEditor.vue.d.ts → i18n/DynamicI18nEditor.vue.d.ts} +0 -0
  95. /package/dist/package/declarations/src/components/{dynamicI18n.d.ts → i18n/dynamicI18n.d.ts} +0 -0
  96. /package/dist/package/declarations/src/components/{LogTable.vue.d.ts → log/LogTable.vue.d.ts} +0 -0
  97. /package/dist/package/declarations/src/components/{log.d.ts → log/log.d.ts} +0 -0
  98. /package/dist/package/declarations/src/components/{Notification.vue.d.ts → notification/Notification.vue.d.ts} +0 -0
  99. /package/dist/package/src/components/{ChangePasswordForm.vue → account/ChangePasswordForm.vue} +0 -0
  100. /package/dist/package/src/components/{ForcedPasswordDialog.vue → account/ForcedPasswordDialog.vue} +0 -0
  101. /package/dist/package/src/components/{Ai.vue → ai/Ai.vue} +0 -0
  102. /package/dist/package/src/components/{DynamicI18nCell.vue → i18n/DynamicI18nCell.vue} +0 -0
  103. /package/dist/package/src/components/{DynamicI18nEditor.vue → i18n/DynamicI18nEditor.vue} +0 -0
  104. /package/dist/package/src/components/{dynamicI18n.ts → i18n/dynamicI18n.ts} +0 -0
  105. /package/dist/package/src/components/{LogTable.vue → log/LogTable.vue} +0 -0
  106. /package/dist/package/src/components/{log.ts → log/log.ts} +0 -0
  107. /package/dist/package/src/components/{Notification.vue → notification/Notification.vue} +0 -0
@@ -20,16 +20,80 @@
20
20
  @confirm="handleSubmit"
21
21
  @close="handleCloseDialog"
22
22
  />
23
+
24
+ <ProDialog
25
+ v-model="credentialsDialog.visible"
26
+ :title="t('system.base.tenant.title.initial_credentials')"
27
+ width="min(620px, calc(100vw - 32px))"
28
+ class="tenant-credentials-dialog"
29
+ append-to-body
30
+ destroy-on-close
31
+ :close-on-click-modal="false"
32
+ @close="handleCloseCredentialsDialog"
33
+ >
34
+ <div class="tenant-credentials-list">
35
+ <div class="tenant-credentials-row">
36
+ <span class="tenant-credentials-label">{{ t("system.base.tenant.field.code") }}</span>
37
+ <span class="tenant-credentials-value">{{ credentialsDialog.tenant_code }}</span>
38
+ <el-tooltip :content="t('system.base.tenant.tooltip.copy_credentials')" placement="top">
39
+ <el-button
40
+ text
41
+ circle
42
+ :aria-label="t('system.base.tenant.tooltip.copy_credentials')"
43
+ @click="handleCopyCredentials"
44
+ >
45
+ <el-icon><CopyDocument /></el-icon>
46
+ </el-button>
47
+ </el-tooltip>
48
+ </div>
49
+ <div class="tenant-credentials-row">
50
+ <span class="tenant-credentials-label">{{ t("system.base.user.field.user_name") }}</span>
51
+ <span class="tenant-credentials-value">{{ credentialsDialog.admin_user_name }}</span>
52
+ </div>
53
+ <div class="tenant-credentials-row">
54
+ <span class="tenant-credentials-label">{{ t("system.base.user.field.password") }}</span>
55
+ <span class="tenant-credentials-value">
56
+ {{ credentialsDialog.passwordVisible ? credentialsDialog.initial_password : "********" }}
57
+ </span>
58
+ <el-tooltip
59
+ :content="
60
+ credentialsDialog.passwordVisible
61
+ ? t('system.base.tenant.tooltip.hide_password')
62
+ : t('system.base.tenant.tooltip.show_password')
63
+ "
64
+ placement="top"
65
+ >
66
+ <el-button
67
+ text
68
+ circle
69
+ :aria-label="
70
+ credentialsDialog.passwordVisible
71
+ ? t('system.base.tenant.tooltip.hide_password')
72
+ : t('system.base.tenant.tooltip.show_password')
73
+ "
74
+ @click="credentialsDialog.passwordVisible = !credentialsDialog.passwordVisible"
75
+ >
76
+ <el-icon><View v-if="!credentialsDialog.passwordVisible" /><Hide v-else /></el-icon>
77
+ </el-button>
78
+ </el-tooltip>
79
+ </div>
80
+ </div>
81
+ <p class="tenant-credentials-warning">{{ t("system.base.tenant.message.initial_credentials_warning") }}</p>
82
+ <template #footer>
83
+ <el-button type="primary" @click="credentialsDialog.visible = false">{{ t("common.action.confirm") }}</el-button>
84
+ </template>
85
+ </ProDialog>
23
86
  </div>
24
87
  </template>
25
88
 
26
89
  <script setup lang="ts">
27
90
  import { computed, reactive, ref } from "vue";
28
91
  import { ElMessage, ElMessageBox } from "element-plus";
29
- import { CirclePlus, Delete, EditPen } from "@element-plus/icons-vue";
92
+ import { CirclePlus, CopyDocument, Delete, EditPen, Hide, View } from "@element-plus/icons-vue";
30
93
  import type { ColumnProps, HeaderActionProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
31
94
  import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
32
95
  import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
96
+ import ProDialog from "@liujitcn/kratos-admin-core/components/Dialog/ProDialog.vue";
33
97
  import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
34
98
  import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
35
99
  import { defBaseTenantService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_tenant";
@@ -40,6 +104,7 @@ import type {
40
104
  } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_tenant";
41
105
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
42
106
  import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
107
+ import { copyText } from "@liujitcn/kratos-admin-core/security";
43
108
  import { t } from "@liujitcn/kratos-admin-core";
44
109
 
45
110
  defineOptions({
@@ -55,6 +120,13 @@ const dialog = reactive({
55
120
  titleKey: "common.action.create_resource",
56
121
  visible: false
57
122
  });
123
+ const credentialsDialog = reactive({
124
+ visible: false,
125
+ tenant_code: "",
126
+ admin_user_name: "",
127
+ initial_password: "",
128
+ passwordVisible: false
129
+ });
58
130
 
59
131
  const formData = reactive<BaseTenantForm>({
60
132
  /** 租户ID */
@@ -290,24 +362,50 @@ function resetForm() {
290
362
  /**
291
363
  * 提交租户表单。
292
364
  */
293
- function handleSubmit() {
294
- formDialogRef.value?.validate()?.then(valid => {
295
- if (!valid) return;
296
-
297
- const submitData = JSON.parse(JSON.stringify(formData)) as BaseTenantForm;
298
- const request = submitData.id
299
- ? defBaseTenantService.UpdateBaseTenant({ base_tenant: submitData })
300
- : defBaseTenantService.CreateBaseTenant({ base_tenant: submitData });
301
- request.then(() => {
302
- ElMessage.success(
303
- t(submitData.id ? "common.message.update_success" : "common.message.create_success", {
304
- resource: t("common.field.tenant")
305
- })
306
- );
307
- handleCloseDialog();
308
- refreshTable();
309
- });
310
- });
365
+ async function handleSubmit() {
366
+ const valid = await formDialogRef.value?.validate();
367
+ if (!valid) return;
368
+
369
+ const submitData = JSON.parse(JSON.stringify(formData)) as BaseTenantForm;
370
+ if (submitData.id) {
371
+ await defBaseTenantService.UpdateBaseTenant({ base_tenant: submitData });
372
+ ElMessage.success(t("common.message.update_success", { resource: t("common.field.tenant") }));
373
+ } else {
374
+ const response = await defBaseTenantService.CreateBaseTenant({ base_tenant: submitData });
375
+ ElMessage.success(t("common.message.create_success", { resource: t("common.field.tenant") }));
376
+ handleCloseDialog();
377
+ refreshTable();
378
+ if (response.initial_password) {
379
+ credentialsDialog.tenant_code = response.tenant_code;
380
+ credentialsDialog.admin_user_name = response.admin_user_name;
381
+ credentialsDialog.initial_password = response.initial_password;
382
+ credentialsDialog.passwordVisible = false;
383
+ credentialsDialog.visible = true;
384
+ }
385
+ return;
386
+ }
387
+ handleCloseDialog();
388
+ refreshTable();
389
+ }
390
+
391
+ /** 关闭凭据弹窗并清除一次性凭据。 */
392
+ function handleCloseCredentialsDialog() {
393
+ credentialsDialog.visible = false;
394
+ credentialsDialog.tenant_code = "";
395
+ credentialsDialog.admin_user_name = "";
396
+ credentialsDialog.initial_password = "";
397
+ credentialsDialog.passwordVisible = false;
398
+ }
399
+
400
+ /** 复制租户编号、管理员账号和初始密码。 */
401
+ async function handleCopyCredentials() {
402
+ const content = [
403
+ `${t("system.base.tenant.field.code")}:${credentialsDialog.tenant_code}`,
404
+ `${t("system.base.user.field.user_name")}:${credentialsDialog.admin_user_name}`,
405
+ `${t("system.base.user.field.password")}:${credentialsDialog.initial_password}`
406
+ ].join("\n");
407
+ await copyText(content);
408
+ ElMessage.success(t("core.clipboard.success"));
311
409
  }
312
410
 
313
411
  /**
@@ -392,3 +490,49 @@ function handleDelete(selected?: number | string | Array<number | string> | Base
392
490
  );
393
491
  }
394
492
  </script>
493
+
494
+ <style scoped lang="scss">
495
+ .tenant-credentials-row :deep(.el-icon) {
496
+ color: var(--el-color-primary);
497
+ font-size: 18px;
498
+ }
499
+
500
+ .tenant-credentials-list {
501
+ display: grid;
502
+ gap: 14px;
503
+ }
504
+
505
+ .tenant-credentials-row {
506
+ display: grid;
507
+ grid-template-columns: minmax(96px, auto) minmax(0, 1fr) auto;
508
+ gap: 12px;
509
+ align-items: center;
510
+ min-height: 32px;
511
+ }
512
+
513
+ .tenant-credentials-label {
514
+ color: var(--el-text-color-secondary);
515
+ white-space: nowrap;
516
+ }
517
+
518
+ .tenant-credentials-value {
519
+ min-width: 0;
520
+ overflow-wrap: anywhere;
521
+ color: var(--el-text-color-primary);
522
+ font-family: var(--el-font-family-monospace);
523
+ }
524
+
525
+ .tenant-credentials-warning {
526
+ margin: 0;
527
+ color: var(--el-color-warning);
528
+ font-size: 13px;
529
+ line-height: 1.6;
530
+ }
531
+
532
+ @media (max-width: 560px) {
533
+ .tenant-credentials-row {
534
+ grid-template-columns: minmax(82px, auto) minmax(0, 1fr) auto;
535
+ gap: 8px;
536
+ }
537
+ }
538
+ </style>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <TenantProjectManager />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import TenantProjectManager from "@liujitcn/kratos-admin-system/components/tenant-project/TenantProjectManager.vue";
7
+
8
+ defineOptions({
9
+ name: "BaseTenantProject",
10
+ inheritAttrs: false
11
+ });
12
+ </script>
@@ -0,0 +1,338 @@
1
+ <template>
2
+ <div class="table-box">
3
+ <ProTable
4
+ ref="proTable"
5
+ :key="isDefaultTenant ? 'default-tenant' : 'current-tenant'"
6
+ row-key="grant_key"
7
+ :columns="columns"
8
+ :header-actions="headerActions"
9
+ :request-api="requestProjectGrantTable"
10
+ />
11
+
12
+ <FormDialog
13
+ v-model="dialog.visible"
14
+ ref="formDialogRef"
15
+ :title="t(dialog.titleKey, { resource: t('system.base.tenant_project_grant.title') })"
16
+ width="640px"
17
+ :model="formData"
18
+ :fields="formFields"
19
+ :rules="rules"
20
+ :confirm-loading="loading"
21
+ @confirm="handleSubmit"
22
+ @close="handleCloseDialog"
23
+ />
24
+ </div>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { computed, reactive, ref } from "vue";
29
+ import { ElMessage, ElMessageBox } from "element-plus";
30
+ import { CirclePlus, Delete, EditPen } from "@element-plus/icons-vue";
31
+ import type { ColumnProps, HeaderActionProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
32
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
33
+ import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
34
+ import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
35
+ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
36
+ import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
37
+ import { useTenantScope } from "@liujitcn/kratos-admin-core/tenant";
38
+ import { t } from "@liujitcn/kratos-admin-core";
39
+ import { defBaseDeptService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_dept";
40
+ import { defBasePostService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_post";
41
+ import { defBaseRoleService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_role";
42
+ import { defBaseTenantProjectService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_tenant_project";
43
+ import { defBaseTenantProjectGrantService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_tenant_project_grant";
44
+ import { defBaseUserService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_user";
45
+ import type { SelectOptionResponse_Option } from "@liujitcn/kratos-admin-system/rpc/common/v1/common";
46
+ import {
47
+ BaseTenantProjectGrantSubjectType,
48
+ type BaseTenantProjectGrant,
49
+ type PageBaseTenantProjectGrantRequest
50
+ } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_tenant_project_grant";
51
+
52
+ defineOptions({
53
+ name: "BaseTenantProjectGrant",
54
+ inheritAttrs: false
55
+ });
56
+
57
+ type ProjectGrantFormState = {
58
+ tenant_id?: number;
59
+ subject_type: BaseTenantProjectGrantSubjectType;
60
+ subject_id?: number;
61
+ project_id: number[];
62
+ all: boolean;
63
+ };
64
+
65
+ const { BUTTONS } = useAuthButtons();
66
+ const proTable = ref<ProTableInstance>();
67
+ const formDialogRef = ref<InstanceType<typeof FormDialog>>();
68
+ const { isDefaultTenant, tenantOptions, tenantColumns, tenantFormField, toRequestTenantId, loadTenantOptions } = useTenantScope();
69
+ const projectOptions = ref<SelectOptionResponse_Option[]>([]);
70
+ const subjectOptions = ref<ProFormOption[]>([]);
71
+ const loading = ref(false);
72
+ const dialog = reactive({
73
+ visible: false,
74
+ editing: false,
75
+ titleKey: "common.action.create_resource"
76
+ });
77
+ const DEFAULT_SUBJECT_TYPE = BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_POST;
78
+ const formData = reactive<ProjectGrantFormState>({
79
+ tenant_id: undefined,
80
+ subject_type: DEFAULT_SUBJECT_TYPE,
81
+ subject_id: undefined,
82
+ project_id: [],
83
+ all: false
84
+ });
85
+ const subjectTypeOptions = computed<ProFormOption[]>(() => [
86
+ { label: t("system.base.tenant_project_grant.subject_type.post"), value: BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_POST },
87
+ { label: t("system.base.tenant_project_grant.subject_type.role"), value: BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_ROLE },
88
+ { label: t("system.base.tenant_project_grant.subject_type.dept"), value: BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_DEPT },
89
+ { label: t("system.base.tenant_project_grant.subject_type.user"), value: BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_USER }
90
+ ]);
91
+ const subjectTypeLabel = computed(() => new Map(subjectTypeOptions.value.map(item => [item.value, item.label])));
92
+ const rules = computed(() => ({
93
+ tenant_id: [{ required: true, message: t("common.validation.required_select", { field: t("common.field.tenant") }), trigger: "change" }],
94
+ subject_type: [{ required: true, type: "number", min: 1, message: t("common.validation.required_select", { field: t("system.base.tenant_project_grant.field.subject_type") }), trigger: "change" }],
95
+ subject_id: [{ required: true, type: "number", min: 1, message: t("common.validation.required_select", { field: t("system.base.tenant_project_grant.field.subject") }), trigger: "change" }]
96
+ }));
97
+ const formFields = computed<ProFormField[]>(() => [
98
+ tenantFormField({
99
+ label: t("common.field.tenant"),
100
+ props: { disabled: dialog.editing || loading.value, onChange: handleTenantChange }
101
+ }),
102
+ {
103
+ prop: "subject_type",
104
+ label: t("system.base.tenant_project_grant.field.subject_type"),
105
+ component: "select",
106
+ options: subjectTypeOptions.value,
107
+ props: { filterable: false, disabled: dialog.editing || loading.value, onChange: handleSubjectTypeChange }
108
+ },
109
+ {
110
+ prop: "subject_id",
111
+ label: t("system.base.tenant_project_grant.field.subject"),
112
+ component: formData.subject_type === BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_DEPT ? "tree-select" : "select",
113
+ options: subjectOptions.value,
114
+ props:
115
+ formData.subject_type === BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_DEPT
116
+ ? { filterable: true, checkStrictly: true, renderAfterExpand: false, style: { width: "100%" }, disabled: dialog.editing || !formData.tenant_id || loading.value }
117
+ : { filterable: true, clearable: true, disabled: dialog.editing || !formData.tenant_id || formData.subject_type === 0 || loading.value }
118
+ },
119
+ {
120
+ prop: "all",
121
+ label: t("system.base.tenant_project_grant.field.scope"),
122
+ labelTooltip: t("system.base.tenant_project_grant.tooltip.scope"),
123
+ component: "switch",
124
+ checkboxLabel: t("system.base.tenant_project_grant.all_projects"),
125
+ props: { disabled: !formData.tenant_id || !formData.subject_id || loading.value }
126
+ },
127
+ {
128
+ prop: "project_id",
129
+ label: t("system.base.tenant_project_grant.field.projects"),
130
+ component: "select",
131
+ options: projectOptions.value,
132
+ visible: () => !formData.all,
133
+ props: { multiple: true, filterable: true, clearable: true, disabled: !formData.tenant_id || loading.value }
134
+ }
135
+ ]);
136
+ const columns = computed<ColumnProps[]>(() => [
137
+ ...tenantColumns({ label: t("common.field.tenant"), minWidth: 150, order: 1 }),
138
+ {
139
+ prop: "subject_type",
140
+ label: t("system.base.tenant_project_grant.field.subject_type"),
141
+ minWidth: 110,
142
+ search: { el: "select", enum: subjectTypeOptions.value },
143
+ render: scope => subjectTypeLabel.value.get((scope.row as BaseTenantProjectGrant).subject_type) ?? String((scope.row as BaseTenantProjectGrant).subject_type)
144
+ },
145
+ { prop: "subject_name", label: t("system.base.tenant_project_grant.field.subject"), minWidth: 160, showOverflowTooltip: true },
146
+ {
147
+ prop: "project_names",
148
+ label: t("system.base.tenant_project_grant.field.projects"),
149
+ minWidth: 220,
150
+ showOverflowTooltip: true,
151
+ render: scope => {
152
+ const row = scope.row as BaseTenantProjectGrant;
153
+ return row.project_id.length === 1 && row.project_id[0] === 0
154
+ ? t("system.base.tenant_project_grant.all_projects")
155
+ : row.project_names.join(", ") || row.project_id.join(", ") || t("common.value.none");
156
+ }
157
+ },
158
+ {
159
+ prop: "operation",
160
+ label: t("common.field.operation"),
161
+ fixed: "right",
162
+ cellType: "actions",
163
+ actions: [
164
+ {
165
+ label: t("common.action.edit"),
166
+ type: "primary",
167
+ link: true,
168
+ icon: EditPen,
169
+ hidden: () => !BUTTONS.value["base:tenant:project:grant:update"],
170
+ onClick: scope => handleOpenDialog(scope.row as BaseTenantProjectGrant)
171
+ },
172
+ {
173
+ label: t("common.action.delete"),
174
+ type: "danger",
175
+ link: true,
176
+ icon: Delete,
177
+ hidden: () => !BUTTONS.value["base:tenant:project:grant:delete"],
178
+ onClick: scope => handleDelete(scope.row as BaseTenantProjectGrant)
179
+ }
180
+ ]
181
+ }
182
+ ]);
183
+ const headerActions = computed<HeaderActionProps[]>(() => [
184
+ {
185
+ label: t("common.action.create"),
186
+ type: "success",
187
+ icon: CirclePlus,
188
+ hidden: () => !BUTTONS.value["base:tenant:project:grant:create"],
189
+ onClick: () => handleOpenDialog()
190
+ },
191
+ {
192
+ label: t("common.action.delete"),
193
+ type: "danger",
194
+ icon: Delete,
195
+ hidden: () => !BUTTONS.value["base:tenant:project:grant:delete"],
196
+ disabled: scope => !scope.selectedList.length,
197
+ onClick: scope => handleDelete(scope.selectedList as BaseTenantProjectGrant[])
198
+ }
199
+ ]);
200
+
201
+ /** 请求项目授权分页列表。 */
202
+ async function requestProjectGrantTable(params: Record<string, unknown>) {
203
+ const page = buildPageRequest(params) as unknown as PageBaseTenantProjectGrantRequest;
204
+ const tenantId = toRequestTenantId(params.tenant_id);
205
+ const data = await defBaseTenantProjectGrantService.PageBaseTenantProjectGrant({ ...page, tenant_id: tenantId });
206
+ return { data: { list: data.grants ?? [], total: data.total } };
207
+ }
208
+
209
+ /** 打开项目授权新增或编辑弹窗。 */
210
+ async function handleOpenDialog(row?: BaseTenantProjectGrant) {
211
+ resetForm();
212
+ dialog.editing = Boolean(row);
213
+ dialog.titleKey = row ? "common.action.edit_resource" : "common.action.create_resource";
214
+ loading.value = true;
215
+ try {
216
+ await loadTenantOptions(true);
217
+ if (!isDefaultTenant.value) formData.tenant_id = Number(tenantOptions.value[0]?.value) || undefined;
218
+ if (row) {
219
+ formData.tenant_id = row.tenant_id;
220
+ formData.subject_type = row.subject_type;
221
+ formData.subject_id = row.subject_id;
222
+ formData.all = row.project_id.length === 1 && row.project_id[0] === 0;
223
+ formData.project_id = formData.all ? [] : [...row.project_id];
224
+ }
225
+ await Promise.all([loadProjectOptions(), loadSubjectOptions()]);
226
+ dialog.visible = true;
227
+ } finally {
228
+ loading.value = false;
229
+ }
230
+ }
231
+
232
+ /** 加载目标租户项目选项。 */
233
+ async function loadProjectOptions() {
234
+ projectOptions.value = formData.tenant_id
235
+ ? (await defBaseTenantProjectService.OptionBaseTenantProject({ tenant_id: toRequestTenantId(formData.tenant_id) })).list ?? []
236
+ : [];
237
+ }
238
+
239
+ /** 根据授权类型加载岗位、角色、部门或用户主体选项。 */
240
+ async function loadSubjectOptions() {
241
+ subjectOptions.value = [];
242
+ if (!formData.tenant_id || formData.subject_type === BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_UNSPECIFIED) return;
243
+ switch (formData.subject_type) {
244
+ case BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_POST:
245
+ subjectOptions.value = (await defBasePostService.OptionBasePost({ tenant_id: formData.tenant_id })).list ?? [];
246
+ return;
247
+ case BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_ROLE:
248
+ subjectOptions.value = (await defBaseRoleService.OptionBaseRole({ tenant_id: formData.tenant_id })).list ?? [];
249
+ return;
250
+ case BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_DEPT:
251
+ subjectOptions.value = ((await defBaseDeptService.OptionBaseDept({ tenant_id: formData.tenant_id })).list ?? []) as unknown as ProFormOption[];
252
+ return;
253
+ case BaseTenantProjectGrantSubjectType.BASE_TENANT_PROJECT_GRANT_SUBJECT_TYPE_USER:
254
+ subjectOptions.value = (await defBaseUserService.OptionBaseUser({ keyword: "", tenant_id: formData.tenant_id })).list ?? [];
255
+ }
256
+ }
257
+
258
+ /** 切换目标租户时重置主体和项目选择。 */
259
+ async function handleTenantChange(value: unknown) {
260
+ if (loading.value) return;
261
+ formData.tenant_id = Number(value) || undefined;
262
+ formData.subject_type = DEFAULT_SUBJECT_TYPE;
263
+ formData.subject_id = undefined;
264
+ formData.project_id = [];
265
+ formData.all = false;
266
+ await Promise.all([loadProjectOptions(), loadSubjectOptions()]);
267
+ }
268
+
269
+ /** 切换授权类型时重新加载对应主体数据。 */
270
+ async function handleSubjectTypeChange(value: unknown) {
271
+ if (loading.value) return;
272
+ formData.subject_type = Number(value) as BaseTenantProjectGrantSubjectType;
273
+ formData.subject_id = undefined;
274
+ await loadSubjectOptions();
275
+ }
276
+
277
+ /** 提交项目授权新增或编辑。 */
278
+ function handleSubmit() {
279
+ formDialogRef.value?.validate()?.then(valid => {
280
+ if (!valid || !formData.tenant_id || !formData.subject_id || formData.subject_type === 0) return;
281
+ const grant: BaseTenantProjectGrant = {
282
+ tenant_id: formData.tenant_id,
283
+ subject_type: formData.subject_type,
284
+ subject_id: formData.subject_id,
285
+ project_id: formData.all ? [0] : [...formData.project_id],
286
+ tenant_name: "",
287
+ subject_name: "",
288
+ subject_code: "",
289
+ project_names: [],
290
+ grant_key: ""
291
+ };
292
+ loading.value = true;
293
+ const request = dialog.editing
294
+ ? defBaseTenantProjectGrantService.UpdateBaseTenantProjectGrant({ base_tenant_project_grant: grant })
295
+ : defBaseTenantProjectGrantService.CreateBaseTenantProjectGrant({ base_tenant_project_grant: grant });
296
+ request.then(() => {
297
+ ElMessage.success(t(dialog.editing ? "common.message.update_success" : "common.message.create_success", { resource: t("system.base.tenant_project_grant.title") }));
298
+ handleCloseDialog();
299
+ proTable.value?.getTableList();
300
+ }).finally(() => { loading.value = false; });
301
+ });
302
+ }
303
+
304
+ /** 关闭并重置项目授权弹窗。 */
305
+ function handleCloseDialog() {
306
+ dialog.visible = false;
307
+ dialog.editing = false;
308
+ resetForm();
309
+ }
310
+
311
+ /** 重置项目授权表单。 */
312
+ function resetForm() {
313
+ formDialogRef.value?.resetFields();
314
+ formDialogRef.value?.clearValidate();
315
+ formData.tenant_id = undefined;
316
+ formData.subject_type = DEFAULT_SUBJECT_TYPE;
317
+ formData.subject_id = undefined;
318
+ formData.project_id = [];
319
+ formData.all = false;
320
+ projectOptions.value = [];
321
+ subjectOptions.value = [];
322
+ }
323
+
324
+ /** 删除项目授权,支持单条和批量记录。 */
325
+ function handleDelete(selected: BaseTenantProjectGrant | BaseTenantProjectGrant[]) {
326
+ const rows = Array.isArray(selected) ? selected : [selected];
327
+ if (!rows.length) return;
328
+ const label = rows.length === 1 ? rows[0].subject_name || rows[0].subject_code || rows[0].grant_key : t("common.dialog.delete_batch", { count: rows.length, unit: "", resource: t("system.base.tenant_project_grant.title") });
329
+ const message = rows.length === 1
330
+ ? `${t("common.dialog.delete_single", { resource: t("system.base.tenant_project_grant.title") })}\n${t("common.dialog.resource_field", { field: t("system.base.tenant_project_grant.field.subject"), value: label })}`
331
+ : label;
332
+ ElMessageBox.confirm(message, t("common.title.warning"), { confirmButtonText: t("common.action.confirm"), cancelButtonText: t("common.action.cancel"), type: "warning" }).then(async () => {
333
+ await Promise.all(rows.map(row => defBaseTenantProjectGrantService.DeleteBaseTenantProjectGrant({ tenant_id: row.tenant_id, subject_type: row.subject_type, subject_id: row.subject_id })));
334
+ ElMessage.success(t("common.message.delete_success", { resource: t("system.base.tenant_project_grant.title") }));
335
+ proTable.value?.getTableList();
336
+ });
337
+ }
338
+ </script>