npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

wittyna-form

v1.0.1

Published

基于 Vue 3 + Element Plus(桌面端)与 Vant(移动端)的一套"配置式表单"组件库。通过一组类型安全的"表单项构造器"方法(如 `useInput`、`useSelect`、`useTableForm` 等),你可以用极少的代码搭建出复杂的表单与表格表单,内置校验、步骤、多端适配、详情模式等能力。

Readme

wittyna-form

基于 Vue 3 + Element Plus(桌面端)与 Vant(移动端)的一套"配置式表单"组件库。通过一组类型安全的"表单项构造器"方法(如 useInputuseSelectuseTableForm 等),你可以用极少的代码搭建出复杂的表单与表格表单,内置校验、步骤、多端适配、详情模式等能力。

  • 🖥️ 桌面端:使用 Element Plus 渲染控件
  • 📱 移动端自动适配:通过 IS_H5 进行 UA 判断,部分控件使用 Vant
  • 🎨 双模式组件:提供 MyForm 组件(纯表单)与 MyFormDialog 组件(对话框/抽屉内表单)
  • 🔒 类型完备:导出 FormSchemaFormItemSchema 等类型定义,便于在 TS 项目中获得智能提示

✨ 特性

  • 📝 配置式表单:通过 defineFormSchema + 各类 useXxx 构造器快速拼装
  • 📲 多端适配:移动端(H5)自动优化布局和交互
  • 校验与错误提示:对接 Element Plus 的校验规则,提供统一的错误对话框
  • 🔄 步骤表单:支持 steps 及子步骤、禁用/跳过、下一步校验拦截
  • 📊 表格表单:一行代码接入 useTableForm 完成表格录入
  • ⚙️ 细粒度控制:支持 condition 条件渲染、autoClearValueformat 值格式化等
  • 🎯 完整的 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,你可以通过 onSubmitonSave 等定义交互。
  • 表单项通过 useXxx 构造器定义,具备 requiredrulesconditioninitialValue 等常见能力。

🎛️ 在对话框/抽屉中使用(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:对话框/抽屉内表单组件

🛠️ 构造器

表单配置:

  • defineFormSchemadefineFormStepSchema

基础表单项:

  • useInputuseRequiredInputuseTextarea
  • useInputNumberuseInputNumberSuffix
  • useDatePickeruseTimePicker

选择类:

  • useRadiouseSelectuseSingleSelectuseObjectSelect
  • useRemoteSelectuseCheckboxuseTreeSelectuseAutoComplete

布局/复合:

  • useRowLayoutuseEasyRowLayoutuseTableLayout
  • useTabsLayoutuseDialogLayoutuseBlockuseBlockListuseLoop

表格:

  • useTableFormuseEasyTableForm/2

文本/按钮:

  • useTextuseSimpleText/2/3useButton
  • useLinkButtonuseNoDisabledButton

上传:

  • useUploadSingleImageuseUploadFile

🔗 Hooks / 工具

  • useFormValueuseRootFormValueuseFormRefs
  • 生命周期:onBeforeInitonBeforeInitPostonInitiatedonBeforeSubmit

📝 类型

  • FormSchemaFormItemSchemaFormStepSchema
  • FormValueCheckOptionComponentSchemaSpan

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.cssvant/lib/index.css
  • 移动端判断由内置 IS_H5 完成(基于 navigator.userAgent),移动端标签位置、表格表单等会做适配
  • 移动端表单项默认 labelPosition"top",桌面端为 "left"