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

form-for-view

v1.0.10

Published

基于组件插槽自动生成表单并且自动回填数据构建页面的插件

Readme

formForView

基于组件插槽自动生成表单并且自动回填数据构建页面的 Vue 3 插件

功能特性

  • 📝 自动表单生成: 根据插槽元素自动生成对应的表单控件
  • 🎯 智能字段识别: 自动识别文本、图片、视频等不同类型的字段
  • 💾 自动保存: 表单数据变化时自动保存到 localStorage
  • 📚 历史记录: 支持查看和恢复历史记录
  • 🛠️ 自定义工具条: 支持添加自定义工具按钮
  • 🎨 Element Plus 集成: 基于 Element Plus 组件库
  • 🎛️ 样式编辑: 支持在运行时调整类名或编辑字段对应的样式

安装

npm install form-for-view
# 或
yarn add form-for-view
# 或
pnpm add form-for-view

快速开始

全局注册

import { createApp } from 'vue'
import FormForView from 'form-for-view'

const app = createApp(App)
app.use(FormForView)
app.mount('#app')

导出方式

import FormForView, { FormForView as FormForViewComponent, PageForView } from 'form-for-view'
  • default 导出:插件安装入口,配合 app.use(FormForView) 使用
  • FormForView / PageForView 命名导出:组件本身,适合局部注册或按需引入

局部使用示例

<template>
  <div id="app">
    <h1>formForView 插件示例</h1>

    <div class="mode-buttons">
      <el-button @click="formType = 'edit'" :type="formType === 'edit' ? 'primary' : 'default'">
        编辑模式
      </el-button>
      <el-button @click="formType = 'show'" :type="formType === 'show' ? 'primary' : 'default'">
        页面模式
      </el-button>
      <el-button @click="isValidate = !isValidate" :type="isValidate ? 'primary' : 'default'">
        开启校验
      </el-button>
    </div>
    <div class="content-container" v-if="formType === 'show'">
      <h2>PageForView 页面渲染示例</h2>
      <PageForView :formData="formData" :style="styleMap" v-slot="{ ffvArray }">
        <div class="header-section">
          <h2 edit="title">
            产品标题</h2>
          <p edit="subtitle">产品副标题描述</p>
        </div>

        <el-carousel indicator-position="outside" edit="carousel" edit-type="array" array-limit="4">
          <el-carousel-item v-for="(item, index) in ffvArray('carousel')" :key="index">
            <h3 array-key="name" class="carousel-title">{{ item.name }}</h3>
            <img array-key="src" alt="产品图片" class="carousel-image" :src="item.src">
          </el-carousel-item>
        </el-carousel>

        <div class="image-section">
          <img edit="mainImage" style="width:300px; height: auto;"
            src="https://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960"
            alt="产品图片" class="main-image">
        </div>

        <div edit="backgroundImage" edit-type="background-image" style="width: 300px;height: 200px;"></div>

        <div class="description-section">
          <h3>产品描述</h3>
          <div edit="description">
            这是一个产品描述文本,您可以在编辑模式下修改这些内容。
            支持多行文本编辑,并且可以实时预览效果。
          </div>
        </div>
      </PageForView>
    </div>
    <FormForView v-else :form-data="formData" :style="styleMap" :form-type="formType" :tools="tools" @save="handleSave" :rules="rules"
      :is-validate="isValidate" @update:form-data="handleUpdate" @update:style="handleStyleUpdate" @upload="handleUpload" @choose-data="handleChooseData"
      :autoTimes="30" v-slot="{ ffvArray, ffvData }">
      <div class="content-container">
        <div class="header-section">
          <div v-for="(item, index) in ffvArray('List')" :key="index">
            <el-checkbox :label="item.name" v-model="item.checked">{{ item.name }}</el-checkbox>
          </div>
          <h2 edit="title">产品标题</h2>
          <p edit="subtitle">产品副标题描述</p>
          <p edit="chooseDataid" edit-type="choosedata">{{ ffvData['chooseDataid'] }}</p>
        </div>

        <el-carousel indicator-position="outside" edit="carousel" edit-type="array" array-limit="4"
          array-keys="name:text,src:file,chooseDataId:choosedata">
          <el-carousel-item v-for="(item, index) in ffvArray('carousel')" :key="index">
            <h3 text="2xl" justify="center">{{ item.name }}</h3>
            <img alt="产品图片" class="carousel-image" :src="item.src">
            <span> {{ item.chooseDataId }}</span>
          </el-carousel-item>
        </el-carousel>
        <div class="image-section">
          <img edit="mainImage" style="width:300px; height: auto;"
            src="https://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960"
            alt="产品图片" class="main-image">
        </div>
        <div edit="backgroundImage" edit-type="background-image" style="
          background-image: url('https://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960');
          width: 300px;height: 200px;">
        </div>
        <div class="description-section">
          <h3>产品描述</h3>
          <div edit="description">
            这是一个产品描述文本,您可以在编辑模式下修改这些内容。
            支持多行文本编辑,并且可以实时预览效果。
          </div>
        </div>

        <div class="features-section">
          <h3>产品特性</h3>
          <ul>
            <li edit="feature1">
              特性一:高质量材料</li>
            <li edit="feature2">特性二:精湛工艺</li>
            <li edit="feature3">特性三:优质服务</li>
          </ul>
        </div>

        <div class="media-section">
          <h3>产品展示</h3>
          <video edit="demoVideo" controls class="demo-video"
            src="https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4">
            您的浏览器不支持视频播放。
          </video>
        </div>

        <div class="price-section">
          <span class="price-label">价格:</span>
          <span edit="price" class="price-value">¥299.00</span>
        </div>
      </div>
    </FormForView>


  </div>
</template>

<script setup>
import { ref } from 'vue'
import { FormForView, PageForView } from 'form-for-view'

const formType = ref('edit')
const isValidate = ref(true)
const styleMap = ref({})
const rules = ref({
  title: [{ required: true, message: '请输入标题!!!!', trigger: 'blur' }, { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' }],
})
const List = ref([
  {
    name: '特性一:高质量材料',
  },
  {
    name: '特性二:精湛工艺',
  },
  {
    name: '特性三:优质服务',
  }
])
const formData = ref({
  List,
  title: '智能家居控制器',
  subtitle: '让您的家更智能、更舒适',
  mainImage: 'https://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960',
  backgroundImage: 'https://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960',
  description: '这款智能家居控制器采用最先进的技术,支持语音控制、远程操控等多种功能。让您随时随地掌控家中的一切设备,享受智能生活带来的便利。',
  feature1: 'AI语音控制,解放双手',
  feature2: '远程控制,随时随地',
  feature3: '节能环保,智能调节',
  demoVideo: 'https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4',
  price: '¥399.00',
  carousel: [
    {
      src: 'https://bpic.51yuansu.com/backgd/cover/00/07/60/5b6aebab85276.jpg?x-oss-process=image/resize,h_360,m_lfit/sharpen,100',
      name: '产品图片1'
    },
    {
      src: 'https://bpic.51yuansu.com/backgd/cover/00/07/60/5b6aebab85276.jpg?x-oss-process=image/resize,h_360,m_lfit/sharpen,100',
      name: '产品图片2'
    },
    {
      src: 'https://img2.baidu.com/it/u=2859622770,128426944&fm=253&fmt=auto&app=138&f=JPEG?w=1313&h=500',
      name: '产品图片3'
    }
  ]
})

const tools = ref([
  {
    name: '预览',
    call: (data) => {
      console.log('当前数据预览:', data)
      alert('数据已输出到控制台')
    }
  },
  {
    name: '重置',
    call: (data) => {
      if (confirm('确定要重置所有数据吗?')) {
        formData.value = {
          title: '产品标题',
          subtitle: '产品副标题描述',
          mainImage: 'https://trae-api-sg.mchost.guru/api/ide/v1/text_to_image?prompt=modern%20product%20showcase%20with%20clean%20background&image_size=square_hd',
          description: '这是一个产品描述文本,您可以在编辑模式下修改这些内容。',
          feature1: '特性一:高质量材料',
          feature2: '特性二:精湛工艺',
          feature3: '特性三:优质服务',
          demoVideo: 'https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4',
          price: '¥299.00'
        }
      }
    }
  },
  {
    name: '导出',
    call: (data) => {
      const jsonStr = JSON.stringify(data, null, 2)
      const blob = new Blob([jsonStr], { type: 'application/json' })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = 'form-data.json'
      a.click()
      URL.revokeObjectURL(url)
    }
  }
])

const handleSave = (data) => {
  console.log('保存数据:', data)
  // 这里可以添加保存到后端的逻辑
}

const handleUpload = (callBack) => {
  console.log('上传文件回调触发')
  // 这里可以添加上传文件到后端的逻辑
  // 上传完成后,调用回调函数并传递文件URL
  callBack('https://bpic.51yuansu.com/backgd/cover/00/07/60/5b6aebab85276.jpg?x-oss-process=image/resize,h_360,m_lfit/sharpen,100')
}
const handleChooseData = (callBack) => {
  console.log('选择数据回调触发')
  // 这里可以添加上传文件到后端的逻辑
  // 上传完成后,调用回调函数并传递文件URL
  callBack(1)
}

const handleUpdate = (data) => {
  console.log('更新数据:', data)
  formData.value = data
}

const handleStyleUpdate = (data) => {
  styleMap.value = { ...data }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.mode-buttons {
  margin-bottom: 30px;
  text-align: center;
}

.mode-buttons .el-button {
  margin: 0 10px;
}

.content-container {
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  padding: 40px;
  margin-top: 20px;
}

.header-section {
  text-align: center;
  margin-bottom: 40px;
}

.header-section h2 {
  font-size: 32px;
  color: #303133;
  margin-bottom: 10px;
}

.header-section p {
  font-size: 18px;
  color: #606266;
}

.image-section {
  text-align: center;
  margin-bottom: 40px;
}

.main-image {
  max-width: 100%;
  height: auto;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.description-section {
  margin-bottom: 40px;
}

.description-section h3 {
  font-size: 24px;
  color: #303133;
  margin-bottom: 15px;
}

.description-section div {
  font-size: 16px;
  line-height: 1.6;
  color: #606266;
}

.features-section {
  margin-bottom: 40px;
}

.features-section h3 {
  font-size: 24px;
  color: #303133;
  margin-bottom: 15px;
}

.features-section ul {
  list-style: none;
  padding: 0;
}

.features-section li {
  font-size: 16px;
  color: #606266;
  margin-bottom: 10px;
  padding-left: 20px;
  position: relative;
}

.features-section li:before {
  content: "✓";
  position: absolute;
  left: 0;
  color: #67c23a;
  font-weight: bold;
}

.media-section {
  margin-bottom: 40px;
  text-align: center;
}

.media-section h3 {
  font-size: 24px;
  color: #303133;
  margin-bottom: 15px;
}

.demo-video {
  max-width: 100%;
  height: auto;
  border-radius: 8px;
}

.price-section {
  text-align: center;
  padding: 20px;
  background: #f5f7fa;
  border-radius: 8px;
}

.price-label {
  font-size: 18px;
  color: #606266;
  margin-right: 10px;
}

.price-value {
  font-size: 32px;
  font-weight: bold;
  color: #f56c6c;
}
</style>

API 参考

Props

| 参数名 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | formData | Object | {} | 表单数据对象 | | style | Object | {} | 可选的样式映射对象,传入后会作用于渲染结果 | | rules | Object | {} | 表单验证规则对象 | | formType | String | 'show' | 表单类型: 'edit'(编辑模式)、'show'(页面模式) | | tools | Array | [] | 工具条配置,每个元素包含 name 和 call 属性 | | isValidate | Boolean | false | 是否开启校验 | | autoTimes | Number | 0 | 自动保存时间间隔,单位秒(0 表示不自动保存) |

Events

| 事件名 | 说明 | 参数 | |--------|------|------| | save | 保存表单数据时触发 | (formData: Object) | | upload | 上传文件时触发 | (callBack: (url: string) => void) | | choose-data | 选择数据时触发 | (callBack: (result: unknown) => void) | | update:form-data | 表单数据更新时触发 | (formData: Object) | | update:style | 样式编辑应用后触发 | (styleMap: Record<string, Record<string, string>>) |

插槽指令

在插槽元素上使用 edit 指令来标记可编辑字段: 在插槽元素上使用 edit-type 指令来指定编辑类型:

  • text (默认):普通文本字段
  • background-image:背景图片字段
  • array:数组数据容器(配合子元素使用 array-key 标注键)
  • choosedata:选择数据字段(需配合 choose-data 事件配置)
<!-- 文本字段 -->
<p edit="title">标题文本</p>
<div edit="content">内容文本</div>

<!-- 图片字段 -->
<img edit="avatar" src="default.jpg" alt="头像">

<!-- 媒体文件 -->
<video edit="video" src="default.mp4"></video>
<audio edit="audio" src="default.mp3"></audio>

进阶用法

数组模式

  • 容器属性
    • edit:数组字段名,例如 carousel
    • edit-type="array":声明为数组容器
    • array-limit:可选,限制数组最大项数
    • array-keys:可选,指定键的类型映射(JSON 对象或逗号分隔 key:type) 例:name:text,src:file,chooseDataid:choosedata
  • 子元素属性
    • array-key:标注该元素对应的键名,例如 namesrc

在 FormForView 中使用

<FormForView :form-data="formData" :form-type="formType" @upload="handleUpload" v-slot="{ ffvArray }">
  <el-carousel indicator-position="outside" edit="carousel" edit-type="array" array-limit="4" array-keys="name:text,src:file">
    <el-carousel-item v-for="(item, index) in ffvArray('carousel')" :key="index">
      <h3>{{ item.name }}</h3>
      <img :src="item.src" />
    </el-carousel-item>
  </el-carousel>
</FormForView>
  • 编辑模式交互
    • 数组以 el-tabs 渲染,每项一个标签页,支持新增/删除(受 array-limit 限制)
    • text/textarea/file 键类型自动匹配控件;file 键提供上传按钮
    • 快速模式下,双击数组容器会弹出对话框,使用 el-tabs 编辑整组数据

在 PageForView 中使用

<PageForView :formData="formData" v-slot="{ ffvArray }">
  <el-carousel indicator-position="outside" edit="carousel" edit-type="array">
    <el-carousel-item v-for="(item, index) in ffvArray('carousel')" :key="index">
      <h3 array-key="name">{{ item.name }}</h3>
      <img array-key="src" :src="item.src" />
    </el-carousel-item>
  </el-carousel>
</PageForView>

背景图片 (edit-type="background-image")

<div edit="bg" edit-type="background-image" style="height: 200px"></div>

样式编辑

  • 编辑模式下可通过工具条或元素浮层打开样式编辑面板
  • 面板支持切换目标类名,并通过 style 映射作用到 FormForView / PageForView
  • 样式变更会通过 update:style 事件抛出,便于业务侧持久化
<FormForView
  :form-data="formData"
  :style="styleMap"
  form-type="edit"
  @update:style="handleStyleUpdate"
>
  <div class="hero-title" edit="title">标题</div>
</FormForView>

<PageForView :formData="formData" :style="styleMap">
  <div class="hero-title" edit="title">标题</div>
</PageForView>
const styleMap = ref({})

const handleStyleUpdate = (nextStyleMap) => {
  styleMap.value = { ...nextStyleMap }
}

自定义控件(规划)

  • 支持通过属性扩展自定义编辑类型与渲染策略
  • 建议结合 rules 与业务校验在表单侧进行增强

常见问题

  • 上传事件签名:统一为 (callBack: (url: string) => void),示例与文档已更新
  • 选择数据事件签名:推荐按 (callBack: (result: unknown) => void) 理解,业务侧可传字符串、数字或对象
  • 插件安装方式:app.use() 请使用默认导出,命名导出用于局部组件注册
  • edit-type 命名:统一为中划线小写(如 background-imagearray),兼容旧值 backgroundImage
  • 表单校验:请优先使用组件 rules prop,不再推荐在插槽节点上写 rule
  • 文本换行显示:建议在页面样式中为文本元素设置 white-space: pre-line 以识别 \n
  • 样式编辑:样式通过 styleMap 作用到 FormForView / PageForView,若需要持久化,请监听 update:style

SSR 与 Client-Only

  • PageForView 可用于 SSR 场景(纯数据渲染路径)
  • FormForView 包含编辑态能力(历史记录、浮层、样式注入、DOM 扫描),建议仅在客户端渲染

Nuxt 使用示例

<template>
  <ClientOnly>
    <FormForView :form-data="formData" form-type="edit" @choose-data="handleChooseData" />
  </ClientOnly>
</template>

PHP 模板场景

  • 如果前台页面不是 Vue,而是 PHP 模板渲染,可使用实验性的 PHP SDK
  • SDK 提供服务端版 PageForView,接收 formData + styleMap 输出最终 HTML
  • 适合 SEO 页面、CMS 模板页、服务端首屏渲染场景

相关文件:

CDN 使用

<link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css" />
<link rel="stylesheet" href="https://unpkg.com/form-for-view/dist/index.css" />
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/element-plus"></script>
<script src="https://unpkg.com/@element-plus/icons-vue"></script>
<script src="https://unpkg.com/form-for-view/dist/index.umd.js"></script>
<script>
  const app = Vue.createApp({})
  app.use(ElementPlus)
  const ffv = (window.FormForView && window.FormForView.default) || window.FormForView
  app.use(ffv)
  app.mount('#app')
</script>
  • 在浏览器全局环境下,优先使用 window.FormForView.default
  • 若后续导出策略调整,window.FormForView 兜底写法可保持兼容
  • 可直接使用 form-for-view / page-for-view 标签

UMD 导出形态评估(P2)

  • 当前 UMD 为 named + default,兼容 npm/ESM 与 CDN 双场景
  • 若改为 default-only,CDN 用法会更直观,但会影响 named 导出语义与现有接入方
  • 现阶段建议维持现状,并统一推荐如下全局安装方式:
const ffv = (window.FormForView && window.FormForView.default) || window.FormForView
app.use(ffv)

模式说明

编辑模式 (edit)

  • 显示工具条,包含编辑、退出、历史记录和自定义工具按钮
  • 点击编辑按钮弹出抽屉表单,宽度为 50%
  • 表单控件聚焦时同步高亮对应页面元素
  • 支持文件上传功能
  • 支持样式编辑抽屉
  • 自动保存功能

页面模式 (show)

  • 仅显示静态内容
  • 无编辑功能
  • 适合最终展示

PageForView 组件

  • 用于渲染数据的页面组件
  • 支持自定义数据展示模板
<template>
  <PageForView :formData="formData">
    <h1 edit="title">默认标题</h1>
    <img edit="banner" />
    <p edit="desc">默认描述</p>
    <div edit="bg" edit-type="background-image" style="height: 200px"></div>
  </PageForView>
  </template>

<script setup>
import { PageForView } from 'form-for-view'
const formData = {
  title: '产品标题',
  banner: 'https://example.com/banner.png',
  desc: '这里是产品描述文本',
  bg: 'https://example.com/bg.jpg'
}
</script>

历史记录

  • 自动保存每次修改到 localStorage
  • 支持查看最近 10 条历史记录
  • 点击历史记录可恢复到对应状态

依赖

  • Vue 3.x
  • Element Plus 2.x
  • @element-plus/icons-vue

License

更新日志

1.0.9

  • 补充 SSR / CDN 使用说明与 smoke 示例
  • 样式编辑面板升级为侧边抽屉,支持样式编辑与样式预览切换
  • 统一事件名称为 kebab-case(如 choose-dataupdate:form-data
  • 修复内联操作清理逻辑与若干交互问题

1.0.0

  • 初始版本发布
  • 支持编辑模式、快速模式、页面模式
  • 自动生成表单控件并支持数据回填

1.0.1

  • 按需引入与注册 Element Plus 组件与样式,移除全量引入,显著降低包体积
  • 文档文案 和用例错误调整

1.0.3

  • 新增 PageForView 组件,用于渲染数据

1.0.4

  • 导出bug修改

1.0.5

  • 新增 edit-type="array"PageForView 中的完整支持,插槽注入 ffvArray
  • FormForView 默认插槽注入 ffvArray/ffvData,兼容第三方容器延迟渲染
  • 数组编辑 UI 改为 el-tabs,支持新增/删除、上限 array-limit
  • 快速编辑支持数组整项编辑,对话框中以 el-tabs 管理各项

1.0.6

  • 一些优化和调整

1.0.7

  • 上传回调增加 保存机制会触发保存事件
  • 快速选中框增加了 按钮组
  • 新增 rules prop 用于配置表单验证规则 参考elementui的验证
  • 删除了行间的 rule 属性,统一通过 prop 配置(尽量较少对原结构的修改)

1.0.8

  • 调整了快速编辑模式的高亮蒙层追加位置
  • 调整了按需引入与注册 Element Plus 组件 避免控制台多次注册的警告
  • 新增 edit-type="choosedata" ,插槽注入 ffvArray 使用
  • 新增 choose-data 事件,用于选择数据时触发