ProPage 页面骨架
基于 plus-pro-components / Page 的 dh-design-pc 页面级封装,适合“筛选表单 + 表格 + 分页 + 工具栏”的标准业务列表页。
当前组件支持两种模式:
- 基础模式:业务方自行传入
search-model / data / total / page / page-size - request 模式:通过
request、params、before-search-submit管理整页数据流
ProPage 会把分页区域稳定放在表格可视区底部;表格内容出现纵向滚动时,分页不会跟着滚出视线。需要关闭时传 sticky-pagination="false"。
ProPage 默认使用 PC 端业务列表规范:表格常规密度、空值使用 -、单元格单行省略、最小表格宽度 960px、操作列最多平铺 3 项。业务方可通过 table 属性覆盖这些默认值。
ts
import { createApp } from 'vue'
import { ProPage } from '@szhn/dh-design-pc'
const app = createApp({})
app.use(ProPage)基础用法
<template>
<dh-pro-page
v-model:search-model="searchModel"
:columns="columns"
:request="requestAssetList"
>
<template #search-toolbar>
<el-button type="primary">添加成员</el-button>
<el-button>导入</el-button>
<el-button>导出花名册</el-button>
<el-button disabled>批量删除</el-button>
<el-input placeholder="请输入文字" style="width: 200px" />
</template>
</dh-pro-page>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { pageColumns, requestAssetList } from './shared'
const searchModel = ref<Record<string, any>>({})
const columns = pageColumns
</script>手动控制搜索值
<template>
<div class="pro-page-demo">
<div class="pro-page-demo__actions">
<el-button type="primary" @click="setName">设置名称</el-button>
<el-button @click="setStatus">设置状态</el-button>
<el-button @click="logName">获取名称</el-button>
<el-button @click="clearFields">清空搜索项</el-button>
</div>
<dh-pro-page
ref="pageRef"
:columns="pageColumns"
:request="requestAssetList"
:search="{ showNumber: 2 }"
/>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { pageColumns, requestAssetList } from './shared'
const pageRef = ref<any>(null)
const setName = () => pageRef.value?.setSearchFieldsValue?.({ name: '2name' })
const setStatus = () => pageRef.value?.setSearchFieldsValue?.({ status: '1' })
const logName = () => console.log(pageRef.value?.getSearchFieldsValue?.('name'))
const clearFields = () => pageRef.value?.clearSearchFieldsValue?.()
</script>
<style scoped>
.pro-page-demo {
display: flex;
flex-direction: column;
gap: 16px;
}
.pro-page-demo__actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
</style>搜索参数处理
<template>
<div class="pro-page-demo">
<el-alert title="打开控制台可看到 beforeSearchSubmit 转换后的查询参数。" type="info" :closable="false" />
<dh-pro-page
:columns="pageColumns"
:request="requestAssetList"
:search="{ showNumber: 2 }"
:before-search-submit="beforeSearchSubmit"
/>
</div>
</template>
<script lang="ts" setup>
import { pageColumns, requestAssetList } from './shared'
const beforeSearchSubmit = (values: Record<string, any>) => {
const payload = {
assetName: values.name,
assetStatus: values.status,
}
console.log('beforeSearchSubmit', payload)
return payload
}
</script>
<style scoped>
.pro-page-demo {
display: flex;
flex-direction: column;
gap: 12px;
}
</style>默认搜索参数
<template>
<dh-pro-page
:columns="pageColumns"
:request="requestAssetList"
:search="{
showNumber: 2,
defaultValues: {
status: '1',
name: '1name',
},
}"
/>
</template>
<script lang="ts" setup>
import { pageColumns, requestAssetList } from './shared'
</script>默认分页参数
<template>
<dh-pro-page
:columns="pageColumns"
:request="requestAssetList"
:search="{ showNumber: 2 }"
:default-page-info="{ page: 2, pageSize: 5 }"
/>
</template>
<script lang="ts" setup>
import { pageColumns, requestAssetList } from './shared'
</script>自定义搜索按钮
<template>
<dh-pro-page :columns="pageColumns" :request="requestAssetList" :search="{ showNumber: 2 }">
<template #search-footer="{ handleReset, handleSearch, handleUnfold, isShowUnfold }">
<div class="pro-page-footer">
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button @click="handleUnfold">{{ isShowUnfold ? '收起' : '展开' }}</el-button>
</div>
</template>
</dh-pro-page>
</template>
<script lang="ts" setup>
import { pageColumns, requestAssetList } from './shared'
</script>
<style scoped>
.pro-page-footer {
display: flex;
gap: 12px;
}
</style>自定义搜索表单插槽
<template>
<dh-pro-page
v-model:search-model="searchModel"
:columns="pageColumns"
:request="requestAssetList"
:search="{ showNumber: 2 }"
search-slot
>
<template #plus-field-name="{ prop, column }">
<el-input
:model-value="searchModel[prop]"
:placeholder="`自定义 ${column.label}`"
@update:model-value="updateSearchModel(prop, $event)"
/>
</template>
<template #plus-label-name="{ label }">
<span class="pro-page-label">{{ label }}(自定义)</span>
</template>
</dh-pro-page>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { pageColumns, requestAssetList } from './shared'
const searchModel = ref<Record<string, any>>({})
const updateSearchModel = (prop: string, value: string) => {
searchModel.value[prop] = value
}
</script>
<style scoped>
.pro-page-label {
color: var(--el-color-primary);
font-weight: 600;
}
</style>搜索和表格展示顺序控制
<template>
<dh-pro-page
:columns="columns"
:request="requestAssetList"
:search="{ showNumber: 2 }"
/>
</template>
<script lang="ts" setup>
import type { PcProColumn } from '@szhn/dh-design-pc'
import { pageColumns, requestAssetList } from './shared'
const columns: PcProColumn[] = pageColumns.map(column => {
if (column.prop === 'name') return { ...column, search: { order: 2 } }
if (column.prop === 'status') return { ...column, search: { order: 3 } }
if (column.prop === 'time') return { ...column, search: { order: 1 } }
return column
})
</script>model-value / v-model
<template>
<div class="pro-page-demo">
<el-button @click="setExternalValues">通过外部 model 更新筛选项</el-button>
<dh-pro-page
v-model:search-model="searchModel"
:columns="pageColumns"
:request="requestAssetList"
:search="{ showNumber: 2 }"
/>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { pageColumns, requestAssetList } from './shared'
const searchModel = ref<Record<string, any>>({
status: '0',
})
const setExternalValues = () => {
searchModel.value = {
...searchModel.value,
name: '3name',
status: '2',
}
}
</script>
<style scoped>
.pro-page-demo {
display: flex;
flex-direction: column;
gap: 16px;
}
</style>增删改查(CRUD)
<template>
<div class="pro-page-demo">
<dh-pro-page
:columns="pageColumns"
:request="requestAssetList"
:search="{ showNumber: 2 }"
:action-items="actionItems"
>
<template #table-title>
<div class="pro-page-title-actions">
<el-button type="primary" @click="dialogVisible = true">新增</el-button>
<el-button type="danger">批量删除</el-button>
</div>
</template>
<template #table-toolbar>
<div class="pro-page-title-actions">
<el-button plain>查看日志</el-button>
<el-button plain>导出数据</el-button>
</div>
</template>
</dh-pro-page>
<el-dialog v-model="dialogVisible" title="新增记录" width="520">
<el-form label-width="80px">
<el-form-item label="名称">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="form.status">
<el-option label="未解决" value="0" />
<el-option label="已解决" value="1" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { actionItems, pageColumns, requestAssetList } from './shared'
const dialogVisible = ref(false)
const form = reactive({
name: '',
status: '0',
})
</script>
<style scoped>
.pro-page-demo {
display: flex;
flex-direction: column;
gap: 16px;
}
.pro-page-title-actions {
display: flex;
gap: 12px;
}
</style>ProPage API
Props
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| columns | 表格与搜索共享 schema | PcProColumn[] | [] |
| search-model / v-model:search-model | 搜索表单值 | Record | {} |
| data | 表格数据(基础模式) | Array | [] |
| loading | 加载状态(基础模式) | boolean | false |
| total | 总条数(基础模式) | number | 0 |
| page / v-model:page | 当前页码 | number | defaultPageInfo.page |
| page-size / v-model:page-size | 每页条数 | number | defaultPageInfo.pageSize |
| request | request 模式的数据加载函数 | Function | — |
| search | Search 透传配置;传 false 可隐藏搜索区 | object / false | {} |
| table | Table 透传配置,默认带业务列表表格规范 | object | {} |
| params | request 模式附加参数 | object | {} |
| post-data | 对 request 结果中的 data 做二次处理 | Function | — |
| before-search-submit | 请求前转换搜索参数 | Function | — |
| default-page-info | 默认分页参数 | object | |
| default-page-size-list | 默认分页选项列表 | Array | [10,20,50,100] |
| pagination | 分页配置;传 false 可关闭分页 | object / false | — |
| page-info-map | 自定义 request 中的分页字段名 | object | |
| search-slot | 是否将 plus-field-* / plus-label-* 等插槽映射到搜索区 | boolean | false |
| sticky-pagination | 分页是否固定在表格可视区底部 | boolean | true |
| search-layout | dh-pro-search 的布局 | string | auto |
| search-col | dh-pro-search 的列数 | number | 自动推导 |
| search-label-width | 搜索区 label 宽度 | string / number | 100px |
| action-items | 业务操作列配置 | PcProActionItem[] | [] |
| show-more-filter | 是否展示“更多筛选”入口 | boolean | false |
其中 search 属性会继续透传给 dh-pro-search,所以像 advancedPanel、advancedTitle、advancedDescription 这类内建高级筛选面板能力,也可以直接在 ProPage 场景里使用。
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| update:search-model | 搜索表单值变化 | (v: Record) |
| update:page | 页码变化 | (n: number) |
| update:page-size | 每页条数变化 | (n: number) |
| search | 点击查询 | (v: Record) |
| reset | 点击重置 | (v: Record) |
| open-advanced | 点击“更多筛选” | — |
| request-error | request 失败 | (error) |
| request-complete | request 完成 | (result) |
| pagination-change | 页码或 pageSize 变化 | ({ page, pageSize }) |
Slots
| 插槽名 | 说明 |
|---|---|
| search-footer | 自定义搜索按钮区 |
| search-toolbar | 搜索区 footer 左侧业务操作区 |
| advanced / advanced-nav / advanced-footer | 透传到搜索区内建高级筛选面板 |
| table-title | 表格标题左侧区域 |
| table-toolbar | 表格工具栏左侧区域 |
| table-expand | 展开行 |
| table-append | 表格底部附加区域 |
| table-empty | 空状态 |
| pagination-left / pagination-right | 分页器左右区域 |
| plus-cell-* / plus-header-* | 自定义表格单元格 / 表头 |
| plus-field-* / plus-label-* / plus-extra-* / plus-previous-* | 开启 search-slot 后映射到搜索区 |
Expose
| 方法名 | 说明 | 类型 |
|---|---|---|
| getList | 手动触发 request 重新拉数 | Function |
| handleReset | 调用底层搜索区 reset | Function |
| setSearchFieldsValue | 批量设置搜索值 | Function |
| getSearchFieldsValue | 获取全部或单个搜索值 | Function |
| clearSearchFieldsValue | 清空搜索值 | Function |
| setTableData | 手动覆盖当前表格数据 | Function |
