devject-design
v0.4.0
Published
Devject Design 是一个基于 Vue 3 和 Element Plus 的可视化表单设计器与渲染器组件库。它允许用户通过拖拽方式快速创建表单布局,并支持将设计好的表单导出为 JSON 数据,或直接在应用中渲染。
Readme
Devject Design
Devject Design 是一个基于 Vue 3 和 Element Plus 的可视化表单设计器与渲染器组件库。它允许用户通过拖拽方式快速创建表单布局,并支持将设计好的表单导出为 JSON 数据,或直接在应用中渲染。
✨ 特性
- ⚡ 基于 Vue 3 + TypeScript:使用最新的技术栈,提供完整的类型定义。
- 🎨 Element Plus 风格:与 Element Plus UI 完美融合。
- 🧩 可视化设计器:支持拖拽、属性配置、实时预览。
- 📝 表单渲染器:根据 JSON 结构自动渲染表单,支持数据双向绑定与校验。
- 👁️ 节点可见/只读:所有内置组件支持“可见”控制,输入类组件支持“只读”控制。
- 📤 导入/导出:支持 JSON 格式的布局与数据导入导出。
- 📊 高级组件:
- 静态表格 (StaticTable):支持 Excel 导入(自动识别表头、清空旧数据)与导出。
- 表格表单 (Table):动态表格组件,支持分页、增删改查及数据绑定。
- 树形控件 (Tree):支持层级路径格式(如
父节点/子节点)的 Excel 导入导出,支持父子关联配置。
- 🏗️ 复杂布局:内置栅格布局 (FlexRow)、折叠面板 (Collapse)、标签页 (Tabs) 等容器组件。
📦 安装
使用 npm 或 yarn 安装:
npm install devject-design依赖项
本库依赖 vue 和 element-plus,请确保项目中已安装:
npm install vue element-plus如果使用表格/树形组件的 Excel 导入导出功能,还需要安装 xlsx:
npm install xlsx⚙️ 配置说明
本库支持通过 Props 配置上传和下载接口。
属性说明
FormDesigner 和 FormRenderer 组件都支持以下 Props:
| 属性名 | 说明 | 类型 | 默认值 |
| :--- |:---------------------------------------------| :--- | :--- |
| upload-url | 文件上传接口地址 | string | - |
| download-url | 文件下载/访问基础路径 | string | - |
| upload-parse | 上传响应解析路径,支持 $、$.data.url 这类写法 | string | $ |
| file-headers | 文件上传与下载时透传的请求头(如 Authorization) | Record<string, string> | - |
示例
在你的 Vite 项目中,你可以使用环境变量来传递这些值:
// .env
VITE_DEVJECT_UPLOAD_URL=/api/file/upload
VITE_DEVJECT_DOWNLOAD_URL=/api/file/download
VITE_DEVJECT_UPLOAD_PARSE=$.data.url<template>
<FormDesigner
:upload-url="uploadUrl"
:download-url="downloadUrl"
:upload-parse="uploadParse"
:file-headers="fileHeaders"
/>
</template>
<script setup lang="ts">
import { FormDesigner } from 'devject-design'
import { getToken } from '@/utils/common'
const uploadUrl = import.meta.env.VITE_DEVJECT_UPLOAD_URL
const downloadUrl = import.meta.env.VITE_DEVJECT_DOWNLOAD_URL
const uploadParse = import.meta.env.VITE_DEVJECT_UPLOAD_PARSE || '$'
const token = getToken()
const fileHeaders = token ? { Authorization: token.startsWith('Bearer ') ? token : `Bearer ${token}` } : {}
</script>上传/下载鉴权
当你的文件接口启用了登录校验时,请通过 file-headers 透传认证头。file-headers 会同时作用于:
- 文件上传组件(
/upload请求) - 文件下载/图片预览(
/download请求)
🚀 快速开始
全局注册
在你的入口文件(如 main.ts)中引入 Element Plus 和 Devject Design:
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import DevjectDesign from 'devject-design'
import 'devject-design/dist/style.css'
const app = createApp(App)
app.use(ElementPlus)
app.use(DevjectDesign)
app.mount('#app')使用表单设计器 (FormDesigner)
FormDesigner 支持通过 v-model 双向绑定布局数据,并提供了导入导出的方法。
<template>
<div style="width:100%;height: 100%;overflow: hidden">
<!-- 顶部操作栏示例 -->
<div>
<button @click="handleExport">导出布局</button>
<button @click="handleImport">导入布局</button>
</div>
<FormDesigner
ref="designerRef"
v-model="layoutSchema"
:upload-url="uploadUrl"
:download-url="downloadUrl"
:upload-parse="uploadParse"
:file-headers="fileHeaders"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { FormDesigner } from 'devject-design'
import type { LayoutSchema } from 'devject-design'
import { getToken } from '@/utils/common'
const uploadUrl = import.meta.env.VITE_DEVJECT_UPLOAD_URL
const downloadUrl = import.meta.env.VITE_DEVJECT_DOWNLOAD_URL
const uploadParse = import.meta.env.VITE_DEVJECT_UPLOAD_PARSE || '$'
const token = getToken()
const fileHeaders = token ? { Authorization: token.startsWith('Bearer ') ? token : `Bearer ${token}` } : {}
// 双向绑定布局数据
const layoutSchema = ref<LayoutSchema>()
const designerRef = ref()
// 调用组件方法导出
const handleExport = () => {
if (designerRef.value) {
const layout = designerRef.value.exportLayout()
console.log('Exported:', layout)
}
}
// 调用组件方法导入
const handleImport = () => {
if (designerRef.value) {
designerRef.value.importLayout({
version: 1,
root: { children: [] }
})
}
}
</script>使用表单渲染器 (FormRenderer)
FormRenderer 支持通过 v-model 绑定表单数据,并提供了数据导入导出方法。
<template>
<div>
<!-- 操作按钮 -->
<button @click="getFormData">获取数据</button>
<button @click="setFormData">设置数据</button>
<FormRenderer
ref="rendererRef"
:schema="formSchema"
v-model="formData"
:upload-url="uploadUrl"
:download-url="downloadUrl"
:upload-parse="uploadParse"
:file-headers="fileHeaders"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { FormRenderer } from 'devject-design'
import type { LayoutSchema, FormDataModel } from 'devject-design'
import { getToken } from '@/utils/common'
const uploadUrl = import.meta.env.VITE_DEVJECT_UPLOAD_URL
const downloadUrl = import.meta.env.VITE_DEVJECT_DOWNLOAD_URL
const uploadParse = import.meta.env.VITE_DEVJECT_UPLOAD_PARSE || '$'
const token = getToken()
const fileHeaders = token ? { Authorization: token.startsWith('Bearer ') ? token : `Bearer ${token}` } : {}
const rendererRef = ref()
// 表单结构定义
const formSchema = ref<LayoutSchema>({ /* ... */ })
// 表单数据双向绑定
const formData = ref<FormDataModel>({})
// 调用组件方法获取数据
const getFormData = async () => {
if (rendererRef.value) {
try {
// 默认会先进行校验,校验通过后返回数据
const data = await rendererRef.value.exportData()
console.log('Form Data:', data)
} catch (e) {
console.error('Validation failed')
}
}
}
// 调用组件方法设置数据
const setFormData = () => {
if (rendererRef.value) {
rendererRef.value.importData({ field1: 'value1' })
}
}
</script>🧩 内置组件详解
除了基础的 Input, Select, Radio, Checkbox 外,本库还包含以下高级组件:
1. 静态表格 (StaticTable)
用于展示或编辑固定的行列数据。
- Excel 导入: 支持直接导入
.xlsx文件。导入时会自动识别首行作为表头(列名),并自动清空旧的列配置和数据。 - Excel 导出: 支持将当前表格数据导出为 Excel 文件。
- 数据绑定: 修改列标题不会丢失该列已有的数据。
2. 表格表单 (Table)
用于展示动态数据列表。
- 分页支持: 可配置是否显示分页及每页条数。
- 增删改查: 内置添加和删除行操作。
- 列配置: 支持自定义列类型(文本、数字、下拉等)及校验规则。
3. 树形控件 (Tree)
用于展示层级数据。
- Excel 导入: 支持导入路径格式的 Excel(例如:
一级/二级/三级),系统会自动解析为树形结构。 - 关联配置: 提供
关联开关。开启时,父子节点选中状态联动;关闭时,各节点独立选中。 - 缩进设置: 可自定义树节点的缩进大小。
4. 布局组件
- FlexRow: 栅格布局,支持自定义列宽比例。
- Collapse: 折叠面板,支持多面板展开/收起。
- Tabs: 标签页布局,支持动态增删标签页。
📚 组件 API
FormDesigner
表单设计器组件,提供完整的画布、组件库和属性面板。
Props
| 属性 | 说明 | 类型 | 默认值 |
| :--- | :--- | :--- | :--- |
| modelValue | 表单布局结构绑定 (v-model) | LayoutSchema | - |
| upload-url | 文件上传接口地址 | string | - |
| download-url | 文件下载/访问基础路径 | string | - |
| upload-parse | 上传响应解析路径 | string | $ |
| file-headers | 文件上传/下载透传请求头 | Record<string, string> | - |
Exposed Methods
| 方法名 | 说明 | 参数 | 返回值 |
| :--- | :--- | :--- | :--- |
| exportLayout | 导出当前布局结构 | - | LayoutSchema |
| importLayout | 导入布局结构 | json: LayoutSchema | void |
FormRenderer
表单渲染器组件,用于展示设计好的表单。
Props
| 属性 | 说明 | 类型 | 默认值 |
| :--- | :--- | :--- | :--- |
| schema | (必填) 表单布局结构对象 | LayoutSchema | - |
| modelValue | (必填) 表单数据绑定 (v-model) | FormDataModel | {} |
| upload-url | 文件上传接口地址 | string | - |
| download-url | 文件下载/访问基础路径 | string | - |
| upload-parse | 上传响应解析路径 | string | $ |
| file-headers | 文件上传/下载透传请求头 | Record<string, string> | - |
Exposed Methods
| 方法名 | 说明 | 参数 | 返回值 |
| :--- | :--- | :--- | :--- |
| exportData | 导出当前表单数据 (默认进行校验) | validateBeforeExport?: boolean (默认 true) | Promise<FormDataModel> |
| importData | 导入表单数据 | data: FormDataModel | void |
| validate | 校验表单 | - | Promise<boolean> |
🛠 自定义组件开发
Devject Design 支持通过注册自定义组件来扩展设计器的能力。
1. 注册组件 API
使用 registerComponent 函数来注册一个新的组件。
import { registerComponent, type ComponentDefinition } from 'devject-design';
const myComponentDef: ComponentDefinition = {
type: 'my-custom-type', // 组件唯一标识
typeCn: '我的组件', // 设计器内显示的组件中文名
component: MyComponentVue, // Vue 组件对象
palette: {
label: '我的组件',
group: '自定义', // 在组件面板中的分组
icon: 'Operation' // (可选) 组件面板显示的图标名称 (支持所有 Element Plus 图标)
},
defaultProps: (id) => ({
id,
type: 'my-custom-type',
props: {
// 组件默认属性
someProp: 'defaultValue'
}
}),
// 自定义校验逻辑
validate: (value, node, formData) => {
if (!value) return '该项不能为空';
if (value.length < 5) return '长度至少为5';
return true; // 校验通过返回 true
},
// 可选:数据处理器
dataProcessor: {
import: (val) => val, // 导入时处理数据
export: (val) => val // 导出时处理数据
}
};
registerComponent(myComponentDef);2. 编写 Vue 组件
自定义组件会接收 node props,其中包含组件的配置信息。
<!-- MyComponent.vue -->
<template>
<div class="my-component">
<h3>{{ node.props.label || 'My Component' }}</h3>
<p>属性值: {{ node.props.someProp }}</p>
<!-- 如果是输入类组件,可以使用 inject 获取 formModel -->
</div>
</template>
<script setup lang="ts">
import type { SchemaNode } from 'devject-design';
defineProps<{
node: SchemaNode
}>();
</script>3. 集成示例
在你的应用入口或组件初始化处进行注册:
import { registerComponent } from 'devject-design';
import MyCustomComponent from './MyCustomComponent.vue';
// 注册组件
registerComponent({
type: 'custom-widget',
typeCn: '自定义控件',
component: MyCustomComponent,
palette: { label: '自定义控件', group: '业务组件' },
defaultProps: (id) => ({
id,
type: 'custom-widget',
props: { label: '默认标题' }
})
});4. 自定义属性编辑器
除了注册组件本身,你还可以为组件属性注册自定义编辑器。
1. 编写编辑器组件
编辑器组件接收以下 Props:
modelValue: 当前属性值nodeId: 当前选中的组件 ID- 其他自定义 Props
<!-- MyColorEditor.vue -->
<template>
<el-color-picker :modelValue="modelValue" @change="$emit('update:modelValue', $event)" />
</template>
<script setup lang="ts">
defineProps<{
modelValue: string,
nodeId: string
}>();
defineEmits(['update:modelValue']);
</script>2. 注册编辑器
使用 registerEditor 函数注册编辑器。
import { registerEditor } from 'devject-design';
import MyColorEditor from './MyColorEditor.vue';
registerEditor('my-color-editor', MyColorEditor);3. 在组件配置中使用
在 propsConfig 中引用已注册的编辑器名称。
registerComponent({
// ...
propsConfig: [
{
label: "颜色配置",
name: "textColor",
type: "custom",
editor: "my-color-editor" // 使用注册的编辑器名称
}
]
});✅ 表单校验
Devject Design 提供了一套灵活的表单校验机制,支持内置的必填校验和自定义的函数校验。
1. 必填校验
在设计器中选中组件,勾选"必填"属性即可开启。
- 系统会自动根据组件类型(如输入框、下拉框)生成相应的提示语,例如"请输入用户名"或"请选择城市"。
- 提示语生成逻辑:
请[输入/选择][组件标题]。
2. 自定义校验
通过 registerComponent 注册组件时,可以提供 validate 函数来实现复杂的校验逻辑。
registerComponent({
// ... 其他配置
validate: (value, node, formData) => {
// value: 当前字段的值
// node: 当前组件的配置节点信息
// formData: 整个表单的数据对象
if (!value && node.props.required) {
return true; // 如果为空且必填,由内置必填规则处理,这里可以返回 true 跳过
}
if (value && value.length < 5) {
return '长度不能少于 5 个字符'; // 返回字符串作为错误提示
}
// 支持异步校验 (返回 Promise)
// return fetch('/api/check?val=' + value).then(res => res.ok ? true : '校验失败');
return true; // 校验通过
}
});3. 触发校验
- 自动触发:表单输入或失去焦点时自动触发。
- 手动触发:通过
FormRenderer的validate()方法。 - 导出时触发:调用
exportData()时默认会自动校验,校验失败则抛出异常。
4. 可见与只读
- 所有内置组件都支持
visible属性;当值为false时,节点会在FormRenderer预览态中隐藏。 - 设计器画布不会因为
visible=false而隐藏节点,方便继续选中和编辑。 - 输入类组件额外支持
readonly属性,用于将单个组件设为只读。 FormRenderer顶层readonly仍然保留,适合整张表单统一只读;节点级readonly适合精细控制单个组件。
📄 许可证
MIT
