@liujitcn/kratos-admin-system 0.0.74 → 0.0.75
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 +5 -1
- package/dist/package/declarations/src/components/tenant-project/TenantProjectText.vue.d.ts +11 -1
- package/dist/package/declarations/src/components/tenant-project/tenant-project-catalog.d.ts +42 -0
- package/dist/package/declarations/src/components/tenant-project/tenant-project-manager-data.d.ts +9 -0
- package/dist/package/declarations/src/components/tenant-project/tenant-project-scope.d.ts +63 -0
- package/dist/package/declarations/src/index.d.ts +3 -0
- package/dist/package/src/components/tenant-project/TenantProjectText.vue +16 -3
- package/dist/package/src/components/tenant-project/tenant-project-catalog.ts +139 -0
- package/dist/package/src/components/tenant-project/tenant-project-manager-data.ts +17 -0
- package/dist/package/src/components/tenant-project/tenant-project-scope.ts +156 -0
- package/dist/package/src/index.ts +15 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -48,7 +48,9 @@ packages/modules/system
|
|
|
48
48
|
| `src/components/log/log.ts` | 日志列表列和查询辅助函数。 |
|
|
49
49
|
| `src/components/notification/Notification.vue` | System 提供的站内信顶部工具入口。 |
|
|
50
50
|
| `src/components/tenant-project/TenantProjectSelect.vue` | 租户项目树选择器,默认租户显示“租户 / 项目”,普通租户显示当前租户项目。 |
|
|
51
|
-
| `src/components/tenant-project/TenantProjectText.vue` |
|
|
51
|
+
| `src/components/tenant-project/TenantProjectText.vue` | 租户项目列表展示组件,按当前租户身份自动组合名称;支持仅传项目 ID 并从公共目录解析名称。 |
|
|
52
|
+
| `src/components/tenant-project/tenant-project-catalog.ts` | 租户项目公共目录缓存,按账号加载树或项目选项并维护 ID 到租户/项目名称的映射。 |
|
|
53
|
+
| `src/components/tenant-project/tenant-project-scope.ts` | 租户项目范围组合状态,提供与搜索条件配套的 ProTable 列、范围归一化和展示文本解析。 |
|
|
52
54
|
| `src/components/tenant-project/TenantProjectManager.vue` | 可复用的租户项目管理组件,支持追加列、业务数据加载、操作和作用域插槽。 |
|
|
53
55
|
| `src/components/tenant-project/tenant-project-manager.ts` | 租户项目管理组件的扩展类型、双键上下文和数据合并辅助函数。 |
|
|
54
56
|
| `src/components/tenant-project-grant/ProjectGrantDialog.vue` | 岗位、角色、部门和用户的项目授权弹窗。 |
|
|
@@ -168,6 +170,8 @@ export const adminModules = [systemAdminModule];
|
|
|
168
170
|
|
|
169
171
|
跨模块跳转使用 Vue Router;复用 System 代码时只引用 `package.json#exports` 公开的 npm 子路径。
|
|
170
172
|
|
|
173
|
+
业务列表需要“租户 / 二级资源”查询条件与所属列时,使用根入口导出的 `useTenantProjectScope`:它按当前账号加载公共项目目录(默认租户为租户树,普通租户为项目选项),维护项目 ID 到“租户名 / 项目名”的映射,并返回 `column`(同时包含表格渲染和 `search.render`,列与查询条件天然共用同一份范围状态)和 `normalizeParams`、`projectSelections` 等范围工具。二级资源名称通过 `resourceLabelKey`、`allResourceLabelKey` 指定国际化键,默认使用项目文案,换成门店等资源时只需替换加载目录与国际化键。列表行只需提供项目 ID(可选租户 ID),单元格由 `TenantProjectText` 自动解析为两级文本;导出可复用 `resolveTenantProjectLabel`。
|
|
174
|
+
|
|
171
175
|
租户项目管理组件通过 `TenantProjectManager` 公开。默认租户创建项目时在表单中选择目标租户,普通租户由服务端自动使用当前租户;默认租户查询全部项目,普通租户按项目授权范围查询。外部业务模块可以传入 `extraColumns`、`extraActions` 和 `loadExtraData`:扩展列可通过 `after` 指定插入到哪个基础字段后面,列的同名表格插槽会沿用该位置,未配置时默认插入备注后面,例如 `{ prop: "address", after: "name" }` 配合 `#address` 插槽即可把内容放到项目名称后面;数据加载器接收当前页的 `tenant_id + project_id` 集合,单条操作回调接收同一组双键以及项目基础数据。组件也会透传表格具名插槽,并向行插槽增加 `tenantProject` 上下文;保存业务配置后可通过组件实例的 `refresh()` 刷新列表。按外部业务字段搜索或排序时,应由外部接口参与分页查询,不能只在当前页加载后过滤。
|
|
172
176
|
|
|
173
177
|
外部项目可以在自己的页面中包装该组件,并通过菜单将项目管理页面的 `component` 指向包装页面:
|
|
@@ -2,7 +2,13 @@ import type { SelectOptionResponse_Option } from "@liujitcn/kratos-admin-core/rp
|
|
|
2
2
|
/** 租户项目展示组件属性。 */
|
|
3
3
|
interface TenantProjectTextProps {
|
|
4
4
|
/** 包含租户和项目字段的列表行。 */
|
|
5
|
-
row
|
|
5
|
+
row?: Record<string, any>;
|
|
6
|
+
/** 仅传项目 ID 时使用的项目 ID。 */
|
|
7
|
+
projectId?: number;
|
|
8
|
+
/** 仅传项目 ID 时使用的租户 ID。 */
|
|
9
|
+
tenantId?: number;
|
|
10
|
+
/** 二级资源名称国际化键,默认使用项目。 */
|
|
11
|
+
resourceLabelKey?: string;
|
|
6
12
|
/** 项目名称字段。 */
|
|
7
13
|
projectField?: string;
|
|
8
14
|
/** 租户名称字段。 */
|
|
@@ -11,6 +17,10 @@ interface TenantProjectTextProps {
|
|
|
11
17
|
tenantOptions?: SelectOptionResponse_Option[];
|
|
12
18
|
}
|
|
13
19
|
declare const __VLS_export: import("vue").DefineComponent<TenantProjectTextProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<TenantProjectTextProps> & Readonly<{}>, {
|
|
20
|
+
tenantId: number;
|
|
21
|
+
projectId: number;
|
|
22
|
+
row: Record<string, any>;
|
|
23
|
+
resourceLabelKey: string;
|
|
14
24
|
projectField: string;
|
|
15
25
|
tenantField: string;
|
|
16
26
|
tenantOptions: SelectOptionResponse_Option[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SelectOptionResponse_Option } from "@liujitcn/kratos-admin-core/rpc/common/v1/common";
|
|
2
|
+
import type { TreeBaseTenantProjectResponse_Option } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_tenant_project";
|
|
3
|
+
/** 租户项目范围。 */
|
|
4
|
+
export type TenantProjectScopeSelection = {
|
|
5
|
+
/** 租户 ID。 */
|
|
6
|
+
tenant_id: number;
|
|
7
|
+
/** 项目 ID。 */
|
|
8
|
+
project_id: number;
|
|
9
|
+
};
|
|
10
|
+
/** 租户项目名称信息。 */
|
|
11
|
+
export type TenantProjectDisplayInfo = {
|
|
12
|
+
/** 租户名称。 */
|
|
13
|
+
tenantName: string;
|
|
14
|
+
/** 项目名称。 */
|
|
15
|
+
projectName: string;
|
|
16
|
+
};
|
|
17
|
+
/** 租户项目表格行范围字段。 */
|
|
18
|
+
export type TenantProjectScopeRow = {
|
|
19
|
+
/** 租户 ID。 */
|
|
20
|
+
tenant_id?: number;
|
|
21
|
+
/** 项目 ID。 */
|
|
22
|
+
project_id?: number;
|
|
23
|
+
/** 租户名称。 */
|
|
24
|
+
tenant_name?: string;
|
|
25
|
+
/** 项目名称。 */
|
|
26
|
+
project_name?: string;
|
|
27
|
+
};
|
|
28
|
+
/** 当前账号租户项目目录。 */
|
|
29
|
+
export type TenantProjectCatalog = {
|
|
30
|
+
/** 租户项目树。 */
|
|
31
|
+
treeOptions: TreeBaseTenantProjectResponse_Option[];
|
|
32
|
+
/** 普通租户项目选项。 */
|
|
33
|
+
projectOptions: SelectOptionResponse_Option[];
|
|
34
|
+
};
|
|
35
|
+
/** 读取当前共享租户项目名称映射。 */
|
|
36
|
+
export declare function getTenantProjectDisplayMap(): Map<string, TenantProjectDisplayInfo>;
|
|
37
|
+
/** 读取项目目录中的名称信息。 */
|
|
38
|
+
export declare function resolveTenantProjectDisplay(tenantId?: number, projectId?: number): TenantProjectDisplayInfo | undefined;
|
|
39
|
+
/** 根据租户项目 ID 生成展示文本。 */
|
|
40
|
+
export declare function resolveTenantProjectLabel(row: TenantProjectScopeRow, isDefaultTenant: boolean): string;
|
|
41
|
+
/** 加载并缓存当前账号可见的租户项目目录。 */
|
|
42
|
+
export declare function loadTenantProjectOptions(isDefaultTenant: boolean, force?: boolean): Promise<TenantProjectCatalog>;
|
package/dist/package/declarations/src/components/tenant-project/tenant-project-manager-data.d.ts
CHANGED
|
@@ -19,6 +19,15 @@ export declare function arrangeTenantProjectColumns<T extends TenantProjectColum
|
|
|
19
19
|
}>): T[];
|
|
20
20
|
/** 创建租户项目扩展数据的稳定键。 */
|
|
21
21
|
export declare function tenantProjectKey(tenantId: number, projectId: number): string;
|
|
22
|
+
/** 租户项目树节点解析结果。 */
|
|
23
|
+
export type TenantProjectValueSelection = {
|
|
24
|
+
/** 租户 ID,选中租户节点或项目节点时返回。 */
|
|
25
|
+
tenant_id?: number;
|
|
26
|
+
/** 项目 ID,仅选中项目节点时返回。 */
|
|
27
|
+
project_id?: number;
|
|
28
|
+
};
|
|
29
|
+
/** 解析租户项目树节点值,租户节点返回租户 ID,项目节点返回租户和项目 ID。 */
|
|
30
|
+
export declare function parseTenantProjectValue(value?: string): TenantProjectValueSelection;
|
|
22
31
|
/** 将外部业务字段合并到项目行,同时保护公共项目字段。 */
|
|
23
32
|
export declare function mergeTenantProjectExtraData(project: BaseTenantProject, extra?: Record<string, unknown>): BaseTenantProject & Record<string, unknown>;
|
|
24
33
|
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from "vue";
|
|
2
|
+
import type { ColumnProps } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
|
|
3
|
+
import type { SelectOptionResponse_Option } from "@liujitcn/kratos-admin-core/rpc/common/v1/common";
|
|
4
|
+
import type { TreeBaseTenantProjectResponse_Option } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_tenant_project";
|
|
5
|
+
import { type TenantProjectDisplayInfo, type TenantProjectScopeSelection } from "./tenant-project-catalog";
|
|
6
|
+
export type { TenantProjectDisplayInfo, TenantProjectScopeRow, TenantProjectScopeSelection } from "./tenant-project-catalog";
|
|
7
|
+
export { getTenantProjectDisplayMap, resolveTenantProjectLabel } from "./tenant-project-catalog";
|
|
8
|
+
export { parseTenantProjectValue } from "./tenant-project-manager-data";
|
|
9
|
+
/** 租户项目范围参数。 */
|
|
10
|
+
export type TenantProjectScopeParams = {
|
|
11
|
+
/** 租户项目树值。 */
|
|
12
|
+
tenant_project_tree_value?: string;
|
|
13
|
+
/** 租户 ID。 */
|
|
14
|
+
tenant_id?: number;
|
|
15
|
+
/** 项目 ID。 */
|
|
16
|
+
project_id?: number;
|
|
17
|
+
};
|
|
18
|
+
/** 租户二级资源范围配置。 */
|
|
19
|
+
export interface TenantProjectScopeOptions {
|
|
20
|
+
/** 二级资源名称国际化键,默认使用 common.field.project。 */
|
|
21
|
+
resourceLabelKey?: string;
|
|
22
|
+
/** 全部二级资源名称国际化键。 */
|
|
23
|
+
allResourceLabelKey?: string;
|
|
24
|
+
/** 搜索参数键。 */
|
|
25
|
+
searchKey?: string;
|
|
26
|
+
/** 搜索项排序。 */
|
|
27
|
+
order?: number;
|
|
28
|
+
/** 表格列最小宽度。 */
|
|
29
|
+
minWidth?: number;
|
|
30
|
+
/** 表格列绑定的项目 ID 字段。 */
|
|
31
|
+
projectField?: string;
|
|
32
|
+
/** 表格列绑定的租户 ID 字段。 */
|
|
33
|
+
tenantField?: string;
|
|
34
|
+
}
|
|
35
|
+
/** 租户项目范围状态。 */
|
|
36
|
+
export interface TenantProjectScope {
|
|
37
|
+
/** 当前账号是否为默认租户。 */
|
|
38
|
+
isDefaultTenant: ComputedRef<boolean>;
|
|
39
|
+
/** 当前范围列标题。 */
|
|
40
|
+
scopeLabel: ComputedRef<string>;
|
|
41
|
+
/** 当前范围选择器占位文案。 */
|
|
42
|
+
allLabel: ComputedRef<string>;
|
|
43
|
+
/** 当前账号可见的树选项。 */
|
|
44
|
+
treeOptions: Ref<TreeBaseTenantProjectResponse_Option[]>;
|
|
45
|
+
/** 当前账号可见的普通项目选项。 */
|
|
46
|
+
projectOptions: Ref<SelectOptionResponse_Option[]>;
|
|
47
|
+
/** 当前目录名称映射。 */
|
|
48
|
+
displayMap: ComputedRef<Map<string, TenantProjectDisplayInfo>>;
|
|
49
|
+
/** 加载当前账号可见范围。 */
|
|
50
|
+
loadOptions: (force?: boolean) => Promise<TreeBaseTenantProjectResponse_Option[] | SelectOptionResponse_Option[]>;
|
|
51
|
+
/** 当前范围的全部项目。 */
|
|
52
|
+
projectSelections: (tenantId?: number) => TenantProjectScopeSelection[];
|
|
53
|
+
/** 归一化搜索参数。 */
|
|
54
|
+
normalizeParams: <T extends TenantProjectScopeParams>(params: T) => T & TenantProjectScopeParams;
|
|
55
|
+
/** 创建与搜索条件配套的表格列。 */
|
|
56
|
+
column: ComputedRef<ColumnProps>;
|
|
57
|
+
}
|
|
58
|
+
/** 创建租户项目范围状态。 */
|
|
59
|
+
export declare function useTenantProjectScope(options?: TenantProjectScopeOptions): TenantProjectScope;
|
|
60
|
+
/** 创建项目范围列。 */
|
|
61
|
+
export declare function createTenantProjectColumn(scope: TenantProjectScope): ComputedRef<ColumnProps<{
|
|
62
|
+
[x: string]: any;
|
|
63
|
+
}>>;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { ADMIN_AI_EXTENSION, getAdminAiExtension } from "./ai";
|
|
2
2
|
export type { AdminAiExtension } from "./ai";
|
|
3
3
|
export { default as TenantProjectManager } from "./components/tenant-project/TenantProjectManager.vue";
|
|
4
|
+
export { default as TenantProjectSelect } from "./components/tenant-project/TenantProjectSelect.vue";
|
|
5
|
+
export { default as TenantProjectText } from "./components/tenant-project/TenantProjectText.vue";
|
|
6
|
+
export { createTenantProjectColumn, getTenantProjectDisplayMap, parseTenantProjectValue, resolveTenantProjectLabel, useTenantProjectScope, type TenantProjectDisplayInfo, type TenantProjectScope, type TenantProjectScopeOptions, type TenantProjectScopeParams, type TenantProjectScopeRow, type TenantProjectScopeSelection } from "./components/tenant-project/tenant-project-scope";
|
|
4
7
|
export { mergeTenantProjectExtraData, arrangeTenantProjectColumns, tenantProjectKey, type TenantProjectAction, type TenantProjectContext, type TenantProjectExtraColumn, type TenantProjectExtraData, type TenantProjectExtraDataLoadContext, type TenantProjectExtraDataLoader, type TenantProjectKey, type TenantProjectManagerProps } from "./components/tenant-project/tenant-project-manager";
|
|
5
8
|
export { systemAdminModule } from "./module";
|
|
@@ -6,13 +6,20 @@
|
|
|
6
6
|
import { computed } from "vue";
|
|
7
7
|
import { useTenantScope } from "@liujitcn/kratos-admin-core/tenant";
|
|
8
8
|
import type { SelectOptionResponse_Option } from "@liujitcn/kratos-admin-core/rpc/common/v1/common";
|
|
9
|
+
import { resolveTenantProjectDisplay } from "./tenant-project-catalog";
|
|
9
10
|
|
|
10
11
|
defineOptions({ name: "TenantProjectText" });
|
|
11
12
|
|
|
12
13
|
/** 租户项目展示组件属性。 */
|
|
13
14
|
interface TenantProjectTextProps {
|
|
14
15
|
/** 包含租户和项目字段的列表行。 */
|
|
15
|
-
row
|
|
16
|
+
row?: Record<string, any>;
|
|
17
|
+
/** 仅传项目 ID 时使用的项目 ID。 */
|
|
18
|
+
projectId?: number;
|
|
19
|
+
/** 仅传项目 ID 时使用的租户 ID。 */
|
|
20
|
+
tenantId?: number;
|
|
21
|
+
/** 二级资源名称国际化键,默认使用项目。 */
|
|
22
|
+
resourceLabelKey?: string;
|
|
16
23
|
/** 项目名称字段。 */
|
|
17
24
|
projectField?: string;
|
|
18
25
|
/** 租户名称字段。 */
|
|
@@ -22,6 +29,10 @@ interface TenantProjectTextProps {
|
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
const props = withDefaults(defineProps<TenantProjectTextProps>(), {
|
|
32
|
+
row: () => ({}),
|
|
33
|
+
projectId: undefined,
|
|
34
|
+
tenantId: undefined,
|
|
35
|
+
resourceLabelKey: "common.field.project",
|
|
25
36
|
projectField: "project_name",
|
|
26
37
|
tenantField: "tenant_name",
|
|
27
38
|
tenantOptions: () => []
|
|
@@ -29,9 +40,11 @@ const props = withDefaults(defineProps<TenantProjectTextProps>(), {
|
|
|
29
40
|
const { isDefaultTenant, resolveTenantLabel } = useTenantScope();
|
|
30
41
|
|
|
31
42
|
const label = computed(() => {
|
|
32
|
-
const
|
|
43
|
+
const projectId = props.projectId ?? props.row.project_id;
|
|
44
|
+
const displayInfo = resolveTenantProjectDisplay(props.tenantId ?? props.row.tenant_id, projectId);
|
|
45
|
+
const projectName = props.row[props.projectField] ?? props.row.project_name ?? props.row.name ?? displayInfo?.projectName ?? projectId ?? "";
|
|
33
46
|
if (!isDefaultTenant.value) return String(projectName);
|
|
34
|
-
const tenantName = props.row[props.tenantField] ?? resolveTenantLabel(props.row, "tenant_id", props.tenantOptions);
|
|
47
|
+
const tenantName = props.row[props.tenantField] ?? resolveTenantLabel(props.row, "tenant_id", props.tenantOptions) ?? displayInfo?.tenantName;
|
|
35
48
|
return tenantName ? `${tenantName} / ${projectName}` : String(projectName);
|
|
36
49
|
});
|
|
37
50
|
</script>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { SelectOptionResponse_Option } from "@liujitcn/kratos-admin-core/rpc/common/v1/common";
|
|
2
|
+
import { defBaseTenantProjectService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_tenant_project";
|
|
3
|
+
import type { TreeBaseTenantProjectResponse_Option } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_tenant_project";
|
|
4
|
+
|
|
5
|
+
/** 租户项目范围。 */
|
|
6
|
+
export type TenantProjectScopeSelection = {
|
|
7
|
+
/** 租户 ID。 */
|
|
8
|
+
tenant_id: number;
|
|
9
|
+
/** 项目 ID。 */
|
|
10
|
+
project_id: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** 租户项目名称信息。 */
|
|
14
|
+
export type TenantProjectDisplayInfo = {
|
|
15
|
+
/** 租户名称。 */
|
|
16
|
+
tenantName: string;
|
|
17
|
+
/** 项目名称。 */
|
|
18
|
+
projectName: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** 租户项目表格行范围字段。 */
|
|
22
|
+
export type TenantProjectScopeRow = {
|
|
23
|
+
/** 租户 ID。 */
|
|
24
|
+
tenant_id?: number;
|
|
25
|
+
/** 项目 ID。 */
|
|
26
|
+
project_id?: number;
|
|
27
|
+
/** 租户名称。 */
|
|
28
|
+
tenant_name?: string;
|
|
29
|
+
/** 项目名称。 */
|
|
30
|
+
project_name?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** 当前账号租户项目目录。 */
|
|
34
|
+
export type TenantProjectCatalog = {
|
|
35
|
+
/** 租户项目树。 */
|
|
36
|
+
treeOptions: TreeBaseTenantProjectResponse_Option[];
|
|
37
|
+
/** 普通租户项目选项。 */
|
|
38
|
+
projectOptions: SelectOptionResponse_Option[];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type SharedCatalog = TenantProjectCatalog & {
|
|
42
|
+
mode?: boolean;
|
|
43
|
+
loaded: boolean;
|
|
44
|
+
request?: Promise<TenantProjectCatalog>;
|
|
45
|
+
displayMap: Map<string, TenantProjectDisplayInfo>;
|
|
46
|
+
projectMap: Map<string, TenantProjectDisplayInfo>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const sharedCatalog: SharedCatalog = {
|
|
50
|
+
loaded: false,
|
|
51
|
+
treeOptions: [],
|
|
52
|
+
projectOptions: [],
|
|
53
|
+
displayMap: new Map(),
|
|
54
|
+
projectMap: new Map()
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** 创建租户项目键。 */
|
|
58
|
+
function scopeKey(tenantId: number, projectId: number) {
|
|
59
|
+
return `${tenantId}:${projectId}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** 清空共享项目目录缓存。 */
|
|
63
|
+
function resetCatalog(mode: boolean) {
|
|
64
|
+
sharedCatalog.mode = mode;
|
|
65
|
+
sharedCatalog.loaded = false;
|
|
66
|
+
sharedCatalog.treeOptions = [];
|
|
67
|
+
sharedCatalog.projectOptions = [];
|
|
68
|
+
sharedCatalog.displayMap.clear();
|
|
69
|
+
sharedCatalog.projectMap.clear();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 将项目目录写入共享名称缓存。 */
|
|
73
|
+
function indexProject(tenantId: number, projectId: number, projectName: string, tenantName: string) {
|
|
74
|
+
const info = { tenantName, projectName };
|
|
75
|
+
sharedCatalog.displayMap.set(scopeKey(tenantId, projectId), info);
|
|
76
|
+
sharedCatalog.projectMap.set(String(projectId), info);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function indexTree(options: TreeBaseTenantProjectResponse_Option[], tenantName = "") {
|
|
80
|
+
for (const option of options) {
|
|
81
|
+
if (option.type === "project") {
|
|
82
|
+
indexProject(option.tenant_id, option.project_id, option.label, tenantName);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
indexTree(option.children ?? [], option.label);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 读取当前共享租户项目名称映射。 */
|
|
90
|
+
export function getTenantProjectDisplayMap() {
|
|
91
|
+
return sharedCatalog.displayMap;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** 读取项目目录中的名称信息。 */
|
|
95
|
+
export function resolveTenantProjectDisplay(tenantId?: number, projectId?: number) {
|
|
96
|
+
if (!projectId) return undefined;
|
|
97
|
+
return sharedCatalog.displayMap.get(scopeKey(tenantId ?? 0, projectId)) ?? sharedCatalog.projectMap.get(String(projectId));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** 根据租户项目 ID 生成展示文本。 */
|
|
101
|
+
export function resolveTenantProjectLabel(row: TenantProjectScopeRow, isDefaultTenant: boolean) {
|
|
102
|
+
const displayInfo = resolveTenantProjectDisplay(row.tenant_id, row.project_id);
|
|
103
|
+
const projectName = row.project_name ?? displayInfo?.projectName ?? row.project_id ?? "-";
|
|
104
|
+
if (!isDefaultTenant) return String(projectName);
|
|
105
|
+
const tenantName = row.tenant_name ?? displayInfo?.tenantName;
|
|
106
|
+
return tenantName ? `${tenantName} / ${projectName}` : String(projectName);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 加载并缓存当前账号可见的租户项目目录。 */
|
|
110
|
+
export async function loadTenantProjectOptions(isDefaultTenant: boolean, force = false): Promise<TenantProjectCatalog> {
|
|
111
|
+
if (sharedCatalog.mode !== isDefaultTenant) resetCatalog(isDefaultTenant);
|
|
112
|
+
if (sharedCatalog.loaded && !force) return sharedCatalog;
|
|
113
|
+
if (sharedCatalog.request && !force) return sharedCatalog.request;
|
|
114
|
+
sharedCatalog.request = (async () => {
|
|
115
|
+
if (isDefaultTenant) {
|
|
116
|
+
const response = await defBaseTenantProjectService.TreeBaseTenantProject({ keyword: "" });
|
|
117
|
+
sharedCatalog.treeOptions = response.list ?? [];
|
|
118
|
+
indexTree(sharedCatalog.treeOptions);
|
|
119
|
+
} else {
|
|
120
|
+
const response = await defBaseTenantProjectService.OptionBaseTenantProject({ tenant_id: 0 });
|
|
121
|
+
sharedCatalog.projectOptions = response.list ?? [];
|
|
122
|
+
for (const option of sharedCatalog.projectOptions) indexProject(0, Number(option.value), option.label, "");
|
|
123
|
+
sharedCatalog.treeOptions = sharedCatalog.projectOptions.map(option => ({
|
|
124
|
+
value: `project:0:${Number(option.value)}`,
|
|
125
|
+
label: option.label,
|
|
126
|
+
type: "project",
|
|
127
|
+
tenant_id: 0,
|
|
128
|
+
project_id: Number(option.value),
|
|
129
|
+
disabled: option.disabled,
|
|
130
|
+
children: []
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
sharedCatalog.loaded = true;
|
|
134
|
+
return sharedCatalog;
|
|
135
|
+
})().finally(() => {
|
|
136
|
+
sharedCatalog.request = undefined;
|
|
137
|
+
});
|
|
138
|
+
return sharedCatalog.request;
|
|
139
|
+
}
|
|
@@ -51,6 +51,23 @@ export function tenantProjectKey(tenantId: number, projectId: number): string {
|
|
|
51
51
|
return `${tenantId}:${projectId}`;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/** 租户项目树节点解析结果。 */
|
|
55
|
+
export type TenantProjectValueSelection = {
|
|
56
|
+
/** 租户 ID,选中租户节点或项目节点时返回。 */
|
|
57
|
+
tenant_id?: number;
|
|
58
|
+
/** 项目 ID,仅选中项目节点时返回。 */
|
|
59
|
+
project_id?: number;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** 解析租户项目树节点值,租户节点返回租户 ID,项目节点返回租户和项目 ID。 */
|
|
63
|
+
export function parseTenantProjectValue(value?: string): TenantProjectValueSelection {
|
|
64
|
+
if (!value) return {};
|
|
65
|
+
const [type, tenantId, projectId] = value.split(":");
|
|
66
|
+
if (type === "tenant" && Number.isFinite(Number(tenantId))) return { tenant_id: Number(tenantId), project_id: 0 };
|
|
67
|
+
if (type === "project" && Number.isFinite(Number(tenantId)) && Number.isFinite(Number(projectId))) return { tenant_id: Number(tenantId), project_id: Number(projectId) };
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
|
|
54
71
|
/** 将外部业务字段合并到项目行,同时保护公共项目字段。 */
|
|
55
72
|
export function mergeTenantProjectExtraData(
|
|
56
73
|
project: BaseTenantProject,
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { computed, h, ref } from "vue";
|
|
2
|
+
import type { ComputedRef, Ref } from "vue";
|
|
3
|
+
import type { ColumnProps, SearchRenderScope } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
|
|
4
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
5
|
+
import { useTenantScope } from "@liujitcn/kratos-admin-core/tenant";
|
|
6
|
+
import type { SelectOptionResponse_Option } from "@liujitcn/kratos-admin-core/rpc/common/v1/common";
|
|
7
|
+
import type { TreeBaseTenantProjectResponse_Option } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_tenant_project";
|
|
8
|
+
import TenantProjectSelect, { type TenantProjectSelection as TenantProjectNodeSelection } from "./TenantProjectSelect.vue";
|
|
9
|
+
import TenantProjectText from "./TenantProjectText.vue";
|
|
10
|
+
import { getTenantProjectDisplayMap, loadTenantProjectOptions, type TenantProjectDisplayInfo, type TenantProjectScopeSelection } from "./tenant-project-catalog";
|
|
11
|
+
import { parseTenantProjectValue } from "./tenant-project-manager-data";
|
|
12
|
+
|
|
13
|
+
export type { TenantProjectDisplayInfo, TenantProjectScopeRow, TenantProjectScopeSelection } from "./tenant-project-catalog";
|
|
14
|
+
export { getTenantProjectDisplayMap, resolveTenantProjectLabel } from "./tenant-project-catalog";
|
|
15
|
+
export { parseTenantProjectValue } from "./tenant-project-manager-data";
|
|
16
|
+
|
|
17
|
+
/** 租户项目范围参数。 */
|
|
18
|
+
export type TenantProjectScopeParams = {
|
|
19
|
+
/** 租户项目树值。 */
|
|
20
|
+
tenant_project_tree_value?: string;
|
|
21
|
+
/** 租户 ID。 */
|
|
22
|
+
tenant_id?: number;
|
|
23
|
+
/** 项目 ID。 */
|
|
24
|
+
project_id?: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** 租户二级资源范围配置。 */
|
|
28
|
+
export interface TenantProjectScopeOptions {
|
|
29
|
+
/** 二级资源名称国际化键,默认使用 common.field.project。 */
|
|
30
|
+
resourceLabelKey?: string;
|
|
31
|
+
/** 全部二级资源名称国际化键。 */
|
|
32
|
+
allResourceLabelKey?: string;
|
|
33
|
+
/** 搜索参数键。 */
|
|
34
|
+
searchKey?: string;
|
|
35
|
+
/** 搜索项排序。 */
|
|
36
|
+
order?: number;
|
|
37
|
+
/** 表格列最小宽度。 */
|
|
38
|
+
minWidth?: number;
|
|
39
|
+
/** 表格列绑定的项目 ID 字段。 */
|
|
40
|
+
projectField?: string;
|
|
41
|
+
/** 表格列绑定的租户 ID 字段。 */
|
|
42
|
+
tenantField?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 租户项目范围状态。 */
|
|
46
|
+
export interface TenantProjectScope {
|
|
47
|
+
/** 当前账号是否为默认租户。 */
|
|
48
|
+
isDefaultTenant: ComputedRef<boolean>;
|
|
49
|
+
/** 当前范围列标题。 */
|
|
50
|
+
scopeLabel: ComputedRef<string>;
|
|
51
|
+
/** 当前范围选择器占位文案。 */
|
|
52
|
+
allLabel: ComputedRef<string>;
|
|
53
|
+
/** 当前账号可见的树选项。 */
|
|
54
|
+
treeOptions: Ref<TreeBaseTenantProjectResponse_Option[]>;
|
|
55
|
+
/** 当前账号可见的普通项目选项。 */
|
|
56
|
+
projectOptions: Ref<SelectOptionResponse_Option[]>;
|
|
57
|
+
/** 当前目录名称映射。 */
|
|
58
|
+
displayMap: ComputedRef<Map<string, TenantProjectDisplayInfo>>;
|
|
59
|
+
/** 加载当前账号可见范围。 */
|
|
60
|
+
loadOptions: (force?: boolean) => Promise<TreeBaseTenantProjectResponse_Option[] | SelectOptionResponse_Option[]>;
|
|
61
|
+
/** 当前范围的全部项目。 */
|
|
62
|
+
projectSelections: (tenantId?: number) => TenantProjectScopeSelection[];
|
|
63
|
+
/** 归一化搜索参数。 */
|
|
64
|
+
normalizeParams: <T extends TenantProjectScopeParams>(params: T) => T & TenantProjectScopeParams;
|
|
65
|
+
/** 创建与搜索条件配套的表格列。 */
|
|
66
|
+
column: ComputedRef<ColumnProps>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 创建租户项目范围状态。 */
|
|
70
|
+
export function useTenantProjectScope(options: TenantProjectScopeOptions = {}): TenantProjectScope {
|
|
71
|
+
const { isDefaultTenant } = useTenantScope();
|
|
72
|
+
const treeOptions = ref<TreeBaseTenantProjectResponse_Option[]>([]);
|
|
73
|
+
const projectOptions = ref<SelectOptionResponse_Option[]>([]);
|
|
74
|
+
const resourceLabelKey = options.resourceLabelKey ?? "common.field.project";
|
|
75
|
+
const allResourceLabelKey = options.allResourceLabelKey ?? "system.base.tenant_project.all";
|
|
76
|
+
const scopeLabel = computed(() => isDefaultTenant.value ? `${t("common.field.tenant")} / ${t(resourceLabelKey)}` : t(resourceLabelKey));
|
|
77
|
+
const allLabel = computed(() => t(allResourceLabelKey));
|
|
78
|
+
const displayMap = computed(() => getTenantProjectDisplayMap());
|
|
79
|
+
|
|
80
|
+
async function loadOptions(force = false) {
|
|
81
|
+
const catalog = await loadTenantProjectOptions(isDefaultTenant.value, force);
|
|
82
|
+
treeOptions.value = catalog.treeOptions;
|
|
83
|
+
projectOptions.value = catalog.projectOptions;
|
|
84
|
+
return isDefaultTenant.value ? treeOptions.value : projectOptions.value;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function projectSelections(tenantId = 0) {
|
|
88
|
+
if (!isDefaultTenant.value) return projectOptions.value.map(option => ({ tenant_id: 0, project_id: Number(option.value) })).filter(item => item.project_id > 0);
|
|
89
|
+
const selections: TenantProjectScopeSelection[] = [];
|
|
90
|
+
const collect = (options: TreeBaseTenantProjectResponse_Option[]) => {
|
|
91
|
+
for (const option of options) {
|
|
92
|
+
if (option.type === "project" && option.project_id > 0 && (!tenantId || option.tenant_id === tenantId)) selections.push({ tenant_id: option.tenant_id, project_id: option.project_id });
|
|
93
|
+
if (option.children?.length) collect(option.children);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
collect(treeOptions.value);
|
|
97
|
+
return selections;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function normalizeParams<T extends TenantProjectScopeParams>(params: T) {
|
|
101
|
+
const result = { ...params } as T & TenantProjectScopeParams;
|
|
102
|
+
const selection = parseTenantProjectValue(params.tenant_project_tree_value);
|
|
103
|
+
result.tenant_id = isDefaultTenant.value ? selection.tenant_id ?? 0 : 0;
|
|
104
|
+
result.project_id = selection.project_id ?? Number(params.project_id ?? 0);
|
|
105
|
+
delete result.tenant_project_tree_value;
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const column = computed<ColumnProps>(() => ({
|
|
110
|
+
prop: options.projectField ?? "project_id",
|
|
111
|
+
label: scopeLabel.value,
|
|
112
|
+
minWidth: options.minWidth ?? (isDefaultTenant.value ? 220 : 160),
|
|
113
|
+
showOverflowTooltip: true,
|
|
114
|
+
render: ({ row }) => h(TenantProjectText, {
|
|
115
|
+
row,
|
|
116
|
+
projectId: row[options.projectField ?? "project_id"],
|
|
117
|
+
tenantId: row[options.tenantField ?? "tenant_id"],
|
|
118
|
+
resourceLabelKey
|
|
119
|
+
}),
|
|
120
|
+
search: {
|
|
121
|
+
key: options.searchKey ?? "tenant_project_tree_value",
|
|
122
|
+
order: options.order ?? 1,
|
|
123
|
+
render: (scope: SearchRenderScope) => renderTenantProjectSearch(scope, loadOptions, treeOptions, allLabel.value)
|
|
124
|
+
},
|
|
125
|
+
enum: async () => ({ data: await loadOptions() }),
|
|
126
|
+
isFilterEnum: false
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
return { isDefaultTenant, scopeLabel, allLabel, treeOptions, projectOptions, displayMap, loadOptions, projectSelections, normalizeParams, column };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function renderTenantProjectSearch(
|
|
133
|
+
scope: SearchRenderScope,
|
|
134
|
+
loadOptions: (force?: boolean) => Promise<TreeBaseTenantProjectResponse_Option[] | SelectOptionResponse_Option[]>,
|
|
135
|
+
options: Ref<TreeBaseTenantProjectResponse_Option[]>,
|
|
136
|
+
placeholder: string
|
|
137
|
+
) {
|
|
138
|
+
const value = scope.searchParam.tenant_project_tree_value;
|
|
139
|
+
return h(TenantProjectSelect, {
|
|
140
|
+
modelValue: value ? { value: String(value) } : undefined,
|
|
141
|
+
options: options.value,
|
|
142
|
+
clearable: scope.clearable,
|
|
143
|
+
filterable: true,
|
|
144
|
+
checkStrictly: true,
|
|
145
|
+
renderAfterExpand: false,
|
|
146
|
+
placeholder: scope.placeholder || placeholder,
|
|
147
|
+
style: { width: "100%" },
|
|
148
|
+
onVisibleChange: (visible: boolean) => { if (visible) void loadOptions(); },
|
|
149
|
+
"onUpdate:modelValue": (selection: TenantProjectNodeSelection | undefined) => { scope.searchParam.tenant_project_tree_value = selection?.value; }
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** 创建项目范围列。 */
|
|
154
|
+
export function createTenantProjectColumn(scope: TenantProjectScope) {
|
|
155
|
+
return scope.column;
|
|
156
|
+
}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
export { ADMIN_AI_EXTENSION, getAdminAiExtension } from "./ai";
|
|
2
2
|
export type { AdminAiExtension } from "./ai";
|
|
3
3
|
export { default as TenantProjectManager } from "./components/tenant-project/TenantProjectManager.vue";
|
|
4
|
+
export { default as TenantProjectSelect } from "./components/tenant-project/TenantProjectSelect.vue";
|
|
5
|
+
export { default as TenantProjectText } from "./components/tenant-project/TenantProjectText.vue";
|
|
6
|
+
export {
|
|
7
|
+
createTenantProjectColumn,
|
|
8
|
+
getTenantProjectDisplayMap,
|
|
9
|
+
parseTenantProjectValue,
|
|
10
|
+
resolveTenantProjectLabel,
|
|
11
|
+
useTenantProjectScope,
|
|
12
|
+
type TenantProjectDisplayInfo,
|
|
13
|
+
type TenantProjectScope,
|
|
14
|
+
type TenantProjectScopeOptions,
|
|
15
|
+
type TenantProjectScopeParams,
|
|
16
|
+
type TenantProjectScopeRow,
|
|
17
|
+
type TenantProjectScopeSelection
|
|
18
|
+
} from "./components/tenant-project/tenant-project-scope";
|
|
4
19
|
export {
|
|
5
20
|
mergeTenantProjectExtraData,
|
|
6
21
|
arrangeTenantProjectColumns,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liujitcn/kratos-admin-system",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.75",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "kratos-admin system management module",
|
|
7
7
|
"license": "MIT",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"sortablejs": "^1.15.2",
|
|
17
17
|
"swagger-ui-dist": "^5.32.15",
|
|
18
18
|
"vue-element-plus-x": "^2.0.3",
|
|
19
|
-
"@liujitcn/kratos-admin-core": "0.0.
|
|
19
|
+
"@liujitcn/kratos-admin-core": "0.0.75"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"@element-plus/icons-vue": "^2.3.1",
|