47-MetaGrid元数据表格

47-MetaGrid:16.5KB的元数据表格

列表页的万能组件------三种数据模式(server paging/store/manual)、#fieldName具名插槽定制列、#operations操作列、三种导出、行内编辑、复选选择。16.5KB的Vue文件是组件库里最大的通用件。这篇拆模板层的插槽路由和script层的三模式数据源。

文章目录

源码:browise-vue/src/components/grid/MetaGrid.vue(16.5KB)

底座:vxe-grid(VXE-Table 4.7)+ el-pagination


一、模板层:vxe-grid的插槽路由

vue 复制代码
<vxe-grid :columns="gridColumns" :data="displayData" ...>
  <!-- 动态列插槽:#[字段名]_default -->
  <template v-for="f in slotFieldNames" :key="f" #[`${f}_default`]="scope">
    <slot :name="f" v-bind="scope" />
  </template>
  <!-- 操作列固定插槽 -->
  <template v-if="hasOperations" #operations_default="scope">
    <slot name="operations" v-bind="scope" />
  </template>
</vxe-grid>

插槽转发模式 ------vxe-grid的列渲染走字段名_default插槽------MetaGrid把父组件传来的同名插槽原样转发进去:

vue 复制代码
<!-- 使用方 -->
<MetaGrid storeName="userStore">
  <template #psnSex="{ row }">
    <el-tag :type="row.psnSex === '1' ? 'success' : 'info'">
      {{ row.psnSex === '1' ? '男' : '女' }}
    </el-tag>
  </template>
  <template #operations="{ row }">
    <el-button @click="edit(row)">编辑</el-button>
  </template>
</MetaGrid>

#[...]的动态插槽名 ------Vue3的v-slot简写支持模板字符串------父组件声明什么字段名的插槽,哪一列就被定制 ------其他列走默认渲染。定制粒度=列,不需要整个columns重写。

slotFieldNames的来源 ------useSlots()拿父组件传的插槽名数组------过滤出与columns字段匹配的------只转发有效的。


二、三种数据模式

typescript 复制代码
const props = withDefaults(defineProps<{
  storeName?: string     // 模式1/2:store的名字
  pageUrl?: string       // 模式1:服务端分页URL
  columns?: FieldMeta[]  // 模式2/3:列定义
  ...
}>)
模式 配置 数据来源 典型场景
server paging pageUrl 自己发请求翻页 大数据量标准列表页
store绑定 storeName useStore(storeName).rowset 主从联动/编辑提交
manual columns+data prop 父组件灌数据 一次性展示/嵌套表格

模式1(server paging)的自循环:

typescript 复制代码
async function loadPage() {
  loading.value = true
  const resp = await httpClient.request({
    url: props.pageUrl, method: props.pageMethod,
    data: { ...queryParams, minRow, maxRow }
  })
  displayData.value = resp.data.rows
  displayTotal.value = resp.data.total
  loading.value = false
}
onPageChange → loadPage()   // 翻页自动加载

模式2(store绑定)的响应式直通:

typescript 复制代码
const stores = inject(STORE_MAP_KEY, new Map())
const store = computed(() => stores.get(props.storeName))
const displayData = computed(() => store.value?.rowset.getPrimary())
// rowset的行变化(增删改)→ displayData自动重算 → 表格更新

关键差异 ------模式1的displayData是本地ref (自己管);模式2是computed直通store (别人管,谁改rowset谁负责)。同一组件两种数据所有权模型------props驱动哪个就走哪个。


三、gridColumns:FieldMeta到vxe列的翻译

typescript 复制代码
const gridColumns = computed(() => {
  const cols = (props.columns || store.value?.meta?.columns || [])
    .filter(f => f.visible !== false)      // meta里visible=false的列不显示
    .map(f => ({
      field: f.name,
      title: f.label,
      width: f.width || undefined,
      // codeName有值 → 字典翻译formatter
      formatter: f.codeName ? dictFormatter(f.codeName) : undefined,
      // displayType → 编辑器类型(行内编辑时)
      editRender: props.editable ? editorFor(f.displayType) : undefined,
      slots: { default: `${f.name}_default` }   // 插槽路由
    }))
  // 复选列/序号列/操作列按配置前置后置
  ...
})

columns的三级来源 ------props.columns > store.meta.columns > 空------直接传列定义(模式3)或靠元数据自动生成(describe的结果) 。零代码模式下MetaGrid连columns都不传------describe元数据经store.meta到达------列从元数据自动长出来。

dictFormatter ------codeName字段挂字典翻译------显示时row[f.name]的码值("1")翻译成名称("男")------字典来自useDict的缓存(第53篇dict模块的前端侧)。


四、行内编辑的editRender

typescript 复制代码
function editorFor(displayType: string) {
  switch (displayType) {
    case 'date':   return { name: '$input', props: { type: 'date' } }
    case 'number': return { name: '$input', props: { type: 'number' } }
    case 'datetime': return { name: '$input', props: { type: 'datetime' } }
    default:       return { name: '$input' }
  }
}

editabled的表格开启行内编辑 ------双击单元格进入编辑------vxe的edit-closed事件触发------新值写回Row:

typescript 复制代码
function onEditClosed({ row, column }) {
  const storeRow = 找到对应的Row实例
  storeRow.setItemValue(column.field, row[column.field])
  // setItemValue的脏标记自动生效------collect('dirty')能收集到
}

行内编辑直通脏标记协议------表格改完collect提交------和表单编辑同一条链。


五、三种导出

typescript 复制代码
function onExport(command: string) {
  if (command === 'server')     → window.open(exportUrl + '?token=...&' + 查询参数)
  if (command === 'client')     → 导出displayData(当前页数据)到Excel
  if (command === 'checked')    → 过滤选中的行导出
}
导出 数据范围 实现
server 服务端全量(按当前查询条件) 后端ExportController(11.7KB通用导出)
client 当前加载的数据 前端直接生成(数据已在内存)
checked 勾选的行 复选过滤后同client

server导出带token的坑 ------window.open发GET请求不能带Authorization头------token拼在query string(getAccessToken())------后端ExportController兼容query取token(与JwtAuthFilter的Cookie通道同理------第26篇双通道的第三变体)。


✅ 亮点:动态插槽名转发实现列级定制、三种数据模式的所有权差异(本地ref vs computed直通)、FieldMeta到vxe列的翻译层(含字典formatter)、行内编辑直通脏标记、三种导出的token传递方案。适合封装通用表格的人。扩展方向:第13篇describe元数据、第46篇useCenter、第24篇autoCount(total的来源)。

相关推荐
智能运维指南39 分钟前
研制过程“两张皮“怎么破:研制一体化支撑平台的定位、能力边界与落地前提
java·前端·人工智能·嘉为蓝鲸
yz_aiks1 小时前
Spring AI 2.1 ChatMemory 已过时用什么替代:迁移到 Session Memory 实战(JDBC 存储与会话压缩)
java·spring boot·大模型·ai agent·spring ai
金銀銅鐵2 小时前
[Java] 用 GUI 展示 EnumMap 增删键值对时的内部变化
java·后端·ai编程
Oiiouui2 小时前
Godot-GridGame架构屏幕UI层
架构·游戏引擎·godot
学长毕设2 小时前
基于SpringBoot的上门洗车服务系统设计与实现
java·spring boot·后端
jason成都2 小时前
RTKLIB-Java 多基线联合RTK:FIX率从0%到91.5%的三层根因修复
java·gnss·rtklib·rtklib_java
鱼宵2 小时前
Stream 终端操作一次讲透:collect、count、reduce 怎么选
java·stream·终端操作
白山编程大哥2 小时前
Java 后端如何优雅地把 List 集合返回给前端
java·前端·list
Linux运维技术栈3 小时前
CVE-2026-91403 高危漏洞修复:Rocky Linux 9 多版本 JDK 环境清理实操
java·linux·运维
On The Road :3 小时前
Java 多线程详解:从入门到线程安全
java·python·安全