You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
vite-vue3-lowcode/src/visual-editor/visual-editor.utils.ts

280 lines
7.3 KiB

import type { VisualEditorProps } from './visual-editor.props'
import { inject, provide } from 'vue'
import { useDotProp } from '@/visual-editor/hooks/useDotProp'
import type { RequestEnum, ContentTypeEnum } from '@/enums/httpEnum'
/**
* @description 组件属性
*/
export interface VisualEditorBlockData {
_vid: string // 组件id 时间戳
moduleName: keyof ComponentModules // 组件所属的模块(基础组件、容器组件)
componentKey: string // 映射 VisualEditorConfig 中 componentMap 的 component对象
label: string // 组件标签名称
top: number // 组件的top定位
left: number // 组件的left定位
adjustPosition: boolean // 是否需要调整位置
focus: boolean // 当前是否为选中状态
zIndex: number // z-index值
width: number // 组件宽度
height: number // 组件高度
hasResize: boolean // 是否调整过宽度或者高度
props: Record<string, any> // 组件的设计属性
model: Record<string, string> // 绑定的字段
slotName?: string // 组件唯一标识
animations?: Animation[] // 动画集
actions: Action[] // 组件动作集合
events: { label: string; value: string }[] // 组件事件集合
[prop: string]: any
}
/**
* @description 组件动作事件处理
*/
export interface ActionHandle {
key: string
name: string
link: string
data?: {
bind?: string
recv?: string
}
}
/**
* @description 组件动作
*/
export interface Action {
key: string
name: string
event: string
handle: ActionHandle[]
}
/**
* @description 页面配置
*/
export interface PageConfig {
bgImage: string // 背景图片
bgColor: string // 背景颜色
}
/**
* @description 页面对象
*/
export interface VisualEditorPage {
title: string // 页面标题
path: string // 页面路径
isDefault?: boolean // 404是重定向到默认页面
config: PageConfig // 页面配置
blocks: VisualEditorBlockData[] // 当前页面的所有组件
}
/**
* @description 可以认为是 路由=>页面
*/
export interface VisualEditorPages {
[path: string]: VisualEditorPage
}
/**
* @description 实体类型
*/
export type EntityType = {
key: string // 绑定的字段 输入
name: string // 实体名称 输入
type: string // 数据类型 选择
value: string // 默认值 输入
}
/**
* @description 数据模型
*/
export interface VisualEditorModel {
name: string // 数据源名称
key: string // 绑定的字段 该字段创建的时候生成
entitys: EntityType[] // 实体集合
}
/**
* @description 接口请求对象
*/
export interface FetchApiItem {
key: string // 随机生成的key
name: string // 当前api名字
options: {
url: string // 请求的url
method: keyof typeof RequestEnum // 请求的方法
contentType: keyof typeof ContentTypeEnum // 请求的内容类型
}
data: {
bind: string // 请求绑定对应的某个实体
recv: string // 响应的结果绑定到某个实体上
}
}
/**
* @description 动作集合
*/
export interface VisualEditorActions {
fetch: {
name: '接口请求'
apis: FetchApiItem[]
}
dialog: {
name: '对话框'
handlers: []
}
}
/**
* @description 总的数据集
*/
export interface VisualEditorModelValue {
pages: VisualEditorPages // 页面
models: VisualEditorModel[] // 实体
actions: VisualEditorActions // 动作
}
/**
* @description 动画项
*/
export interface Animation {
label: string // 动画名称
value: string // 动画类名
duration: number // 动画持续时间
delay: number // 动画延迟多久执行
count: number // 动画执行次数
infinite: boolean // 是否无限循环动画
}
/**
* @description 单个组件注册规则
*/
export interface VisualEditorComponent {
key: string // 组件名称
moduleName: keyof ComponentModules // 模块名称
_vid?: string // 组件id 时间戳
label: string
preview: () => JSX.Element
render: (data: {
props: any
model: any
block: VisualEditorBlockData
size: { width?: number; height?: number }
custom: Record<string, any>
}) => JSX.Element
props?: Record<string, VisualEditorProps>
animations?: Animation[] // 动画集
events?: { label: string; value: string }[] // 组件事件集合
resize?: { width?: boolean; height?: boolean }
}
export interface VisualEditorMarkLines {
x: { left: number; showLeft: number }[]
y: { top: number; showTop: number }[]
}
export function createNewBlock({
component,
left,
top
}: {
component: VisualEditorComponent
top: number
left: number
}): VisualEditorBlockData {
component._vid = `${component._vid}`.startsWith('vid_') ? component._vid : `vid_${component._vid}`
return {
top,
left,
_vid: component._vid!,
moduleName: component.moduleName,
componentKey: component!.key,
label: component!.label,
adjustPosition: true,
focus: false,
zIndex: 0,
width: 0,
height: 0,
hasResize: false,
props: Object.keys(component.props || {}).reduce((prev, curr) => {
const { propObj, prop } = useDotProp(prev, curr)
if (component.props![curr]?.defaultValue) {
propObj[prop] = prev[curr] = component.props![curr]?.defaultValue
}
return prev
}, {}),
animations: [], // 动画集
actions: [], // 动作集合
events: component.events || [], // 事件集合
model: {}
}
}
export interface VisualDragEvent {
dragstart: {
on: (cb: () => void) => void
off: (cb: () => void) => void
emit: () => void
}
dragend: {
on: (cb: () => void) => void
off: (cb: () => void) => void
emit: () => void
}
}
export const VisualDragProvider = (() => {
const VISUAL_DRAG_PROVIDER = '@@VISUAL_DRAG_PROVIDER'
return {
provide: (data: VisualDragEvent) => {
provide(VISUAL_DRAG_PROVIDER, data)
},
inject: () => {
return inject(VISUAL_DRAG_PROVIDER) as VisualDragEvent
}
}
})()
// 组件模块
export interface ComponentModules {
baseWidgets: VisualEditorComponent[] // 基础组件
containerComponents: VisualEditorComponent[] // 容器组件
}
/**
* @description 创建编辑器配置
* @returns {} 返回编辑器注册组件的方法等
*/
export function createVisualEditorConfig() {
const componentModules: ComponentModules = {
baseWidgets: [],
containerComponents: []
}
// const componentList: VisualEditorComponent[] = []
const componentMap: Record<string, VisualEditorComponent> = {}
return {
componentModules,
componentMap,
registry: <
_,
Props extends Record<string, VisualEditorProps> = {},
Model extends Record<string, string> = {}
>(
moduleName: keyof ComponentModules,
key: string,
component: {
label: string
preview: () => JSX.Element
render: (data: {
props: { [k in keyof Props]: any }
model: Partial<{ [k in keyof Model]: any }>
block: VisualEditorBlockData
size: { width?: number; height?: number }
custom: Record<string, any>
}) => JSX.Element
props?: Props
model?: Model
resize?: { width?: boolean; height?: boolean }
}
) => {
const comp = { ...component, key, moduleName }
componentModules[moduleName].push(comp)
componentMap[key] = comp
}
}
}
export type VisualEditorConfig = ReturnType<typeof createVisualEditorConfig>