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

@hz_yujin/vue-smart-table

v0.1.2

Published

Vue 3 ProSmartTable:基于 el-table 全量透传,列 schema 驱动查询 / 表单 / 列管理抽屉

Readme

@hz_yujin/vue-smart-table

本包位于 monorepo packages/smart-table。仓库根目录见 README

基于 Element Plus el-table 的 Smart Table:一份列 schema 同时驱动表格列、查询条件与新增/编辑表单,并内置列管理抽屉、分页、骨架屏。

@hz_yujin/vue-base-table(VXE)互补,互不影响。

目录


安装

npm install @hz_yujin/vue-smart-table element-plus @element-plus/icons-vue

element-plus@element-plus/icons-vuevuepeerDependencies,需由宿主项目安装。

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import '@hz_yujin/vue-smart-table/style.css'

import { ProSmartTable } from '@hz_yujin/vue-smart-table'
// 或全局注册
import VueSmartTable from '@hz_yujin/vue-smart-table'

const app = createApp(App)
app.use(ElementPlus, { locale: zhCn })
app.use(VueSmartTable) // 可选
app.mount('#app')

快速开始

<script setup lang="ts">
import { ref } from 'vue'
import {
  ProSmartTable,
  type SmartColumnSchema,
} from '@hz_yujin/vue-smart-table'
import '@hz_yujin/vue-smart-table/style.css'

const columns = ref<SmartColumnSchema[]>([
  {
    id: 'name',
    prop: 'name',
    label: '名称',
    minWidth: 160,
    searchable: true,
    formable: true,
    required: true,
    formType: 'input',
    source: 'fixed',
  },
  {
    id: 'status',
    prop: 'status',
    label: '状态',
    minWidth: 120,
    searchable: true,
    formable: true,
    formType: 'select',
    formOptions: [
      { label: '启用', value: '启用' },
      { label: '禁用', value: '禁用' },
    ],
    source: 'fixed',
  },
])

const data = ref([
  { name: '示例 A', status: '启用' },
  { name: '示例 B', status: '禁用' },
])

function onSearch(model: Record<string, unknown>) {
  console.log('search', model)
}
</script>

<template>
  <ProSmartTable
    v-model:columns="columns"
    :data="data"
    persist-key="demo-smart-table"
    form-title="记录"
    border
    stripe
    @search="onSearch"
    @create="(row) => data.push(row)"
    @update="(row) => console.log('update', row)"
  />
</template>

未在 ProSmartTable 上声明的属性(如 borderstripeheightrow-key)会透传给底层 el-table


设计思路

| 区域 | 由谁驱动 | |------|----------| | 表格列 | columnsvisible !== false 且有 prop 的项 | | 查询区 | searchable: true 的列 | | 新增/编辑表单 | formable: true 的列 | | 控件类型 | formType + formOptions / formProps |

业务侧通常只需维护一份 SmartColumnSchema[],再监听 search / create / update / page-change 对接接口。

能力一览

| 能力 | 说明 | |------|------| | el-table 透传 | 未声明的 attrs / 默认插槽全部落到 el-table | | 查询 | 可收起/展开;默认收起展示前 3 项 | | 新增/编辑 | 内置弹框 + 必填校验;可用插槽自定义字段 | | 列管理 | 显隐、拖拽排序、增删列、查询/表单开关、固定列 | | 持久化 | persistKeylocalStorage;也可自行存服务端 | | 分页 | 默认开启;local 前端切片 / remote 服务端分页 | | 排序 | 支持本地 / 远程(sortable: 'custom' 或 remote 分页自动提升) | | 骨架屏 | 默认开启;无数据时骨架,有数据刷新转圈 | | 序号 / 多选 | showSeq / showSelection |


SmartColumnSchema

interface SmartColumnSchema {
  /** 稳定唯一 id(拖拽 / 删除 / 持久化必填) */
  id: string
  /** 对应 el-table-column 的 prop / 数据字段 */
  prop: string
  /** 列标题 */
  label: string
  minWidth?: number
  width?: number | string
  /** 是否可排序;远程分页下 true 会自动提升为 'custom' */
  sortable?: boolean | 'custom'
  /** 是否作为查询条件 */
  searchable?: boolean
  /** 是否出现在新增/编辑表单 */
  formable?: boolean
  /** 表单是否必填(仅 formable 时有效) */
  required?: boolean
  /** 表单 / 查询控件类型 */
  formType?: SmartFormControlType
  /** select / radio / cascader / tree-select 等选项 */
  formOptions?: SmartSelectOption[]
  /** 查询占位符 */
  searchPlaceholder?: string
  /** 固定列 */
  fixed?: boolean | 'left' | 'right'
  /** 是否在表格中显示,默认 true */
  visible?: boolean
  /** fixed:业务预置;custom:列管理新增 */
  source?: 'fixed' | 'custom'
  /** 是否允许删除(fixed 默认不可删,custom 默认可删) */
  deletable?: boolean
  /** 透传给 el-table-column */
  columnProps?: Record<string, unknown>
  /** 透传给表单控件 */
  formProps?: Record<string, unknown>
}

formType 一览

| formType | 说明 | |----------|------| | input | 输入框(默认) | | password | 密码 | | textarea | 多行文本 | | number | 数字 | | select / select-multiple | 下拉单选 / 多选 | | radio | 单选 | | checkbox / checkbox-group | 复选 / 复选组 | | switch | 开关 | | date / datetime | 日期 / 日期时间 | | daterange / datetimerange | 日期范围 / 日期时间范围(亦支持 date-range / datetime-range 别名) | | time / timerange | 时间 / 时间范围(亦支持 time-range) | | month / year / week | 月 / 年 / 周 | | cascader | 级联 | | tree-select | 树选择 | | autocomplete | 自动完成 | | rate / slider / color | 评分 / 滑块 / 颜色 |

选项结构:

interface SmartSelectOption {
  label: string
  value: string | number | boolean
  disabled?: boolean
  children?: SmartSelectOption[] // cascader / tree-select
}

列配置示例

const columns: SmartColumnSchema[] = [
  {
    id: 'workshop',
    prop: 'workshop',
    label: '车间',
    minWidth: 120,
    searchable: true,
    formable: true,
    required: true,
    formType: 'tree-select',
    formOptions: [
      {
        label: '东区',
        value: 'east',
        children: [
          { label: '一车间', value: '一车间' },
          { label: '二车间', value: '二车间' },
        ],
      },
    ],
    formProps: { checkStrictly: false, filterable: true },
    source: 'fixed',
    deletable: false,
  },
  {
    id: 'duration',
    prop: 'duration',
    label: '持续时长(分)',
    minWidth: 120,
    sortable: 'custom',
    searchable: true,
    formable: true,
    formType: 'number',
    formProps: { min: 0, max: 999, controlsPosition: 'right' },
    source: 'fixed',
  },
]

Props

| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | data | Record<string, unknown>[] | [] | 表格数据 | | columns | SmartColumnSchema[] | [] | 列 schema;支持 v-model:columns | | showToolbar | boolean | true | 是否展示工具栏 | | showColumnEditor | boolean | true | 是否展示「编辑列」按钮 | | showRefresh | boolean | true | 是否展示刷新按钮 | | showCreate | boolean | true | 是否展示默认「新增」按钮 | | showSeq | boolean | true | 是否展示序号列 | | seqWidth | number \| string | 60 | 序号列宽度 | | showSelection | boolean | false | 是否展示多选列 | | showSearch | boolean | true | 是否展示查询区 | | searchCollapsible | boolean | true | 查询区是否可收起展开 | | searchCollapseVisibleCount | number | 3 | 收起时展示的字段数 | | searchDefaultExpanded | boolean | false | 查询区默认是否展开 | | persistKey | string | — | 有值时列配置写入 localStorage | | formTitle | string | '' | 弹框标题前缀,如「告警」→「新增告警」 | | formWidth | number \| string | '45%' | 弹框宽度 | | loading | boolean | false | 加载中(骨架 / 转圈) | | skeleton | boolean \| ProSmartTableSkeletonConfig | true | 骨架屏;false 关闭 | | pagination | false \| ProSmartTablePagination | {} | 分页;false 关闭 |

skeleton 配置

interface ProSmartTableSkeletonConfig {
  rows?: number      // 骨架行数,默认取 pageSize(上限 10),否则 5
  animated?: boolean // 默认 true
  onlyEmpty?: boolean // 默认 true:仅无数据时用骨架,有数据刷新仍用转圈
}

pagination 配置

interface ProSmartTablePagination {
  currentPage?: number
  pageSize?: number
  total?: number
  pageSizes?: number[]
  layout?: string // 默认 total, sizes, prev, pager, next, jumper
  background?: boolean
  size?: 'large' | 'default' | 'small' // 默认 small
  pagerCount?: number
  /** local:前端切片;remote:data 为当前页,需传 total */
  mode?: 'local' | 'remote'
}

未显式传 mode 时:有 totalremote,否则 local


Events

| 事件 | 参数 | 说明 | |------|------|------| | update:columns | SmartColumnSchema[] | 列变更(配合 v-model:columns) | | columns-change | SmartColumnSchema[] | 列管理保存成功 | | columns-reset | — | 列管理「恢复默认」;业务侧应清空服务端配置 | | search | model | 点击查询,或重置后也会再抛一次 search | | reset | — | 点击重置 | | create | model | 新增弹框确认(已过必填校验) | | update | model | 编辑弹框确认(已过必填校验) | | page-change | { currentPage, pageSize } | 页码 / 每页条数变化 | | update:pagination | ProSmartTablePagination | 分页受控同步 | | refresh | — | 点击刷新 | | sort-change | { column, prop, order } | 透传 el-table;远程排序请监听此事件 |

reset 后组件会清空查询模型并发出 search(带空条件),远程列表可统一在 @search 里拉数。


插槽

| 插槽 | 作用域 | 说明 | |------|--------|------| | toolbar-left | — | 工具栏左侧;覆盖默认「新增」时可配合 show-create={false} | | toolbar-right | — | 工具栏右侧(刷新 / 编辑列按钮之前) | | col-{prop} | el-table 单元格 scope | 自定义列渲染,如 #col-status | | (默认插槽) | — | 追加额外 el-table-column(如操作列) | | form-{prop} | { model, column, value, mode } | 覆盖某个表单字段;mode: create | edit | | form-extra | { model, mode, visible } | 写在弹框内部,用于嵌套二级弹框 | | pagination | — | 完全自定义分页 | | skeleton | — | 自定义骨架屏 |

自定义列 + 操作列

<ProSmartTable :data="data" :columns="columns" ...>
  <template #col-status="{ row }">
    <el-tag :type="row.status === '启用' ? 'success' : 'info'">
      {{ row.status }}
    </el-tag>
  </template>

  <el-table-column label="操作" width="140" fixed="right">
    <template #default="{ row }">
      <el-button link type="primary" @click="tableRef?.openEdit(row)">
        编辑
      </el-button>
    </template>
  </el-table-column>
</ProSmartTable>

方法(expose)

interface ProSmartTableExpose {
  tableRef: TableInstance | null // 底层 el-table
  openColumnEditor: () => void
  openCreate: () => void
  openEdit: (row: Record<string, unknown>) => void
  getSearchModel: () => Record<string, unknown>
  getColumns: () => SmartColumnSchema[]
}
<script setup lang="ts">
import { ref } from 'vue'
import type { ProSmartTableExpose } from '@hz_yujin/vue-smart-table'

const tableRef = ref<ProSmartTableExpose>()

function onEdit(row: Record<string, unknown>) {
  tableRef.value?.openEdit(row)
}
</script>

<template>
  <ProSmartTable ref="tableRef" ... />
</template>

分页

本地分页(默认)

不传 total 时,组件对 data 做前端切片:

<ProSmartTable :data="allRows" :columns="columns" />

远程分页

<script setup lang="ts">
import { ref } from 'vue'
import type { ProSmartTablePagination } from '@hz_yujin/vue-smart-table'

const pagination = ref<ProSmartTablePagination>({
  currentPage: 1,
  pageSize: 10,
  total: 0,
  mode: 'remote',
  pageSizes: [10, 20, 50],
})

async function onPageChange({ currentPage, pageSize }) {
  pagination.value = { ...pagination.value, currentPage, pageSize }
  await fetchList()
}
</script>

<template>
  <ProSmartTable
    :data="list"
    :columns="columns"
    v-model:pagination="pagination"
    :loading="loading"
    @page-change="onPageChange"
    @search="onSearch"
  />
</template>

关闭分页::pagination="false"


远程列表(推荐写法)

与 playground SmartTableDemo 一致的模式:

  1. v-model:pagination + mode: 'remote'
  2. @search / @reset(重置也会走 search)回到第 1 页并拉数
  3. @page-change 更新页码后拉数
  4. @create / @update 调接口后刷新
  5. 自定义「新增」时设 :show-create="false",用 #toolbar-left + openCreate()
const lastSearchModel = ref<Record<string, unknown>>({})
const pagination = ref<ProSmartTablePagination>({
  currentPage: 1,
  pageSize: 10,
  total: 0,
  mode: 'remote',
})

async function onSearch(model: Record<string, unknown>) {
  lastSearchModel.value = { ...model }
  pagination.value = { ...pagination.value, currentPage: 1 }
  await fetchList()
}

async function onPageChange(payload: { currentPage: number; pageSize: number }) {
  pagination.value = { ...pagination.value, ...payload }
  await fetchList()
}

async function fetchList() {
  loading.value = true
  try {
    const res = await api.list({
      ...lastSearchModel.value,
      currentPage: pagination.value.currentPage,
      pageSize: pagination.value.pageSize,
    })
    tableData.value = res.list
    pagination.value = {
      ...pagination.value,
      total: res.total,
      currentPage: res.currentPage,
      pageSize: res.pageSize,
    }
  } finally {
    loading.value = false
  }
}

查询 / 重置时组件会 silent 把页码置 1(不额外抛 page-change),避免连打两次接口;业务只需在 @search 里请求即可。


排序

  • 本地sortable: true,el-table 本地排序即可。
  • 远程:设 sortable: 'custom',或分页为 remotesortable: true 会自动提升为 custom,监听 @sort-change
function onSortChange(payload: {
  prop: string
  order: 'ascending' | 'descending' | null
}) {
  sortState.value = !payload.prop || !payload.order
    ? {}
    : {
        sortBy: payload.prop,
        sortOrder: payload.order === 'ascending' ? 'asc' : 'desc',
      }
  pagination.value = { ...pagination.value, currentPage: 1 }
  void fetchList()
}

sort-change 由组件显式转发,不会再落入 attrs 重复绑定到 el-table


列管理与持久化

右上角「编辑列」打开 ProColumnEditor,可:

  • 显隐、拖拽排序
  • 改 field / 标题 / 最小宽
  • 开关:排序 / 查询 / 表单
  • 改表单类型、左右固定
  • 新增自定义列、删除(受 deletable 控制)
  • 「恢复默认」

本地持久化

<ProSmartTable persist-key="alarm-list" v-model:columns="columns" ... />

persistKey 时,保存列配置写入 localStorage;恢复默认会清除该 key。

服务端持久化

<ProSmartTable
  v-model:columns="columns"
  @columns-change="(cols) => saveTableColumns(cols)"
  @columns-reset="() => saveTableColumns([])"
/>

「恢复默认」后组件会应用内置默认列(首次传入的 columns 快照)并发出 columns-reset。业务侧建议把服务端配置存成 [],下次读取空数组时回落默认列。


表单自定义 / 嵌套弹框

<ProSmartTable ...>
  <!-- 覆盖某个表单字段 -->
  <template #form-deviceName="{ model }">
    <el-input :model-value="model.deviceName" readonly placeholder="请选择设备">
      <template #append>
        <el-button @click="pickerVisible = true">选择</el-button>
      </template>
    </el-input>
  </template>

  <!-- 二级弹框必须写在 form-extra 内(父级弹框插槽) -->
  <template #form-extra="{ model }">
    <ProDialog v-model="pickerVisible" nested title="选择设备" show-footer>
      <!-- 选中后写回 model.deviceName / model.deviceCode -->
    </ProDialog>
  </template>
</ProSmartTable>
  • #form-{prop}model / column / value / modecreate | edit
  • #form-extramodel / mode / visible

必填:required: true 时,null / undefined / 空串 / 纯空格 / 空数组视为未填;0 / false 合法。


骨架屏与加载

| 场景 | 表现 | |------|------| | loading=true 且无数据 | 默认骨架屏(onlyEmpty: true) | | loading=true 且已有数据 | 表格上转圈 | | 点击刷新 | 先发 refresh,并等到外部 loading 结束再停动画 |

<!-- 关闭骨架 -->
<ProSmartTable :skeleton="false" :loading="loading" />

<!-- 自定义 -->
<ProSmartTable :skeleton="{ rows: 8, animated: true, onlyEmpty: true }" />

全局注册

import VueSmartTable from '@hz_yujin/vue-smart-table'
app.use(VueSmartTable)

注册后可直接使用 <ProSmartTable> / <ProColumnEditor>

也可按需引入:

import {
  ProSmartTable,
  ProColumnEditor,
  SMART_FORM_CONTROL_OPTIONS,
  createColumnId,
  normalizeColumn,
  cloneColumns,
  createEmptyColumn,
  resolveFormControlType,
} from '@hz_yujin/vue-smart-table'

工具函数

| 函数 | 说明 | |------|------| | createColumnId(prefix?) | 生成列 id | | normalizeColumn(col) | 补齐默认值(visible / formType / deletable 等) | | cloneColumns(cols) | 深拷贝并 normalize | | createEmptyColumn(partial?) | 新建 custom 列 | | resolveFormControlType(type) | 统一别名(如 date-rangedaterange) | | SMART_FORM_CONTROL_OPTIONS | 列管理里表单类型下拉选项 |


本地开发

# 在 monorepo 根目录
pnpm --filter @hz_yujin/vue-smart-table build
# playground 演示页:SmartTableDemo
pnpm --filter playground dev

License

MIT