表单设计器
This commit is contained in:
parent
00e5bf6315
commit
34a5eb0ddd
@ -7,6 +7,7 @@ import {
|
|||||||
environmentProjectsConfig,
|
environmentProjectsConfig,
|
||||||
jenkinsServerViewsJobsConfig,
|
jenkinsServerViewsJobsConfig,
|
||||||
departmentUsersConfig,
|
departmentUsersConfig,
|
||||||
|
departmentTreeConfig,
|
||||||
projectGroupAppsConfig
|
projectGroupAppsConfig
|
||||||
} from './presets/cascade';
|
} from './presets/cascade';
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ export const CASCADE_DATA_SOURCE_REGISTRY: CascadeDataSourceRegistry = {
|
|||||||
[CascadeDataSourceType.ENVIRONMENT_PROJECTS]: environmentProjectsConfig,
|
[CascadeDataSourceType.ENVIRONMENT_PROJECTS]: environmentProjectsConfig,
|
||||||
[CascadeDataSourceType.JENKINS_SERVER_VIEWS_JOBS]: jenkinsServerViewsJobsConfig,
|
[CascadeDataSourceType.JENKINS_SERVER_VIEWS_JOBS]: jenkinsServerViewsJobsConfig,
|
||||||
[CascadeDataSourceType.DEPARTMENT_USERS]: departmentUsersConfig,
|
[CascadeDataSourceType.DEPARTMENT_USERS]: departmentUsersConfig,
|
||||||
|
[CascadeDataSourceType.DEPARTMENT_TREE]: departmentTreeConfig,
|
||||||
[CascadeDataSourceType.PROJECT_GROUP_APPS]: projectGroupAppsConfig
|
[CascadeDataSourceType.PROJECT_GROUP_APPS]: projectGroupAppsConfig
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,13 @@ import { getCascadeDataSourceConfig } from './CascadeDataSourceRegistry';
|
|||||||
* 级联数据源服务类
|
* 级联数据源服务类
|
||||||
*/
|
*/
|
||||||
class CascadeDataSourceService {
|
class CascadeDataSourceService {
|
||||||
|
/**
|
||||||
|
* 判断配置是否为递归模式
|
||||||
|
*/
|
||||||
|
private isRecursiveMode(config: any): boolean {
|
||||||
|
return !!config.recursive;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载第一级数据
|
* 加载第一级数据
|
||||||
* @param type 级联数据源类型
|
* @param type 级联数据源类型
|
||||||
@ -18,8 +25,19 @@ class CascadeDataSourceService {
|
|||||||
async loadFirstLevel(type: CascadeDataSourceType): Promise<CascadeOption[]> {
|
async loadFirstLevel(type: CascadeDataSourceType): Promise<CascadeOption[]> {
|
||||||
const config = getCascadeDataSourceConfig(type);
|
const config = getCascadeDataSourceConfig(type);
|
||||||
|
|
||||||
if (!config || config.levels.length === 0) {
|
if (!config) {
|
||||||
console.error(`❌ 级联数据源类型 ${type} 未配置或配置为空`);
|
console.error(`❌ 级联数据源类型 ${type} 未配置`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归模式
|
||||||
|
if (this.isRecursiveMode(config)) {
|
||||||
|
return this.loadRecursiveLevel(config.recursive!, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 固定层级模式
|
||||||
|
if (!config.levels || config.levels.length === 0) {
|
||||||
|
console.error(`❌ 级联数据源类型 ${type} 未配置层级`);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +61,18 @@ class CascadeDataSourceService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 递归模式:使用同一配置加载所有层级
|
||||||
|
if (this.isRecursiveMode(config)) {
|
||||||
|
const parentValue = selectedOptions[selectedOptions.length - 1];
|
||||||
|
return this.loadRecursiveLevel(config.recursive!, parentValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 固定层级模式
|
||||||
|
if (!config.levels) {
|
||||||
|
console.error(`❌ 级联数据源类型 ${type} 未配置层级`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const levelIndex = selectedOptions.length;
|
const levelIndex = selectedOptions.length;
|
||||||
|
|
||||||
if (levelIndex >= config.levels.length) {
|
if (levelIndex >= config.levels.length) {
|
||||||
@ -58,7 +88,60 @@ class CascadeDataSourceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载单个层级的数据
|
* 加载递归层级数据(无限层级)
|
||||||
|
* @param recursiveConfig 递归配置
|
||||||
|
* @param parentValue 父级值(null 表示加载根节点)
|
||||||
|
* @returns 选项列表
|
||||||
|
*/
|
||||||
|
private async loadRecursiveLevel(
|
||||||
|
recursiveConfig: any,
|
||||||
|
parentValue: any
|
||||||
|
): Promise<CascadeOption[]> {
|
||||||
|
try {
|
||||||
|
// 构建请求参数
|
||||||
|
const params: Record<string, any> = { ...recursiveConfig.params };
|
||||||
|
|
||||||
|
// 添加父级参数
|
||||||
|
if (parentValue !== null) {
|
||||||
|
params[recursiveConfig.parentParam] = parentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发起请求
|
||||||
|
const response = await request.get(recursiveConfig.url, { params });
|
||||||
|
const data = response || [];
|
||||||
|
|
||||||
|
// 转换为级联选项格式
|
||||||
|
return data.map((item: any) => {
|
||||||
|
const option: CascadeOption = {
|
||||||
|
label: item[recursiveConfig.labelField],
|
||||||
|
value: item[recursiveConfig.valueField]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 判断是否为叶子节点
|
||||||
|
if (recursiveConfig.hasChildren) {
|
||||||
|
// 使用自定义判断函数
|
||||||
|
option.isLeaf = !recursiveConfig.hasChildren(item);
|
||||||
|
} else if ('isLeaf' in item) {
|
||||||
|
// 使用后端返回的 isLeaf 字段
|
||||||
|
option.isLeaf = item.isLeaf;
|
||||||
|
} else if ('hasChildren' in item) {
|
||||||
|
// 使用后端返回的 hasChildren 字段
|
||||||
|
option.isLeaf = !item.hasChildren;
|
||||||
|
} else {
|
||||||
|
// 默认不是叶子节点(允许继续展开)
|
||||||
|
option.isLeaf = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ 加载递归级联数据失败:`, error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载单个层级的数据(固定层级模式)
|
||||||
* @param levelConfig 层级配置
|
* @param levelConfig 层级配置
|
||||||
* @param parentValue 父级值
|
* @param parentValue 父级值
|
||||||
* @param hasNextLevel 是否有下一级
|
* @param hasNextLevel 是否有下一级
|
||||||
@ -108,11 +191,18 @@ class CascadeDataSourceService {
|
|||||||
/**
|
/**
|
||||||
* 获取级联配置的层级数
|
* 获取级联配置的层级数
|
||||||
* @param type 级联数据源类型
|
* @param type 级联数据源类型
|
||||||
* @returns 层级数
|
* @returns 层级数(递归模式返回 -1 表示无限层级)
|
||||||
*/
|
*/
|
||||||
getLevelCount(type: CascadeDataSourceType): number {
|
getLevelCount(type: CascadeDataSourceType): number {
|
||||||
const config = getCascadeDataSourceConfig(type);
|
const config = getCascadeDataSourceConfig(type);
|
||||||
return config?.levels.length || 0;
|
if (!config) return 0;
|
||||||
|
|
||||||
|
// 递归模式返回 -1 表示无限层级
|
||||||
|
if (this.isRecursiveMode(config)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.levels?.length || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -53,7 +53,7 @@ export const jenkinsServerViewsJobsConfig: CascadeDataSourceConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 部门 → 用户
|
* 部门 → 用户(固定两级)
|
||||||
*/
|
*/
|
||||||
export const departmentUsersConfig: CascadeDataSourceConfig = {
|
export const departmentUsersConfig: CascadeDataSourceConfig = {
|
||||||
description: '部门 → 用户',
|
description: '部门 → 用户',
|
||||||
@ -73,6 +73,23 @@ export const departmentUsersConfig: CascadeDataSourceConfig = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门树(递归无限层级)⭐ 新增
|
||||||
|
* 适用于组织架构等需要无限层级的场景
|
||||||
|
*/
|
||||||
|
export const departmentTreeConfig: CascadeDataSourceConfig = {
|
||||||
|
description: '部门树(无限层级)',
|
||||||
|
recursive: {
|
||||||
|
url: '/api/v1/department/tree',
|
||||||
|
parentParam: 'parentId',
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'id',
|
||||||
|
// 可选:自定义判断是否有子节点
|
||||||
|
// hasChildren: (item) => item.hasChildren
|
||||||
|
// 如果不提供,会自动使用后端返回的 isLeaf 或 hasChildren 字段
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 项目组 → 应用
|
* 项目组 → 应用
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -21,10 +21,11 @@ export enum DataSourceType {
|
|||||||
* 级联数据源类型枚举
|
* 级联数据源类型枚举
|
||||||
*/
|
*/
|
||||||
export enum CascadeDataSourceType {
|
export enum CascadeDataSourceType {
|
||||||
ENVIRONMENT_PROJECTS = 'ENVIRONMENT_PROJECTS', // 环境 → 项目
|
ENVIRONMENT_PROJECTS = 'ENVIRONMENT_PROJECTS', // 环境 → 项目(固定2级)
|
||||||
JENKINS_SERVER_VIEWS_JOBS = 'JENKINS_SERVER_VIEWS_JOBS', // Jenkins → View → Job
|
JENKINS_SERVER_VIEWS_JOBS = 'JENKINS_SERVER_VIEWS_JOBS', // Jenkins → View → Job(固定3级)
|
||||||
DEPARTMENT_USERS = 'DEPARTMENT_USERS', // 部门 → 用户
|
DEPARTMENT_USERS = 'DEPARTMENT_USERS', // 部门 → 用户(固定2级)
|
||||||
PROJECT_GROUP_APPS = 'PROJECT_GROUP_APPS' // 项目组 → 应用
|
DEPARTMENT_TREE = 'DEPARTMENT_TREE', // 部门树(递归无限层级)⭐
|
||||||
|
PROJECT_GROUP_APPS = 'PROJECT_GROUP_APPS' // 项目组 → 应用(固定2级)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,11 +67,27 @@ export interface CascadeLevelConfig {
|
|||||||
isLeaf?: (item: any) => boolean; // 判断是否为叶子节点
|
isLeaf?: (item: any) => boolean; // 判断是否为叶子节点
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归级联配置(用于无限层级场景,如组织架构)
|
||||||
|
*/
|
||||||
|
export interface RecursiveCascadeConfig {
|
||||||
|
url: string; // 接口地址
|
||||||
|
labelField: string; // 标签字段
|
||||||
|
valueField: string; // 值字段
|
||||||
|
parentParam: string; // 父级参数名(如 'parentId')
|
||||||
|
params?: Record<string, any>; // 额外参数
|
||||||
|
hasChildren?: (item: any) => boolean; // 判断是否有子节点(可选,默认根据后端 isLeaf 字段)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 级联数据源配置接口
|
* 级联数据源配置接口
|
||||||
|
* 支持两种模式:
|
||||||
|
* 1. 固定层级模式 - 使用 levels 配置
|
||||||
|
* 2. 递归层级模式 - 使用 recursive 配置(无限层级)
|
||||||
*/
|
*/
|
||||||
export interface CascadeDataSourceConfig {
|
export interface CascadeDataSourceConfig {
|
||||||
levels: CascadeLevelConfig[]; // 级联层级配置
|
levels?: CascadeLevelConfig[]; // 固定层级配置
|
||||||
|
recursive?: RecursiveCascadeConfig; // 递归层级配置(无限层级)
|
||||||
description?: string; // 描述
|
description?: string; // 描述
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -85,20 +85,35 @@ export const useCascaderLoadData = (field: FieldConfig) => {
|
|||||||
// 提取已选择的值
|
// 提取已选择的值
|
||||||
const selectedValues = selectedOptions.map(opt => opt.value);
|
const selectedValues = selectedOptions.map(opt => opt.value);
|
||||||
|
|
||||||
|
// 获取目标选项(最后一个被选择的选项)
|
||||||
|
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 加载子级数据
|
// 加载子级数据
|
||||||
const childrenData = await cascadeDataSourceService.loadChildren(sourceType, selectedValues);
|
const childrenData = await cascadeDataSourceService.loadChildren(sourceType, selectedValues);
|
||||||
console.log(`✅ 懒加载成功,子级数据:`, childrenData);
|
console.log(`✅ 懒加载成功,子级数据:`, childrenData);
|
||||||
|
|
||||||
|
// 转换为级联选项格式
|
||||||
|
const children = convertToCascadeFieldOption(childrenData);
|
||||||
|
|
||||||
// 更新最后一个选项的 children
|
// 更新最后一个选项的 children
|
||||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
// 重要:需要设置 loading: false 和 children
|
||||||
targetOption.loading = false;
|
targetOption.loading = false;
|
||||||
targetOption.children = convertToCascadeFieldOption(childrenData);
|
|
||||||
|
// 如果有子数据,设置 children;如果没有,设置为 undefined 并标记为叶子节点
|
||||||
|
if (children && children.length > 0) {
|
||||||
|
targetOption.children = children;
|
||||||
|
} else {
|
||||||
|
targetOption.children = undefined;
|
||||||
|
targetOption.isLeaf = true;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`❌ 懒加载失败:`, error);
|
console.error(`❌ 懒加载失败:`, error);
|
||||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
|
||||||
|
// 出错时标记为叶子节点(不再展开)
|
||||||
targetOption.loading = false;
|
targetOption.loading = false;
|
||||||
targetOption.children = [];
|
targetOption.children = undefined;
|
||||||
|
targetOption.isLeaf = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[sourceType]
|
[sourceType]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user