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

schemagine

v0.3.14

Published

Schema-driven dynamic form & data management engine for Vue 3

Downloads

1,424

Readme

Schemagine

Schema 驱动的 Vue 3 动态表单与数据管理引擎。 · MIT 许可证

Docs Playground

English | 简体中文


只需用一份 JSON ModuleSchema 定义字段、权限、公式与关联关系,Schemagine 即可渲染出完整的列表 / 卡片 / 新增三种视图:支持排序筛选的表格、乐观锁行内编辑、公式字段、级联权限、Schema 平滑迁移,以及可插拔的 Service 层对接你自己的后端。

基于 Vue 3.5 + TypeScript + Pinia + Element Plus + vxe-table 构建,以 ESM + CJS 双格式库发布,附带完整类型声明。

✨ 功能特性

| 领域 | 特性 | |------|------| | Schema 驱动视图 | 一份 ModuleSchema 驱动全部三种视图:列表(表格)、卡片浏览、新增(批量列表式 / 单卡片录入),无需为每个模块编写 CRUD 代码。 | | 强大的表格 | 基于 vxe-table:虚拟滚动、固定列、列设置、每列表头排序 + 内容筛选(10 种算子,候选值带出现次数)。 | | 编辑能力 | 两种列表编辑模式(inline-dblclick / select-then-edit)、24 种字段类型、mathjs 公式字段、聚合计算(求和 / 平均 / 计数 / 最大 / 最小)。 | | 数据安全 | 所有写操作携带乐观锁(version + 自动重试)、行级 / 批量删除带二次确认(引擎负责渲染与确认,删除执行由宿主完成)、竞态保护与实例级 ErrorBoundary。 | | 权限体系 | 模块级 + 字段级权限、角色覆盖、visibleWhen / editableWhen 条件字段表达式,配合 globalContext prop 使用。 | | Schema 演进 | 版本化迁移脚本(fromVersion → toVersion)、previousKeys 字段重命名映射、视图配置与筛选预设自动迁移。 | | 后端无关 | 所有数据访问通过 6 个可注入的 Service 接口(记录 / Schema / 候选值 / 用户视图配置 / 关联关系 / 媒体),内置 localStorage Mock,开箱即可演示。 | | 可视化 Schema 编辑器 | 内置编辑器页面:字段增删改、公式构建器、权限配置、依赖关系图、JSON 导入导出、实时预览。 | | 效率功能 | CSV 导出(遵循当前筛选 / 排序 / 可见列)、时间范围预设、FK 字段快速新建、底部标签页、按用户持久化的视图配置(列宽、卡片布局)。 | | 主题定制 | 全部样式基于 --sg-* CSS Token;零配置跟随 Element Plus 主题(含 html.dark 暗色模式),支持按 Token 覆盖;appearance prop 控制表格边框 / 值展示(标签 / 纯文本 / 经典单色)/ 卡片密度。 | | 扩展注册表 | registerFieldType 自定义字段类型(渲染 / 表单 / 行内编辑三处接入)、registerDialog 自定义弹窗、vxe 插槽透传与 getTableInstance() 实例暴露。 | | i18n 与移动端 | 内置 zh-CN 语言包 + registerLocale 注入 + locale prop 切换;窄屏自动切移动卡片形态(搜索 / 触底加载 / 动作降级)。 | | 媒体管理(可选导出) | 图片字段的四种接入模式一应俱全:默认 URL 渲染零配置;schemagine/media 子路径按需引入 OSS 直传(S3 兼容 / 七牛,纯 JS 签名零 SDK)、宿主上传 API、媒体库组件集(管理页 MediaLibrary / 选择弹窗 MediaPickerDialog / 通用 HTTP 实现 createHttpMediaService),模式切换编辑面自动降级。 | | 多实例安全 | 每个 <SchemaEngine> 实例通过 create*State() 工厂创建隔离状态并经 Vue provide/inject 注入,可在同一页面嵌入多个模块。 |

🏗 架构

SchemaEngine(入口)               ← 加载 Schema、权限、用户配置,编排全局流程
  └─ SchemaContextProvider        ← provide 上下文(schemaMeta / recordState / uiState)
       └─ ViewContainer           ← 根据 viewMode 切换子视图
            ├─ ListView           ← 列表视图
            │    ├─ 每列表头排序 / 筛选弹层
            │    ├─ ListActionBar ← 列表级操作按钮
            │    ├─ SchemaTable   ← vxe-table 封装
            │    └─ BottomTabs + SchemaPagination
            ├─ CardView           ← 卡片浏览(上/下条导航)
            │    └─ SchemaCard    ← 16 列 Grid 布局
            └─ CreateView / CardCreateView  ← 批量 / 单条录入
  └─ GlobalDialogHost             ← 快速新建、列设置、公式详情、版本冲突等弹窗宿主

数据流(严格单向):UI → Composables/状态 → Service → 你的后端

📦 安装

pnpm add schemagine

对等依赖(需在宿主项目中安装):vue ^3.5、pinia ^3.0、element-plus ^2.13、vue-router ^5.0、vxe-table ^4.18、vxe-pc-ui ^4.13、xe-utils ^4.0、mathjs ^15。

pnpm add vue pinia element-plus vue-router vxe-table vxe-pc-ui xe-utils mathjs

在应用入口注册插件:

import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import VxePcUI from 'vxe-pc-ui'
import VxeTable from 'vxe-table'

app.use(createPinia())
app.use(ElementPlus)
app.use(VxePcUI)
app.use(VxeTable)

引入样式(Element Plus / vxe-table 样式 + 引擎样式,引擎样式内含 --sg-* Token 基线):

import 'element-plus/dist/index.css'
import 'vxe-pc-ui/lib/style.css'
import 'vxe-table/lib/style.css'
import 'schemagine/dist/schemagine.css'

⚠️ 必须配置 Vite resolve.dedupe。 Schemagine 以库模式构建,vue / pinia / vue-router 均为 external。不去重时可能出现两份 Pinia 实例,导致 Cannot read properties of undefined (reading '_s') 崩溃:

// vite.config.ts
export default defineConfig({
  resolve: { dedupe: ['pinia', 'vue', 'vue-router'] },
})

🚀 快速开始

方式 A — Mock 模式(无需后端)

// main.ts
import { initMockServices } from 'schemagine'

initMockServices() // 基于 localStorage 的示例模块,刷新后数据保留
<script setup lang="ts">
import { SchemaEngine } from 'schemagine'
</script>

<template>
  <SchemaEngine module-id="module-voucher" />
</template>

内置示例模块:module-voucher(凭证管理,完整功能演示)、module-ap(FK 关联)、module-invoice、module-sales-order、module-receivable、module-user、module-workshop,以及边界场景模块(module-empty、module-no-perm)。调用 resetAllStorage() 可重置数据。也支持只 Mock 部分 Service,其余覆盖为真实实现。

方式 B — 对接自己的后端(Service 注入)

引擎本身不包含任何业务 API。实现以下接口并在挂载前注入:

import { setRecordService, setSchemaService, setCandidateService,
         setUserViewConfigService, setRelationService, setMediaService } from 'schemagine'

setRecordService(new MyRecordService())          // list / getDetail / patchField / create / batchCreate / listFieldValueCandidates
setSchemaService(new MySchemaService())          // loadModuleSchema / loadModulePermissions / validateSchema / saveModuleSchema / listModuleIds
setCandidateService(new MyCandidateService())    // FK 下拉候选值
setUserViewConfigService(new MyViewConfigService()) // 按用户持久化的列宽、布局等
setRelationService(new MyRelationService())      // 一对多 / 多对多关联
setMediaService(new MyMediaService())            // 可选:图片字段的媒体库

所有方法返回统一响应结构:

interface ApiResponse<T> {
  success: boolean
  data: T
  message?: string
  errorCode?: string
  details?: unknown[]
}

未注入 Service 时,引擎回退到返回 SERVICE_NOT_INITIALIZED 错误的 Stub 实现,不会崩溃。

然后渲染模块:

<script setup lang="ts">
import { SchemaEngine } from 'schemagine'

function onDataChanged(payload: { moduleId: string }) {
  // 刷新角标、通知等
}
</script>

<template>
  <SchemaEngine
    module-id="my-module"
    :readonly="false"
    :global-context="{ currentUser, systemSettings }"
    @module-loaded="onLoaded"
    @data-changed="onDataChanged"
  />
</template>

🧩 <SchemaEngine> API

// Props
moduleId: string                        // 必填 — 要加载的 Schema / 数据模块
initialViewMode?: 'list' | 'card' | 'create'
embedded?: boolean                      // 嵌入模式(移除外边距与背景)
readonly?: boolean                      // 全局只读
globalContext?: Record<string, unknown> // 供 visibleWhen / editableWhen 条件表达式使用

// 事件
module-loaded       { moduleId }
view-mode-change    { mode }
data-changed        { moduleId }
error               { moduleId, code, message }
request-open-dialog { dialogType, payload }
action-trigger      { action: ListAction, context? }  // 列表级操作,含批量删除(type: 'delete')
row-action          { rowId, field, actionId }        // 行级操作,含已确认的删除

// 通过 ref 暴露
refresh(): void                     // 重新加载模块
setViewMode(mode): void             // 切换视图
getCurrentRecord(): RecordEntity | undefined

删除是标准数据操作:引擎负责渲染操作列、批量工具栏及全部二次确认弹窗(由 schema.operations.delete 与 permissions.delete 共同决定是否可用),实际的删除执行通过 row-action / action-trigger 事件交由宿主完成。

📐 Schema 一览

interface ModuleSchema {
  id: string
  name: string
  version: string                       // 语义化版本号,驱动迁移
  moduleType: 'list' | 'card' | 'both'
  fields: FieldSchema[]
  permissions: ModulePermissions        // view / create / edit / delete / export / configure
  defaultViewMode: 'list' | 'card' | 'create'
  createMode?: 'list' | 'card'
  listEditMode?: 'inline-dblclick' | 'select-then-edit'
  operations?: { delete?: DeleteOperationConfig }  // 标准数据操作
  formulaConfig?: FormulaConfig
  listActions?: ListAction[]
  migrations?: SchemaMigration[]
  status: 'active' | 'disabled' | 'error'
}

interface FieldSchema {
  id: string; name: string; key: string; label: string
  type: FieldType
  required: boolean; readonly: boolean; visible: boolean; sortable: boolean; filterable: boolean
  // 布局:order / width / fixed …
  // 行为:defaultValue、validationRules、permission、visibleWhen / editableWhen
  // 组合:formula、dynamicMax、options、targetModule + displayField(FK)、
  //       relationConfig / reverseRefConfig、aggregation、rowAction、prefixStr / suffixStr
  // 演进:previousKeys(重命名映射)
}

type FieldType =
  | 'text' | 'number' | 'date' | 'datetime' | 'boolean'
  | 'select' | 'multi-select' | 'status' | 'percent' | 'currency' | 'money'
  | 'phone' | 'email' | 'url'
  | 'fk' | 'one-to-many' | 'many-to-many' | 'reverse-ref'
  | 'formula' | 'image' | 'mediaImage' | 'attachment' | 'json' | 'action'

筛选采用标准化的 FilterClause[] 协议,支持 10 种算子:eq · neq · like · notLike · in · notIn · between · notBetween · isNull · isNotNull。

🔧 组合式函数

| Composable | 用途 | |-----------|------| | useSchema(meta?, ui?) | Schema 加载、视图模式切换 | | usePermission(meta?) | 权限检查(模块 / 字段 / 记录) | | useCellEdit(records?, meta?, ui?) | 单元格编辑、乐观锁重试 | | useFormula(records?, meta?) | 公式计算(mathjs) | | useAggregation() | 求和 / 平均 / 计数 / 最大 / 最小 | | useDraftLifecycle() | 批量新增草稿生命周期 | | useDynamicMax() | 动态最大值校验 | | compareVersions / runSchemaMigrations | 语义化版本比较与迁移执行器 | | useSchemaMeta() / useRecords() / useUi() | 在自定义组件中消费注入的实例状态 | | createSchemaMetaState() / createRecordState() / createUiState() | 多实例独立状态工厂 |

工具函数:formatMoney、getOperatorLabel / OPERATOR_LABEL_MAP、resolveDataOperations / applyBuiltinOperations、resolveEnumTagStyle、resolveMediaUrl 等,均从包根导出并附带类型。

| Composable(规则) | 用途 | |-----------|------| | useRules() | 在 <SchemaEngine> 内消费规则状态(编译、compute/validate/动作规划) |

📜 声明式规则(schemagine/rules)

独立子路径导出的规则运行时:框架无关、零依赖——库只产出 Effect 描述符("规划"),宿主负责执行("解释")。求值器(如 mathjs)、函数词典(内置仅 now/sum/count/min/max/abs/ceilTo/roundTo,业务函数一律由宿主经词典注册)、动作与 Flow 均以注入接入。

import { createRuntime, validateRules } from 'schemagine/rules'

const runtime = createRuntime({ evaluate, parse, functions, flows, actions })
const compiled = runtime.compileModule(schema)
runtime.evalCondition(['status', 'eq', 0], { record })     // 对象形 | 三段形 | 表达式串
runtime.runComputes(compiled, { record }, 'price')         // watch 触发 + 声明序级联 → SET/FORCE
runtime.runValidates(compiled, { record }, 'price', 5, 0)  // { ok, message?, force? }
runtime.planAction(actionRule, { record })                 // confirm → invoke/open/navigate → toast
await runtime.runFlow(flow, { record }, executor)          // 步骤四要素 action/params/when/as

八类规则位 condition/compute/map/lookup/validate/action/style/aggregate,挂载于 ModuleSchema.rules、FieldSchema.rules 与 RowActionConfig.action。schema 交 <SchemaEngine> 渲染时引擎原生接线:行内编辑触发 compute 链与 validate 门禁,action 点击产出 Effect 交 rulesExecutor prop,labelWhen 按行切换按钮文案,aggregate 规则扩展统计条;Schema Editor 内置规则编辑面板,validateSchema 对非法规则给出 error 诊断。同构校验器 validateRules / validateFlow / validateDictionary 可在服务端复用。

🎨 主题定制

全部组件样式 Token 化为 --sg-* CSS 变量(随库打包在 schemagine.css 中)。三种可叠加的定制方式:

/* 1) 零配置 — 跟随 Element Plus 主题,含 html.dark 暗色模式 */

/* 2) 在任意祖先覆盖 Token — 支持只改部分 */
:root {
  --sg-color-primary: #7c3aed;  /* 品牌色;浅色阶与 focus 光晕自动跟随 */
  --sg-radius-md: 6px;
  --sg-font-size-base: 13px;
}

/* 3) 覆盖 Element Plus 的 --el-* 变量 — 引擎基色经 var(--el-*, 回退) 传导,同样生效 */

已 Token 化的维度:颜色(品牌 / 功能 / 文字 / 边框 / 填充 / 背景)、8 档字号、2px 网格间距、控件高度、7 档圆角、4 档阴影 + focus 光晕、z-index 层级、过渡时长。完整基线见 docs/15-样式Token基线.md。

🖥 演示应用(本仓库)

pnpm install
pnpm dev        # 启动 Vite,默认打开凭证管理演示模块(Mock 模式)

| 路由 | 模块 | 说明 | |------|------|------| | /voucher | module-voucher | 完整功能演示(公式字段、FK 关联) | | /ap | module-ap | FK 关联演示 | | /module/:moduleId | 任意 Mock 模块 | 通用模块路由 | | /editor | — | Schema 可视化编辑器 |

🛠 开发

pnpm install
pnpm dev              # 开发服务器
pnpm type-check       # vue-tsc 类型检查
pnpm test:unit        # Vitest 单元测试
pnpm test:e2e         # Playwright E2E(需先构建)
pnpm test:e2e:p0      # 仅 P0 核心链路用例(另有 :p1 / :p2 / :all;PowerShell 脚本)
pnpm lint             # ESLint + oxlint
pnpm format           # oxfmt
pnpm build            # 类型检查 + 应用构建
pnpm build:lib        # 库构建 → dist/schemagine.{mjs,cjs} + schemagine.css + *.d.ts

环境要求:Node ^20.19.0 || >=22.12.0,pnpm。

发布(pnpm publish,prepublishOnly 自动构建)仅包含 dist/:ESM + CJS + CSS + 类型声明。CI 发布示例见 引用指南.md 发布章节。

📚 文档

| 文档 | 内容 | |------|------| | 英文文档站 | 英文入口:介绍、安装、快速开始、15 分钟教程、六 Service 契约 | | docs/17-集成与使用指南.md | 外部项目集成指南(首选,完整参考):安装、Service 契约、完整 API 与类型参考、扩展注册、i18n、npm 发布、FAQ | | examples/minimal-vite | 可运行的最小示例:单模块 schema + 内存数据源,零后端 | | 说明文档(docs/18) | 项目概述与维护记录索引 | | docs/01-项目概述.md | 概述、目录结构、架构分层 | | docs/02-组件文档.md | 全部组件(表格 / 卡片 / 字段 / 筛选) | | docs/03-引擎核心.md | 引擎入口、视图容器、弹窗宿主 | | docs/04-状态管理.md | 状态设计(instanceState 主链路与 Store) | | docs/05-组合式函数.md | Composables 参考 | | docs/06-服务层.md | Service 接口 + Mock 适配器 | | docs/07-类型系统.md | TypeScript 类型系统(含 FieldSchema 权威附录) | | docs/08-生命周期与钩子.md | 生命周期、钩子、数据流图 | | docs/09-Schema编辑器.md | Schema 可视化编辑器 | | docs/10-路由与模块.md | 路由与演示模块 | | docs/15-样式Token基线.md | 样式 Token 基线(组件样式一律 var(--sg-*)) | | docs/20-外观与格式契约.md | EngineAppearance 外观契约、displayStyle、保守导出 | | docs/archive/ | 历史评估 / 路线图文档(已归档) |

❓ 常见问题

引擎会和宿主应用的 Pinia 状态冲突吗? 不会。每个 <SchemaEngine> 通过 create*State() 工厂创建隔离的实例状态,Pinia Store 也是动态注册的,多实例安全共存(配合上文必需的 Vite dedupe 配置)。

大数据量下表现如何? 表格基于 vxe-table 虚拟滚动;列表走服务端分页;CSV 导出按页拉取并设有安全上限(5,000 行)。

Schema 变更后旧数据怎么办? 在 migrations[] 中声明迁移(fromVersion → toVersion),重命名字段用 previousKeys — 引擎加载时自动迁移记录、视图配置与筛选预设。

可以替换 UI 层吗? 可以 — 通过 useSchemaMeta() / useRecords() 消费状态,用导出的积木(SchemaTable、FieldEditorFactory、ValueRenderer 等)自由组合自己的视图。


📄 许可证

MIT