@liujitcn/kratos-admin-core 0.0.49 → 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.
- package/README.md +4 -1
- package/dist/package/declarations/src/api/system/admin/v1/base_tenant.d.ts +3 -1
- package/dist/package/declarations/src/components/Dialog/FormDialog.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/Grid/components/GridItem.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/ProForm/components/ProFormItem.vue.d.ts +4 -4
- package/dist/package/declarations/src/components/ProForm/index.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/ProForm/interface/index.d.ts +1 -1
- package/dist/package/declarations/src/components/ProTable/interface/index.d.ts +1 -1
- package/dist/package/declarations/src/components/SearchForm/components/SearchFormItem.vue.d.ts +2 -2
- package/dist/package/declarations/src/components/TenantSelect/index.vue.d.ts +23 -0
- package/dist/package/declarations/src/components/TenantText/index.vue.d.ts +13 -0
- package/dist/package/declarations/src/index.d.ts +1 -0
- package/dist/package/declarations/src/locales/generated.d.ts +4 -0
- package/dist/package/declarations/src/locales/index.d.ts +8 -0
- package/dist/package/declarations/src/rpc/base/v1/config.d.ts +11 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_tenant.d.ts +10 -1
- package/dist/package/declarations/src/stores/modules/config.d.ts +2 -0
- package/dist/package/declarations/src/tenant.d.ts +65 -0
- package/dist/package/src/api/system/admin/v1/base_tenant.ts +10 -1
- package/dist/package/src/components/ProForm/components/ProFormItem.vue +3 -0
- package/dist/package/src/components/ProForm/interface/index.ts +1 -0
- package/dist/package/src/components/ProTable/interface/index.ts +2 -0
- package/dist/package/src/components/SearchForm/components/SearchFormItem.vue +39 -2
- package/dist/package/src/components/TenantSelect/index.vue +57 -0
- package/dist/package/src/components/TenantText/index.vue +28 -0
- package/dist/package/src/index.ts +1 -0
- package/dist/package/src/locales/en-US.json +1 -0
- package/dist/package/src/locales/index.ts +27 -1
- package/dist/package/src/locales/ja-JP.json +1 -0
- package/dist/package/src/locales/zh-CN.json +1 -0
- package/dist/package/src/locales/zh-TW.json +1 -0
- package/dist/package/src/rpc/base/v1/config.ts +12 -0
- package/dist/package/src/rpc/system/admin/v1/base_tenant.ts +11 -1
- package/dist/package/src/stores/modules/config.ts +7 -0
- package/dist/package/src/tenant.ts +157 -0
- package/dist/package/src/views/login/components/LoginForm.vue +2 -2
- 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
|
-
},
|
|
10
|
+
}, __VLS_139: string, __VLS_140: {
|
|
11
11
|
option: import("element-plus").TransferDataItem;
|
|
12
12
|
field: ProFormField;
|
|
13
13
|
model: Record<string, any>;
|
|
14
|
-
},
|
|
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
|
|
21
|
+
[K in NonNullable<typeof __VLS_139>]?: (props: typeof __VLS_140) => any;
|
|
22
22
|
} & {
|
|
23
|
-
[K in NonNullable<typeof
|
|
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[];
|
package/dist/package/declarations/src/components/SearchForm/components/SearchFormItem.vue.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ interface SearchFormItem {
|
|
|
6
6
|
[key: string]: any;
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
-
declare var
|
|
9
|
+
declare var __VLS_46: {};
|
|
10
10
|
type __VLS_Slots = {} & {
|
|
11
|
-
default?: (props: typeof
|
|
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<
|
|
114
|
+
CreateBaseTenant(request: CreateBaseTenantRequest): Promise<CreateBaseTenantResponse>;
|
|
106
115
|
/** 更新租户 */
|
|
107
116
|
UpdateBaseTenant(request: UpdateBaseTenantRequest): Promise<Empty>;
|
|
108
117
|
/** 删除租户 */
|
|
@@ -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;
|
|
@@ -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"));
|
|
@@ -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,
|
|
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 ||
|
|
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 =>
|
|
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<
|
|
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
|
+
}
|
|
@@ -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.
|
|
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"
|