PageTemplate
页面模板组件,集成搜索表单、数据表格、表单弹窗、详情弹窗等功能,是 vue-admin-kit 的核心组件。
基础用法
vue
<script setup lang="ts">
import { ref } from "vue";
import { useState, PageTemplate, Table } from "vue-admin-kit";
import type { PageTemplateExposed } from "vue-admin-kit";
const pageTemplateRef = ref<PageTemplateExposed>();
useState({
api: {
list: (params) => getList(params),
add: (data) => addItem(data),
edit: (data) => updateItem(data),
delete: (id) => deleteItem(id),
detail: (id) => getDetail(id),
},
searchConfig: [{ type: "input", prop: "name", label: "名称" }],
formConfig: [{ type: "input", prop: "name", label: "名称", required: true }],
columnsConfig: [{ field: "name", title: "名称" }],
tableOptions: {
operateColumns: [
{
title: "编辑",
onClick: (row) => pageTemplateRef.value?.handleEdit(row),
},
{
title: "删除",
onClick: (row) => pageTemplateRef.value?.handleDelete(row),
},
],
},
});
</script>
<template>
<PageTemplate ref="pageTemplateRef">
<template #table>
<Table />
</template>
</PageTemplate>
</template>Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
autoIntegration | boolean | true | 是否自动集成 CRUD 操作 |
autoGetList | boolean | true | 是否在组件挂载时自动获取列表数据 |
provideName | string | 'pageData' | provide 注入名称,用于多实例场景 |
size | 'small'/'default'/'large' | - | 表单项尺寸,不传则从全局配置获取 |
pageData | pageDataType | - | 页面数据,不传则通过 inject 获取 |
关于多表格场景
从 v1.0.3 开始,每个 PageTemplate 实例自动创建独立的高度状态,无需配置 instanceKey。详见 多表格场景 文档。
Events
| 事件 | 参数 | 说明 |
|---|---|---|
search | - | 点击搜索按钮时触发 |
reset | - | 点击重置按钮时触发 |
getDataSuccess | response | 获取列表数据成功后触发 |
submitDataSuccess | - | 表单提交成功后触发 |
drawerClose | - | 表单弹窗关闭时触发 |
vue
<template>
<PageTemplate
@search="handleSearch"
@reset="handleReset"
@get-data-success="handleDataSuccess"
@submit-data-success="handleSubmitSuccess"
>
<template #table>
<Table />
</template>
</PageTemplate>
</template>
<script setup>
const handleSearch = () => {
console.log("搜索触发");
};
const handleDataSuccess = (response) => {
console.log("数据获取成功", response);
};
</script>Exposed Methods
通过 ref 获取组件实例后可调用以下方法:
typescript
interface PageTemplateExposed {
/** 打开新增弹窗 */
handleAdd: (data?: object, row?: object) => void;
/** 打开编辑弹窗 */
handleEdit: (row: object) => void;
/** 打开详情弹窗 */
handleDetail: (row: object) => void;
/** 删除数据(支持单条或批量) */
handleDelete: (row: object | object[]) => void;
/** 触发搜索 */
handleSearch: () => void;
/** 重置搜索条件并搜索 */
handleReset: () => void;
/** 导入文件 */
importFile: (
api: Function,
templateName: string,
successCallback?: Function
) => void;
/** 下载文件 */
downloadFile: (api: Function, fileName: string) => void;
}使用示例
vue
<script setup>
import { ref } from "vue";
import type { PageTemplateExposed } from "vue-admin-kit";
const pageTemplateRef = ref<PageTemplateExposed>();
// 新增
const handleAdd = () => {
pageTemplateRef.value?.handleAdd();
};
// 新增时传入默认数据
const handleAddWithData = () => {
pageTemplateRef.value?.handleAdd({ status: "1", type: "default" });
};
// 编辑
const handleEdit = (row) => {
pageTemplateRef.value?.handleEdit(row);
};
// 详情
const handleDetail = (row) => {
pageTemplateRef.value?.handleDetail(row);
};
// 删除单条
const handleDelete = (row) => {
pageTemplateRef.value?.handleDelete(row);
};
// 批量删除
const handleBatchDelete = (rows) => {
pageTemplateRef.value?.handleDelete(rows);
};
// 导入
const handleImport = () => {
pageTemplateRef.value?.importFile(importApi, "用户导入模板", () => {
// 导入成功后刷新列表
pageTemplateRef.value?.handleSearch();
});
};
// 导出
const handleExport = () => {
pageTemplateRef.value?.downloadFile(exportApi, "用户列表");
};
</script>Slots
| 插槽名 | 说明 | 作用域参数 |
|---|---|---|
header | 搜索区域上方的自定义内容 | - |
search | 搜索表单中的自定义内容 | - |
table | 表格区域(必须) | - |
form | 表单弹窗中的自定义表单内容 | { formData, formRules, isEdit } |
detail | 详情弹窗中的自定义内容 | { detailData } |
插槽使用示例
vue
<template>
<PageTemplate ref="pageTemplateRef">
<!-- 头部自定义内容 -->
<template #header>
<div class="page-header">
<h2>用户管理</h2>
</div>
</template>
<!-- 搜索表单扩展 -->
<template #search>
<el-form-item label="自定义">
<el-input v-model="customField" />
</el-form-item>
</template>
<!-- 表格区域 -->
<template #table>
<Table />
</template>
<!-- 自定义表单内容 -->
<template #form="{ formData, isEdit }">
<el-form-item label="自定义字段">
<el-input v-model="formData.customField" :disabled="isEdit" />
</el-form-item>
</template>
<!-- 自定义详情内容 -->
<template #detail="{ detailData }">
<el-descriptions :column="2" border>
<el-descriptions-item label="名称">
{{ detailData.name }}
</el-descriptions-item>
</el-descriptions>
</template>
</PageTemplate>
</template>useState 配置
useState 是 PageTemplate 的核心配置 Hook,用于定义页面的各项配置。
完整配置
typescript
import { useState } from "vue-admin-kit";
const { data, getData, delData, resetSearchParams } = useState({
// API 配置
api: {
list: (params) => getList(params), // 列表查询
add: (data) => addItem(data), // 新增
edit: (data) => updateItem(data), // 编辑
delete: (id) => deleteItem(id), // 删除
detail: (id) => getDetail(id), // 详情
batchDelete: (ids) => batchDelete(ids), // 批量删除
export: (params) => exportData(params), // 导出
import: (file) => importData(file), // 导入
},
// 初始查询参数
params: {
pageNum: 1,
pageSize: 10,
status: "1", // 自定义默认参数
},
// 搜索配置
searchConfig: [...],
// 表单配置
formConfig: [...],
// 列配置
columnsConfig: [...],
// 表格选项
tableOptions: {
data: [], // 表格数据
loading: false, // 加载状态
tableOperationConfig: [...], // 工具栏按钮
operateColumns: [...], // 行操作按钮
seqConfig: { startIndex: 0 }, // 序号配置
otherHeight: 0, // 额外高度偏移(用于补偿 Tab 栏等元素)
},
// 分页配置
paginationConfig: {
pageSizes: [10, 20, 50, 100],
size: "default",
},
// provide 注入名称(多实例场景)
provideName: "pageData",
// 参数转换钩子
transformParams: (params) => {
// 在发送请求前转换参数
return { ...params, customParam: "value" };
},
// 响应转换钩子
transformResponse: (response) => {
// 转换接口返回的数据格式
return {
rows: response.data.list,
total: response.data.total,
};
},
// 请求处理器
requestHandlers: {
onError: (error, context) => {
console.error("请求错误", error, context);
},
onSuccess: (response, context) => {
console.log("请求成功", response, context);
},
},
// UI 配置(控制搜索区域和表格区域是否使用 Card 包裹)
ui: {
searchCard: true, // 搜索区域使用 Card 包裹
tableCard: false, // 表格区域不使用 Card 包裹
},
// 自定义日志器
logger: console,
});useState 返回值
typescript
const {
data, // 页面状态数据(响应式)
getData, // 获取列表数据函数
delData, // 删除数据函数
resetSearchParams, // 重置搜索参数函数
autoIntegrateOperations, // 自动集成操作函数
crudOperation, // CRUD 操作封装函数
} = useState({ ... });
// 手动刷新数据
getData(api.list);
// 删除数据
delData([id1, id2], api.delete);
// 重置搜索参数
resetSearchParams();UI 配置
ui 配置用于控制页面布局样式,决定搜索区域和表格区域是否使用 el-card 组件包裹。
配置格式
支持两种等效的配置格式:
typescript
useState({
ui: {
searchCard: true, // 搜索区域使用 Card 包裹
tableCard: false, // 表格区域不使用 Card 包裹
},
});typescript
useState({
ui: {
searchCard: { show: true },
tableCard: { show: false },
},
});配置参数
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
searchCard | boolean | { show: boolean } | false | 搜索区域是否使用 el-card 包裹 |
tableCard | boolean | { show: boolean } | false | 表格区域是否使用 el-card 包裹 |
使用场景
场景一:默认布局(无 Card 包裹)
适用于页面本身已有容器样式,或需要自定义布局的场景。
typescript
useState({
// 不配置 ui,或配置为 false
ui: {
searchCard: false,
tableCard: false,
},
});场景二:搜索区域使用 Card
适用于需要突出搜索区域,与表格区域视觉分离的场景。
typescript
useState({
ui: {
searchCard: true,
tableCard: false,
},
});场景三:全部使用 Card 包裹
适用于需要统一卡片风格的后台管理页面。
typescript
useState({
ui: {
searchCard: true,
tableCard: true,
},
});完整示例
vue
<script setup lang="ts">
import { ref } from "vue";
import { useState, PageTemplate, Table } from "vue-admin-kit";
import type { PageTemplateExposed } from "vue-admin-kit";
const pageTemplateRef = ref<PageTemplateExposed>();
useState({
api: {
list: (params) => getList(params),
},
searchConfig: [
{ type: "input", prop: "name", label: "名称" },
{ type: "select", prop: "status", label: "状态", dictType: "sys_status" },
],
columnsConfig: [
{ type: "seq", title: "序号", width: 60 },
{ field: "name", title: "名称", minWidth: 120 },
{
field: "status",
title: "状态",
dictType: "sys_status",
displayType: "tag",
},
],
// UI 配置:搜索区域使用 Card 包裹
ui: {
searchCard: true,
tableCard: false,
},
});
</script>
<template>
<PageTemplate ref="pageTemplateRef">
<template #table>
<Table />
</template>
</PageTemplate>
</template>提示
- 两种格式完全兼容,简写格式更简洁,完整格式便于后续扩展更多配置项
- Card 包裹会自动添加
shadow="hover"效果 - 如果不配置
ui,默认不使用 Card 包裹
pageDataType 类型
通过 inject('pageData') 可以在子组件中获取页面状态:
typescript
interface pageDataType {
// 表格配置
tableOptions?: {
data?: any[]; // 表格数据
loading?: boolean; // 加载状态
seqConfig?: any; // 序号配置
tableOperationConfig?: OperateColumn[]; // 工具栏按钮
operateColumns?: OperateColumn[]; // 行操作按钮
};
// 查询参数
params?: {
pageNum: number; // 当前页码
pageSize: number; // 每页条数
[key: string]: any; // 其他搜索参数
};
// 数据总数
total?: number;
// 是否显示搜索栏
showSearch?: boolean;
// 分页配置
paginationConfig?: {
pageSizes: number[];
size: "large" | "default" | "small";
};
// 已选中的数据
checkTableData?: any[];
// API 配置
api?: {
list: Function;
add?: Function;
edit?: Function;
delete?: Function;
detail?: Function;
};
// 配置
searchConfig?: SearchConfigItem[];
formConfig?: FormConfigItem[];
columnsConfig?: ColumnConfigItem[];
// 方法
getData?: (api: Function, callback?: Function) => void;
selectChangeEvent?: (records: any[]) => void;
transformParams?: (params: any) => any;
transformResponse?: (response: any) => { rows: any[]; total: number };
}工具栏按钮配置
typescript
import { defineTableOperations, showWhen } from "vue-admin-kit";
const tableOperationConfig = defineTableOperations([
{
title: "新增",
type: "primary",
icon: "Plus",
onClick: () => pageTemplateRef.value?.handleAdd(),
hasPermi: ["system:user:add"],
fixed: "left",
},
{
title: "批量删除",
type: "danger",
icon: "Delete",
onClick: () => {
const rows = pageData.checkTableData;
if (rows?.length) {
pageTemplateRef.value?.handleDelete(rows);
}
},
hasPermi: ["system:user:remove"],
},
{
title: "导出",
type: "warning",
icon: "Download",
onClick: () => pageTemplateRef.value?.downloadFile(exportApi, "用户列表"),
hasPermi: ["system:user:export"],
fixed: "right",
},
]);行操作按钮配置
typescript
import { defineOperateColumns, showWhen, showWhenIn } from "vue-admin-kit";
const operateColumns = defineOperateColumns([
{
title: "详情",
type: "info",
link: true,
onClick: (row) => pageTemplateRef.value?.handleDetail(row),
},
{
title: "编辑",
type: "primary",
link: true,
onClick: (row) => pageTemplateRef.value?.handleEdit(row),
hasPermi: ["system:user:edit"],
show: showWhen("status", "1"), // 状态为 1 时显示
},
{
title: "删除",
type: "danger",
link: true,
onClick: (row) => pageTemplateRef.value?.handleDelete(row),
hasPermi: ["system:user:remove"],
show: showWhenIn("status", ["0", "1"]), // 状态为 0 或 1 时显示
},
]);完整示例
vue
<script setup lang="ts">
import { ref } from "vue";
import {
useState,
PageTemplate,
Table,
defineSearchConfig,
defineFormConfig,
defineColumnsConfig,
defineApiConfig,
definePermissions,
defineTableOperations,
defineOperateColumns,
} from "vue-admin-kit";
import type { PageTemplateExposed } from "vue-admin-kit";
import { listUser, addUser, updateUser, deleteUser, getUser } from "@/api/user";
const pageTemplateRef = ref<PageTemplateExposed>();
const api = defineApiConfig({
list: listUser,
add: addUser,
edit: updateUser,
delete: deleteUser,
detail: getUser,
});
const permissions = definePermissions({
add: ["system:user:add"],
edit: ["system:user:edit"],
delete: ["system:user:remove"],
});
const searchConfig = defineSearchConfig([
{ type: "input", prop: "userName", label: "用户名" },
{ type: "select", prop: "status", label: "状态", dictType: "sys_status" },
]);
const formConfig = defineFormConfig([
{ type: "input", prop: "userName", label: "用户名", required: true },
{ type: "input", prop: "nickName", label: "昵称", required: true },
{ type: "radio", prop: "status", label: "状态", dictType: "sys_status" },
]);
const columnsConfig = defineColumnsConfig([
{ type: "checkbox", width: 50 },
{ type: "seq", title: "序号", width: 60 },
{ field: "userName", title: "用户名", minWidth: 120 },
{
field: "status",
title: "状态",
dictType: "sys_status",
displayType: "tag",
},
]);
useState({
api,
searchConfig,
formConfig,
columnsConfig,
tableOptions: {
tableOperationConfig: defineTableOperations([
{
title: "新增",
type: "primary",
icon: "Plus",
hasPermi: permissions.add,
onClick: () => pageTemplateRef.value?.handleAdd(),
},
]),
operateColumns: defineOperateColumns([
{
title: "编辑",
hasPermi: permissions.edit,
onClick: (row) => pageTemplateRef.value?.handleEdit(row),
},
{
title: "删除",
type: "danger",
hasPermi: permissions.delete,
onClick: (row) => pageTemplateRef.value?.handleDelete(row),
},
]),
},
});
</script>
<template>
<PageTemplate ref="pageTemplateRef">
<template #table>
<Table />
</template>
</PageTemplate>
</template>