Skip to content

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

属性类型默认值说明
autoIntegrationbooleantrue是否自动集成 CRUD 操作
autoGetListbooleantrue是否在组件挂载时自动获取列表数据
provideNamestring'pageData'provide 注入名称,用于多实例场景
size'small'/'default'/'large'-表单项尺寸,不传则从全局配置获取
pageDatapageDataType-页面数据,不传则通过 inject 获取

关于多表格场景

从 v1.0.3 开始,每个 PageTemplate 实例自动创建独立的高度状态,无需配置 instanceKey。详见 多表格场景 文档。

Events

事件参数说明
search-点击搜索按钮时触发
reset-点击重置按钮时触发
getDataSuccessresponse获取列表数据成功后触发
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 },
  },
});

配置参数

属性类型默认值说明
searchCardboolean | { show: boolean }false搜索区域是否使用 el-card 包裹
tableCardboolean | { 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>

Released under the MIT License.