init commit

This commit is contained in:
iqudoo
2026-06-05 17:29:25 +08:00
parent eb4e8f1a04
commit ab495cf9a6
3 changed files with 583 additions and 111 deletions

View File

@@ -6,4 +6,5 @@ VITE_APP_VERSION_NAME=开发版
VITE_APP_CACHE_PROFIX=logistics_dev_admin VITE_APP_CACHE_PROFIX=logistics_dev_admin
# API配置 # API配置
VITE_API_BASE_URL=http://127.0.0.1:9081/api # VITE_API_BASE_URL=http://127.0.0.1:9081/api
VITE_API_BASE_URL=https://vet.iqudoo.com/api

View File

@@ -0,0 +1,75 @@
<template>
<div class="demo-doc">
<h3 v-if="title" class="demo-doc__title">{{ title }}</h3>
<a-collapse v-model:activeKey="activeKeys" :bordered="false" ghost>
<a-collapse-panel
v-for="(doc, index) in docs"
:key="String(index)"
:header="doc.title"
>
<pre class="demo-code">{{ doc.content }}</pre>
</a-collapse-panel>
</a-collapse>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
const props = defineProps({
title: String,
docs: { type: Array, default: () => [] },
});
const activeKeys = ref([]);
watch(
() => props.docs,
(docs) => {
activeKeys.value = docs.map((_, i) => String(i));
},
{ immediate: true }
);
</script>
<style scoped>
.demo-doc {
margin-bottom: 20px;
padding: 12px 16px;
background: #fafafa;
border: 1px solid #f0f0f0;
border-radius: 8px;
}
.demo-doc__title {
margin: 0 0 8px;
font-size: 15px;
font-weight: 600;
color: rgba(0, 0, 0, 0.85);
}
.demo-doc :deep(.ant-collapse-header) {
padding: 8px 0 !important;
font-size: 13px;
font-weight: 500;
}
.demo-doc :deep(.ant-collapse-content-box) {
padding: 0 0 8px !important;
}
.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>

View File

@@ -1,115 +1,148 @@
<template> <template>
<div class="demo-page"> <div class="demo-page">
<div class="demo-intro"> <div class="demo-hero">
<p>本页演示项目中 <code>@/iview</code> 封装组件的常见用法数据均为本地 Mock无需后端接口</p> <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> </div>
<IViewTabs v-model:activeKey="activeSection" :animated="false" embedded> <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"> <IViewTabPane key="form" tab="表单 IViewForm">
<div v-if="activeSection === 'form'" class="demo-section"> <div v-if="activeSection === 'form'" class="demo-section">
<IViewForm <DemoDoc title="IViewForm" :docs="docForm" />
ref="formRef" <div class="demo-live">
v-model="formData" <IViewForm ref="formRef" v-model="formData" :config="formConfig" :read-only="formReadOnly" />
:config="formConfig" <div class="demo-actions">
:read-only="formReadOnly" <a-button type="primary" @click="handleFormSubmit">提交校验</a-button>
/> <a-button @click="handleFormReset">重置</a-button>
<div class="demo-actions"> <a-button @click="formReadOnly = !formReadOnly">
<a-button type="primary" @click="handleFormSubmit">提交校验</a-button> {{ formReadOnly ? "切换为编辑" : "切换为只读" }}
<a-button @click="handleFormReset">重置</a-button> </a-button>
<a-button @click="formReadOnly = !formReadOnly"> </div>
{{ formReadOnly ? "切换为编辑" : "切换为只读" }} <a-alert v-if="formResult" type="info" show-icon :message="'表单数据:' + formResult" style="margin-top: 16px" />
</a-button>
</div> </div>
<a-alert
v-if="formResult"
type="info"
show-icon
:message="'表单数据:' + formResult"
style="margin-top: 16px"
/>
</div> </div>
</IViewTabPane> </IViewTabPane>
<!-- 列表 -->
<IViewTabPane key="list" tab="列表 IViewTable"> <IViewTabPane key="list" tab="列表 IViewTable">
<div v-if="activeSection === 'list'" class="demo-section"> <div v-if="activeSection === 'list'" class="demo-section">
<IViewFilter <DemoDoc title="IViewFilter + IViewTable" :docs="docList" />
v-model="filterParams" <div class="demo-live">
:config="filterConfig" <IViewFilter v-model="filterParams" :config="filterConfig" @search="handleFilterSearch"
@search="handleFilterSearch" @reset="handleFilterReset" />
@reset="handleFilterReset" <div class="demo-list-header">
/> <span class="demo-list-title">订单列表示例 {{ filteredList.length }} </span>
<div class="demo-list-header"> <a-button type="primary" @click="handleAddRecord">
<span class="demo-list-title">订单列表示例 {{ filteredList.length }} </span> <template #icon><plus-outlined /></template>
<a-button type="primary" @click="handleAddRecord"> 新增
<template #icon><plus-outlined /></template> </a-button>
新增 </div>
</a-button> <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>
<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>
<!-- 详情 -->
<IViewTabPane key="detail" tab="详情 IViewDescriptions"> <IViewTabPane key="detail" tab="详情 IViewDescriptions">
<div v-if="activeSection === 'detail'" class="demo-section"> <div v-if="activeSection === 'detail'" class="demo-section">
<IViewDescriptions <DemoDoc title="IViewDescriptions" :docs="docDetail" />
:record-data="detailRecord" <div class="demo-live">
:columns-list="detailColumns" <IViewDescriptions :record-data="detailRecord" :columns-list="detailColumns" :column-size="2"
:column-size="2" :hidden-label="false">
:hidden-label="false" <template #bodyCell="{ column, record }">
> <template v-if="column.dataIndex === 'status'">
<template #bodyCell="{ column, record }"> <a-tag :color="statusColor(record.status)">{{ statusLabel(record.status) }}</a-tag>
<template v-if="column.dataIndex === 'status'"> </template>
<a-tag :color="statusColor(record.status)">{{ statusLabel(record.status) }}</a-tag>
</template> </template>
</template> </IViewDescriptions>
</IViewDescriptions> </div>
</div> </div>
</IViewTabPane> </IViewTabPane>
<!-- 整页布局 -->
<IViewTabPane key="layout" tab="页面 ILayoutPage"> <IViewTabPane key="layout" tab="页面 ILayoutPage">
<div v-if="activeSection === 'layout'" class="demo-section"> <div v-if="activeSection === 'layout'" class="demo-section">
<a-alert <DemoDoc title="ILayoutPage" :docs="docLayout" />
type="warning" <div class="demo-live">
show-icon <a-alert type="warning" show-icon message="ILayoutPage 需配置真实 API完整可运行示例请参考「系统设置 → 管理员账号」页面。"
message="ILayoutPage 需配置真实 API完整示例请参考「系统设置 → 管理员账号」页面。" style="margin-bottom: 16px" />
style="margin-bottom: 16px" <pre class="demo-code">{{ layoutPageExample }}</pre>
/> </div>
<pre class="demo-code">{{ layoutPageExample }}</pre> </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> </div>
</IViewTabPane> </IViewTabPane>
</IViewTabs> </IViewTabs>
<IViewDrawer :open="drawerOpen" title="订单详情" @close="drawerOpen = false"> <IViewDrawer :open="drawerOpen" title="订单详情" @close="drawerOpen = false">
<IViewDescriptions <IViewDescriptions v-if="currentRecord" :record-data="currentRecord" :columns-list="detailColumns"
v-if="currentRecord" :column-size="1" :hidden-label="false">
:record-data="currentRecord"
:columns-list="detailColumns"
:column-size="1"
:hidden-label="false"
>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'status'"> <template v-if="column.dataIndex === 'status'">
<a-tag :color="statusColor(record.status)">{{ statusLabel(record.status) }}</a-tag> <a-tag :color="statusColor(record.status)">{{ statusLabel(record.status) }}</a-tag>
@@ -131,9 +164,307 @@ import IViewTable from "@/iview/IViewTable.vue";
import IViewDescriptions from "@/iview/IViewDescriptions.vue"; import IViewDescriptions from "@/iview/IViewDescriptions.vue";
import IViewConfirm from "@/iview/IViewConfirm.vue"; import IViewConfirm from "@/iview/IViewConfirm.vue";
import IViewDrawer from "@/iview/IViewDrawer.vue"; import IViewDrawer from "@/iview/IViewDrawer.vue";
import DemoDoc from "./DemoDoc.vue";
import { showSuccessTips, showErrorTips } from "@/iview/utils/messageTips"; import { showSuccessTips, showErrorTips } from "@/iview/utils/messageTips";
const activeSection = ref("form"); 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 formRef = ref(null);
@@ -384,50 +715,77 @@ const detailColumns = ref([
{ title: "备注", dataIndex: "remark", span: 2 }, { title: "备注", dataIndex: "remark", span: 2 },
]); ]);
// ── 通用组件示例 ──────────────────────────────────────
const handleNormalConfirm = () => {
showSuccessTips("已确认操作");
};
const handleImportantConfirm = () => {
showSuccessTips("重要操作已确认");
};
// ── ILayoutPage 配置示例 ────────────────────────────── // ── ILayoutPage 配置示例 ──────────────────────────────
const layoutPageExample = `const config = ref({ 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: [{ listPages: [{
title: "列表标题", title: "列表标题",
name: "recordList", name: "recordList",
api: "api.xxx.list", // 列表接口 api: "api.xxx.list",
rowKey: "guid", rowKey: "guid",
isDefault: true, isDefault: true,
showFilter: true, showFilter: true,
pagination: { pageSize: 10 }, pagination: { pageSize: 10 },
columns: [/* 列配置 */], columns: [
filters: { fields: [/* 筛选项 */] }, { title: "名称", dataIndex: "name" },
{ title: "操作", dataIndex: "action", bodySlot: true, fixed: "right" },
],
filters: { fields: [
{ name: "keyword", label: "关键词", type: "text" },
]},
}], }],
formPages: [{ formPages: [{
name: "create", name: "create",
title: "新增", title: "新增",
submitApi: "api.xxx.create", submitApi: "api.xxx.create",
formConfig: { fields: [/* 表单字段 */] }, formConfig: { fields: [/* ... */] },
}, { }, {
name: "update", name: "update",
title: "编辑", title: "编辑",
paramsKey: ["guid"], paramsKey: ["guid"],
recordApi: "api.xxx.detail", recordApi: "api.xxx.detail",
submitApi: "api.xxx.update", submitApi: "api.xxx.update",
formConfig: { fields: [/* 表单字段 */] }, formConfig: { fields: [/* ... */] },
}], }],
detailPages: [{ detailPages: [{
name: "detail", name: "detail",
title: "详情", title: "详情",
paramsKey: ["guid"], paramsKey: ["guid"],
recordApi: "api.xxx.detail", recordApi: "api.xxx.detail",
columns: [/* 详情字段 */], columns: [/* ... */],
}], }],
}); });
<\/script>`;
// 模板中使用
<ILayoutPage ref="pageRef" :config="config">
<template #listActions="{ refresh }">
<a-button type="primary" @click="pageRef.showFormPage('create')">添加</a-button>
</template>
<template #bodyCell="{ column, record }">
<!-- 自定义列渲染 -->
</template>
</ILayoutPage>`;
</script> </script>
<style scoped> <style scoped>
@@ -436,24 +794,61 @@ const layoutPageExample = `const config = ref({
background: #fff; background: #fff;
} }
.demo-intro { .demo-hero {
margin-bottom: 16px; margin-bottom: 20px;
color: rgba(0, 0, 0, 0.65); padding-bottom: 16px;
font-size: 13px; border-bottom: 1px solid #f0f0f0;
line-height: 1.6;
} }
.demo-intro code { .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; padding: 2px 6px;
background: #f5f5f5; background: #f5f5f5;
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 13px;
}
.demo-hero__tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
} }
.demo-section { .demo-section {
padding: 4px 0 16px; 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 { .demo-actions {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -477,15 +872,16 @@ const layoutPageExample = `const config = ref({
.demo-code { .demo-code {
margin: 0; margin: 0;
padding: 16px; padding: 12px 16px;
background: #fafafa; background: #fff;
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
border-radius: 6px; border-radius: 6px;
font-size: 12px; font-size: 12px;
line-height: 1.6; line-height: 1.65;
overflow-x: auto; overflow-x: auto;
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-all; word-break: break-all;
color: #333; color: #333;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
} }
</style> </style>