wittyna-form
v1.0.1
Published
基于 Vue 3 + Element Plus(桌面端)与 Vant(移动端)的一套"配置式表单"组件库。通过一组类型安全的"表单项构造器"方法(如 `useInput`、`useSelect`、`useTableForm` 等),你可以用极少的代码搭建出复杂的表单与表格表单,内置校验、步骤、多端适配、详情模式等能力。
Readme
wittyna-form
基于 Vue 3 + Element Plus(桌面端)与 Vant(移动端)的一套"配置式表单"组件库。通过一组类型安全的"表单项构造器"方法(如 useInput、useSelect、useTableForm 等),你可以用极少的代码搭建出复杂的表单与表格表单,内置校验、步骤、多端适配、详情模式等能力。
- 🖥️ 桌面端:使用 Element Plus 渲染控件
- 📱 移动端自动适配:通过
IS_H5进行 UA 判断,部分控件使用 Vant - 🎨 双模式组件:提供 MyForm 组件(纯表单)与 MyFormDialog 组件(对话框/抽屉内表单)
- 🔒 类型完备:导出
FormSchema、FormItemSchema等类型定义,便于在 TS 项目中获得智能提示
✨ 特性
- 📝 配置式表单:通过
defineFormSchema+ 各类useXxx构造器快速拼装 - 📲 多端适配:移动端(H5)自动优化布局和交互
- ✅ 校验与错误提示:对接 Element Plus 的校验规则,提供统一的错误对话框
- 🔄 步骤表单:支持
steps及子步骤、禁用/跳过、下一步校验拦截 - 📊 表格表单:一行代码接入
useTableForm完成表格录入 - ⚙️ 细粒度控制:支持
condition条件渲染、autoClearValue、format值格式化等 - 🎯 完整的 TS 类型导出,智能提示更友好
📦 安装
使用你偏好的包管理工具安装(需自行安装 peer 依赖:vue、element-plus、vant):
pnpm add wittyna-form element-plus vant vue或
npm i wittyna-form element-plus vant vue或
yarn add wittyna-form element-plus vant vue建议在入口处全局引入样式(Element Plus + Vant):
import "element-plus/dist/index.css";
import "vant/lib/index.css";🚀 快速上手
下面示例展示了最小可用表单,包含输入框、单选、下拉、复选框、表格表单及校验与事件:
import { createApp } from "vue";
import {
MyForm,
defineFormSchema,
useInput,
useInputNumberSuffix,
useRadio,
useSelect,
useCheckbox,
useTableForm,
useDatePicker,
type FormValue,
type CheckOption,
} from "wittyna-form";
import "element-plus/dist/index.css";
import "vant/lib/index.css";
createApp({
render() {
return (
<MyForm
useFormSchema={(formData: FormValue) =>
defineFormSchema({
title: "我的表单",
formItems: [
useInput("name", "姓名", { required: true }),
useInputNumberSuffix("age", "年龄", "岁", { required: true }),
useRadio("sex", "性别", { required: true }, {
options: [
{ label: "男", value: "male" },
{ label: "女", value: "female" },
],
}),
useSelect(
"hobby",
"爱好",
{
required: true,
onChange(v, formValue, selected: CheckOption) {
console.log("hobby change", v, selected);
},
},
{
options: [
{ label: "运动", value: "sport" },
{ label: "编程", value: "program" },
{ label: "看书", value: "read" },
],
},
),
useCheckbox(
"sports",
"具体运动",
{
required: true,
condition: (formValue) => formValue.hobby === "sport",
},
{
options: [
{ label: "羽毛球", value: "badminton" },
{ label: "网球", value: "tennis" },
{ label: "爬山", value: "mountain" },
],
},
),
useTableForm(
"career",
"履历",
{ required: true },
{
columns: [
useInput("company", "公司"),
useInput("position", "职位"),
useDatePicker("startDate", "开始日期", {}, { type: "date" }),
useDatePicker("endDate", "结束日期", {}, { type: "date" }),
],
},
),
],
onSubmit(formValue, changed) {
console.log("submit:", formValue, changed);
},
})
}
/>
);
},
}).mount("#app");💡 提示
useFormSchema会接收并返回一个FormSchema,你可以通过onSubmit、onSave等定义交互。- 表单项通过
useXxx构造器定义,具备required、rules、condition、initialValue等常见能力。
🎛️ 在对话框/抽屉中使用(MyFormDialog)
MyFormDialog 封装了表单在对话框或抽屉中的展示逻辑,并通过 open() 暴露"打开并等待确认"的 Promise。
<script setup lang="ts">
import { ref } from "vue";
import { MyFormDialog, defineFormSchema, useInput } from "wittyna-form";
const dialogRef = ref<{ open: () => Promise<any> } | null>(null);
async function openDialog() {
try {
const result = await dialogRef.value?.open();
console.log("确认提交的表单值:", result);
} catch {
console.log("用户取消/关闭");
}
}
</script>
<template>
<ElButton @click="openDialog">打开表单</ElButton>
<MyFormDialog
ref="dialogRef"
v-model="visible"
title="编辑资料"
:useFormItems="() => [useInput('name', '姓名', { required: true })]"
:handleOk="(v) => Promise.resolve(console.log('ok', v))"
:handleCancel="() => console.log('cancel')"
/>
</template>MyFormDialog Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| title | string | - | 对话框标题 |
| width | string \| number | 500 | 对话框宽度 |
| modelValue | boolean | - | 显示/隐藏状态(v-model) |
| useFormItems | () => FormItemSchema[] | - | 表单项构造函数 |
| formItems | FormItemSchema[] | - | 静态表单项配置 |
| handleOk | (formValue) => void \| Promise<void> | - | 确认回调 |
| handleCancel | () => void \| Promise<void> | - | 取消回调 |
| isView | boolean | false | 是否以"只读详情"模式渲染 |
| isDrawer | boolean | false | 使用抽屉替代对话框 |
| initialData | object | {} | 初始化表单数据 |
| labelWidth | string | "100px" | 标签宽度 |
| defaultFormItemSpan | Span | 24 | 表单项默认栅格 |
| size | "large" \| "default" \| "small" | - | 表单尺寸 |
| dialogProps | Partial<DialogProps> | - | 透传给 el-dialog 的属性 |
📋 步骤表单
通过在 FormSchema 中配置 steps: FormStepSchema[] 即可启用步骤表单,支持子步骤与条件展示。
defineFormSchema({
title: "步骤表单",
steps: [
{
title: "基本信息",
formItems: [
useInput("name", "姓名", { required: true }),
useInput("email", "邮箱", { required: true }),
],
},
{
title: "详细信息",
children: [
{
formTitle: "个人资料",
formItems: [
useSelect("type", "用户类型", { required: true }, {
options: [
{ label: "普通用户", value: "normal" },
{ label: "企业用户", value: "enterprise" },
],
}),
],
},
{
formTitle: "企业信息",
showChildren: (formValue) => formValue.type === "enterprise",
formItems: [
useInput("companyName", "企业名称", { required: true }),
useInput("desc", "企业描述"),
],
},
],
},
],
});步骤表单特性
- 步骤之间
next()会自动进行校验,未通过会弹出统一错误提示 - 可通过
disabled控制禁用步骤或子步骤 showChildren(formValue)控制子步骤是否显示- 支持嵌套步骤(children)以 Tabs 形式展示
📖 导出与 API 概览
入口文件导出如下(节选,常用项):
🧩 组件
MyForm:核心表单组件MyFormDialog:对话框/抽屉内表单组件
🛠️ 构造器
表单配置:
defineFormSchema、defineFormStepSchema
基础表单项:
useInput、useRequiredInput、useTextareauseInputNumber、useInputNumberSuffixuseDatePicker、useTimePicker
选择类:
useRadio、useSelect、useSingleSelect、useObjectSelectuseRemoteSelect、useCheckbox、useTreeSelect、useAutoComplete
布局/复合:
useRowLayout、useEasyRowLayout、useTableLayoutuseTabsLayout、useDialogLayout、useBlock、useBlockList、useLoop
表格:
useTableForm、useEasyTableForm/2
文本/按钮:
useText、useSimpleText/2/3、useButtonuseLinkButton、useNoDisabledButton
上传:
useUploadSingleImage、useUploadFile
🔗 Hooks / 工具
useFormValue、useRootFormValue、useFormRefs- 生命周期:
onBeforeInit、onBeforeInitPost、onInitiated、onBeforeSubmit
📝 类型
FormSchema、FormItemSchema、FormStepSchemaFormValue、CheckOption、ComponentSchema、Span等
FormSchema 接口
interface FormSchema<T = FormValue> {
title?: string; // 标题
padding?: string | number; // 表单内边距
colon?: boolean; // 是否显示 label 冒号
formItems?: FormItemSchema<T>[];// 表单项
disabled?: boolean | Ref<boolean>;
detailMode?: boolean | Ref<boolean>;
steps?: FormStepSchema<T>[];
onSave?: (formValue: T) => void;
onSubmit?: (formValue: T, changed: Partial<T>) => void;
onBack?: () => void;
submitTitle?: MaybeRef<string>;
showErrorDialog?: boolean; // 提交/下一步校验失败是否弹窗
defaultFormItemSpan?: Span; // 默认 12/24 栅格
formProps?: Partial<FormProps>; // 透传 ElForm 属性
customOperations?: Array<{ // 自定义底部按钮
icon?: string;
label: string;
handler: (formValue: FormValue) => void;
isShow?: boolean | Ref<boolean>;
type?: "primary" | "default" | "warning" | "danger" | "success" | "info";
}>;
}FormItemSchema 接口
interface FormItemSchema<T = FormValue, R = AnyProps, S = any> {
label?: string | Ref<string>;
prop?: string; // 不传则视为非表单展示组件(如 Title)
required?: boolean | ((formValue: T) => boolean);
disabled?: boolean | ((formValue: T) => boolean);
detailMode?: boolean | ((formValue: T) => boolean);
defaultValue?: any | ((formValue: T) => any);
initialValue?: any | ((formValue: T) => any); // 会写入表单数据
format?: [(get) => S, (set) => any]; // 值格式化
onChange?: (v: S, formValue: T, ...args: any[]) => void;
onValueChange?: (v: S, formValue: T) => void;
rules?: Arrayable<FormItemRule> | ((formValue: T) => Arrayable<FormItemRule>);
condition?: (formValue: T) => boolean; // 条件渲染
autoClearValue?: boolean; // 隐藏时清空值
component?: ComponentSchema<R>; // 组件名与 props/slots
span?: Span; // 栅格
labelWidth?: string | number;
labelPosition?: "left" | "right" | "top";
// 表格列增强:align/headerAlign/columnWidth/columnHeader/columnChildren...
}MyForm 实例暴露
interface MyFormExpose {
getValue: () => FormValue;
validate: (noSkip?: boolean) => Promise<FormValue>; // 失败时抛错并弹窗(默认)
formSchema: FormSchema;
}🎨 样式与移动端适配
- 默认全局引入
element-plus/dist/index.css和vant/lib/index.css - 移动端判断由内置
IS_H5完成(基于navigator.userAgent),移动端标签位置、表格表单等会做适配 - 移动端表单项默认
labelPosition为"top",桌面端为"left"
