@liujitcn/kratos-admin-core 0.0.48 → 0.0.50

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 (40) hide show
  1. package/README.md +4 -1
  2. package/dist/package/declarations/src/api/system/admin/v1/base_tenant.d.ts +3 -1
  3. package/dist/package/declarations/src/components/Dialog/FormDialog.vue.d.ts +1 -1
  4. package/dist/package/declarations/src/components/Grid/components/GridItem.vue.d.ts +1 -1
  5. package/dist/package/declarations/src/components/ProForm/components/ProFormItem.vue.d.ts +4 -4
  6. package/dist/package/declarations/src/components/ProForm/index.vue.d.ts +1 -1
  7. package/dist/package/declarations/src/components/ProForm/interface/index.d.ts +1 -1
  8. package/dist/package/declarations/src/components/ProTable/interface/index.d.ts +1 -1
  9. package/dist/package/declarations/src/components/SearchForm/components/SearchFormItem.vue.d.ts +2 -2
  10. package/dist/package/declarations/src/components/TenantSelect/index.vue.d.ts +23 -0
  11. package/dist/package/declarations/src/components/TenantText/index.vue.d.ts +13 -0
  12. package/dist/package/declarations/src/index.d.ts +1 -0
  13. package/dist/package/declarations/src/locales/generated.d.ts +4 -0
  14. package/dist/package/declarations/src/locales/index.d.ts +8 -0
  15. package/dist/package/declarations/src/rpc/base/v1/config.d.ts +11 -0
  16. package/dist/package/declarations/src/rpc/system/admin/v1/base_tenant.d.ts +10 -1
  17. package/dist/package/declarations/src/stores/modules/config.d.ts +2 -0
  18. package/dist/package/declarations/src/tenant.d.ts +65 -0
  19. package/dist/package/declarations/src/utils/tableColumn.d.ts +4 -5
  20. package/dist/package/src/api/system/admin/v1/base_tenant.ts +10 -1
  21. package/dist/package/src/components/ProForm/components/ProFormItem.vue +3 -0
  22. package/dist/package/src/components/ProForm/interface/index.ts +1 -0
  23. package/dist/package/src/components/ProTable/index.vue +2 -2
  24. package/dist/package/src/components/ProTable/interface/index.ts +2 -0
  25. package/dist/package/src/components/SearchForm/components/SearchFormItem.vue +39 -2
  26. package/dist/package/src/components/TenantSelect/index.vue +57 -0
  27. package/dist/package/src/components/TenantText/index.vue +28 -0
  28. package/dist/package/src/index.ts +1 -0
  29. package/dist/package/src/locales/en-US.json +1 -0
  30. package/dist/package/src/locales/index.ts +27 -1
  31. package/dist/package/src/locales/ja-JP.json +1 -0
  32. package/dist/package/src/locales/zh-CN.json +1 -0
  33. package/dist/package/src/locales/zh-TW.json +1 -0
  34. package/dist/package/src/rpc/base/v1/config.ts +12 -0
  35. package/dist/package/src/rpc/system/admin/v1/base_tenant.ts +11 -1
  36. package/dist/package/src/stores/modules/config.ts +7 -0
  37. package/dist/package/src/tenant.ts +157 -0
  38. package/dist/package/src/utils/tableColumn.ts +5 -38
  39. package/dist/package/src/views/login/components/LoginForm.vue +2 -2
  40. package/package.json +13 -1
package/README.md CHANGED
@@ -67,7 +67,7 @@ packages/core
67
67
  | `src/request.ts` | 业务模块使用的请求客户端入口。 |
68
68
  | `src/security.ts` | OAuth、密码加密、密码强度和 WebAuthn 入口。 |
69
69
  | `src/table.ts` | ProTable 请求和选择值处理入口。 |
70
- | `src/tenant.ts` | 租户选项入口。 |
70
+ | `src/tenant.ts` | 租户上下文、租户选项、查询范围转换以及租户列/表单字段生成入口。 |
71
71
  | `src/components/ProTable.ts` | ProTable 对外 Adapter,稳定组件入口为 `components/ProTable`。 |
72
72
  | `src/bootstrap.ts` | 创建 Vue 应用,注册 core 与宿主选择的业务模块后挂载。 |
73
73
  | `src/App.vue` | 根组件和路由出口。 |
@@ -96,6 +96,8 @@ packages/core
96
96
  | `src/components/SvgIcon/` | SVG 图标渲染。 |
97
97
  | `src/components/SwitchDark/` | 明暗主题切换。 |
98
98
  | `src/components/TreeFilter/` | 树形筛选器。 |
99
+ | `src/components/TenantSelect/` | 默认租户租户选择器,普通租户自动隐藏。 |
100
+ | `src/components/TenantText/` | 租户名称展示组件,支持名称选项和租户 ID 兜底。 |
99
101
  | `src/components/Upload/` | 单/多文件和单/多图片上传组件。 |
100
102
  | `src/components/WangEditor/` | 富文本编辑器封装。 |
101
103
  | `src/config/index.ts` | 默认应用配置。 |
@@ -164,6 +166,7 @@ core 通过 `ADMIN_STATIC_VIEWS` 注册全部默认静态页面,后注册业
164
166
  | 运行时 Store | `@liujitcn/kratos-admin-core/stores/runtime` |
165
167
  | ProTable Adapter | `@liujitcn/kratos-admin-core/components/ProTable` |
166
168
  | ProTable 类型 | `@liujitcn/kratos-admin-core/components/ProTable/interface` |
169
+ | SearchForm | `@liujitcn/kratos-admin-core/components/SearchForm/index.vue` |
167
170
 
168
171
  其他 Vue 组件以 `package.json#exports` 中的明确白名单为准。禁止使用 `components/ProTable/index.vue`、`utils/*`、`hooks/*`、`stores/modules/*` 等实现路径。发布构建会把内部 `@/` 别名转换成包内相对路径,内部实现不占用公共子路径。
169
172
 
@@ -1,9 +1,11 @@
1
1
  import type { SelectOptionResponse } from "../../../../rpc/common/v1/common";
2
- import type { OptionBaseTenantRequest } from "../../../../rpc/system/admin/v1/base_tenant";
2
+ import type { OptionBaseTenantRequest, PageBaseTenantRequest, PageBaseTenantResponse } from "../../../../rpc/system/admin/v1/base_tenant";
3
3
  /** BaseTenantServiceImpl 管理端运行壳租户服务。 */
4
4
  export declare class BaseTenantServiceImpl {
5
5
  /** 查询租户选项。 */
6
6
  OptionBaseTenant(request: OptionBaseTenantRequest): Promise<SelectOptionResponse>;
7
+ /** 查询租户分页列表,供编码筛选和名称解析使用。 */
8
+ PageBaseTenant(request: PageBaseTenantRequest): Promise<PageBaseTenantResponse>;
7
9
  }
8
10
  /** defBaseTenantService 管理端运行壳租户服务实例。 */
9
11
  export declare const defBaseTenantService: BaseTenantServiceImpl;
@@ -59,11 +59,11 @@ declare const __VLS_base: import("vue").DefineComponent<FormDialogProps, {
59
59
  confirmText: string;
60
60
  cancelText: string;
61
61
  confirmLoading: boolean;
62
+ colSpan: number;
62
63
  rules: FormRules;
63
64
  labelWidth: string;
64
65
  labelPosition: ProFormLabelPosition;
65
66
  gutter: number;
66
- colSpan: number;
67
67
  formProps: Record<string, any>;
68
68
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
69
69
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -17,12 +17,12 @@ type __VLS_Slots = {} & {
17
17
  declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
18
18
  offset: number;
19
19
  span: number;
20
- suffix: boolean;
21
20
  xs: Responsive;
22
21
  sm: Responsive;
23
22
  md: Responsive;
24
23
  lg: Responsive;
25
24
  xl: Responsive;
25
+ suffix: boolean;
26
26
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
27
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
28
28
  declare const _default: typeof __VLS_export;
@@ -7,20 +7,20 @@ interface ProFormItemProps {
7
7
  declare var __VLS_10: string, __VLS_11: {
8
8
  field: ProFormField;
9
9
  model: Record<string, any>;
10
- }, __VLS_133: string, __VLS_134: {
10
+ }, __VLS_139: string, __VLS_140: {
11
11
  option: import("element-plus").TransferDataItem;
12
12
  field: ProFormField;
13
13
  model: Record<string, any>;
14
- }, __VLS_179: string, __VLS_180: {
14
+ }, __VLS_185: string, __VLS_186: {
15
15
  field: ProFormField;
16
16
  model: Record<string, any>;
17
17
  };
18
18
  type __VLS_Slots = {} & {
19
19
  [K in NonNullable<typeof __VLS_10>]?: (props: typeof __VLS_11) => any;
20
20
  } & {
21
- [K in NonNullable<typeof __VLS_133>]?: (props: typeof __VLS_134) => any;
21
+ [K in NonNullable<typeof __VLS_139>]?: (props: typeof __VLS_140) => any;
22
22
  } & {
23
- [K in NonNullable<typeof __VLS_179>]?: (props: typeof __VLS_180) => any;
23
+ [K in NonNullable<typeof __VLS_185>]?: (props: typeof __VLS_186) => any;
24
24
  };
25
25
  declare const __VLS_base: import("vue").DefineComponent<ProFormItemProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ProFormItemProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
26
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -22,11 +22,11 @@ declare const __VLS_base: import("vue").DefineComponent<ProFormProps, {
22
22
  resetFields: typeof resetFields;
23
23
  clearValidate: typeof clearValidate;
24
24
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ProFormProps> & Readonly<{}>, {
25
+ colSpan: number;
25
26
  rules: FormRules;
26
27
  labelWidth: string;
27
28
  labelPosition: ProFormLabelPosition;
28
29
  gutter: number;
29
- colSpan: number;
30
30
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
31
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
32
32
  declare const _default: typeof __VLS_export;
@@ -1,7 +1,7 @@
1
1
  import type { FormItemRule } from "element-plus";
2
2
  import type { Component } from "vue";
3
3
  /** ProForm 支持的字段组件类型。 */
4
- export type ProFormComponentType = "input" | "password" | "textarea" | "input-number" | "color-picker" | "segmented" | "switch" | "checkbox" | "select" | "dict" | "radio-group" | "checkbox-group" | "tree-select" | "date-picker" | "cron-expression" | "transfer" | "image-upload" | "images-upload" | "file-upload" | "files-upload" | "rich-text" | "dynamic-list" | "kv-list" | "slot";
4
+ export type ProFormComponentType = "input" | "password" | "textarea" | "input-number" | "color-picker" | "segmented" | "switch" | "checkbox" | "select" | "tenant-select" | "dict" | "radio-group" | "checkbox-group" | "tree-select" | "date-picker" | "cron-expression" | "transfer" | "image-upload" | "images-upload" | "file-upload" | "files-upload" | "rich-text" | "dynamic-list" | "kv-list" | "slot";
5
5
  /** ProForm 标签布局方向。 */
6
6
  export type ProFormLabelPosition = "left" | "right" | "top";
7
7
  /** ProForm 选择型字段选项。 */
@@ -16,7 +16,7 @@ export interface EnumProps {
16
16
  /** Element Plus 表格内置列类型。 */
17
17
  export type TypeProps = "index" | "selection" | "radio" | "expand" | "sort";
18
18
  /** ProTable 搜索表单支持的控件类型。 */
19
- export type SearchType = "input" | "input-number" | "select" | "select-v2" | "tree-select" | "cascader" | "date-picker" | "time-picker" | "time-select" | "switch" | "slider";
19
+ export type SearchType = "input" | "input-number" | "select" | "tenant-select" | "select-v2" | "tree-select" | "cascader" | "date-picker" | "time-picker" | "time-select" | "radio" | "switch" | "slider";
20
20
  /** ProTable 组件属性。 */
21
21
  export interface ProTableProps {
22
22
  columns: ColumnProps[];
@@ -6,9 +6,9 @@ interface SearchFormItem {
6
6
  [key: string]: any;
7
7
  };
8
8
  }
9
- declare var __VLS_27: {};
9
+ declare var __VLS_46: {};
10
10
  type __VLS_Slots = {} & {
11
- default?: (props: typeof __VLS_27) => any;
11
+ default?: (props: typeof __VLS_46) => any;
12
12
  };
13
13
  declare const __VLS_base: import("vue").DefineComponent<SearchFormItem, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<SearchFormItem> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -0,0 +1,23 @@
1
+ import type { SelectOptionResponse_Option } from "../../rpc/common/v1/common";
2
+ /** 租户选择组件属性。 */
3
+ interface TenantSelectProps {
4
+ /** 当前选择的租户。 */
5
+ modelValue?: number | string;
6
+ /** 外部传入的租户选项。 */
7
+ options?: SelectOptionResponse_Option[];
8
+ /** 是否显示加载状态。 */
9
+ loading?: boolean;
10
+ }
11
+ declare const __VLS_export: import("vue").DefineComponent<TenantSelectProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
12
+ "update:modelValue": (value: string | number | undefined) => any;
13
+ change: (value: string | number | undefined) => any;
14
+ }, string, import("vue").PublicProps, Readonly<TenantSelectProps> & Readonly<{
15
+ "onUpdate:modelValue"?: ((value: string | number | undefined) => any) | undefined;
16
+ onChange?: ((value: string | number | undefined) => any) | undefined;
17
+ }>, {
18
+ options: SelectOptionResponse_Option[];
19
+ modelValue: number | string;
20
+ loading: boolean;
21
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const _default: typeof __VLS_export;
23
+ export default _default;
@@ -0,0 +1,13 @@
1
+ import type { SelectOptionResponse_Option } from "../../rpc/common/v1/common";
2
+ /** 租户展示组件属性。 */
3
+ interface TenantTextProps {
4
+ /** 租户ID。 */
5
+ tenantId?: number | string;
6
+ /** 已知的租户名称。 */
7
+ tenantName?: string;
8
+ /** 外部传入的租户选项。 */
9
+ options?: SelectOptionResponse_Option[];
10
+ }
11
+ declare const __VLS_export: import("vue").DefineComponent<TenantTextProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<TenantTextProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const _default: typeof __VLS_export;
13
+ export default _default;
@@ -9,3 +9,4 @@ export { defLanguageService } from "./api/base/v1/language";
9
9
  export { setAdminDocumentTitle } from "./documentTitle";
10
10
  export { kratosAdminModule } from "./modules/kratosAdmin";
11
11
  export { bootstrapAdminApp } from "./bootstrap";
12
+ export { refreshAdminRuntimeConfig } from "./stores/modules/config";
@@ -340,6 +340,7 @@ export declare const LOCALE_MESSAGES: {
340
340
  "common.field.sort": string;
341
341
  "common.field.status": string;
342
342
  "common.field.tenant": string;
343
+ "common.field.project": string;
343
344
  "common.field.updated_at": string;
344
345
  "common.field.user": string;
345
346
  "common.message.create_success": string;
@@ -705,6 +706,7 @@ export declare const LOCALE_MESSAGES: {
705
706
  "common.field.sort": string;
706
707
  "common.field.status": string;
707
708
  "common.field.tenant": string;
709
+ "common.field.project": string;
708
710
  "common.field.updated_at": string;
709
711
  "common.field.user": string;
710
712
  "common.message.create_success": string;
@@ -1070,6 +1072,7 @@ export declare const LOCALE_MESSAGES: {
1070
1072
  "common.field.sort": string;
1071
1073
  "common.field.status": string;
1072
1074
  "common.field.tenant": string;
1075
+ "common.field.project": string;
1073
1076
  "common.field.updated_at": string;
1074
1077
  "common.field.user": string;
1075
1078
  "common.message.create_success": string;
@@ -1435,6 +1438,7 @@ export declare const LOCALE_MESSAGES: {
1435
1438
  "common.field.sort": string;
1436
1439
  "common.field.status": string;
1437
1440
  "common.field.tenant": string;
1441
+ "common.field.project": string;
1438
1442
  "common.field.updated_at": string;
1439
1443
  "common.field.user": string;
1440
1444
  "common.message.create_success": string;
@@ -24,6 +24,12 @@ export declare const adminI18n: import("vue-i18n").I18n<{}, {}, {}, "zh-CN" | "e
24
24
  export declare function normalizeLocale(value?: string): SupportedLocale;
25
25
  /** 注册模块语言包并校验语言键、占位符和命名空间。 */
26
26
  export declare function registerLocaleMessages(modules: AdminModule[]): void;
27
+ /** 应用全部语言的数据库自定义翻译覆盖项。 */
28
+ export declare function applyCustomLocaleMessages(customs: Array<{
29
+ locale?: string;
30
+ key?: string;
31
+ value?: string;
32
+ }>): void;
27
33
  /** 初始化持久化语言偏好并同步日期库。 */
28
34
  export declare function initializeLocale(): SupportedLocale;
29
35
  /** 应用后端语言配置,并在当前语言不可用时回退到接口第一项。 */
@@ -44,6 +50,8 @@ export declare function registerLocaleChangeHandler(handler: () => void | Promis
44
50
  export declare function t(key: string, params?: LocaleParams): string;
45
51
  /** 从指定语言包读取固定文案,供第三方组件配置和跨语言兼容判断使用。 */
46
52
  export declare function getLocaleText(locale: SupportedLocale, key: string): string;
53
+ /** 从代码语言包读取未应用数据库覆盖的默认文案。 */
54
+ export declare function getDefaultLocaleText(locale: SupportedLocale, key: string): string;
47
55
  /** 管理端响应式语言状态。 */
48
56
  export declare function useLocaleStore(): {
49
57
  locale: Readonly<globalThis.Ref<"zh-CN" | "en-US" | "ja-JP" | "zh-TW", "zh-CN" | "en-US" | "ja-JP" | "zh-TW">>;
@@ -29,6 +29,17 @@ export interface GetConfigResponse {
29
29
  configs: ConfigItem[];
30
30
  /** AI 助手是否可用 */
31
31
  ai_enabled: boolean;
32
+ /** 全部语言的自定义国际化覆盖项 */
33
+ i18n_customs: I18nCustomItem[];
34
+ }
35
+ /** 全部语言的自定义国际化覆盖项。 */
36
+ export interface I18nCustomItem {
37
+ /** 语言区域 */
38
+ locale: string;
39
+ /** 国际化语言键 */
40
+ key: string;
41
+ /** 自定义翻译内容 */
42
+ value: string;
32
43
  }
33
44
  /** Base系统配置公共服务 */
34
45
  export interface ConfigService {
@@ -36,6 +36,15 @@ export interface CreateBaseTenantRequest {
36
36
  /** 租户表单 */
37
37
  base_tenant: BaseTenantForm | undefined;
38
38
  }
39
+ /** 创建租户响应参数 */
40
+ export interface CreateBaseTenantResponse {
41
+ /** 租户编号 */
42
+ tenant_code: string;
43
+ /** 租户管理员账号 */
44
+ admin_user_name: string;
45
+ /** 租户管理员初始密码,仅在随机生成时返回 */
46
+ initial_password: string;
47
+ }
39
48
  /** 更新租户请求参数 */
40
49
  export interface UpdateBaseTenantRequest {
41
50
  /** 租户表单 */
@@ -102,7 +111,7 @@ export interface BaseTenantService {
102
111
  /** 查询租户 */
103
112
  GetBaseTenant(request: GetBaseTenantRequest): Promise<BaseTenantForm>;
104
113
  /** 创建租户 */
105
- CreateBaseTenant(request: CreateBaseTenantRequest): Promise<Empty>;
114
+ CreateBaseTenant(request: CreateBaseTenantRequest): Promise<CreateBaseTenantResponse>;
106
115
  /** 更新租户 */
107
116
  UpdateBaseTenant(request: UpdateBaseTenantRequest): Promise<Empty>;
108
117
  /** 删除租户 */
@@ -24,3 +24,5 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"admin-conf
24
24
  background: string;
25
25
  }>;
26
26
  }>;
27
+ /** 刷新当前管理端语言和站点运行配置。 */
28
+ export declare function refreshAdminRuntimeConfig(): Promise<void>;
@@ -1 +1,66 @@
1
+ import { type ComputedRef, type Ref } from "vue";
2
+ import type { ProFormField } from "./components/ProForm/interface";
3
+ import type { ColumnProps, EnumProps, RenderScope, SearchType } from "./components/ProTable/interface";
4
+ import type { SelectOptionResponse_Option } from "./rpc/common/v1/common";
1
5
  export * from "./utils/tenant";
6
+ /** 查询租户编码筛选选项,值为租户编码而不是租户 ID。 */
7
+ export declare function requestTenantCodeOptions(): Promise<{
8
+ data: EnumProps[];
9
+ }>;
10
+ /** 租户上下文状态。 */
11
+ export interface TenantScope {
12
+ /** 当前账号是否为默认租户。 */
13
+ isDefaultTenant: ComputedRef<boolean>;
14
+ /** 当前账号可用的租户选项。 */
15
+ tenantOptions: Ref<SelectOptionResponse_Option[]>;
16
+ /** 租户选项名称映射。 */
17
+ tenantNameMap: ComputedRef<Map<string, string>>;
18
+ /** 加载默认租户可选择的租户。 */
19
+ loadTenantOptions: (includeCurrentTenant?: boolean) => Promise<void>;
20
+ /** 将页面租户筛选值转换成后端请求值。 */
21
+ toRequestTenantId: (value: unknown) => number | undefined;
22
+ /** 创建默认租户可见的表单字段。 */
23
+ tenantFormField: (options: TenantFormFieldOptions) => ProFormField;
24
+ /** 创建默认租户可见的表格租户列。 */
25
+ tenantColumns: (options: TenantColumnOptions) => ColumnProps[];
26
+ /** 将行数据转换成租户名称。 */
27
+ resolveTenantLabel: (row: Record<string, any>, field?: string, options?: SelectOptionResponse_Option[]) => string;
28
+ }
29
+ /** 租户表单字段配置。 */
30
+ export interface TenantFormFieldOptions {
31
+ /** 字段绑定名称。 */
32
+ prop?: string;
33
+ /** 字段标签。 */
34
+ label: string;
35
+ /** 表单控件附加属性。 */
36
+ props?: Record<string, any>;
37
+ /** 是否允许编辑态修改租户。 */
38
+ disabledOnEdit?: boolean;
39
+ }
40
+ /** 租户表格列配置。 */
41
+ export interface TenantColumnOptions {
42
+ /** 列绑定字段。 */
43
+ prop?: string;
44
+ /** 列标题。 */
45
+ label: string;
46
+ /** 查询字段排序。 */
47
+ order?: number;
48
+ /** 查询参数字段,默认使用列字段。 */
49
+ searchKey?: string;
50
+ /** 列最小宽度。 */
51
+ minWidth?: number | string;
52
+ /** 是否在表格中显示,隐藏时仍可保留查询项。 */
53
+ isShow?: boolean;
54
+ /** 是否允许在列设置中切换。 */
55
+ isSetting?: boolean;
56
+ /** 是否带租户查询项。 */
57
+ searchable?: boolean;
58
+ /** 自定义租户选项来源。 */
59
+ enum?: ColumnProps["enum"];
60
+ /** 查询控件类型。 */
61
+ searchEl?: SearchType;
62
+ /** 自定义单元格展示。 */
63
+ render?: (scope: RenderScope) => ReturnType<NonNullable<ColumnProps["render"]>>;
64
+ }
65
+ /** 创建租户范围能力,统一默认租户和普通租户的前端行为。 */
66
+ export declare function useTenantScope(): TenantScope;
@@ -2,10 +2,9 @@ import type { ColumnProps } from "../components/ProTable/interface";
2
2
  /** 表格单元格支持的对齐方式。 */
3
3
  export type TableAlign = "left" | "center" | "right";
4
4
  /**
5
- * 根据列语义和当前表格数据解析单元格对齐方式。
5
+ * 根据列显式配置和组件类型解析单元格对齐方式。
6
6
  *
7
- * 显式 align 始终优先;字典、枚举及预置状态列居中,
8
- * 金额列和纯数字列右对齐,其余内容左对齐。空表时会先按列语义返回默认值,
9
- * 数据加载后由响应式表格重新解析。
7
+ * 显式 align 始终优先;选择列、状态列和图片列默认居中,金额列默认右对齐,
8
+ * 字典与枚举列默认居中。普通字段不根据字段名或实际数据猜测,默认左对齐。
10
9
  */
11
- export declare function resolveTableColumnAlign(column: ColumnProps, rows?: Record<string, any>[]): TableAlign;
10
+ export declare function resolveTableColumnAlign(column: ColumnProps): TableAlign;
@@ -1,6 +1,6 @@
1
1
  import service from "../../../../utils/request";
2
2
  import type { SelectOptionResponse } from "../../../../rpc/common/v1/common";
3
- import type { OptionBaseTenantRequest } from "../../../../rpc/system/admin/v1/base_tenant";
3
+ import type { OptionBaseTenantRequest, PageBaseTenantRequest, PageBaseTenantResponse } from "../../../../rpc/system/admin/v1/base_tenant";
4
4
 
5
5
  const BASE_TENANT_URL = "/v1/admin/base/tenant";
6
6
 
@@ -14,6 +14,15 @@ export class BaseTenantServiceImpl {
14
14
  params: request
15
15
  });
16
16
  }
17
+
18
+ /** 查询租户分页列表,供编码筛选和名称解析使用。 */
19
+ PageBaseTenant(request: PageBaseTenantRequest): Promise<PageBaseTenantResponse> {
20
+ return service<PageBaseTenantRequest, PageBaseTenantResponse>({
21
+ url: BASE_TENANT_URL,
22
+ method: "get",
23
+ params: request
24
+ });
25
+ }
17
26
  }
18
27
 
19
28
  /** defBaseTenantService 管理端运行壳租户服务实例。 */
@@ -42,6 +42,8 @@
42
42
  </el-option>
43
43
  </el-select>
44
44
 
45
+ <TenantSelect v-else-if="field.component === 'tenant-select'" v-model="fieldValue" v-bind="fieldProps" />
46
+
45
47
  <el-radio-group v-else-if="field.component === 'radio-group'" v-model="fieldValue" v-bind="fieldProps">
46
48
  <el-radio v-for="option in fieldOptions" :key="String(option.value)" :value="option.value" :disabled="option.disabled">
47
49
  {{ option.label }}
@@ -96,6 +98,7 @@ import { computed, defineAsyncComponent } from "vue";
96
98
  import type { UploadUserFile } from "element-plus";
97
99
  import type { ProFormField, ProFormOption } from "../../../components/ProForm/interface";
98
100
  import Dict from "../../../components/Dict/index.vue";
101
+ import TenantSelect from "../../../components/TenantSelect/index.vue";
99
102
 
100
103
  // 非基础表单控件按需加载,避免 ProForm 基础包携带上传、富文本、Cron 等重组件。
101
104
  const CronExpression = defineAsyncComponent(() => import("../../../components/CronExpression/index.vue"));
@@ -12,6 +12,7 @@ export type ProFormComponentType =
12
12
  | "switch"
13
13
  | "checkbox"
14
14
  | "select"
15
+ | "tenant-select"
15
16
  | "dict"
16
17
  | "radio-group"
17
18
  | "checkbox-group"
@@ -266,8 +266,8 @@ const processTableData = computed(() => {
266
266
  );
267
267
  });
268
268
 
269
- /** 解析 ProTable 列对齐方式,随当前页数据类型变化。 */
270
- const resolveColumnAlign = (column: ColumnProps) => resolveTableColumnAlign(column, processTableData.value);
269
+ /** 解析 ProTable 列对齐方式,普通字段不根据字段名或行数据推断。 */
270
+ const resolveColumnAlign = (column: ColumnProps) => resolveTableColumnAlign(column);
271
271
 
272
272
  /** 数据加载完成后按传入行 Key 恢复多选状态,用于页面重建后的选择态续接。 */
273
273
  function restoreSelectedRows() {
@@ -24,12 +24,14 @@ export type SearchType =
24
24
  | "input"
25
25
  | "input-number"
26
26
  | "select"
27
+ | "tenant-select"
27
28
  | "select-v2"
28
29
  | "tree-select"
29
30
  | "cascader"
30
31
  | "date-picker"
31
32
  | "time-picker"
32
33
  | "time-select"
34
+ | "radio"
33
35
  | "switch"
34
36
  | "slider";
35
37
 
@@ -10,12 +10,34 @@
10
10
  />
11
11
  <ElSelect
12
12
  v-else-if="column.search?.el === 'select' && !column.search?.render"
13
- v-bind="{ ...handleSearchProps, ...placeholder, searchParam: _searchParam, clearable }"
13
+ v-bind="{ ...handleSearchProps, ...placeholder, clearable }"
14
14
  v-model="_searchParam[column.search?.key ?? handleProp(column.prop!)]"
15
15
  :style="handleSearchStyle"
16
16
  >
17
17
  <el-option v-for="(col, index) in columnEnum" :key="index" :label="col[fieldNames.label]" :value="col[fieldNames.value]" />
18
18
  </ElSelect>
19
+ <ElRadioGroup
20
+ v-else-if="column.search?.el === 'radio' && !column.search?.render"
21
+ v-bind="handleRadioProps"
22
+ v-model="_searchParam[column.search?.key ?? handleProp(column.prop!)]"
23
+ :style="handleSearchStyle"
24
+ >
25
+ <component
26
+ :is="radioOptionType === 'button' ? ElRadioButton : ElRadio"
27
+ v-for="(col, index) in columnEnum"
28
+ :key="index"
29
+ :value="col[fieldNames.value]"
30
+ :disabled="col.disabled"
31
+ >
32
+ {{ col[fieldNames.label] }}
33
+ </component>
34
+ </ElRadioGroup>
35
+ <TenantSelect
36
+ v-else-if="column.search?.el === 'tenant-select' && !column.search?.render"
37
+ v-bind="{ ...handleSearchProps, ...placeholder, clearable }"
38
+ v-model="_searchParam[column.search?.key ?? handleProp(column.prop!)]"
39
+ :style="handleSearchStyle"
40
+ />
19
41
  <component
20
42
  v-else
21
43
  :is="searchComponent"
@@ -38,6 +60,9 @@ import {
38
60
  ElDatePicker,
39
61
  ElInput,
40
62
  ElInputNumber,
63
+ ElRadio,
64
+ ElRadioButton,
65
+ ElRadioGroup,
41
66
  ElSelect,
42
67
  ElSelectV2,
43
68
  ElSlider,
@@ -47,6 +72,7 @@ import {
47
72
  ElTreeSelect
48
73
  } from "element-plus";
49
74
  import Dict from "../../../components/Dict/index.vue";
75
+ import TenantSelect from "../../../components/TenantSelect/index.vue";
50
76
  import { handleProp } from "../../../utils";
51
77
  import { ColumnProps, SearchType } from "../../../components/ProTable/interface";
52
78
  import { useLocaleStore } from "../../../locales";
@@ -67,12 +93,14 @@ const searchComponentMap: Record<SearchType, Component> = {
67
93
  input: ElInput,
68
94
  "input-number": ElInputNumber,
69
95
  select: ElSelect,
96
+ "tenant-select": TenantSelect,
70
97
  "select-v2": ElSelectV2,
71
98
  "tree-select": ElTreeSelect,
72
99
  cascader: ElCascader,
73
100
  "date-picker": ElDatePicker,
74
101
  "time-picker": ElTimePicker,
75
102
  "time-select": ElTimeSelect,
103
+ radio: ElRadioGroup,
76
104
  switch: ElSwitch,
77
105
  slider: ElSlider
78
106
  };
@@ -149,6 +177,15 @@ const handleSearchProps = computed(() => {
149
177
  return searchProps;
150
178
  });
151
179
 
180
+ /** 处理 radio 选项类型,避免将渲染辅助参数透传给 Element Plus。 */
181
+ const radioOptionType = computed(() => (props.column.search?.props?.optionType === "button" ? "button" : "default"));
182
+
183
+ /** 过滤 radio 专用渲染参数后透传给 radio-group。 */
184
+ const handleRadioProps = computed(() => {
185
+ const { optionType: _optionType, ...searchProps } = props.column.search?.props ?? {};
186
+ return searchProps;
187
+ });
188
+
152
189
  // 处理默认 placeholder
153
190
  const placeholder = computed(() => {
154
191
  const search = props.column.search;
@@ -168,6 +205,6 @@ const placeholder = computed(() => {
168
205
  // 是否有清除按钮 (当搜索项有默认值时,清除按钮不显示)
169
206
  const clearable = computed(() => {
170
207
  const search = props.column.search;
171
- return search?.props?.clearable ?? (search?.defaultValue == null || search?.defaultValue == undefined);
208
+ return search?.props?.clearable ?? (search?.defaultValue == null || false);
172
209
  });
173
210
  </script>
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <el-select
3
+ v-if="isDefaultTenant"
4
+ :model-value="modelValue"
5
+ v-bind="$attrs"
6
+ :loading="isLoading"
7
+ @update:model-value="value => emit('update:modelValue', value)"
8
+ @change="value => emit('change', value)"
9
+ >
10
+ <el-option v-for="option in options" :key="String(option.value)" :label="option.label" :value="option.value" :disabled="option.disabled" />
11
+ </el-select>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { computed, onMounted, ref } from "vue";
16
+ import type { SelectOptionResponse_Option } from "../../rpc/common/v1/common";
17
+ import { useTenantScope } from "../../tenant";
18
+
19
+ defineOptions({ name: "TenantSelect", inheritAttrs: false });
20
+
21
+ /** 租户选择组件属性。 */
22
+ interface TenantSelectProps {
23
+ /** 当前选择的租户。 */
24
+ modelValue?: number | string;
25
+ /** 外部传入的租户选项。 */
26
+ options?: SelectOptionResponse_Option[];
27
+ /** 是否显示加载状态。 */
28
+ loading?: boolean;
29
+ }
30
+
31
+ const props = withDefaults(defineProps<TenantSelectProps>(), {
32
+ modelValue: undefined,
33
+ options: undefined,
34
+ loading: false
35
+ });
36
+ const emit = defineEmits<{
37
+ /** 更新当前租户。 */
38
+ (event: "update:modelValue", value: number | string | undefined): void;
39
+ /** 租户发生变化。 */
40
+ (event: "change", value: number | string | undefined): void;
41
+ }>();
42
+
43
+ const { isDefaultTenant, tenantOptions, loadTenantOptions } = useTenantScope();
44
+ const internalLoading = ref(false);
45
+ const options = computed(() => props.options ?? tenantOptions.value);
46
+ const isLoading = computed(() => props.loading || internalLoading.value);
47
+
48
+ onMounted(async () => {
49
+ if (!isDefaultTenant.value || props.options) return;
50
+ internalLoading.value = true;
51
+ try {
52
+ await loadTenantOptions();
53
+ } finally {
54
+ internalLoading.value = false;
55
+ }
56
+ });
57
+ </script>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <span>{{ label }}</span>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { computed } from "vue";
7
+ import type { SelectOptionResponse_Option } from "../../rpc/common/v1/common";
8
+ import { useTenantScope } from "../../tenant";
9
+
10
+ defineOptions({ name: "TenantText" });
11
+
12
+ /** 租户展示组件属性。 */
13
+ interface TenantTextProps {
14
+ /** 租户ID。 */
15
+ tenantId?: number | string;
16
+ /** 已知的租户名称。 */
17
+ tenantName?: string;
18
+ /** 外部传入的租户选项。 */
19
+ options?: SelectOptionResponse_Option[];
20
+ }
21
+
22
+ const props = defineProps<TenantTextProps>();
23
+ const { resolveTenantLabel } = useTenantScope();
24
+ const label = computed(() => {
25
+ if (props.tenantName) return props.tenantName;
26
+ return resolveTenantLabel({ tenant_id: props.tenantId }, "tenant_id", props.options);
27
+ });
28
+ </script>
@@ -10,3 +10,4 @@ export { defLanguageService } from "./api/base/v1/language";
10
10
  export { setAdminDocumentTitle } from "./documentTitle";
11
11
  export { kratosAdminModule } from "./modules/kratosAdmin";
12
12
  export { bootstrapAdminApp } from "./bootstrap";
13
+ export { refreshAdminRuntimeConfig } from "./stores/modules/config";
@@ -335,6 +335,7 @@
335
335
  "common.field.sort": "Sort order",
336
336
  "common.field.status": "Status",
337
337
  "common.field.tenant": "Tenant",
338
+ "common.field.project": "Project",
338
339
  "common.field.updated_at": "Updated at",
339
340
  "common.field.user": "User",
340
341
  "common.message.create_success": "{resource} created",
@@ -37,6 +37,8 @@ export const LOCALE_STORAGE_KEY = "kratos-admin:locale";
37
37
  const mutableLocale = ref<SupportedLocale>(DEFAULT_LOCALE);
38
38
  const mutableLanguageOptions = ref<LocaleOption[]>([]);
39
39
  const localeChangeHandlers = new Set<() => void | Promise<void>>();
40
+ const defaultLocaleMessages = new Map<SupportedLocale, LocaleMessages>();
41
+ const customLocaleMessages = new Map<SupportedLocale, LocaleMessages>();
40
42
 
41
43
  /** 管理端 Vue I18n 实例。 */
42
44
  export const adminI18n = createI18n({
@@ -100,7 +102,25 @@ export function registerLocaleMessages(modules: AdminModule[]): void {
100
102
  });
101
103
  });
102
104
 
103
- SUPPORTED_LOCALES.forEach(locale => adminI18n.global.setLocaleMessage(locale, merged.get(locale) ?? {}));
105
+ SUPPORTED_LOCALES.forEach(locale => {
106
+ const messages = { ...(merged.get(locale) ?? {}) };
107
+ defaultLocaleMessages.set(locale, messages);
108
+ customLocaleMessages.set(locale, messages);
109
+ adminI18n.global.setLocaleMessage(locale, messages);
110
+ });
111
+ }
112
+
113
+ /** 应用全部语言的数据库自定义翻译覆盖项。 */
114
+ export function applyCustomLocaleMessages(customs: Array<{ locale?: string; key?: string; value?: string }>): void {
115
+ SUPPORTED_LOCALES.forEach(locale => {
116
+ const messages = { ...(defaultLocaleMessages.get(locale) ?? {}) };
117
+ customs.forEach(item => {
118
+ if (!item.locale || normalizeLocale(item.locale) !== locale || !item.key || !item.value || !Object.prototype.hasOwnProperty.call(messages, item.key)) return;
119
+ messages[item.key] = item.value;
120
+ });
121
+ customLocaleMessages.set(locale, messages);
122
+ adminI18n.global.setLocaleMessage(locale, messages);
123
+ });
104
124
  }
105
125
 
106
126
  /** 初始化持久化语言偏好并同步日期库。 */
@@ -177,6 +197,11 @@ export function getLocaleText(locale: SupportedLocale, key: string): string {
177
197
  return messages[key] ?? fallbackMessages[key] ?? "";
178
198
  }
179
199
 
200
+ /** 从代码语言包读取未应用数据库覆盖的默认文案。 */
201
+ export function getDefaultLocaleText(locale: SupportedLocale, key: string): string {
202
+ return defaultLocaleMessages.get(locale)?.[key] ?? defaultLocaleMessages.get(DEFAULT_LOCALE)?.[key] ?? "";
203
+ }
204
+
180
205
  /** 管理端响应式语言状态。 */
181
206
  export function useLocaleStore() {
182
207
  return {
@@ -203,6 +228,7 @@ function fallbackNativeLanguageName(languageCode: SupportedLocale): string {
203
228
  }
204
229
 
205
230
  function applyLocale(locale: SupportedLocale): void {
231
+ adminI18n.global.setLocaleMessage(locale, { ...(customLocaleMessages.get(locale) ?? defaultLocaleMessages.get(locale) ?? {}) });
206
232
  mutableLocale.value = locale;
207
233
  adminI18n.global.locale.value = locale;
208
234
  const dayjsLocale = DAYJS_LOCALE_MAP[locale];
@@ -335,6 +335,7 @@
335
335
  "common.field.sort": "並べ替え",
336
336
  "common.field.status": "ステータス",
337
337
  "common.field.tenant": "テナント",
338
+ "common.field.project": "プロジェクト",
338
339
  "common.field.updated_at": "更新時間",
339
340
  "common.field.user": "ユーザー",
340
341
  "common.message.create_success": "{resource} が正常に追加されました",
@@ -335,6 +335,7 @@
335
335
  "common.field.sort": "排序",
336
336
  "common.field.status": "状态",
337
337
  "common.field.tenant": "租户",
338
+ "common.field.project": "项目",
338
339
  "common.field.updated_at": "更新时间",
339
340
  "common.field.user": "用户",
340
341
  "common.message.create_success": "新增{resource}成功",
@@ -335,6 +335,7 @@
335
335
  "common.field.sort": "排序",
336
336
  "common.field.status": "狀態",
337
337
  "common.field.tenant": "租戶",
338
+ "common.field.project": "專案",
338
339
  "common.field.updated_at": "更新時間",
339
340
  "common.field.user": "使用者",
340
341
  "common.message.create_success": "新增{resource}成功",
@@ -40,6 +40,18 @@ export interface GetConfigResponse {
40
40
  configs: ConfigItem[];
41
41
  /** AI 助手是否可用 */
42
42
  ai_enabled: boolean;
43
+ /** 全部语言的自定义国际化覆盖项 */
44
+ i18n_customs: I18nCustomItem[];
45
+ }
46
+
47
+ /** 全部语言的自定义国际化覆盖项。 */
48
+ export interface I18nCustomItem {
49
+ /** 语言区域 */
50
+ locale: string;
51
+ /** 国际化语言键 */
52
+ key: string;
53
+ /** 自定义翻译内容 */
54
+ value: string;
43
55
  }
44
56
 
45
57
  /** Base系统配置公共服务 */
@@ -51,6 +51,16 @@ export interface CreateBaseTenantRequest {
51
51
  base_tenant: BaseTenantForm | undefined;
52
52
  }
53
53
 
54
+ /** 创建租户响应参数 */
55
+ export interface CreateBaseTenantResponse {
56
+ /** 租户编号 */
57
+ tenant_code: string;
58
+ /** 租户管理员账号 */
59
+ admin_user_name: string;
60
+ /** 租户管理员初始密码,仅在随机生成时返回 */
61
+ initial_password: string;
62
+ }
63
+
54
64
  /** 更新租户请求参数 */
55
65
  export interface UpdateBaseTenantRequest {
56
66
  /** 租户表单 */
@@ -124,7 +134,7 @@ export interface BaseTenantService {
124
134
  /** 查询租户 */
125
135
  GetBaseTenant(request: GetBaseTenantRequest): Promise<BaseTenantForm>;
126
136
  /** 创建租户 */
127
- CreateBaseTenant(request: CreateBaseTenantRequest): Promise<Empty>;
137
+ CreateBaseTenant(request: CreateBaseTenantRequest): Promise<CreateBaseTenantResponse>;
128
138
  /** 更新租户 */
129
139
  UpdateBaseTenant(request: UpdateBaseTenantRequest): Promise<Empty>;
130
140
  /** 删除租户 */
@@ -4,6 +4,7 @@ import { BaseConfigSite } from "../../rpc/base/v1/config";
4
4
  import type { LoginCaptchaConfig, SiteConfigState, SiteDisplayConfig } from "../../stores/interface";
5
5
  import defaultLogoUrl from "../../assets/images/logo.svg";
6
6
  import defaultBackgroundUrl from "../../assets/images/login_left.png";
7
+ import { applyCustomLocaleMessages } from "../../locales";
7
8
 
8
9
  const CAPTCHA_TYPE_KEY = "captchaType";
9
10
  const SHOW_TENANT_CODE_KEY = "showTenantCode";
@@ -117,6 +118,7 @@ export const useConfigStore = defineStore("admin-config", {
117
118
  });
118
119
  const configMap = buildConfigMap(configResponse.configs ?? []);
119
120
 
121
+ applyCustomLocaleMessages(configResponse.i18n_customs ?? []);
120
122
  this.setDisplayConfig(normalizeSiteDisplayConfig(configMap));
121
123
  this.setLoginCaptchaConfig(normalizeLoginCaptchaConfig(configMap));
122
124
  this.showTenantCode = !["false", "0"].includes((configMap[SHOW_TENANT_CODE_KEY] ?? "true").toLowerCase());
@@ -126,3 +128,8 @@ export const useConfigStore = defineStore("admin-config", {
126
128
  }
127
129
  }
128
130
  });
131
+
132
+ /** 刷新当前管理端语言和站点运行配置。 */
133
+ export async function refreshAdminRuntimeConfig(): Promise<void> {
134
+ await useConfigStore().loadDisplayConfig();
135
+ }
@@ -1 +1,158 @@
1
+ import { computed, ref, type ComputedRef, type Ref } from "vue";
2
+ import type { ProFormField } from "./components/ProForm/interface";
3
+ import type { ColumnProps, EnumProps, RenderScope, SearchType } from "./components/ProTable/interface";
4
+ import { defBaseTenantService } from "./api/system/admin/v1/base_tenant";
5
+ import type { SelectOptionResponse_Option } from "./rpc/common/v1/common";
6
+ import { useUserStore } from "./stores/runtime";
7
+ import { DEFAULT_TENANT_CODE, requestTenantOptions as requestTenantOptionData } from "./utils/tenant";
8
+
1
9
  export * from "./utils/tenant";
10
+
11
+ /** 查询租户编码筛选选项,值为租户编码而不是租户 ID。 */
12
+ export async function requestTenantCodeOptions() {
13
+ const options: EnumProps[] = [];
14
+ const pageSize = 100;
15
+ for (let pageNum = 1; ; pageNum += 1) {
16
+ const response = await defBaseTenantService.PageBaseTenant({ code: "", name: "", page_num: pageNum, page_size: pageSize });
17
+ const tenants = response.base_tenants ?? [];
18
+ options.push(...tenants.map(tenant => ({ value: tenant.code, label: tenant.name })));
19
+ if (options.length >= response.total || tenants.length === 0) break;
20
+ }
21
+ return { data: options };
22
+ }
23
+
24
+ /** 租户上下文状态。 */
25
+ export interface TenantScope {
26
+ /** 当前账号是否为默认租户。 */
27
+ isDefaultTenant: ComputedRef<boolean>;
28
+ /** 当前账号可用的租户选项。 */
29
+ tenantOptions: Ref<SelectOptionResponse_Option[]>;
30
+ /** 租户选项名称映射。 */
31
+ tenantNameMap: ComputedRef<Map<string, string>>;
32
+ /** 加载默认租户可选择的租户。 */
33
+ loadTenantOptions: (includeCurrentTenant?: boolean) => Promise<void>;
34
+ /** 将页面租户筛选值转换成后端请求值。 */
35
+ toRequestTenantId: (value: unknown) => number | undefined;
36
+ /** 创建默认租户可见的表单字段。 */
37
+ tenantFormField: (options: TenantFormFieldOptions) => ProFormField;
38
+ /** 创建默认租户可见的表格租户列。 */
39
+ tenantColumns: (options: TenantColumnOptions) => ColumnProps[];
40
+ /** 将行数据转换成租户名称。 */
41
+ resolveTenantLabel: (row: Record<string, any>, field?: string, options?: SelectOptionResponse_Option[]) => string;
42
+ }
43
+
44
+ /** 租户表单字段配置。 */
45
+ export interface TenantFormFieldOptions {
46
+ /** 字段绑定名称。 */
47
+ prop?: string;
48
+ /** 字段标签。 */
49
+ label: string;
50
+ /** 表单控件附加属性。 */
51
+ props?: Record<string, any>;
52
+ /** 是否允许编辑态修改租户。 */
53
+ disabledOnEdit?: boolean;
54
+ }
55
+
56
+ /** 租户表格列配置。 */
57
+ export interface TenantColumnOptions {
58
+ /** 列绑定字段。 */
59
+ prop?: string;
60
+ /** 列标题。 */
61
+ label: string;
62
+ /** 查询字段排序。 */
63
+ order?: number;
64
+ /** 查询参数字段,默认使用列字段。 */
65
+ searchKey?: string;
66
+ /** 列最小宽度。 */
67
+ minWidth?: number | string;
68
+ /** 是否在表格中显示,隐藏时仍可保留查询项。 */
69
+ isShow?: boolean;
70
+ /** 是否允许在列设置中切换。 */
71
+ isSetting?: boolean;
72
+ /** 是否带租户查询项。 */
73
+ searchable?: boolean;
74
+ /** 自定义租户选项来源。 */
75
+ enum?: ColumnProps["enum"];
76
+ /** 查询控件类型。 */
77
+ searchEl?: SearchType;
78
+ /** 自定义单元格展示。 */
79
+ render?: (scope: RenderScope) => ReturnType<NonNullable<ColumnProps["render"]>>;
80
+ }
81
+
82
+ /** 创建租户范围能力,统一默认租户和普通租户的前端行为。 */
83
+ export function useTenantScope(): TenantScope {
84
+ const userStore = useUserStore();
85
+ const isDefaultTenant = computed(() => userStore.userInfo.tenant_code === DEFAULT_TENANT_CODE);
86
+ const tenantOptions = ref<SelectOptionResponse_Option[]>([]);
87
+ const tenantNameMap = computed(() => new Map(tenantOptions.value.map(item => [String(item.value), item.label] as const)));
88
+
89
+ async function loadTenantOptions(includeCurrentTenant = false) {
90
+ if (!includeCurrentTenant && !isDefaultTenant.value) return;
91
+ if (tenantOptions.value.length) return;
92
+ const response = await defBaseTenantService.OptionBaseTenant({ keyword: "" });
93
+ tenantOptions.value = response.list ?? [];
94
+ }
95
+
96
+ function toRequestTenantId(value: unknown) {
97
+ if (!isDefaultTenant.value || value === undefined || value === null || value === "") return undefined;
98
+ const tenantId = Number(value);
99
+ return Number.isFinite(tenantId) && tenantId > 0 ? tenantId : undefined;
100
+ }
101
+
102
+ function tenantFormField(options: TenantFormFieldOptions): ProFormField {
103
+ const prop = options.prop ?? "tenant_id";
104
+ return {
105
+ prop,
106
+ label: options.label,
107
+ component: "tenant-select",
108
+ props: (model: Record<string, any>) => ({
109
+ filterable: true,
110
+ ...(options.disabledOnEdit ? { disabled: Boolean(model.id) } : {}),
111
+ ...options.props
112
+ }),
113
+ visible: () => isDefaultTenant.value,
114
+ };
115
+ }
116
+
117
+ function tenantColumns(options: TenantColumnOptions): ColumnProps[] {
118
+ if (!isDefaultTenant.value) return [];
119
+ const prop = options.prop ?? "tenant_id";
120
+ return [
121
+ {
122
+ prop,
123
+ label: options.label,
124
+ minWidth: options.minWidth ?? 140,
125
+ isShow: options.isShow,
126
+ isSetting: options.isSetting,
127
+ align: "left",
128
+ showOverflowTooltip: true,
129
+ search:
130
+ options.searchable === false
131
+ ? undefined
132
+ : { el: options.searchEl ?? "tenant-select", key: options.searchKey ?? prop, props: { filterable: true }, order: options.order },
133
+ enum: options.enum ?? requestTenantOptionData,
134
+ render: options.render ?? (scope => resolveTenantLabel(scope.row, prop))
135
+ }
136
+ ];
137
+ }
138
+
139
+ function resolveTenantLabel(row: Record<string, any>, field = "tenant_id", options = tenantOptions.value) {
140
+ const directLabel = row.tenant_name ?? row[`${field}_name`];
141
+ if (directLabel) return String(directLabel);
142
+ const value = row[field];
143
+ if (value === undefined || value === null || value === "") return "";
144
+ const nameMap = new Map(options.map(item => [String(item.value), item.label] as const));
145
+ return nameMap.get(String(value)) ?? String(value);
146
+ }
147
+
148
+ return {
149
+ isDefaultTenant,
150
+ tenantOptions,
151
+ tenantNameMap,
152
+ loadTenantOptions,
153
+ toRequestTenantId,
154
+ tenantFormField,
155
+ tenantColumns,
156
+ resolveTenantLabel
157
+ };
158
+ }
@@ -3,53 +3,20 @@ import type { ColumnProps, TypeProps } from "../components/ProTable/interface";
3
3
  /** 表格单元格支持的对齐方式。 */
4
4
  export type TableAlign = "left" | "center" | "right";
5
5
 
6
- /** 获取表格行的列值,支持点号分隔的多级字段。 */
7
- function getTableCellValue(row: Record<string, any>, prop: string) {
8
- return prop.split(".").reduce((value, key) => (value == null ? undefined : value[key]), row as any);
9
- }
10
-
11
- /** 判断值是否为可用于数值对齐的 Number 类型。 */
12
- function isPureNumber(value: unknown) {
13
- return typeof value === "number" && Number.isFinite(value);
14
- }
15
-
16
- /** 判断列字段名是否表示时间值。 */
17
- function isTimeColumn(column: ColumnProps) {
18
- const prop = column.prop?.toLowerCase();
19
- return !!prop && /(^|[._])(created|updated|deleted|expires|scheduled|execute|start|end|login|logout|last)[._]?at?$/.test(prop);
20
- }
21
-
22
- /** 判断列字段名是否表示数值。 */
23
- function isNumericColumn(column: ColumnProps) {
24
- const prop = column.prop?.toLowerCase();
25
- return !!prop && /(^|[._])(id|sort|size|count|total|rows|priority|status_code|latency|duration|process_time|ttl|retention|attempt|qps|rate|number|num|amount|days|minutes|seconds|ms)$/.test(prop);
26
- }
27
-
28
6
  /**
29
- * 根据列语义和当前表格数据解析单元格对齐方式。
7
+ * 根据列显式配置和组件类型解析单元格对齐方式。
30
8
  *
31
- * 显式 align 始终优先;字典、枚举及预置状态列居中,
32
- * 金额列和纯数字列右对齐,其余内容左对齐。空表时会先按列语义返回默认值,
33
- * 数据加载后由响应式表格重新解析。
9
+ * 显式 align 始终优先;选择列、状态列和图片列默认居中,金额列默认右对齐,
10
+ * 字典与枚举列默认居中。普通字段不根据字段名或实际数据猜测,默认左对齐。
34
11
  */
35
- export function resolveTableColumnAlign(column: ColumnProps, rows: Record<string, any>[] = []): TableAlign {
12
+ export function resolveTableColumnAlign(column: ColumnProps): TableAlign {
36
13
  if (column.align === "left" || column.align === "center" || column.align === "right") return column.align;
37
14
 
38
15
  const centeredTypes: TypeProps[] = ["selection", "radio", "index", "expand", "sort"];
39
16
  if (centeredTypes.includes(column.type as TypeProps)) return "center";
40
17
  if (column.cellType === "actions" || column.cellType === "status" || column.cellType === "image") return "center";
41
18
  if (column.cellType === "money") return "right";
42
- if (column.dictCode || column.tag) return "center";
43
- if (column.enum) return "center";
44
- if (isTimeColumn(column)) return "center";
45
- if (isNumericColumn(column)) return "right";
46
-
47
- if (column.prop) {
48
- const values = rows
49
- .map(row => getTableCellValue(row, column.prop as string))
50
- .filter(value => value !== undefined && value !== null && value !== "");
51
- if (values.length && values.every(isPureNumber)) return "right";
52
- }
19
+ if (column.dictCode || column.tag || column.enum) return "center";
53
20
 
54
21
  return "left";
55
22
  }
@@ -551,10 +551,10 @@ const getOauthLoginRedirectURL = () => {
551
551
  return resolveFrontendRouteURL(router, { path: route.path, query });
552
552
  };
553
553
 
554
- /** 查询配置启用的三方登录方式。 */
554
+ /** 查询配置启用的三方登录方式,空列表字段省略时不展示三方登录。 */
555
555
  const loadOauthProviders = async () => {
556
556
  const result = await defOauthService.ListOauthProvider({});
557
- oauthProviders.value = result.providers.map(withOauthProviderDisplay);
557
+ oauthProviders.value = (result.providers ?? []).map(withOauthProviderDisplay);
558
558
  };
559
559
 
560
560
  /** 发起三方登录授权跳转。 */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@liujitcn/kratos-admin-core",
3
3
  "private": false,
4
- "version": "0.0.48",
4
+ "version": "0.0.50",
5
5
  "type": "module",
6
6
  "description": "kratos-admin frontend runtime and base modules",
7
7
  "author": {
@@ -153,6 +153,10 @@
153
153
  "types": "./dist/package/declarations/src/components/ProTable/interface/index.d.ts",
154
154
  "default": "./dist/package/src/components/ProTable/interface/index.ts"
155
155
  },
156
+ "./components/SearchForm/index.vue": {
157
+ "types": "./dist/package/declarations/src/components/SearchForm/index.vue.d.ts",
158
+ "default": "./dist/package/src/components/SearchForm/index.vue"
159
+ },
156
160
  "./components/SelectIcon/index.vue": {
157
161
  "types": "./dist/package/declarations/src/components/SelectIcon/index.vue.d.ts",
158
162
  "default": "./dist/package/src/components/SelectIcon/index.vue"
@@ -161,6 +165,14 @@
161
165
  "types": "./dist/package/declarations/src/components/TreeFilter/index.vue.d.ts",
162
166
  "default": "./dist/package/src/components/TreeFilter/index.vue"
163
167
  },
168
+ "./components/TenantSelect/index.vue": {
169
+ "types": "./dist/package/declarations/src/components/TenantSelect/index.vue.d.ts",
170
+ "default": "./dist/package/src/components/TenantSelect/index.vue"
171
+ },
172
+ "./components/TenantText/index.vue": {
173
+ "types": "./dist/package/declarations/src/components/TenantText/index.vue.d.ts",
174
+ "default": "./dist/package/src/components/TenantText/index.vue"
175
+ },
164
176
  "./components/Upload/Img.vue": {
165
177
  "types": "./dist/package/declarations/src/components/Upload/Img.vue.d.ts",
166
178
  "default": "./dist/package/src/components/Upload/Img.vue"