@liujitcn/kratos-admin-system 0.0.56 → 0.0.57

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 (26) hide show
  1. package/dist/package/declarations/src/api/system/admin/v1/base_table_source.d.ts +3 -3
  2. package/dist/package/declarations/src/rpc/system/admin/v1/auth.d.ts +16 -0
  3. package/dist/package/declarations/src/rpc/system/admin/v1/base_table_source.d.ts +14 -2
  4. package/dist/package/declarations/src/rpc/system/admin/v1/base_tenant_project_grant.d.ts +1 -1
  5. package/dist/package/src/api/system/admin/v1/base_table_source.ts +4 -3
  6. package/dist/package/src/components/account/ChangePasswordForm.vue +1 -1
  7. package/dist/package/src/components/tenant-project/TenantProjectManager.vue +0 -1
  8. package/dist/package/src/rpc/system/admin/v1/auth.ts +18 -0
  9. package/dist/package/src/rpc/system/admin/v1/base_table_source.ts +16 -2
  10. package/dist/package/src/rpc/system/admin/v1/base_tenant_project_grant.ts +1 -1
  11. package/dist/package/src/views/base/api/index.vue +2 -1
  12. package/dist/package/src/views/base/api-log/index.vue +5 -3
  13. package/dist/package/src/views/base/area/index.vue +27 -7
  14. package/dist/package/src/views/base/backup-management/archive-config/index.vue +7 -2
  15. package/dist/package/src/views/base/data-access-log/index.vue +5 -3
  16. package/dist/package/src/views/base/job/index.vue +2 -1
  17. package/dist/package/src/views/base/login-log/index.vue +5 -3
  18. package/dist/package/src/views/base/login-policy/index.vue +63 -4
  19. package/dist/package/src/views/base/menu/index.vue +19 -3
  20. package/dist/package/src/views/base/message-category/index.vue +0 -1
  21. package/dist/package/src/views/base/oauth-client/index.vue +2 -1
  22. package/dist/package/src/views/base/operation-log/index.vue +5 -3
  23. package/dist/package/src/views/base/permission-log/index.vue +5 -3
  24. package/dist/package/src/views/base/policy-evaluation-log/index.vue +5 -3
  25. package/dist/package/src/views/tool/code-gen/table/index.vue +4 -6
  26. package/package.json +2 -2
@@ -1,11 +1,11 @@
1
1
  import type { StringValues } from "@liujitcn/kratos-admin-system/rpc/common/v1/types";
2
- import type { BaseTableSourceService, OptionBaseTableRequest, OptionBaseTableSourceRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_table_source";
2
+ import type { BaseTableSourceService, OptionBaseTableRequest, OptionBaseTableResponse, OptionBaseTableSourceRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_table_source";
3
3
  /** 数据源元数据服务实现。 */
4
4
  export declare class BaseTableSourceServiceImpl implements BaseTableSourceService {
5
5
  /** 查询已初始化的数据源名称。 */
6
6
  OptionBaseTableSource(request: OptionBaseTableSourceRequest): Promise<StringValues>;
7
- /** 查询指定数据源中的数据库表名称。 */
8
- OptionBaseTable(request: OptionBaseTableRequest): Promise<StringValues>;
7
+ /** 查询指定数据源中的数据库表选项。 */
8
+ OptionBaseTable(request: OptionBaseTableRequest): Promise<OptionBaseTableResponse>;
9
9
  }
10
10
  /** 数据源元数据服务实例。 */
11
11
  export declare const defBaseTableSourceService: BaseTableSourceServiceImpl;
@@ -46,6 +46,20 @@ export interface UserInfoForm {
46
46
  /** 获取个人中心用户信息请求参数 */
47
47
  export interface GetUserProfileRequest {
48
48
  }
49
+ /** 获取当前用户密码策略请求参数 */
50
+ export interface GetCurrentPasswordPolicyRequest {
51
+ }
52
+ /** 当前用户生效的密码策略 */
53
+ export interface CurrentPasswordPolicy {
54
+ /** 密码最小长度 */
55
+ min_length: number;
56
+ /** 密码至少满足的字符类别数量 */
57
+ min_complexity_classes: number;
58
+ /** 禁止重复使用的历史密码数量,零表示不启用 */
59
+ history_count: number;
60
+ /** 密码有效期天数,零表示长期有效 */
61
+ max_age_days: number;
62
+ }
49
63
  /** 用户资料表单 */
50
64
  export interface UserProfileForm {
51
65
  /** 用户名 */
@@ -158,6 +172,8 @@ export interface AuthService {
158
172
  GetUserInfo(request: GetUserInfoRequest): Promise<UserInfoForm>;
159
173
  /** 获取个人中心用户信息 */
160
174
  GetUserProfile(request: GetUserProfileRequest): Promise<UserProfileForm>;
175
+ /** 获取当前用户生效的密码策略 */
176
+ GetCurrentPasswordPolicy(request: GetCurrentPasswordPolicyRequest): Promise<CurrentPasswordPolicy>;
161
177
  /** 修改个人中心密码 */
162
178
  UpdateUserPassword(request: UpdateUserPasswordRequest): Promise<Empty>;
163
179
  /** 修改个人中心手机号 */
@@ -7,10 +7,22 @@ export interface OptionBaseTableRequest {
7
7
  /** 数据源名称 */
8
8
  source_name: string;
9
9
  }
10
+ /** 数据库表选项。 */
11
+ export interface BaseTableOption {
12
+ /** 数据库表名 */
13
+ name: string;
14
+ /** 数据表注释 */
15
+ comment: string;
16
+ }
17
+ /** 数据库表选项响应。 */
18
+ export interface OptionBaseTableResponse {
19
+ /** 数据库表选项列表 */
20
+ tables: BaseTableOption[];
21
+ }
10
22
  /** Admin数据源元数据服务,提供数据备份、数据归档和代码生成共用的数据源信息。 */
11
23
  export interface BaseTableSourceService {
12
24
  /** 查询已初始化的数据源名称。 */
13
25
  OptionBaseTableSource(request: OptionBaseTableSourceRequest): Promise<StringValues>;
14
- /** 查询指定数据源中的数据库表名称。 */
15
- OptionBaseTable(request: OptionBaseTableRequest): Promise<StringValues>;
26
+ /** 查询指定数据源中的数据库表选项。 */
27
+ OptionBaseTable(request: OptionBaseTableRequest): Promise<OptionBaseTableResponse>;
16
28
  }
@@ -90,7 +90,7 @@ export interface DeleteBaseTenantProjectGrantRequest {
90
90
  /** 授权主体ID */
91
91
  subject_id: number;
92
92
  }
93
- /** BaseTenantProjectGrantService 管理岗位、角色、部门和用户在目标租户下的项目授权。 */
93
+ /** Admin项目授权服务 */
94
94
  export interface BaseTenantProjectGrantService {
95
95
  /** 查询项目授权分页列表。 */
96
96
  PageBaseTenantProjectGrant(request: PageBaseTenantProjectGrantRequest): Promise<PageBaseTenantProjectGrantResponse>;
@@ -3,6 +3,7 @@ import type { StringValues } from "@liujitcn/kratos-admin-system/rpc/common/v1/t
3
3
  import type {
4
4
  BaseTableSourceService,
5
5
  OptionBaseTableRequest,
6
+ OptionBaseTableResponse,
6
7
  OptionBaseTableSourceRequest
7
8
  } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_table_source";
8
9
 
@@ -19,9 +20,9 @@ export class BaseTableSourceServiceImpl implements BaseTableSourceService {
19
20
  });
20
21
  }
21
22
 
22
- /** 查询指定数据源中的数据库表名称。 */
23
- OptionBaseTable(request: OptionBaseTableRequest): Promise<StringValues> {
24
- return service<OptionBaseTableRequest, StringValues>({
23
+ /** 查询指定数据源中的数据库表选项。 */
24
+ OptionBaseTable(request: OptionBaseTableRequest): Promise<OptionBaseTableResponse> {
25
+ return service<OptionBaseTableRequest, OptionBaseTableResponse>({
25
26
  url: `${BASE_TABLE_SOURCE_URL}/table/option`,
26
27
  method: "get",
27
28
  params: request
@@ -6,7 +6,7 @@
6
6
  :fields="passwordFormFields"
7
7
  :rules="passwordFormRules"
8
8
  />
9
- <PasswordStrength :password="passwordForm.new_pwd" class="change-password-form__strength" />
9
+ <PasswordStrength :password="passwordForm.new_pwd" load-current-policy class="change-password-form__strength" />
10
10
  <div class="change-password-form__footer">
11
11
  <el-button @click="resetPasswordForm">{{ t("common.action.reset") }}</el-button>
12
12
  <el-button type="primary" :loading="submitLoading" @click="handleSubmitPassword">
@@ -425,7 +425,6 @@ async function handleBeforeSetStatus(row: BaseTenantProject) {
425
425
  );
426
426
  await defBaseTenantProjectService.SetBaseTenantProjectStatus({ id: row.id, status: nextStatus });
427
427
  ElMessage.success(t("common.message.status_success", { action: text }));
428
- await refreshTable();
429
428
  return true;
430
429
  } catch {
431
430
  return false;
@@ -60,6 +60,22 @@ export interface UserInfoForm {
60
60
  export interface GetUserProfileRequest {
61
61
  }
62
62
 
63
+ /** 获取当前用户密码策略请求参数 */
64
+ export interface GetCurrentPasswordPolicyRequest {
65
+ }
66
+
67
+ /** 当前用户生效的密码策略 */
68
+ export interface CurrentPasswordPolicy {
69
+ /** 密码最小长度 */
70
+ min_length: number;
71
+ /** 密码至少满足的字符类别数量 */
72
+ min_complexity_classes: number;
73
+ /** 禁止重复使用的历史密码数量,零表示不启用 */
74
+ history_count: number;
75
+ /** 密码有效期天数,零表示长期有效 */
76
+ max_age_days: number;
77
+ }
78
+
63
79
  /** 用户资料表单 */
64
80
  export interface UserProfileForm {
65
81
  /** 用户名 */
@@ -210,6 +226,8 @@ export interface AuthService {
210
226
  GetUserInfo(request: GetUserInfoRequest): Promise<UserInfoForm>;
211
227
  /** 获取个人中心用户信息 */
212
228
  GetUserProfile(request: GetUserProfileRequest): Promise<UserProfileForm>;
229
+ /** 获取当前用户生效的密码策略 */
230
+ GetCurrentPasswordPolicy(request: GetCurrentPasswordPolicyRequest): Promise<CurrentPasswordPolicy>;
213
231
  /** 修改个人中心密码 */
214
232
  UpdateUserPassword(request: UpdateUserPasswordRequest): Promise<Empty>;
215
233
  /** 修改个人中心手机号 */
@@ -17,10 +17,24 @@ export interface OptionBaseTableRequest {
17
17
  source_name: string;
18
18
  }
19
19
 
20
+ /** 数据库表选项。 */
21
+ export interface BaseTableOption {
22
+ /** 数据库表名 */
23
+ name: string;
24
+ /** 数据表注释 */
25
+ comment: string;
26
+ }
27
+
28
+ /** 数据库表选项响应。 */
29
+ export interface OptionBaseTableResponse {
30
+ /** 数据库表选项列表 */
31
+ tables: BaseTableOption[];
32
+ }
33
+
20
34
  /** Admin数据源元数据服务,提供数据备份、数据归档和代码生成共用的数据源信息。 */
21
35
  export interface BaseTableSourceService {
22
36
  /** 查询已初始化的数据源名称。 */
23
37
  OptionBaseTableSource(request: OptionBaseTableSourceRequest): Promise<StringValues>;
24
- /** 查询指定数据源中的数据库表名称。 */
25
- OptionBaseTable(request: OptionBaseTableRequest): Promise<StringValues>;
38
+ /** 查询指定数据源中的数据库表选项。 */
39
+ OptionBaseTable(request: OptionBaseTableRequest): Promise<OptionBaseTableResponse>;
26
40
  }
@@ -113,7 +113,7 @@ export interface DeleteBaseTenantProjectGrantRequest {
113
113
  subject_id: number;
114
114
  }
115
115
 
116
- /** BaseTenantProjectGrantService 管理岗位、角色、部门和用户在目标租户下的项目授权。 */
116
+ /** Admin项目授权服务 */
117
117
  export interface BaseTenantProjectGrantService {
118
118
  /** 查询项目授权分页列表。 */
119
119
  PageBaseTenantProjectGrant(request: PageBaseTenantProjectGrantRequest): Promise<PageBaseTenantProjectGrantResponse>;
@@ -119,7 +119,8 @@
119
119
  v-model="editDialog.visible"
120
120
  ref="editDialogRef"
121
121
  :title="t('system.base.api.title.edit')"
122
- width="760px"
122
+ width="min(960px, calc(100vw - 32px))"
123
+ label-width="150px"
123
124
  :model="editForm"
124
125
  :fields="editFields"
125
126
  @confirm="handleSubmitEdit"
@@ -3,7 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
- import { computed, ref } from "vue";
6
+ import { computed, onMounted, ref } from "vue";
7
7
  import type { ColumnProps } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
8
8
  import { t } from "@liujitcn/kratos-admin-core";
9
9
  import { formatLogDateTime } from "@liujitcn/kratos-admin-system/components/log/log";
@@ -27,7 +27,9 @@ import type { BaseApiLog, PageBaseApiLogRequest } from "@liujitcn/kratos-admin-s
27
27
  defineOptions({ name: "BaseApiLog", inheritAttrs: false });
28
28
 
29
29
  const page = ref<InstanceType<typeof LogTable>>();
30
- const { tenantColumns } = useTenantScope();
30
+ const { tenantColumns, loadTenantOptions, resolveTenantLabel } = useTenantScope();
31
+
32
+ onMounted(() => void loadTenantOptions(true));
31
33
  const resultOptions = computed(() =>
32
34
  createLogEnumOptions([
33
35
  [BaseLogResult.BASE_LOG_RESULT_UNSPECIFIED, t("system.base.log.result.unspecified")],
@@ -61,7 +63,7 @@ const config = computed<LogTableConfig>(() => ({
61
63
  trace: requestLogTrace,
62
64
  detailFields: [
63
65
  { key: "id", label: t("system.base.log.field.id") },
64
- { key: "tenant_id", label: t("system.base.log.field.tenant_id") },
66
+ { key: "tenant_id", label: t("common.field.tenant"), format: value => resolveTenantLabel({ tenant_id: value }) },
65
67
  { key: "tenant_code", label: t("system.base.log.field.tenant_code") },
66
68
  { key: "user_id", label: t("system.base.log.field.user_id") },
67
69
  { key: "user_name", label: t("system.base.log.field.user_name") },
@@ -149,6 +149,14 @@ const columns = computed<ColumnProps[]>(() => [
149
149
  label: t("common.field.operation"),
150
150
  cellType: "actions",
151
151
  actions: [
152
+ {
153
+ label: t("common.action.create"),
154
+ type: "primary",
155
+ link: true,
156
+ icon: CirclePlus,
157
+ hidden: () => !BUTTONS.value["base:area:create"],
158
+ onClick: scope => handleOpenDialog(undefined, scope.row as BaseArea)
159
+ },
152
160
  {
153
161
  label: t("common.action.edit"),
154
162
  type: "primary",
@@ -220,32 +228,44 @@ function refreshTable() {
220
228
  proTable.value?.getTableList();
221
229
  }
222
230
  /** 加载表单选择项。 */
223
- async function loadFormOptions() {
231
+ async function loadFormOptions(selectedParent?: BaseArea) {
224
232
  const parentIdFormResponse = await defBaseAreaService.OptionBaseArea({ parent_id: 0, lazy: true } as Parameters<
225
233
  typeof defBaseAreaService.OptionBaseArea
226
234
  >[0]);
235
+ const options = normalizeLazyTreeOptions((parentIdFormResponse.list ?? []) as GeneratedTreeOption[]).filter(
236
+ option => Number(option.value) !== 0
237
+ );
238
+ if (selectedParent && !options.some(option => Number(option.value) === selectedParent.id)) {
239
+ options.push({
240
+ label: selectedParent.name,
241
+ value: selectedParent.id,
242
+ isLeaf: !selectedParent.has_children
243
+ });
244
+ }
227
245
  return [
228
246
  { label: t("system.base.area.value.root"), value: 0 },
229
- ...normalizeLazyTreeOptions((parentIdFormResponse.list ?? []) as GeneratedTreeOption[]).filter(
230
- option => Number(option.value) !== 0
231
- )
247
+ ...options
232
248
  ];
233
249
  }
234
250
 
235
251
  /**
236
252
  * 打开行政区域弹窗。
237
253
  */
238
- async function handleOpenDialog(id?: number) {
254
+ async function handleOpenDialog(id?: number, selectedParent?: BaseArea) {
239
255
  resetForm();
240
256
  dialog.titleKey = id ? "system.base.area.action.edit" : "system.base.area.action.create";
241
257
  await formDialogRef.value?.open({
242
258
  load: async () => ({
243
- options: await loadFormOptions(),
259
+ options: await loadFormOptions(selectedParent),
244
260
  data: id ? await defBaseAreaService.GetBaseArea({ id }) : undefined
245
261
  }),
246
262
  commit: ({ options, data }) => {
247
263
  parentIdFormOptions.value = options;
248
- if (data) Object.assign(formData, data);
264
+ if (data) {
265
+ Object.assign(formData, data);
266
+ } else {
267
+ formData.parent_id = selectedParent?.id ?? 0;
268
+ }
249
269
  }
250
270
  });
251
271
  }
@@ -4,6 +4,9 @@
4
4
  <FormDialog
5
5
  ref="formDialogRef"
6
6
  v-model="dialog.visible"
7
+ width="min(900px, calc(100vw - 32px))"
8
+ label-width="140px"
9
+ :col-span="12"
7
10
  :title="t(dialog.editing ? 'common.action.edit_resource' : 'common.action.create_resource', { resource: t('system.backup.archive.config') })"
8
11
  :model="formData"
9
12
  :fields="formFields"
@@ -124,7 +127,7 @@ async function requestTableOptions(sourceName: string) {
124
127
  loadingTables.value = true;
125
128
  try {
126
129
  const data = await defBaseTableSourceService.OptionBaseTable({ source_name: sourceName });
127
- return (data.value ?? []).map(value => ({ label: value, value }));
130
+ return (data.tables ?? []).map(item => ({ label: tableOptionLabel(item.name, item.comment), value: item.name }));
128
131
  } finally {
129
132
  loadingTables.value = false;
130
133
  }
@@ -151,8 +154,8 @@ async function setStatus(row: BaseTableArchiveForm) {
151
154
  }
152
155
  );
153
156
  await defBaseTableArchiveService.SetBaseTableArchiveStatus({ id: row.id, status });
157
+ row.status = status;
154
158
  ElMessage.success(t("common.message.status_success", { action }));
155
- await proTable.value?.getTableList();
156
159
  return true;
157
160
  } catch {
158
161
  return false;
@@ -160,4 +163,6 @@ async function setStatus(row: BaseTableArchiveForm) {
160
163
  }
161
164
  async function remove(selected: number | number[] | BaseTableArchiveForm) { let ids: number[]; if (Array.isArray(selected)) ids = selected.map(Number); else if (typeof selected === "object") ids = [selected.id]; else ids = normalizeSelectedIds(selected).map(Number); if (!ids.length) return; await ElMessageBox.confirm(t("common.dialog.delete_selected", { resource: t("system.backup.archive.config") }), t("common.title.warning"), { type: "warning" }); await defBaseTableArchiveService.DeleteBaseTableArchive({ id: ids.join(",") }); ElMessage.success(t("common.message.delete_success", { resource: t("system.backup.archive.config") })); refresh(); }
162
165
  function modeLabel(value: BaseTableArchiveMode) { return modeOptions.value.find(item => item.value === value)?.label ?? String(value); }
166
+ /** 格式化数据表选项,优先显示中文注释并保留物理表名。 */
167
+ function tableOptionLabel(name: string, comment: string) { return comment && comment !== name ? `${comment}(${name})` : name; }
163
168
  </script>
@@ -3,7 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
- import { computed, ref } from "vue";
6
+ import { computed, onMounted, ref } from "vue";
7
7
  import type { ColumnProps } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
8
8
  import { t } from "@liujitcn/kratos-admin-core";
9
9
  import { formatLogDateTime } from "@liujitcn/kratos-admin-system/components/log/log";
@@ -27,7 +27,9 @@ import type { BaseDataAccessLog, PageBaseDataAccessLogRequest } from "@liujitcn/
27
27
  defineOptions({ name: "BaseDataAccessLog", inheritAttrs: false });
28
28
 
29
29
  const page = ref<InstanceType<typeof LogTable>>();
30
- const { tenantColumns } = useTenantScope();
30
+ const { tenantColumns, loadTenantOptions, resolveTenantLabel } = useTenantScope();
31
+
32
+ onMounted(() => void loadTenantOptions(true));
31
33
  const resultOptions = computed(() => createLogEnumOptions([
32
34
  [BaseLogResult.BASE_LOG_RESULT_UNSPECIFIED, t("system.base.log.result.unspecified")],
33
35
  [BaseLogResult.BASE_LOG_RESULT_SUCCESS, t("system.base.log.result.success")],
@@ -62,7 +64,7 @@ const config = computed<LogTableConfig>(() => ({
62
64
  trace: requestLogTrace,
63
65
  detailFields: [
64
66
  { key: "id", label: t("system.base.log.field.id") },
65
- { key: "tenant_id", label: t("system.base.log.field.tenant_id") },
67
+ { key: "tenant_id", label: t("common.field.tenant"), format: value => resolveTenantLabel({ tenant_id: value }) },
66
68
  { key: "tenant_code", label: t("system.base.log.field.tenant_code") },
67
69
  { key: "user_id", label: t("system.base.log.field.user_id") },
68
70
  { key: "user_name", label: t("system.base.log.field.user_name") },
@@ -7,7 +7,8 @@
7
7
  v-model="dialog.visible"
8
8
  ref="formDialogRef"
9
9
  :title="t(dialog.editing ? 'system.base.job.action.edit' : 'system.base.job.action.create')"
10
- width="1000px"
10
+ width="min(1200px, calc(100vw - 32px))"
11
+ top="4vh"
11
12
  :model="formData"
12
13
  :fields="formFields"
13
14
  :rules="rules"
@@ -3,7 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
- import { computed, ref } from "vue";
6
+ import { computed, onMounted, ref } from "vue";
7
7
  import type { ColumnProps } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
8
8
  import { useTenantScope } from "@liujitcn/kratos-admin-core/tenant";
9
9
  import { t } from "@liujitcn/kratos-admin-core";
@@ -19,7 +19,9 @@ import type { BaseLoginLog, PageBaseLoginLogRequest } from "@liujitcn/kratos-adm
19
19
  defineOptions({ name: "BaseLoginLog", inheritAttrs: false });
20
20
 
21
21
  const page = ref<InstanceType<typeof LogTable>>();
22
- const { tenantColumns } = useTenantScope();
22
+ const { tenantColumns, loadTenantOptions, resolveTenantLabel } = useTenantScope();
23
+
24
+ onMounted(() => void loadTenantOptions(true));
23
25
  const resultOptions = computed(() =>
24
26
  createLogEnumOptions([
25
27
  [BaseLogResult.BASE_LOG_RESULT_UNSPECIFIED, t("system.base.log.result.unspecified")],
@@ -68,7 +70,7 @@ const config = computed<LogTableConfig>(() => ({
68
70
  trace: requestLogTrace,
69
71
  detailFields: [
70
72
  { key: "id", label: t("system.base.log.field.id") },
71
- { key: "tenant_id", label: t("system.base.log.field.tenant_id") },
73
+ { key: "tenant_id", label: t("common.field.tenant"), format: value => resolveTenantLabel({ tenant_id: value }) },
72
74
  { key: "tenant_code", label: t("system.base.log.field.tenant_code") },
73
75
  { key: "user_id", label: t("system.base.log.field.user_id") },
74
76
  { key: "user_name", label: t("system.base.log.field.user_name") },
@@ -295,7 +295,9 @@ const columns = computed<ColumnProps[]>(() => [
295
295
  activeValue: true,
296
296
  inactiveValue: false,
297
297
  activeText: t("common.status.enabled"),
298
- inactiveText: t("common.status.disabled")
298
+ inactiveText: t("common.status.disabled"),
299
+ disabled: () => !BUTTONS.value["base:login-policy:update"],
300
+ beforeChange: scope => handleSetConcurrentLogin(scope.row as BaseLoginPolicy)
299
301
  }
300
302
  },
301
303
  { prop: "password_min_length", label: t("system.base.login_policy.field.password_min_length"), width: 110, align: "right" },
@@ -494,18 +496,75 @@ async function handleSubmit() {
494
496
 
495
497
  /** 删除登录策略。 */
496
498
  async function handleDelete(value: BaseLoginPolicy | BaseLoginPolicy[] | number | number[]) {
497
- const ids = normalizeSelectedIds(value as Parameters<typeof normalizeSelectedIds>[0]);
499
+ const ids = Array.isArray(value)
500
+ ? value.map(item => (typeof item === "object" ? item.id : item))
501
+ : typeof value === "object"
502
+ ? [value.id]
503
+ : normalizeSelectedIds(value);
504
+ if (!ids.length) {
505
+ ElMessage.warning(t("common.message.select_delete_item"));
506
+ return;
507
+ }
498
508
  await ElMessageBox.confirm(t("common.confirm.delete"), t("common.title.warning"), { type: "warning" });
499
509
  await defBaseLoginPolicyService.DeleteBaseLoginPolicy({ id: ids.join(",") });
500
510
  ElMessage.success(t("common.message.operation_success"));
501
511
  proTable.value?.getTableList();
502
512
  }
503
513
 
514
+ /** 设置登录策略是否允许同一账号同时登录。 */
515
+ async function handleSetConcurrentLogin(row: BaseLoginPolicy) {
516
+ const allowConcurrentLogin = !row.allow_concurrent_login;
517
+ const action = t(allowConcurrentLogin ? "common.status.enabled" : "common.status.disabled");
518
+ try {
519
+ await ElMessageBox.confirm(
520
+ t("common.dialog.status_change", {
521
+ action,
522
+ resource: t("system.base.login_policy.field.allow_concurrent_login"),
523
+ field: t("system.base.login_policy.field.target"),
524
+ value: targetLabel(row)
525
+ }),
526
+ t("common.title.notice"),
527
+ {
528
+ confirmButtonText: t("common.action.confirm"),
529
+ cancelButtonText: t("common.action.cancel"),
530
+ type: "warning"
531
+ }
532
+ );
533
+ const policy = await defBaseLoginPolicyService.GetBaseLoginPolicy({ id: row.id });
534
+ policy.allow_concurrent_login = allowConcurrentLogin;
535
+ await defBaseLoginPolicyService.UpdateBaseLoginPolicy({ base_login_policy: policy });
536
+ ElMessage.success(t("common.message.status_success", { action }));
537
+ return true;
538
+ } catch {
539
+ return false;
540
+ }
541
+ }
542
+
504
543
  /** 设置登录策略状态。 */
505
544
  async function handleSetStatus(row: BaseLoginPolicy) {
506
545
  const status = row.status === Status.STATUS_ENABLE ? Status.STATUS_DISABLE : Status.STATUS_ENABLE;
507
- await defBaseLoginPolicyService.SetBaseLoginPolicyStatus({ id: row.id, status });
508
- return true;
546
+ const action = t(status === Status.STATUS_ENABLE ? "common.status.enabled" : "common.status.disabled");
547
+ try {
548
+ await ElMessageBox.confirm(
549
+ t("common.dialog.status_change", {
550
+ action,
551
+ resource: t("system.base.login_policy.title"),
552
+ field: t("system.base.login_policy.field.target"),
553
+ value: targetLabel(row)
554
+ }),
555
+ t("common.title.notice"),
556
+ {
557
+ confirmButtonText: t("common.action.confirm"),
558
+ cancelButtonText: t("common.action.cancel"),
559
+ type: "warning"
560
+ }
561
+ );
562
+ await defBaseLoginPolicyService.SetBaseLoginPolicyStatus({ id: row.id, status });
563
+ ElMessage.success(t("common.message.status_success", { action }));
564
+ return true;
565
+ } catch {
566
+ return false;
567
+ }
509
568
  }
510
569
 
511
570
  /** 关闭登录策略表单。 */
@@ -10,6 +10,8 @@
10
10
  :request-api="requestMenuTable"
11
11
  :pagination="false"
12
12
  :default-expand-all="false"
13
+ :lazy="true"
14
+ :load="loadMenuChildren"
13
15
  :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
14
16
  />
15
17
 
@@ -72,7 +74,8 @@ import type {
72
74
  BaseMenu,
73
75
  BaseMenuAppMeta,
74
76
  BaseMenuForm,
75
- BaseMenuMeta
77
+ BaseMenuMeta,
78
+ TreeBaseMenuRequest
76
79
  } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_menu";
77
80
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
78
81
  import { BaseMenuType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/common";
@@ -1087,18 +1090,31 @@ function filterMenuTree(menuList: BaseMenu[], keywordMap: Record<string, string>
1087
1090
  /** 请求菜单表格数据,并按搜索条件过滤树形结构。 */
1088
1091
  async function requestMenuTable(params: Record<string, string>) {
1089
1092
  await loadEnabledBaseLanguages();
1090
- const data = await defBaseMenuService.TreeBaseMenu({});
1091
1093
  const keywordMap = {
1092
1094
  title: params.title ?? "",
1093
1095
  name: params.name ?? "",
1094
1096
  path: params.path ?? ""
1095
1097
  };
1098
+ const hasKeyword = Object.values(keywordMap).some(keyword => keyword.trim() !== "");
1099
+ const request: TreeBaseMenuRequest = hasKeyword ? {} : { parent_id: 0, lazy: true };
1100
+ const data = await defBaseMenuService.TreeBaseMenu(request);
1096
1101
 
1097
1102
  return {
1098
- data: filterMenuTree(data.base_menus ?? [], keywordMap)
1103
+ data: hasKeyword ? filterMenuTree(data.base_menus ?? [], keywordMap) : (data.base_menus ?? [])
1099
1104
  };
1100
1105
  }
1101
1106
 
1107
+ /** 懒加载菜单表格的直属子节点。 */
1108
+ async function loadMenuChildren(row: BaseMenu, _treeNode: unknown, resolve: (data: BaseMenu[]) => void) {
1109
+ try {
1110
+ const data = await defBaseMenuService.TreeBaseMenu({ parent_id: row.id, lazy: true });
1111
+ resolve(data.base_menus ?? []);
1112
+ } catch {
1113
+ ElMessage.error(t("common.message.load_children_failed", { resource: t("system.base.menu.title.list") }));
1114
+ resolve([]);
1115
+ }
1116
+ }
1117
+
1102
1118
  /** 刷新菜单表格。 */
1103
1119
  function refreshTable() {
1104
1120
  proTable.value?.getTableList();
@@ -364,7 +364,6 @@ async function handleSetStatus(row: BaseMessageCategory) {
364
364
  );
365
365
  await defBaseMessageCategoryService.SetBaseMessageCategoryStatus({ id: row.id, status });
366
366
  ElMessage.success(t("common.message.status_success", { action }));
367
- await proTable.value?.getTableList();
368
367
  return true;
369
368
  } catch {
370
369
  return false;
@@ -13,7 +13,8 @@
13
13
  v-model="dialog.visible"
14
14
  ref="formDialogRef"
15
15
  :title="t(dialog.titleKey)"
16
- width="960px"
16
+ width="min(1200px, calc(100vw - 32px))"
17
+ label-width="150px"
17
18
  :model="formData"
18
19
  :fields="formFields"
19
20
  :rules="rules"
@@ -3,7 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
- import { computed, ref } from "vue";
6
+ import { computed, onMounted, ref } from "vue";
7
7
  import type { ColumnProps } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
8
8
  import { t } from "@liujitcn/kratos-admin-core";
9
9
  import { formatLogDateTime } from "@liujitcn/kratos-admin-system/components/log/log";
@@ -19,7 +19,9 @@ import type { BaseOperationLog, PageBaseOperationLogRequest } from "@liujitcn/kr
19
19
  defineOptions({ name: "BaseOperationLog", inheritAttrs: false });
20
20
 
21
21
  const page = ref<InstanceType<typeof LogTable>>();
22
- const { tenantColumns } = useTenantScope();
22
+ const { tenantColumns, loadTenantOptions, resolveTenantLabel } = useTenantScope();
23
+
24
+ onMounted(() => void loadTenantOptions(true));
23
25
  const resultOptions = computed(() => createLogEnumOptions([
24
26
  [BaseLogResult.BASE_LOG_RESULT_UNSPECIFIED, t("system.base.log.result.unspecified")],
25
27
  [BaseLogResult.BASE_LOG_RESULT_SUCCESS, t("system.base.log.result.success")],
@@ -56,7 +58,7 @@ const config = computed<LogTableConfig>(() => ({
56
58
  trace: requestLogTrace,
57
59
  detailFields: [
58
60
  { key: "id", label: t("system.base.log.field.id") },
59
- { key: "tenant_id", label: t("system.base.log.field.tenant_id") },
61
+ { key: "tenant_id", label: t("common.field.tenant"), format: value => resolveTenantLabel({ tenant_id: value }) },
60
62
  { key: "tenant_code", label: t("system.base.log.field.tenant_code") },
61
63
  { key: "user_id", label: t("system.base.log.field.user_id") },
62
64
  { key: "user_name", label: t("system.base.log.field.user_name") },
@@ -3,7 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
- import { computed, ref } from "vue";
6
+ import { computed, onMounted, ref } from "vue";
7
7
  import type { ColumnProps } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
8
8
  import { t } from "@liujitcn/kratos-admin-core";
9
9
  import { formatLogDateTime } from "@liujitcn/kratos-admin-system/components/log/log";
@@ -19,7 +19,9 @@ import type { BasePermissionLog, PageBasePermissionLogRequest } from "@liujitcn/
19
19
  defineOptions({ name: "BasePermissionLog", inheritAttrs: false });
20
20
 
21
21
  const page = ref<InstanceType<typeof LogTable>>();
22
- const { tenantColumns } = useTenantScope();
22
+ const { tenantColumns, loadTenantOptions, resolveTenantLabel } = useTenantScope();
23
+
24
+ onMounted(() => void loadTenantOptions(true));
23
25
  const resultOptions = computed(() => createLogEnumOptions([
24
26
  [BaseLogResult.BASE_LOG_RESULT_UNSPECIFIED, t("system.base.log.result.unspecified")],
25
27
  [BaseLogResult.BASE_LOG_RESULT_SUCCESS, t("system.base.log.result.success")],
@@ -61,7 +63,7 @@ const config = computed<LogTableConfig>(() => ({
61
63
  trace: requestLogTrace,
62
64
  detailFields: [
63
65
  { key: "id", label: t("system.base.log.field.id") },
64
- { key: "tenant_id", label: t("system.base.log.field.tenant_id") },
66
+ { key: "tenant_id", label: t("common.field.tenant"), format: value => resolveTenantLabel({ tenant_id: value }) },
65
67
  { key: "tenant_code", label: t("system.base.log.field.tenant_code") },
66
68
  { key: "user_id", label: t("system.base.log.field.user_id") },
67
69
  { key: "user_name", label: t("system.base.log.field.user_name") },
@@ -3,7 +3,7 @@
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
- import { computed, ref } from "vue";
6
+ import { computed, onMounted, ref } from "vue";
7
7
  import type { ColumnProps } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
8
8
  import { t } from "@liujitcn/kratos-admin-core";
9
9
  import { formatLogDateTime } from "@liujitcn/kratos-admin-system/components/log/log";
@@ -26,7 +26,9 @@ import type { BasePolicyEvaluationLog, PageBasePolicyEvaluationLogRequest } from
26
26
  defineOptions({ name: "BasePolicyEvaluationLog", inheritAttrs: false });
27
27
 
28
28
  const page = ref<InstanceType<typeof LogTable>>();
29
- const { tenantColumns } = useTenantScope();
29
+ const { tenantColumns, loadTenantOptions, resolveTenantLabel } = useTenantScope();
30
+
31
+ onMounted(() => void loadTenantOptions(true));
30
32
  const decisionOptions = computed(() => createLogEnumOptions([
31
33
  [BasePolicyDecision.BASE_POLICY_DECISION_UNSPECIFIED, t("system.base.log.policy_decision.unspecified")],
32
34
  [BasePolicyDecision.BASE_POLICY_DECISION_ALLOW, t("system.base.log.policy_decision.allow")],
@@ -58,7 +60,7 @@ const config = computed<LogTableConfig>(() => ({
58
60
  trace: requestLogTrace,
59
61
  detailFields: [
60
62
  { key: "id", label: t("system.base.log.field.id") },
61
- { key: "tenant_id", label: t("system.base.log.field.tenant_id") },
63
+ { key: "tenant_id", label: t("common.field.tenant"), format: value => resolveTenantLabel({ tenant_id: value }) },
62
64
  { key: "tenant_code", label: t("system.base.log.field.tenant_code") },
63
65
  { key: "user_id", label: t("system.base.log.field.user_id") },
64
66
  { key: "user_name", label: t("system.base.log.field.user_name") },
@@ -56,7 +56,7 @@
56
56
  </template>
57
57
 
58
58
  <script setup lang="ts">
59
- import { computed, h, onBeforeUnmount, nextTick, reactive, ref } from "vue";
59
+ import { computed, h, onBeforeUnmount, reactive, ref } from "vue";
60
60
  import { useRouter } from "vue-router";
61
61
  import {
62
62
  CirclePlus,
@@ -789,6 +789,7 @@ function refreshTable() {
789
789
 
790
790
  /** 打开新增或编辑弹窗,并加载当前表单所需选项。 */
791
791
  async function handleOpenDialog(tableId?: number) {
792
+ resetForm();
792
793
  await formDialogRef.value?.open({
793
794
  load: async () => {
794
795
  await loadSourceOptions();
@@ -831,7 +832,6 @@ async function handleOpenDialog(tableId?: number) {
831
832
  };
832
833
  },
833
834
  commit: ({ form, tables, menuOptions, businessModuleItems: items, columns, leftTreeColumns }) => {
834
- resetForm();
835
835
  Object.assign(formData, form);
836
836
  databaseTables.value = tables;
837
837
  parentMenuOptions.value = menuOptions;
@@ -863,14 +863,12 @@ function handleCloseDialog() {
863
863
 
864
864
  /** 重置弹窗表单和字段选项。 */
865
865
  function resetForm() {
866
+ formDialogRef.value?.resetFields();
867
+ formDialogRef.value?.clearValidate();
866
868
  Object.assign(formData, { ...createDefaultCodeGenTableForm(), parent_menu_id: undefined });
867
869
  dialog.editing = false;
868
870
  databaseColumns.value = [];
869
871
  leftTreeDatabaseColumns.value = [];
870
- void nextTick(() => {
871
- formDialogRef.value?.resetFields();
872
- formDialogRef.value?.clearValidate();
873
- });
874
872
  }
875
873
 
876
874
  /** 选择业务表后同步数据库注释、默认命名、字段选项和树字段默认值。 */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@liujitcn/kratos-admin-system",
3
3
  "private": false,
4
- "version": "0.0.56",
4
+ "version": "0.0.57",
5
5
  "type": "module",
6
6
  "description": "kratos-admin system management module",
7
7
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  "sortablejs": "^1.15.2",
17
17
  "swagger-ui-dist": "^5.32.15",
18
18
  "vue-element-plus-x": "^2.0.3",
19
- "@liujitcn/kratos-admin-core": "0.0.56"
19
+ "@liujitcn/kratos-admin-core": "0.0.57"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "@element-plus/icons-vue": "^2.3.1",