Files
springboot-shop-sysadmin/src/views/business/IndexDemo.vue
2026-06-05 17:29:25 +08:00

888 lines
30 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="demo-page">
<div class="demo-hero">
<h2 class="demo-hero__title">IView 组件库使用文档</h2>
<p class="demo-hero__desc">
<code>@/iview</code> 是基于 Ant Design Vue 封装的业务组件库提供配置驱动的表单筛选表格详情与整页布局能力
本页包含<strong>说明文档</strong><strong>可交互示例</strong>数据均为本地 Mock无需后端接口
</p>
<div class="demo-hero__tags">
<a-tag color="blue">Vue 3</a-tag>
<a-tag color="green">Ant Design Vue 4</a-tag>
<a-tag>配置驱动</a-tag>
<a-tag>响应式</a-tag>
</div>
</div>
<IViewTabs v-model:activeKey="activeSection" :animated="false" embedded>
<!-- 概览 -->
<IViewTabPane key="overview" tab="概览">
<div v-if="activeSection === 'overview'" class="demo-section">
<a-collapse v-model:activeKey="overviewCollapse" :bordered="false" ghost>
<a-collapse-panel key="arch" header="目录结构">
<pre class="demo-code">{{ docOverview.structure }}</pre>
</a-collapse-panel>
<a-collapse-panel key="components" header="组件一览">
<a-table :columns="componentTableColumns" :data-source="componentTableData" :pagination="false"
size="small" row-key="name" />
</a-collapse-panel>
<a-collapse-panel key="quickstart" header="快速上手">
<pre class="demo-code">{{ docOverview.quickstart }}</pre>
</a-collapse-panel>
</a-collapse>
</div>
</IViewTabPane>
<!-- 表单 -->
<IViewTabPane key="form" tab="表单 IViewForm">
<div v-if="activeSection === 'form'" class="demo-section">
<DemoDoc title="IViewForm" :docs="docForm" />
<div class="demo-live">
<IViewForm ref="formRef" v-model="formData" :config="formConfig" :read-only="formReadOnly" />
<div class="demo-actions">
<a-button type="primary" @click="handleFormSubmit">提交校验</a-button>
<a-button @click="handleFormReset">重置</a-button>
<a-button @click="formReadOnly = !formReadOnly">
{{ formReadOnly ? "切换为编辑" : "切换为只读" }}
</a-button>
</div>
<a-alert v-if="formResult" type="info" show-icon :message="'表单数据:' + formResult" style="margin-top: 16px" />
</div>
</div>
</IViewTabPane>
<!-- 列表 -->
<IViewTabPane key="list" tab="列表 IViewTable">
<div v-if="activeSection === 'list'" class="demo-section">
<DemoDoc title="IViewFilter + IViewTable" :docs="docList" />
<div class="demo-live">
<IViewFilter v-model="filterParams" :config="filterConfig" @search="handleFilterSearch"
@reset="handleFilterReset" />
<div class="demo-list-header">
<span class="demo-list-title">订单列表示例 {{ filteredList.length }} </span>
<a-button type="primary" @click="handleAddRecord">
<template #icon><plus-outlined /></template>
新增
</a-button>
</div>
<IViewTable :loading="listLoading" :columns="listColumns" :data-source="filteredList" row-key="id"
:pagination="tablePagination" @change="handleTableChange">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'status'">
<a-tag :color="statusColor(record.status)">{{ statusLabel(record.status) }}</a-tag>
</template>
<template v-if="column.dataIndex === 'action'">
<a-button size="small" type="link" @click="handleViewRecord(record)">查看</a-button>
<IViewConfirm importance="normal" title="确认删除该记录吗?" @confirm="() => handleDeleteRecord(record)">
<a-button size="small" type="link" danger>删除</a-button>
</IViewConfirm>
</template>
</template>
</IViewTable>
</div>
</div>
</IViewTabPane>
<!-- 详情 -->
<IViewTabPane key="detail" tab="详情 IViewDescriptions">
<div v-if="activeSection === 'detail'" class="demo-section">
<DemoDoc title="IViewDescriptions" :docs="docDetail" />
<div class="demo-live">
<IViewDescriptions :record-data="detailRecord" :columns-list="detailColumns" :column-size="2"
:hidden-label="false">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'status'">
<a-tag :color="statusColor(record.status)">{{ statusLabel(record.status) }}</a-tag>
</template>
</template>
</IViewDescriptions>
</div>
</div>
</IViewTabPane>
<!-- 整页布局 -->
<IViewTabPane key="layout" tab="页面 ILayoutPage">
<div v-if="activeSection === 'layout'" class="demo-section">
<DemoDoc title="ILayoutPage" :docs="docLayout" />
<div class="demo-live">
<a-alert type="warning" show-icon message="ILayoutPage 需配置真实 API完整可运行示例请参考「系统设置 → 管理员账号」页面。"
style="margin-bottom: 16px" />
<pre class="demo-code">{{ layoutPageExample }}</pre>
</div>
</div>
</IViewTabPane>
<!-- 通用组件 -->
<IViewTabPane key="common" tab="通用组件">
<div v-if="activeSection === 'common'" class="demo-section">
<DemoDoc title="IViewConfirm / IViewDrawer / IViewTabs" :docs="docCommon" />
<div class="demo-live">
<h4 class="demo-subtitle">IViewConfirm 确认操作</h4>
<div class="demo-actions">
<IViewConfirm importance="normal" title="确认执行此操作吗?" @confirm="handleNormalConfirm">
<a-button>普通确认PC 气泡 / 移动端 Modal</a-button>
</IViewConfirm>
<IViewConfirm importance="important" title="危险操作确认" entity-name="确认删除" @confirm="handleImportantConfirm">
<a-button danger>重要确认需输入文字</a-button>
</IViewConfirm>
</div>
<h4 class="demo-subtitle">IViewDrawer 抽屉</h4>
<a-button type="primary" @click="drawerOpen = true">打开详情抽屉</a-button>
<h4 class="demo-subtitle">消息提示 messageTips</h4>
<div class="demo-actions">
<a-button @click="showSuccessTips('操作成功')">showSuccessTips</a-button>
<a-button @click="showErrorTips({ msg: '操作失败,请重试' })">showErrorTips</a-button>
</div>
</div>
</div>
</IViewTabPane>
</IViewTabs>
<IViewDrawer :open="drawerOpen" title="订单详情" @close="drawerOpen = false">
<IViewDescriptions v-if="currentRecord" :record-data="currentRecord" :columns-list="detailColumns"
:column-size="1" :hidden-label="false">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'status'">
<a-tag :color="statusColor(record.status)">{{ statusLabel(record.status) }}</a-tag>
</template>
</template>
</IViewDescriptions>
</IViewDrawer>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
import { PlusOutlined } from "@ant-design/icons-vue";
import IViewTabs from "@/iview/IViewTabs.vue";
import IViewTabPane from "@/iview/IViewTabPane.vue";
import IViewForm from "@/iview/IViewForm.vue";
import IViewFilter from "@/iview/IViewFilter.vue";
import IViewTable from "@/iview/IViewTable.vue";
import IViewDescriptions from "@/iview/IViewDescriptions.vue";
import IViewConfirm from "@/iview/IViewConfirm.vue";
import IViewDrawer from "@/iview/IViewDrawer.vue";
import DemoDoc from "./DemoDoc.vue";
import { showSuccessTips, showErrorTips } from "@/iview/utils/messageTips";
const activeSection = ref("overview");
const overviewCollapse = ref(["arch", "components", "quickstart"]);
// ── 文档内容 ──────────────────────────────────────────
const docOverview = {
structure: `src/iview/
├── IViewForm.vue # 配置驱动表单
├── IViewFilter.vue # 筛选栏(基于 IViewForm inline 布局)
├── IViewTable.vue # 表格PC 表格 / 移动端卡片)
├── IViewDescriptions.vue # 详情描述列表
├── IViewConfirm.vue # 确认弹层
├── IViewDrawer.vue # 响应式抽屉
├── IViewTabs.vue # 标签页
├── ILayoutPage.vue # 整页 CRUD 布局
├── form/ # 各类型表单控件
├── display/ # 展示类子组件
├── utils/ # request、messageTips 等工具
└── urlEmbed/ # iframe 嵌入页通信`,
quickstart: `// 1. 按需引入组件
import IViewForm from "@/iview/IViewForm.vue";
import { showSuccessTips, showErrorTips } from "@/iview/utils/messageTips";
import { apiRequest } from "@/iview/utils/request";
// 2. 配置驱动表单
const formData = ref({});
const formConfig = ref({
fields: [
{ name: "username", label: "账号", type: "text", required: true },
{ name: "status", label: "状态", type: "boolean", trueValue: 1, falseValue: 0 },
],
});
// 3. 模板
<IViewForm ref="formRef" v-model="formData" :config="formConfig" />
// 4. 提交
await formRef.value.validate();
await apiRequest("api.xxx.create", formData.value);
showSuccessTips("保存成功");`,
};
const componentTableColumns = [
{ title: "组件", dataIndex: "name", width: 180 },
{ title: "用途", dataIndex: "desc" },
{ title: "典型场景", dataIndex: "scene", ellipsis: true },
];
const componentTableData = [
{ name: "IViewForm", desc: "配置驱动表单,支持 17+ 字段类型", scene: "新增/编辑、设置页" },
{ name: "IViewFilter", desc: "列表筛选栏,支持高级筛选折叠", scene: "列表页顶部搜索" },
{ name: "IViewTable", desc: "表格PC 端可拖拽横向滚动,移动端卡片", scene: "数据列表展示" },
{ name: "IViewDescriptions", desc: "详情描述列表", scene: "详情页、抽屉内容" },
{ name: "IViewConfirm", desc: "确认操作(普通 / 重要)", scene: "删除、危险操作" },
{ name: "IViewDrawer", desc: "响应式抽屉,自动适配侧边栏宽度", scene: "详情、表单弹层" },
{ name: "IViewTabs", desc: "标签页,移动端 Strip 样式", scene: "模块切换、嵌套 Tab" },
{ name: "ILayoutPage", desc: "整页 CRUD 布局引擎", scene: "标准增删改查页面" },
{ name: "apiRequest", desc: "统一 API 请求(自动带 token", scene: "所有后端接口调用" },
{ name: "messageTips", desc: "成功/错误消息提示", scene: "操作反馈" },
];
const docForm = [
{
title: "Props",
content: `IViewForm
v-model / modelValue Object | String 表单数据modelFormat='json' 时为 JSON 字符串)
config Object | String 表单配置 { fields, extrasFields, formatValue, unformatValue }
readOnly Boolean 全局只读
layout 'vertical' | 'horizontal' | 'inline'
labelCol / wrapperCol Object 标签布局inline 时无效)
disableResponsive Boolean 禁用移动端强制 vertical 布局`,
},
{
title: "暴露方法ref",
content: `formRef.value.validate() // 校验全部可见字段,失败 throw
formRef.value.validateField(field) // 校验单个字段
formRef.value.resetFormData() // 重置为 defaultValue`,
},
{
title: "字段配置 fields[]",
content: `通用属性:
name 字段名(必填)
label 标签
type 字段类型(见下方类型表)
required 是否必填
defaultValue 默认值
placeholder 占位符
tips 输入提示
show 显示条件,支持函数或表达式字符串,如 "!!record.guid"
disabled 禁用,支持函数
onChanged 值变化回调
字段类型:
title 分组标题(仅展示 label
text 单行文本minLength / maxlength / textLenMax
textarea 多行文本textRows / maxlength
number 数字min / max / numMin / numMax
radio 单选options: [{ value, label }]
checkbox 多选options
select 下拉options支持远程 optionsApi
date 日期
datetime 日期时间
time 时间
boolean 开关trueValue / falseValue
image 图片上传
video 视频上传
file 文件上传
fileBase64 Base64 文件
imageBase64 Base64 图片
signatureImage 手写签名
扩展registerIViewFormFieldComponent(type, component) 注册自定义字段类型`,
},
{
title: "代码示例",
content: `<IViewForm ref="formRef" v-model="formData" :config="formConfig" />
const formConfig = ref({
extrasFields: ["guid"], // 额外保留但不渲染的字段
fields: [
{ type: "title", label: "基础信息" },
{ name: "name", label: "姓名", type: "text", required: true, maxlength: 20 },
{ name: "city", label: "城市", type: "select", options: [
{ value: "gz", label: "广州" },
]},
{ name: "enabled", label: "启用", type: "boolean", trueValue: 1, falseValue: 0 },
],
});`,
},
];
const docList = [
{
title: "IViewFilter Props",
content: `config Object 筛选项配置 { fields: [...] },字段同 IViewForm
v-model Object 筛选参数
searchButtonText String 搜索按钮文字,默认「搜索」
resetButtonText String 重置按钮文字,默认「重置」
字段 advanced: true → PC 端归入「高级选项」折叠区
移动端首个筛选项始终可见,其余可收入「高级筛选」
事件:@search @reset @init`,
},
{
title: "IViewTable Props",
content: `columns Array 列配置
data-source Array 数据源
row-key String 行唯一键
loading Boolean 加载态
pagination Object | false 分页配置false 关闭分页
show-filter Boolean 是否显示列显示设置
列配置:
title / dataIndex 标题 / 字段名
width 列宽
bodySlot: true 使用 #bodyCell 插槽自定义渲染
fixed: 'right' 固定列
ellipsis 超长省略
customRender 函数 ({ record, column, mobile }) => HTML 字符串
事件:@change(pagination, filters, sorter)
特性PC 端支持列宽拖拽、横向拖拽滚动条;移动端自动切换为卡片布局`,
},
{
title: "代码示例",
content: `<IViewFilter v-model="filterParams" :config="filterConfig" @search="onSearch" />
<IViewTable :columns="columns" :data-source="list" row-key="id"
:pagination="{ current: 1, pageSize: 10, total: 100 }"
@change="handleTableChange">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button @click="edit(record)">编辑</a-button>
</template>
</template>
</IViewTable>`,
},
];
const docDetail = [
{
title: "Props",
content: `record-data Object 详情数据对象
columns-list Array 列配置(同表格列,支持 bodySlot / customRender / span
column-size Number 每行列数,默认 2
hidden-label Boolean 是否隐藏标签,默认 true`,
},
{
title: "列配置扩展",
content: `span Number 占列数(如 span: 2 占满一行)
bodySlot: true 使用 #bodyCell 插槽
show Function | String 条件显示
customRender Function ({ record, column, mobile }) => HTML
<IViewDescriptions :record-data="record" :columns-list="columns" :hidden-label="false">
<template #bodyCell="{ column, record }">
<a-tag v-if="column.dataIndex === 'status'">{{ record.status }}</a-tag>
</template>
</IViewDescriptions>`,
},
];
const docCommon = [
{
title: "IViewConfirm",
content: `importance 'normal' | 'important'
normal PC 端 Popconfirm 气泡;移动端 Modal
important Modal + 需输入 entityName 文字才能确认
title 确认标题
entityName important 模式下需输入的确认文字
@confirm 确认回调,可返回 Promise 自动 loading
<IViewConfirm importance="normal" title="确认删除?" @confirm="handleDelete">
<a-button danger>删除</a-button>
</IViewConfirm>`,
},
{
title: "IViewDrawer",
content: `open / v-model:open 是否打开
title 标题
width 固定宽度(可选,默认自适应)
flushContent 去掉内边距,适合 iframe 全屏
@close 关闭回调
<IViewDrawer :open="open" title="详情" @close="open = false">
<!-- 内容 -->
</IViewDrawer>`,
},
{
title: "IViewTabs",
content: `v-model:activeKey 当前 Tab key
embedded 嵌入模式(去掉外层 padding
level 'primary' | 'secondary' 层级样式
animated 是否开启动画
<IViewTabs v-model:activeKey="key" :animated="false" embedded>
<IViewTabPane key="a" tab="Tab A">...</IViewTabPane>
<IViewTabPane key="b" tab="Tab B">...</IViewTabPane>
</IViewTabs>`,
},
{
title: "messageTips / apiRequest",
content: `import { showSuccessTips, showErrorTips } from "@/iview/utils/messageTips";
import { apiRequest } from "@/iview/utils/request";
showSuccessTips("保存成功");
showErrorTips(error); // 支持表单 errorFields 自动滚动定位
const data = await apiRequest("api.system.admin.info.list", { page: 1 });
// api 字符串对应后端接口标识,请求体自动附带 token`,
},
];
const docLayout = [
{
title: "config 结构",
content: `const config = ref({
listPages: [{ // 列表页(可多个 Tab
title, name, api, rowKey, isDefault,
showFilter, pagination, columns, filters,
defaultParams,
}],
formPages: [{ // 表单页(抽屉内)
name, title,
submitApi, // 新增提交接口
recordApi, // 编辑时拉取详情update 场景)
paramsKey: ["guid"], // 从 record 取参的字段
formConfig: { fields: [...] },
}],
detailPages: [{ // 详情页(抽屉内)
name, title, recordApi, paramsKey, columns,
}],
infoPages: [{ // 顶部信息区(可选)
name, recordApi, columns,
}],
});`,
},
{
title: "暴露方法pageRef",
content: `pageRef.value.showFormPage('create') // 打开新增表单
pageRef.value.showFormPage('update', record) // 打开编辑表单
pageRef.value.showDetailPage('detail', record)// 打开详情
pageRef.value.refreshList() // 刷新列表
pageRef.value.refreshPage() // 刷新整页
pageRef.value.closeFormPage() // 关闭表单抽屉
pageRef.value.patchListRow(id, patch) // 局部更新列表行`,
},
{
title: "插槽",
content: `#listActions="{ type, refresh }" 列表操作区(标题、新增按钮等)
#bodyCell="{ column, record, type, refresh }" 自定义列/详情渲染
#listHeader / #listFooter 列表上下扩展区
#infoHeader / #infoTab / #infoFooter 信息区扩展
#expandedRowRender 表格行展开`,
},
];
// ── 表单示例 ──────────────────────────────────────────
const formRef = ref(null);
const formReadOnly = ref(false);
const formResult = ref("");
const formData = ref({
name: "张三",
age: 28,
gender: "male",
tags: ["vip"],
city: "guangzhou",
birthday: "1998-05-20",
enabled: 1,
remark: "这是一段备注说明",
});
const formConfig = ref({
fields: [
{ type: "title", label: "基础信息" },
{
name: "name",
label: "姓名",
type: "text",
required: true,
maxlength: 20,
placeholder: "请输入姓名",
tips: "2~20 个字符",
},
{
name: "age",
label: "年龄",
type: "number",
required: true,
min: 1,
max: 120,
},
{
name: "gender",
label: "性别",
type: "radio",
required: true,
options: [
{ value: "male", label: "男" },
{ value: "female", label: "女" },
],
},
{
name: "tags",
label: "标签",
type: "checkbox",
options: [
{ value: "vip", label: "VIP" },
{ value: "new", label: "新用户" },
],
},
{
name: "city",
label: "城市",
type: "select",
required: true,
options: [
{ value: "guangzhou", label: "广州" },
{ value: "shenzhen", label: "深圳" },
{ value: "beijing", label: "北京" },
],
},
{
name: "birthday",
label: "生日",
type: "date",
},
{
name: "enabled",
label: "启用状态",
type: "boolean",
trueValue: 1,
falseValue: 0,
defaultValue: 1,
},
{
name: "remark",
label: "备注",
type: "textarea",
maxlength: 200,
textRows: 3,
},
],
});
const handleFormSubmit = async () => {
try {
await formRef.value.validate();
formResult.value = JSON.stringify(formData.value);
showSuccessTips("表单校验通过");
} catch (error) {
showErrorTips(error);
}
};
const handleFormReset = () => {
formRef.value?.resetFormData?.();
formResult.value = "";
};
// ── 列表示例 ──────────────────────────────────────────
const listLoading = ref(false);
const filterParams = ref({});
const drawerOpen = ref(false);
const currentRecord = ref(null);
const mockRecords = ref([
{ id: 1, orderNo: "ORD-20250605001", customer: "广州物流有限公司", amount: 1280.5, status: 1, createTime: "2025-06-05 09:30:00" },
{ id: 2, orderNo: "ORD-20250605002", customer: "深圳科技股份", amount: 5600, status: 2, createTime: "2025-06-05 10:15:00" },
{ id: 3, orderNo: "ORD-20250605003", customer: "北京贸易集团", amount: 890, status: 0, createTime: "2025-06-05 11:00:00" },
{ id: 4, orderNo: "ORD-20250604004", customer: "上海仓储中心", amount: 3200, status: 3, createTime: "2025-06-04 16:20:00" },
{ id: 5, orderNo: "ORD-20250604005", customer: "杭州电商公司", amount: 450, status: 1, createTime: "2025-06-04 18:45:00" },
]);
const filterConfig = ref({
fields: [
{
name: "keyword",
label: "关键词",
type: "text",
placeholder: "订单号 / 客户名称",
},
{
name: "status",
label: "状态",
type: "select",
options: [
{ value: 0, label: "待处理" },
{ value: 1, label: "进行中" },
{ value: 2, label: "已完成" },
{ value: 3, label: "已取消" },
],
},
{
name: "createTime",
label: "创建日期",
type: "date",
advanced: true,
},
],
});
const listColumns = ref([
{ title: "订单号", dataIndex: "orderNo", width: 160 },
{ title: "客户", dataIndex: "customer", ellipsis: true },
{ title: "金额", dataIndex: "amount", width: 100, align: "right" },
{ title: "状态", dataIndex: "status", width: 90, bodySlot: true },
{ title: "创建时间", dataIndex: "createTime", width: 170 },
{ title: "操作", dataIndex: "action", width: 130, bodySlot: true, fixed: "right" },
]);
const filteredList = computed(() => {
let rows = [...mockRecords.value];
const { keyword, status } = filterParams.value;
if (keyword) {
const kw = String(keyword).trim().toLowerCase();
rows = rows.filter(
(r) =>
r.orderNo.toLowerCase().includes(kw) ||
r.customer.toLowerCase().includes(kw)
);
}
if (status !== undefined && status !== null && status !== "") {
rows = rows.filter((r) => r.status === Number(status));
}
return rows;
});
const tablePagination = computed(() => ({
current: paginationState.value.current,
pageSize: paginationState.value.pageSize,
total: filteredList.value.length,
showTotal: (total) => `${total}`,
}));
const paginationState = ref({
current: 1,
pageSize: 10,
});
const statusLabel = (status) => {
const map = { 0: "待处理", 1: "进行中", 2: "已完成", 3: "已取消" };
return map[status] ?? "未知";
};
const statusColor = (status) => {
const map = { 0: "default", 1: "processing", 2: "success", 3: "error" };
return map[status] ?? "default";
};
const handleFilterSearch = () => {
paginationState.value.current = 1;
};
const handleFilterReset = () => {
filterParams.value = {};
paginationState.value.current = 1;
};
const handleTableChange = (pagination) => {
paginationState.value.current = pagination.current;
paginationState.value.pageSize = pagination.pageSize;
};
const handleViewRecord = (record) => {
currentRecord.value = record;
drawerOpen.value = true;
};
const handleDeleteRecord = (record) => {
mockRecords.value = mockRecords.value.filter((r) => r.id !== record.id);
showSuccessTips(`已删除 ${record.orderNo}`);
};
const handleAddRecord = () => {
const id = mockRecords.value.length + 1;
mockRecords.value.unshift({
id,
orderNo: `ORD-202506050${String(id).padStart(2, "0")}`,
customer: "新客户示例",
amount: 999,
status: 0,
createTime: "2025-06-05 12:00:00",
});
showSuccessTips("已添加一条示例记录");
};
// ── 详情示例 ──────────────────────────────────────────
const detailRecord = ref({
orderNo: "ORD-20250605001",
customer: "广州物流有限公司",
amount: 1280.5,
status: 1,
createTime: "2025-06-05 09:30:00",
remark: "优先配送,请联系收货人确认时间。",
});
const detailColumns = ref([
{ title: "订单号", dataIndex: "orderNo" },
{ title: "客户", dataIndex: "customer" },
{ title: "金额", dataIndex: "amount" },
{ title: "状态", dataIndex: "status", bodySlot: true },
{ title: "创建时间", dataIndex: "createTime" },
{ title: "备注", dataIndex: "remark", span: 2 },
]);
// ── 通用组件示例 ──────────────────────────────────────
const handleNormalConfirm = () => {
showSuccessTips("已确认操作");
};
const handleImportantConfirm = () => {
showSuccessTips("重要操作已确认");
};
// ── ILayoutPage 配置示例 ──────────────────────────────
const layoutPageExample = `<template>
<ILayoutPage ref="pageRef" :config="config">
<template #listActions="{ refresh }">
<a-button type="primary" @click="pageRef.showFormPage('create')">添加</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button @click="pageRef.showFormPage('update', record)">编辑</a-button>
<IViewConfirm title="确认删除?" @confirm="() => handleDelete(record)">
<a-button danger>删除</a-button>
</IViewConfirm>
</template>
</template>
</ILayoutPage>
</template>
<script setup>
import ILayoutPage from "@/iview/ILayoutPage.vue";
import { apiRequest } from "@/iview/utils/request";
const pageRef = ref(null);
const config = ref({
listPages: [{
title: "列表标题",
name: "recordList",
api: "api.xxx.list",
rowKey: "guid",
isDefault: true,
showFilter: true,
pagination: { pageSize: 10 },
columns: [
{ title: "名称", dataIndex: "name" },
{ title: "操作", dataIndex: "action", bodySlot: true, fixed: "right" },
],
filters: { fields: [
{ name: "keyword", label: "关键词", type: "text" },
]},
}],
formPages: [{
name: "create",
title: "新增",
submitApi: "api.xxx.create",
formConfig: { fields: [/* ... */] },
}, {
name: "update",
title: "编辑",
paramsKey: ["guid"],
recordApi: "api.xxx.detail",
submitApi: "api.xxx.update",
formConfig: { fields: [/* ... */] },
}],
detailPages: [{
name: "detail",
title: "详情",
paramsKey: ["guid"],
recordApi: "api.xxx.detail",
columns: [/* ... */],
}],
});
<\/script>`;
</script>
<style scoped>
.demo-page {
width: 100%;
background: #fff;
}
.demo-hero {
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
.demo-hero__title {
margin: 0 0 8px;
font-size: 20px;
font-weight: 600;
color: rgba(0, 0, 0, 0.88);
}
.demo-hero__desc {
margin: 0 0 12px;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
line-height: 1.7;
}
.demo-hero__desc code {
padding: 2px 6px;
background: #f5f5f5;
border-radius: 4px;
font-size: 13px;
}
.demo-hero__tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.demo-section {
padding: 4px 0 16px;
}
.demo-live {
padding: 16px;
border: 1px dashed #d9d9d9;
border-radius: 8px;
background: #fff;
}
.demo-subtitle {
margin: 20px 0 12px;
font-size: 14px;
font-weight: 500;
color: rgba(0, 0, 0, 0.65);
}
.demo-subtitle:first-child {
margin-top: 0;
}
.demo-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 16px;
}
.demo-list-header {
display: flex;
justify-content: space-between;
align-items: center;
margin: 16px 0;
flex-wrap: wrap;
gap: 10px;
}
.demo-list-title {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
.demo-code {
margin: 0;
padding: 12px 16px;
background: #fff;
border: 1px solid #f0f0f0;
border-radius: 6px;
font-size: 12px;
line-height: 1.65;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-all;
color: #333;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
}
</style>