Appearance
BizForm Schema 表单
页面级 Schema 表单:fields 声明 17 类控件(input / email / url / textarea / number / select / date / datetime / time / daterange / cascader / switch / radio / checkbox / rate / slider / slot;email / url 复用 BizLinkInput(邮箱/网址双模式),域名白名单、https-only 等经 attrs 透传),24 栅格布局,view 只读态同一套 schema 查看⇄编辑双态渲染,show / disabled 函数联动,required 自动规则 + rules 叠加,v-model:model 双向。
弹层场景用 BizFormDialog(新增 / 编辑二合一弹窗),两者共用同一渲染内核 BizFormBody——本组件即该内核的页面级公开形态(内核由 biz-form-dialog 包内私有模块下沉而来,biz-form-designer 预览态同样复用)。适用于配置页 / 设置页内嵌表单、页面区块编辑表单、详情页查看⇄编辑同构切换。
基础表单
两列栅格(span 12)+ 必填自动规则 + pattern 叠加规则 + 整行 textarea(span 24)。
<script setup lang="ts">
/** 场景:基础表单——两列栅格 + 必填校验 + 提交 */
import { ref } from 'vue'
import { ElButton, ElMessage } from 'element-plus'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField, BizFormInstance } from 'vue3-ep-biz-lib'
const formRef = ref<BizFormInstance>()
const model = ref<Record<string, unknown>>({})
const fields: BizFormField[] = [
{ prop: 'name', label: '姓名', required: true, attrs: { maxlength: 20 } },
{ prop: 'phone', label: '手机号', required: true, rules: [{ pattern: /^1\d{10}$/, message: '手机号格式不正确', trigger: 'blur' }] },
{ prop: 'dept', label: '部门', type: 'select', required: true, options: [
{ label: '研发部', value: 'rd' },
{ label: '产品部', value: 'pd' },
{ label: '市场部', value: 'mk' },
] },
{ prop: 'entryDate', label: '入职日期', type: 'date' },
{ prop: 'remark', label: '备注', type: 'textarea', span: 24, attrs: { rows: 2 } },
]
const submit = (): void => {
void formRef.value?.validate().then((valid) => {
if (valid) ElMessage.success(`提交:${JSON.stringify(formRef.value?.getValues())}`)
})
}
</script>
<template>
<div style="max-width: 720px">
<BizForm ref="formRef" v-model:model="model" :fields="fields" />
<ElButton type="primary" @click="submit">提 交</ElButton>
</div>
</template>
显示代码
全控件类型
控件一屏总览:三列栅格(default-span 8),cascader 级联选项经
children 递归。<script setup lang="ts">
/** 场景:全控件类型总览——15 类字段一屏排布 */
import { ref } from 'vue'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField } from 'vue3-ep-biz-lib'
const model = ref<Record<string, unknown>>({
checkbox: ['a', 'c'],
rate: 4,
slider: 35,
cascader: ['fj', 'xm'],
})
const fields: BizFormField[] = [
{ prop: 'input', label: '单行文本', span: 8 },
{ prop: 'number', label: '数字', type: 'number', span: 8, attrs: { min: 0, max: 100 } },
{ prop: 'select', label: '下拉', type: 'select', span: 8, options: [{ label: '选项一', value: 1 }, { label: '选项二', value: 2 }] },
{ prop: 'date', label: '日期', type: 'date', span: 8 },
{ prop: 'datetime', label: '日期时间', type: 'datetime', span: 8 },
{ prop: 'time', label: '时间', type: 'time', span: 8 },
{ prop: 'daterange', label: '日期区间', type: 'daterange', span: 12 },
{ prop: 'cascader', label: '级联', type: 'cascader', span: 12, options: [
{ label: '福建', value: 'fj', children: [{ label: '厦门', value: 'xm' }, { label: '福州', value: 'fz' }] },
{ label: '广东', value: 'gd', children: [{ label: '深圳', value: 'sz' }, { label: '广州', value: 'gz' }] },
] },
{ prop: 'switch', label: '开关', type: 'switch', span: 8 },
{ prop: 'radio', label: '单选', type: 'radio', span: 8, options: [{ label: '甲', value: 'a' }, { label: '乙', value: 'b' }] },
{ prop: 'checkbox', label: '多选', type: 'checkbox', span: 8, options: [{ label: 'A', value: 'a' }, { label: 'B', value: 'b' }, { label: 'C', value: 'c' }] },
{ prop: 'rate', label: '评分', type: 'rate', span: 12, attrs: { showText: true } },
{ prop: 'slider', label: '滑块', type: 'slider', span: 12 },
{ prop: 'textarea', label: '文本域', type: 'textarea', span: 24, attrs: { rows: 2 } },
]
</script>
<template>
<BizForm v-model:model="model" :fields="fields" label-width="80px" />
</template>
显示代码
邮箱 / 网址字段
type: 'email' / 'url' 复用 BizLinkInput 双模式:校验、后缀建议、域名白名单(allowed-domains)、协议补全与 https-only(https-only)经 attrs 透传,required 自动规则触发时机为 blur。<script setup lang="ts">
/** 场景:邮箱 / 网址字段——type: 'email' / 'url' 复用 BizLinkInput 双模式,差异能力经 attrs 透传 */
import { ref } from 'vue'
import { ElButton, ElMessage } from 'element-plus'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField, BizFormInstance } from 'vue3-ep-biz-lib'
const formRef = ref<BizFormInstance>()
const model = ref<Record<string, unknown>>({})
const fields: BizFormField[] = [
{ prop: 'contact', label: '联系邮箱', type: 'email', required: true, attrs: { allowedDomains: ['company.com'] } },
{ prop: 'site', label: '官网地址', type: 'url', required: true, attrs: { httpsOnly: true } },
{ prop: 'docLink', label: '帮助文档', type: 'url', span: 24, attrs: { placeholder: 'https://docs.example.com' } },
]
const submit = (): void => {
void formRef.value?.validate().then((valid) => {
if (valid) ElMessage.success(`提交:${JSON.stringify(formRef.value?.getValues())}`)
})
}
</script>
<template>
<div style="max-width: 720px">
<BizForm ref="formRef" v-model:model="model" :fields="fields" />
<ElButton type="primary" @click="submit">提 交</ElButton>
</div>
</template>
显示代码
查看 ⇄ 编辑同构
view 只读态:同一套 schema,选项取 label、switch 取是 / 否、区间拼接、级联取全路径;viewFormatter 可自定义文案。<script setup lang="ts">
/** 场景:查看 ⇄ 编辑同构切换——同一套 schema,view 只读态智能取值(选项 label / 是 / 否 / 区间) */
import { ref } from 'vue'
import { ElRadioGroup, ElRadioButton } from 'element-plus'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField } from 'vue3-ep-biz-lib'
const view = ref(true)
const model = ref<Record<string, unknown>>({
name: '王敏',
level: 'B',
onJob: true,
joinRange: ['2026-01-01', '2026-06-30'],
area: ['fj', 'xm'],
score: 4,
})
const fields: BizFormField[] = [
{ prop: 'name', label: '客户名称' },
{ prop: 'level', label: '等级', type: 'select', options: [{ label: 'A 级', value: 'A' }, { label: 'B 级', value: 'B' }] },
{ prop: 'onJob', label: '战略合作', type: 'switch' },
{ prop: 'area', label: '所在区域', type: 'cascader', options: [
{ label: '福建', value: 'fj', children: [{ label: '厦门', value: 'xm' }] },
{ label: '广东', value: 'gd', children: [{ label: '深圳', value: 'sz' }] },
] },
{ prop: 'joinRange', label: '合作周期', type: 'daterange' },
{ prop: 'score', label: '满意度', type: 'rate' },
{ prop: 'note', label: '备注', type: 'textarea', span: 24, viewFormatter: (m) => String(m.note || '—') },
]
</script>
<template>
<div style="max-width: 720px">
<div style="margin-bottom: 12px">
<ElRadioGroup v-model="view">
<ElRadioButton :value="true">查看</ElRadioButton>
<ElRadioButton :value="false">编辑</ElRadioButton>
</ElRadioGroup>
</div>
<BizForm v-model:model="model" :fields="fields" :view="view" label-width="90px" />
</div>
</template>
显示代码
字段联动
show 按表单值显隐(专票才显示税号)、disabled 按值禁用(分期才可编辑预付比例)——函数拿到的就是当前表单数据。<script setup lang="ts">
/** 场景:字段联动——发票类型切换「专票税号必填」、结算周期控制预付款比例禁用 */
import { ref } from 'vue'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField } from 'vue3-ep-biz-lib'
const model = ref<Record<string, unknown>>({ invoiceType: 'normal', settle: 'full' })
const fields: BizFormField[] = [
{ prop: 'invoiceType', label: '发票类型', type: 'radio', options: [
{ label: '普票', value: 'normal' },
{ label: '专票', value: 'special' },
] },
{ prop: 'taxNo', label: '税号', show: (m) => m.invoiceType === 'special', required: true, attrs: { maxlength: 18 } },
{ prop: 'settle', label: '结算周期', type: 'select', options: [
{ label: '全款', value: 'full' },
{ label: '分期', value: 'installment' },
] },
{ prop: 'prepayRatio', label: '预付比例', type: 'number', span: 12, attrs: { min: 0, max: 100 },
disabled: (m) => m.settle !== 'installment',
show: (m) => m.settle === 'installment' },
{ prop: 'remark', label: '条款说明', type: 'textarea', span: 24 },
]
</script>
<template>
<div style="max-width: 720px">
<BizForm v-model:model="model" :fields="fields" />
</div>
</template>
显示代码
设置页内嵌(三列 + 整表禁用)
接口对接设置
锁定
default-span=8 三列紧凑布局;disabled 整表禁用(权限不足只读),defaultValue 初始回填。<script setup lang="ts">
/** 场景:设置页内嵌表单——三列紧凑布局 + 整表禁用开关(权限不足时只读) */
import { ref } from 'vue'
import { ElButton, ElMessage, ElSwitch } from 'element-plus'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField } from 'vue3-ep-biz-lib'
const disabled = ref(false)
const model = ref<Record<string, unknown>>({ retries: 3, timeout: 30, httpsOnly: true })
const fields: BizFormField[] = [
{ prop: 'endpoint', label: '回调地址', span: 24, defaultValue: 'https://api.example.com/hook' },
{ prop: 'retries', label: '重试次数', type: 'number', span: 8, attrs: { min: 0, max: 10 } },
{ prop: 'timeout', label: '超时(秒)', type: 'number', span: 8, attrs: { min: 5, max: 300 } },
{ prop: 'httpsOnly', label: '仅 HTTPS', type: 'switch', span: 8 },
{ prop: 'secret', label: '签名密钥', span: 24, attrs: { showPassword: true } },
]
const save = (): void => {
ElMessage.success('设置已保存')
}
</script>
<template>
<div style="max-width: 760px">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px">
<span style="font-weight: 600">接口对接设置</span>
<ElSwitch v-model="disabled" active-text="锁定" />
</div>
<BizForm v-model:model="model" :fields="fields" :default-span="8" :disabled="disabled" label-width="90px" />
<ElButton type="primary" :disabled="disabled" @click="save">保存设置</ElButton>
</div>
</template>
显示代码
slot 自定义控件
type: 'slot' 渲染具名插槽(name=prop),作用域暴露 model——特殊控件混入 schema 统一布局。<script setup lang="ts">
/** 场景:slot 自定义控件——业务特殊控件(颜色块选择)经具名插槽混入 schema */
import { ref } from 'vue'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField } from 'vue3-ep-biz-lib'
const COLORS = ['#409eff', '#67c23a', '#e6a23c', '#f56c6c']
const model = ref<Record<string, unknown>>({ title: '首页看板', theme: COLORS[0] })
const fields: BizFormField[] = [
{ prop: 'title', label: '看板名称', required: true },
{ prop: 'theme', label: '主题色', type: 'slot' },
]
const pick = (m: Record<string, unknown>, color: string): void => {
m.theme = color
}
</script>
<template>
<div style="max-width: 560px">
<BizForm v-model:model="model" :fields="fields">
<template #theme="{ model: m }">
<div style="display: flex; gap: 8px">
<span
v-for="color in COLORS"
:key="color"
:style="{ backgroundColor: color, width: '28px', height: '28px', borderRadius: '4px', cursor: 'pointer', outline: m.theme === color ? '2px solid var(--el-text-color-primary)' : 'none', outlineOffset: '2px' }"
@click="pick(m, color)"
/>
</div>
</template>
</BizForm>
</div>
</template>
显示代码
动态字段区块
fields 响应式增删(高级配置展开 / 收起):新字段自动按 defaultValue / 类型默认值增量合并。
<script setup lang="ts">
/** 场景:动态字段区块——按开关增删「自定义维度」字段组(schema 增量合并默认值) */
import { ref } from 'vue'
import { ElButton } from 'element-plus'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField } from 'vue3-ep-biz-lib'
const model = ref<Record<string, unknown>>({})
const advanced = ref(false)
const baseFields: BizFormField[] = [
{ prop: 'metric', label: '指标', required: true, defaultValue: '销售额' },
{ prop: 'period', label: '统计周期', type: 'select', options: [{ label: '日', value: 'd' }, { label: '月', value: 'm' }], defaultValue: 'd' },
]
const advancedFields: BizFormField[] = [
{ prop: 'dimension', label: '对比维度', type: 'select', options: [{ label: '渠道', value: 'ch' }, { label: '区域', value: 'rg' }] },
{ prop: 'topN', label: 'Top N', type: 'number', defaultValue: 10, attrs: { min: 1, max: 100 } },
]
const fields = ref<BizFormField[]>([...baseFields])
const toggle = (): void => {
advanced.value = !advanced.value
fields.value = advanced.value ? [...baseFields, ...advancedFields] : [...baseFields]
}
</script>
<template>
<div style="max-width: 640px">
<div style="margin-bottom: 12px">
<ElButton size="small" @click="toggle">{{ advanced ? '收起高级配置' : '展开高级配置' }}</ElButton>
</div>
<BizForm v-model:model="model" :fields="fields" />
</div>
</template>
显示代码
程序化方法
(操作结果)
expose:
validate / getValues / setValues / reset / clearValidate(另有 validateField / scrollToField)。<script setup lang="ts">
/** 场景:expose 方法面板——validate / getValues / setValues / reset 程序化驱动 */
import { ref } from 'vue'
import { ElButton } from 'element-plus'
import { BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField, BizFormInstance } from 'vue3-ep-biz-lib'
const formRef = ref<BizFormInstance>()
const log = ref('(操作结果)')
const fields: BizFormField[] = [
{ prop: 'username', label: '用户名', required: true },
{ prop: 'email', label: '邮箱', required: true, rules: [{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }] },
]
const call = (label: string, fn: () => unknown): void => {
const result = fn()
log.value = `${label} → ${String(result)}`
}
</script>
<template>
<div style="max-width: 640px; display: flex; flex-direction: column; gap: 12px">
<BizForm ref="formRef" :fields="fields" />
<div style="display: flex; gap: 8px; flex-wrap: wrap">
<ElButton size="small" @click="() => void formRef?.validate().then((v) => (log = `validate → ${v}`))">validate</ElButton>
<ElButton size="small" @click="call('getValues', () => JSON.stringify(formRef?.getValues()))">getValues</ElButton>
<ElButton size="small" @click="() => { formRef?.setValues({ username: 'admin', email: 'a@b.c' }); log = 'setValues → 已注入' }">setValues</ElButton>
<ElButton size="small" @click="() => { formRef?.reset(); log = 'reset → 已重置' }">reset</ElButton>
<ElButton size="small" @click="() => { formRef?.clearValidate(); log = 'clearValidate → 已清错误' }">clearValidate</ElButton>
</div>
<pre style="margin: 0; padding: 8px 12px; font-size: 12px; border-radius: 6px; background: var(--el-fill-color-light)">{{ log }}</pre>
</div>
</template>
显示代码
与详情描述联动
编辑保存后交给 BizDescriptions 展示——同一数据的编辑态与展示态组合。
<script setup lang="ts">
/** 场景:与详情描述联动——保存后 BizDescriptions 展示(表单值 → 详情) */
import { ref } from 'vue'
import { ElButton, ElMessage } from 'element-plus'
import { BizDescriptions, BizDivideTitle, BizForm } from 'vue3-ep-biz-lib'
import type { BizFormField } from 'vue3-ep-biz-lib'
const model = ref<Record<string, unknown>>({})
const saved = ref<Record<string, unknown> | null>(null)
const fields: BizFormField[] = [
{ prop: 'serverName', label: '服务器名', required: true, defaultValue: 'prod-web-01' },
{ prop: 'ip', label: 'IP 地址', required: true, defaultValue: '10.0.12.33' },
{ prop: 'env', label: '环境', type: 'select', options: [{ label: '生产', value: 'prod' }, { label: '预发', value: 'pre' }], defaultValue: 'prod' },
{ prop: 'monitor', label: '启用监控', type: 'switch', defaultValue: true },
]
const items = [
{ label: '服务器名', prop: 'serverName' },
{ label: 'IP 地址', prop: 'ip' },
{ label: '环境', prop: 'env' },
{ label: '启用监控', prop: 'monitor' },
]
const save = (): void => {
void Promise.resolve(true).then(() => {
saved.value = { ...model.value }
ElMessage.success('已保存,详情见下方')
})
}
</script>
<template>
<div style="max-width: 720px; display: flex; flex-direction: column; gap: 16px">
<BizForm v-model:model="model" :fields="fields" />
<ElButton type="primary" style="align-self: flex-start" @click="save">保存资产</ElButton>
<template v-if="saved">
<BizDivideTitle title="资产详情" />
<BizDescriptions :items="items" :data="saved" :column="2" />
</template>
</div>
</template>
显示代码
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| fields | BizFormField[] | [] | 字段 schema(见下) |
| model / v-model:model | Record<string, unknown> | {} | 表单数据(缺省字段初始化 defaultValue / 类型默认值) |
| label-width | string | number | '100px' | 标签宽度 |
| gutter | number | 16 | 栅格间隔 |
| default-span | number | 12 | 字段缺省栅格占位(24 制) |
| view | boolean | false | 只读态(控件换文本) |
| disabled | boolean | false | 整表禁用 |
ts
interface BizFormField {
prop: string // 字段名
label: string
type?: BizFormFieldType // 17 类控件(含 email / url),默认 input
span?: number // 栅格占位(24 制),24 占满整行
defaultValue?: unknown // 默认值(初始与 reset 回填)
options?: BizFormFieldOption[] // select/radio/checkbox/cascader(children 递归)
required?: boolean // 自动追加 required 规则
rules?: FormItemRule[] // 附加规则(叠加)
placeholder?: string // 缺省按类型自动生成
attrs?: Record<string, unknown> // 透传内部 el 控件(maxlength/rows/min/max/step...)
show?: (model) => boolean // 联动显隐(false 隐藏,值保留)
disabled?: boolean | ((model) => boolean) // 联动禁用
viewFormatter?: (model) => string // view 态自定义文案
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
事件
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:model | (model) | 任一字段变化整表回发 |
| change | (prop, value, model) | 任一字段变化(含字段名) |
插槽
| 插槽名 | 作用域 | 说明 |
|---|---|---|
字段名(type: slot) | { model } | 自定义控件 |
| default | — | 表单末尾追加区(操作按钮等) |
Expose
| 名称 | 类型 | 说明 |
|---|---|---|
| validate | () => Promise<boolean> | 全量校验 |
| validateField | (props: string | string[]) => Promise<boolean> | 校验指定字段 |
| clearValidate | (props?) => void | 清除校验状态 |
| scrollToField | (prop: string) => void | 滚动到字段(长表单定位) |
| reset | () => void | 以 model + defaultValue 重建并清校验 |
| getValues | () => Record<string, unknown> | 当前数据(浅拷贝) |
| setValues | (partial) => void | 批量设值 |