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的来源)。

相关推荐
wang_shu_mo_ran1 小时前
Spring MVC的常用注解和用法(一)
java·spring·mvc
Android打工仔1 小时前
不要在 Data 层随意把 Cold Flow 转换成 Hot Flow
android·架构·kotlin
晴空蓝天1 小时前
Spring Boot 3.5 脚手架里的 JWT + Redis 双轨会话,双端 token 隔离我是这么设计的
java·spring boot·redis
IT枫斗者枫哥1 小时前
MyBatis 列表查询优化:一页20条数据,21次SQL改成2次
java
君顾11 小时前
本地电竞服务交易系统架构设计与实战:从同城服务撮合到订单履约
java·开发语言·电竞
小刘在重生~1 小时前
十六(3)、《集合扩充》CopyOnWriteArrayList 超详细解析(线程安全集合)
java·笔记·面试·职场和发展
逆境不可逃1 小时前
Pi Agent 学习笔记:对话怎样保存、分支与恢复
java
huaweichenai2 小时前
spring boot使用hutool实现调用外部接口
java·spring boot
AI行业应用研究2 小时前
会务问答机器人落地拆解:三级路由、知识库组织与防幻觉——会务小程序能自己回答参会者提问吗?
大数据·人工智能·安全·小程序·架构