mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-27 06:41:12 +00:00
607 lines
15 KiB
TypeScript
607 lines
15 KiB
TypeScript
/**
|
|
* 低代码页面生成器 - 组件 Schema 定义
|
|
* 定义组件的 JSON 结构和类型
|
|
*/
|
|
|
|
// 基础组件属性接口
|
|
export interface BaseComponentProps {
|
|
id: string;
|
|
type: string;
|
|
label?: string;
|
|
children?: ComponentSchema[];
|
|
style?: Record<string, any>;
|
|
class?: string | string[];
|
|
}
|
|
|
|
// Element Plus 组件类型枚举(只包含已配置的组件)
|
|
export enum ComponentType {
|
|
// 基础组件
|
|
BUTTON = 'el-button',
|
|
LINK = 'el-link',
|
|
TEXT = 'el-text',
|
|
|
|
// 布局组件
|
|
ROW = 'el-row',
|
|
COL = 'el-col',
|
|
CONTAINER = 'el-container',
|
|
HEADER = 'el-header',
|
|
MAIN = 'el-main',
|
|
ASIDE = 'el-aside',
|
|
FOOTER = 'el-footer',
|
|
|
|
// 表单组件
|
|
FORM = 'el-form',
|
|
FORM_ITEM = 'el-form-item',
|
|
INPUT = 'el-input',
|
|
INPUT_NUMBER = 'el-input-number',
|
|
SELECT = 'el-select',
|
|
RADIO = 'el-radio',
|
|
RADIO_GROUP = 'el-radio-group',
|
|
CHECKBOX = 'el-checkbox',
|
|
CHECKBOX_GROUP = 'el-checkbox-group',
|
|
SWITCH = 'el-switch',
|
|
SLIDER = 'el-slider',
|
|
TIME_PICKER = 'el-time-picker',
|
|
DATE_PICKER = 'el-date-picker',
|
|
|
|
// 数据展示组件
|
|
CARD = 'el-card',
|
|
TAG = 'el-tag',
|
|
PROGRESS = 'el-progress',
|
|
|
|
// 反馈组件
|
|
ALERT = 'el-alert',
|
|
|
|
// 其他组件
|
|
DIVIDER = 'el-divider'
|
|
}
|
|
|
|
// 组件 Schema 接口
|
|
export interface ComponentSchema extends BaseComponentProps {
|
|
props?: Record<string, any>;
|
|
events?: Record<string, string>;
|
|
slots?: Record<string, string>;
|
|
}
|
|
|
|
// 页面 Schema 接口
|
|
export interface PageSchema {
|
|
id: string;
|
|
name: string;
|
|
components: ComponentSchema[];
|
|
globalStyle?: Record<string, any>;
|
|
}
|
|
|
|
// 组件配置接口
|
|
export interface ComponentConfig {
|
|
type: ComponentType;
|
|
label: string;
|
|
icon: string;
|
|
category: string;
|
|
defaultProps: Record<string, any>;
|
|
editableProps: string[];
|
|
canHaveChildren: boolean;
|
|
maxChildren?: number;
|
|
}
|
|
|
|
// 组件库配置
|
|
export const COMPONENT_CONFIGS: Record<ComponentType, ComponentConfig> = {
|
|
// 基础组件
|
|
[ComponentType.BUTTON]: {
|
|
type: ComponentType.BUTTON,
|
|
label: '按钮',
|
|
icon: 'el-icon-edit',
|
|
category: '基础',
|
|
defaultProps: {
|
|
type: 'primary',
|
|
size: 'default',
|
|
plain: false,
|
|
round: false,
|
|
circle: false,
|
|
disabled: false,
|
|
loading: false,
|
|
children: '按钮'
|
|
},
|
|
editableProps: ['type', 'size', 'plain', 'round', 'circle', 'disabled', 'loading', 'children'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.LINK]: {
|
|
type: ComponentType.LINK,
|
|
label: '链接',
|
|
icon: 'el-icon-link',
|
|
category: '基础',
|
|
defaultProps: {
|
|
type: 'primary',
|
|
href: '',
|
|
disabled: false,
|
|
underline: true,
|
|
children: '链接文本'
|
|
},
|
|
editableProps: ['type', 'href', 'disabled', 'underline', 'children'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.TEXT]: {
|
|
type: ComponentType.TEXT,
|
|
label: '文本',
|
|
icon: 'el-icon-document',
|
|
category: '基础',
|
|
defaultProps: {
|
|
type: 'primary',
|
|
size: 'default',
|
|
tag: 'span',
|
|
children: '文本内容'
|
|
},
|
|
editableProps: ['type', 'size', 'tag', 'children'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.INPUT]: {
|
|
type: ComponentType.INPUT,
|
|
label: '输入框',
|
|
icon: 'el-icon-edit',
|
|
category: '表单',
|
|
defaultProps: {
|
|
type: 'text',
|
|
placeholder: '请输入内容',
|
|
clearable: true,
|
|
disabled: false,
|
|
readonly: false,
|
|
maxlength: null,
|
|
showWordLimit: false,
|
|
size: 'default'
|
|
},
|
|
editableProps: ['type', 'placeholder', 'clearable', 'disabled', 'readonly', 'maxlength', 'showWordLimit', 'size'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.INPUT_NUMBER]: {
|
|
type: ComponentType.INPUT_NUMBER,
|
|
label: '数字输入框',
|
|
icon: 'el-icon-plus',
|
|
category: '表单',
|
|
defaultProps: {
|
|
min: 0,
|
|
max: 100,
|
|
step: 1,
|
|
precision: 0,
|
|
size: 'default',
|
|
disabled: false,
|
|
controls: true,
|
|
controlsPosition: ''
|
|
},
|
|
editableProps: ['min', 'max', 'step', 'precision', 'size', 'disabled', 'controls', 'controlsPosition'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.SELECT]: {
|
|
type: ComponentType.SELECT,
|
|
label: '选择器',
|
|
icon: 'el-icon-arrow-down',
|
|
category: '表单',
|
|
defaultProps: {
|
|
placeholder: '请选择',
|
|
clearable: true,
|
|
disabled: false,
|
|
multiple: false,
|
|
filterable: false,
|
|
size: 'default'
|
|
},
|
|
editableProps: ['placeholder', 'clearable', 'disabled', 'multiple', 'filterable', 'size'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.RADIO]: {
|
|
type: ComponentType.RADIO,
|
|
label: '单选框',
|
|
icon: 'el-icon-check',
|
|
category: '表单',
|
|
defaultProps: {
|
|
label: '选项',
|
|
disabled: false,
|
|
border: false,
|
|
size: 'default'
|
|
},
|
|
editableProps: ['label', 'disabled', 'border', 'size'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.RADIO_GROUP]: {
|
|
type: ComponentType.RADIO_GROUP,
|
|
label: '单选框组',
|
|
icon: 'el-icon-check',
|
|
category: '表单',
|
|
defaultProps: {
|
|
disabled: false,
|
|
size: 'default',
|
|
textColor: '#ffffff',
|
|
fill: '#409eff'
|
|
},
|
|
editableProps: ['disabled', 'size', 'textColor', 'fill'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.CHECKBOX]: {
|
|
type: ComponentType.CHECKBOX,
|
|
label: '复选框',
|
|
icon: 'el-icon-check',
|
|
category: '表单',
|
|
defaultProps: {
|
|
label: '选项',
|
|
disabled: false,
|
|
border: false,
|
|
size: 'default',
|
|
indeterminate: false
|
|
},
|
|
editableProps: ['label', 'disabled', 'border', 'size', 'indeterminate'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.CHECKBOX_GROUP]: {
|
|
type: ComponentType.CHECKBOX_GROUP,
|
|
label: '复选框组',
|
|
icon: 'el-icon-check',
|
|
category: '表单',
|
|
defaultProps: {
|
|
disabled: false,
|
|
min: null,
|
|
max: null,
|
|
size: 'default',
|
|
textColor: '#ffffff',
|
|
fill: '#409eff'
|
|
},
|
|
editableProps: ['disabled', 'min', 'max', 'size', 'textColor', 'fill'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.SWITCH]: {
|
|
type: ComponentType.SWITCH,
|
|
label: '开关',
|
|
icon: 'el-icon-switch-button',
|
|
category: '表单',
|
|
defaultProps: {
|
|
disabled: false,
|
|
width: 40,
|
|
activeText: '',
|
|
inactiveText: '',
|
|
activeValue: true,
|
|
inactiveValue: false,
|
|
activeColor: '#409eff',
|
|
inactiveColor: '#dcdfe6'
|
|
},
|
|
editableProps: ['disabled', 'width', 'activeText', 'inactiveText', 'activeValue', 'inactiveValue', 'activeColor', 'inactiveColor'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.SLIDER]: {
|
|
type: ComponentType.SLIDER,
|
|
label: '滑块',
|
|
icon: 'el-icon-minus',
|
|
category: '表单',
|
|
defaultProps: {
|
|
min: 0,
|
|
max: 100,
|
|
step: 1,
|
|
disabled: false,
|
|
showStops: false,
|
|
showTooltip: true,
|
|
range: false
|
|
},
|
|
editableProps: ['min', 'max', 'step', 'disabled', 'showStops', 'showTooltip', 'range'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.DATE_PICKER]: {
|
|
type: ComponentType.DATE_PICKER,
|
|
label: '日期选择器',
|
|
icon: 'el-icon-date',
|
|
category: '表单',
|
|
defaultProps: {
|
|
type: 'date',
|
|
placeholder: '选择日期',
|
|
format: 'YYYY-MM-DD',
|
|
valueFormat: 'YYYY-MM-DD',
|
|
disabled: false,
|
|
clearable: true,
|
|
size: 'default'
|
|
},
|
|
editableProps: ['type', 'placeholder', 'format', 'valueFormat', 'disabled', 'clearable', 'size'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.TIME_PICKER]: {
|
|
type: ComponentType.TIME_PICKER,
|
|
label: '时间选择器',
|
|
icon: 'el-icon-time',
|
|
category: '表单',
|
|
defaultProps: {
|
|
placeholder: '选择时间',
|
|
format: 'HH:mm:ss',
|
|
valueFormat: 'HH:mm:ss',
|
|
disabled: false,
|
|
clearable: true,
|
|
size: 'default'
|
|
},
|
|
editableProps: ['placeholder', 'format', 'valueFormat', 'disabled', 'clearable', 'size'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.CARD]: {
|
|
type: ComponentType.CARD,
|
|
label: '卡片',
|
|
icon: 'el-icon-document',
|
|
category: '数据展示',
|
|
defaultProps: {
|
|
header: '卡片标题',
|
|
shadow: 'always',
|
|
bodyStyle: {}
|
|
},
|
|
editableProps: ['header', 'shadow', 'bodyStyle'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.TAG]: {
|
|
type: ComponentType.TAG,
|
|
label: '标签',
|
|
icon: 'el-icon-price-tag',
|
|
category: '数据展示',
|
|
defaultProps: {
|
|
type: 'primary',
|
|
closable: false,
|
|
disableTransitions: false,
|
|
hit: false,
|
|
color: '',
|
|
size: 'default',
|
|
effect: 'light',
|
|
children: '标签'
|
|
},
|
|
editableProps: ['type', 'closable', 'disableTransitions', 'hit', 'color', 'size', 'effect', 'children'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.PROGRESS]: {
|
|
type: ComponentType.PROGRESS,
|
|
label: '进度条',
|
|
icon: 'el-icon-loading',
|
|
category: '数据展示',
|
|
defaultProps: {
|
|
percentage: 50,
|
|
type: 'line',
|
|
strokeWidth: 6,
|
|
textInside: false,
|
|
status: '',
|
|
color: '#409eff',
|
|
showText: true,
|
|
format: null
|
|
},
|
|
editableProps: ['percentage', 'type', 'strokeWidth', 'textInside', 'status', 'color', 'showText'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.ALERT]: {
|
|
type: ComponentType.ALERT,
|
|
label: '警告',
|
|
icon: 'el-icon-warning',
|
|
category: '反馈',
|
|
defaultProps: {
|
|
title: '警告标题',
|
|
type: 'info',
|
|
description: '',
|
|
closable: true,
|
|
center: false,
|
|
closeText: '',
|
|
showIcon: false,
|
|
effect: 'light'
|
|
},
|
|
editableProps: ['title', 'type', 'description', 'closable', 'center', 'closeText', 'showIcon', 'effect'],
|
|
canHaveChildren: false
|
|
},
|
|
|
|
[ComponentType.FORM]: {
|
|
type: ComponentType.FORM,
|
|
label: '表单',
|
|
icon: 'el-icon-document',
|
|
category: '表单',
|
|
defaultProps: {
|
|
model: 'formData',
|
|
rules: 'formRules',
|
|
labelWidth: '120px',
|
|
labelPosition: 'right',
|
|
inline: false,
|
|
labelSuffix: '',
|
|
hideRequiredAsterisk: false,
|
|
showMessage: true,
|
|
inlineMessage: false,
|
|
statusIcon: false,
|
|
validateOnRuleChange: true,
|
|
size: 'default',
|
|
disabled: false
|
|
},
|
|
editableProps: ['labelWidth', 'labelPosition', 'inline', 'labelSuffix', 'hideRequiredAsterisk', 'showMessage', 'inlineMessage', 'statusIcon', 'validateOnRuleChange', 'size', 'disabled'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.FORM_ITEM]: {
|
|
type: ComponentType.FORM_ITEM,
|
|
label: '表单项',
|
|
icon: 'el-icon-tickets',
|
|
category: '表单',
|
|
defaultProps: {
|
|
label: '标签',
|
|
labelWidth: '',
|
|
required: false,
|
|
rules: null,
|
|
error: '',
|
|
showMessage: true,
|
|
inlineMessage: false,
|
|
size: 'default'
|
|
},
|
|
editableProps: ['label', 'labelWidth', 'required', 'showMessage', 'inlineMessage', 'size'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.ROW]: {
|
|
type: ComponentType.ROW,
|
|
label: '行布局',
|
|
icon: 'el-icon-menu',
|
|
category: '布局',
|
|
defaultProps: {
|
|
style: {
|
|
display: 'flex',
|
|
flexDirection: 'row',
|
|
gap: '12px',
|
|
padding: '12px',
|
|
border: '1px dashed #dcdfe6',
|
|
borderRadius: '4px',
|
|
minHeight: '60px'
|
|
}
|
|
},
|
|
editableProps: ['gap', 'justifyContent', 'alignItems'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.COL]: {
|
|
type: ComponentType.COL,
|
|
label: '列布局',
|
|
icon: 'el-icon-menu',
|
|
category: '布局',
|
|
defaultProps: {
|
|
style: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '12px',
|
|
padding: '12px',
|
|
border: '1px dashed #dcdfe6',
|
|
borderRadius: '4px',
|
|
minHeight: '60px'
|
|
}
|
|
},
|
|
editableProps: ['gap', 'justifyContent', 'alignItems'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.CONTAINER]: {
|
|
type: ComponentType.CONTAINER,
|
|
label: '容器',
|
|
icon: 'el-icon-s-grid',
|
|
category: '布局',
|
|
defaultProps: {
|
|
direction: 'vertical'
|
|
},
|
|
editableProps: ['direction'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.HEADER]: {
|
|
type: ComponentType.HEADER,
|
|
label: '头部',
|
|
icon: 'el-icon-top',
|
|
category: '布局',
|
|
defaultProps: {
|
|
height: '60px'
|
|
},
|
|
editableProps: ['height'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.MAIN]: {
|
|
type: ComponentType.MAIN,
|
|
label: '主要区域',
|
|
icon: 'el-icon-s-grid',
|
|
category: '布局',
|
|
defaultProps: {},
|
|
editableProps: [],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.ASIDE]: {
|
|
type: ComponentType.ASIDE,
|
|
label: '侧边栏',
|
|
icon: 'el-icon-s-unfold',
|
|
category: '布局',
|
|
defaultProps: {
|
|
width: '300px'
|
|
},
|
|
editableProps: ['width'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.FOOTER]: {
|
|
type: ComponentType.FOOTER,
|
|
label: '底部',
|
|
icon: 'el-icon-bottom',
|
|
category: '布局',
|
|
defaultProps: {
|
|
height: '60px'
|
|
},
|
|
editableProps: ['height'],
|
|
canHaveChildren: true
|
|
},
|
|
|
|
[ComponentType.DIVIDER]: {
|
|
type: ComponentType.DIVIDER,
|
|
label: '分割线',
|
|
icon: 'el-icon-minus',
|
|
category: '其他',
|
|
defaultProps: {
|
|
direction: 'horizontal',
|
|
contentPosition: 'center',
|
|
children: ''
|
|
},
|
|
editableProps: ['direction', 'contentPosition', 'children'],
|
|
canHaveChildren: false
|
|
}
|
|
};
|
|
|
|
// 组件分类
|
|
export const COMPONENT_CATEGORIES = {
|
|
'基础': [ComponentType.BUTTON, ComponentType.LINK, ComponentType.TEXT],
|
|
'布局': [ComponentType.ROW, ComponentType.COL, ComponentType.CONTAINER, ComponentType.HEADER, ComponentType.MAIN, ComponentType.ASIDE, ComponentType.FOOTER],
|
|
'表单': [ComponentType.FORM, ComponentType.FORM_ITEM, ComponentType.INPUT, ComponentType.INPUT_NUMBER, ComponentType.SELECT, ComponentType.RADIO, ComponentType.RADIO_GROUP, ComponentType.CHECKBOX, ComponentType.CHECKBOX_GROUP, ComponentType.SWITCH, ComponentType.SLIDER, ComponentType.TIME_PICKER, ComponentType.DATE_PICKER],
|
|
'数据展示': [ComponentType.CARD, ComponentType.TAG, ComponentType.PROGRESS],
|
|
'反馈': [ComponentType.ALERT],
|
|
'其他': [ComponentType.DIVIDER]
|
|
};
|
|
|
|
// 生成唯一 ID
|
|
export function generateId(): string {
|
|
return `component_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
}
|
|
|
|
// 创建组件实例
|
|
export function createComponent(type: ComponentType, props: Record<string, any> = {}): ComponentSchema {
|
|
const config = COMPONENT_CONFIGS[type];
|
|
if (!config) {
|
|
throw new Error(`Unknown component type: ${type}`);
|
|
}
|
|
|
|
const component: ComponentSchema = {
|
|
id: generateId(),
|
|
type,
|
|
props: { ...config.defaultProps, ...props },
|
|
children: config.canHaveChildren ? [] : undefined
|
|
};
|
|
|
|
// 布局组件初始化为空容器,不自动生成子组件
|
|
|
|
return component;
|
|
}
|
|
|
|
// 验证组件 Schema
|
|
export function validateComponentSchema(schema: ComponentSchema): boolean {
|
|
if (!schema.id || !schema.type) {
|
|
return false;
|
|
}
|
|
|
|
const config = COMPONENT_CONFIGS[schema.type as ComponentType];
|
|
if (!config) {
|
|
return false;
|
|
}
|
|
|
|
if (config.canHaveChildren && schema.children) {
|
|
return schema.children.every(child => validateComponentSchema(child));
|
|
}
|
|
|
|
return true;
|
|
}
|