17 KiB
公共组件库
:::tip 说明
项目提供丰富的公共组件,位于 ui/src/components/ 目录,基于 AntDesign Vue 二次封装,覆盖表格、表单、弹窗、上传、富文本编辑等常见场景。组件通过 src/plugins/ 中的 setupCustomComponents 全局注册,任何页面直接使用,无需单独 import。
:::
组件总览
| 组件 | 路径 | 说明 |
|---|---|---|
| BasicTable | components/Table/ |
表格(列定义、分页、操作列、全屏、密度、斑马纹) |
| TableAction | components/Table/ |
操作列按钮组(权限控制、下拉折叠) |
| BasicForm | components/Form/ |
表单(schemas 配置化、动态渲染、折叠展开) |
| BasicModal | components/Modal/ |
弹窗(拖拽、全屏、自定义按钮) |
| BasicUpload | components/Upload/ |
文件/图片上传(卡片/列表、限制数量、预览) |
| Editor | components/Editor/ |
富文本编辑器(基于 wangeditor) |
| CropperImage | components/Cropper/ |
图片裁剪(圆形/矩形、实时预览) |
| QrCode | components/Qrcode/ |
二维码生成(canvas/img、内嵌 logo) |
| ChinaArea | components/ChinaArea/ |
省市区级联(懒加载、二/三级联动) |
| Icon | components/icon/ |
图标(支持 AntDesign 图标 + SVG 图标) |
| BasicSelect | components/Select/ |
增强下拉(远程请求、本地缓存) |
| CountTo | components/CountTo/ |
数字动画(前缀/后缀、千分位、缓动曲线) |
| Password | components/Password/ |
密码强度(强度指示器、重复确认、复杂度校验) |
| PageWrapper | components/Page/ |
页面容器(标题卡片、固定底部) |
| Authority | components/Authority/ |
权限容器(按权限码控制子元素渲染) |
| LockScreen | components/Lockscreen/ |
锁屏组件 |
| ImpExcel | components/Excel/ |
Excel 导入/导出 |
| AppProvider | components/Application/ |
应用提供者(全局配置注入) |
| Websocket | components/Websocket/ |
WebSocket 连接 |
| importFile | components/importFile/ |
通用文件导入 |
| numberInput | components/numberInput/ |
数字输入框 |
| Region | components/Region/ |
区域选择 |
| pagination | components/pagination/ |
分页组件 |
BasicTable 表格
表格组件是使用频率最高的组件,基于 a-table + a-pagination 封装,详见 BasicTable 表格 章节。
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
columns |
BasicColumn[] |
[](必填) |
列定义 |
request |
Function |
null |
数据请求函数,自动管理分页和 loading |
dataSource |
Function | Array |
[] |
数据源(与 request 二选一) |
title |
string |
null |
表格标题 |
titleTooltip |
string |
null |
标题提示 |
pagination |
Object | Boolean |
{} |
分页配置,false 隐藏分页 |
rowKey |
string | Function |
undefined |
行数据的唯一标识 |
border |
Boolean |
false |
是否显示边框 |
size |
string |
'default' |
表格尺寸:small / middle / default |
maxHeight |
Number |
- | 最大高度 |
canResize |
Boolean |
true |
是否自适应窗口高度 |
resizeHeightOffset |
Number |
0 |
高度偏移量 |
showTableSetting |
Boolean |
true |
是否显示工具栏 |
tableSetting |
TableSetting |
见下方 | 工具栏配置 |
scroll |
Object |
- | 滚动配置(x / y) |
rowSelection |
Object |
- | 多选配置 |
virtualScroll |
Boolean |
false |
是否启用虚拟滚动(大数据量优化) |
tableSetting 工具栏配置
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
redo |
Boolean |
true |
刷新按钮 |
size |
Boolean |
true |
密度切换 |
setting |
Boolean |
true |
列设置 |
fullscreen |
Boolean |
true |
全屏切换 |
striped |
Boolean |
true |
斑马纹开关 |
BasicColumn 列定义
| 属性 | 类型 | 说明 |
|---|---|---|
title |
string |
列标题 |
dataIndex |
string |
字段名 |
key |
string |
列唯一标识 |
width |
number |
列宽 |
align |
string |
对齐方式 |
fixed |
string |
固定列:left / right |
customRender |
Function |
自定义渲染函数 ({ text, record, index, column }) => VNode |
ellipsis |
boolean |
超出省略(tooltip) |
auth |
string[] |
权限编码,控制列是否显示 |
ifShow |
boolean | Function |
业务控制是否显示 |
Events
| 事件 | 参数 | 说明 |
|---|---|---|
fetch-success |
(data, result) |
数据请求成功 |
fetch-error |
(error) |
数据请求失败 |
columns-change |
(columns) |
列设置变化 |
Slots
| 插槽 | 说明 |
|---|---|
tableTitle |
表格标题左侧区域(常放添加按钮) |
toolbar |
工具栏右侧区域 |
bodyCell |
自定义单元格渲染(操作列) |
Expose 方法
| 方法 | 说明 |
|---|---|
reload(opt?) |
刷新表格(保留分页) |
restReload(opt?) |
重置到第一页并刷新 |
getDataSource() |
获取当前数据 |
setTableData(data) |
设置表格数据 |
updateTableData(index, record) |
更新指定行 |
getColumns() |
获取列配置 |
setColumns(columns) |
设置列配置 |
redoHeight() |
重新计算高度 |
基础用法
<template>
<BasicTable :columns="columns" :request="loadDataTable" ref="tableRef">
<template #tableTitle>
<a-space>
<a-button v-perm="['sys:level:add']" type="primary" @click="handleAdd">
<template #icon><PlusOutlined /></template>
新增
</a-button>
</a-space>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<a-space>
<a-button type="primary" @click="handleEdit(record.id)" v-perm="['sys:level:update']">编辑</a-button>
<a-button type="primary" danger @click="handleDelete(record.id)" v-perm="['sys:level:delete']">删除</a-button>
</a-space>
</template>
</template>
</BasicTable>
</template>
<script setup lang="ts">
import { PlusOutlined } from '@ant-design/icons-vue';
const columns = [
{ title: '名称', dataIndex: 'name', width: 200 },
{ title: '状态', dataIndex: 'status', width: 100 },
{ title: '创建时间', dataIndex: 'createTime', width: 180 },
{ title: '操作', dataIndex: 'action', key: 'action', fixed: 'right', width: 200 },
];
async function loadDataTable({ page, pageSize }) {
const res = await getListApi({ pageNo: page, pageSize });
return { data: res.data, count: res.count };
}
</script>
BasicForm 表单
表单组件通过 schemas 配置化定义表单字段,支持动态渲染、折叠展开,详见 BasicForm 表单 章节。
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
schemas |
FormSchema[] |
[] |
表单字段配置 |
model |
Object |
- | 表单数据对象 |
rules |
FormRules |
- | 表单验证规则 |
layout |
string |
'vertical' |
布局方式 |
labelCol |
Object |
- | 标签栅格配置 |
size |
string |
'default' |
表单尺寸 |
isFull |
Boolean |
true |
组件是否 100% 宽度 |
showActionButtonGroup |
Boolean |
true |
显示操作按钮组 |
showSubmitButton |
Boolean |
true |
显示查询按钮 |
showResetButton |
Boolean |
true |
显示重置按钮 |
submitButtonText |
string |
'查询' |
查询按钮文字 |
resetButtonText |
string |
'重置' |
重置按钮文字 |
rowProps |
Object |
- | 行栅格配置(gutter 等) |
colProps |
Object |
- | 列栅格配置(span 等) |
FormSchema 字段配置
| 属性 | 类型 | 说明 |
|---|---|---|
name |
string |
字段名(必填) |
label |
string |
标签文字 |
component |
ComponentType |
渲染组件:Input / Select / DatePicker 等 |
componentProps |
object |
组件属性 |
defaultValue |
any |
默认值 |
rules |
object | object[] |
字段校验规则 |
slot |
string |
自定义插槽名 |
hidden |
boolean | Function |
是否隐藏 |
colProps |
Object |
栅格配置 |
labelMessage |
string |
标签提示信息 |
suffix |
string |
后缀插槽名 |
Events
| 事件 | 说明 |
|---|---|
submit |
点击查询按钮 |
reset |
点击重置按钮 |
Expose 方法
| 方法 | 说明 |
|---|---|
setFieldsValue(values) |
设置字段值 |
getFieldsValue() |
获取所有字段值 |
resetFields() |
重置字段 |
validate() |
校验表单 |
clearValidate(name?) |
清除校验 |
setProps(formProps) |
设置属性 |
setSchema(schemaProps) |
动态修改 schema |
基础用法
<template>
<BasicForm @register="registerForm" @submit="handleSearch" @reset="handleReset" />
</template>
<script setup lang="ts">
const [registerForm] = useForm({
schemas: [
{
name: 'name',
label: '名称',
component: 'Input',
componentProps: { placeholder: '请输入名称' },
},
{
name: 'status',
label: '状态',
component: 'Select',
componentProps: {
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 },
],
},
},
],
});
</script>
BasicModal 弹窗
弹窗组件基于 a-modal 封装,支持拖拽、自定义按钮,详见 Modal 弹窗 章节。
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title |
string |
'' |
弹窗标题 |
width |
Number |
446 |
弹窗宽度 |
draggable |
Boolean |
true |
是否可拖拽 |
hideFooter |
Boolean |
false |
隐藏底部按钮 |
okText |
string |
'确认' |
确认按钮文字 |
cancelText |
string |
'取消' |
取消按钮文字 |
Events
| 事件 | 说明 |
|---|---|
ok |
点击确认 |
cancel |
点击取消 |
register |
弹窗注册回调 |
Expose 方法
| 方法 | 说明 |
|---|---|
setProps(props) |
设置弹窗属性 |
openModal() |
打开弹窗 |
closeModal() |
关闭弹窗 |
基础用法
<template>
<BasicModal title="编辑用户" :width="600" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
const [registerModal, { openModal, closeModal, setProps }] = useModal();
function handleEdit(record) {
setProps({ title: '编辑用户' });
openModal();
nextTick(() => {
setFieldsValue(record);
});
}
</script>
BasicUpload 上传
文件/图片上传组件,支持卡片/列表模式、数量限制、图片预览。
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
listType |
string |
'picture-card' |
展示类型:picture-card / text / picture |
limit |
Number |
Infinity |
最大上传数量 |
maxSize |
Number |
2 |
文件大小上限(MB) |
accept |
string |
- | 接受的文件类型 |
list |
Ifiles[] |
[] |
已上传文件列表 |
circle |
Boolean |
false |
圆形裁剪 |
uploadTitle |
string |
'上传图片' |
列表模式按钮文字 |
helpText |
string |
'' |
帮助提示文字 |
Events
| 事件 | 参数 | 说明 |
|---|---|---|
uploadChange |
(fileList) |
上传/删除变化 |
delete |
(fileList) |
删除文件 |
基础用法
<!-- 单图上传 -->
<Upload v-model="formData.cover" :limit="1" accept="image/*" />
<!-- 多图上传 -->
<Upload :list="formData.images" :limit="5" @uploadChange="handleImageChange" />
<!-- 文件上传 -->
<Upload list-type="text" accept=".pdf,.doc,.docx" uploadTitle="上传附件" />
Editor 富文本编辑器
基于 wangeditor 封装,支持图片/视频上传、工具栏配置。
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
getHtml |
string |
- | 双向绑定(HTML 内容) |
getText |
string |
- | 双向绑定(纯文本) |
height |
string |
'310' |
编辑器高度 |
width |
string |
'auto' |
编辑器宽度 |
mode |
string |
'default' |
模式:default / simple |
disable |
Boolean |
false |
是否禁用 |
placeholder |
string |
'请输入内容...' |
占位文字 |
基础用法
<template>
<Editor v-model:getHtml="formData.content" height="400px" />
</template>
:::warning 注意
编辑器内部自动处理图片上传,上传接口由 uploadFileUrl 配置。后端图片迁移和 XSS 清洗由 Service 层的 rich_text_fields 声明自动处理。
:::
CropperImage 图片裁剪
基于 cropperjs 封装,支持圆形/矩形裁剪、实时预览。
基础用法
<template>
<CropperImage
:src="formData.avatar"
:circled="true"
height="300px"
@cropend="handleCropend"
/>
</template>
<script setup lang="ts">
function handleCropend({ imgBase64, imgInfo }) {
formData.value.avatar = imgBase64;
}
</script>
BasicSelect 增强下拉
远程数据下拉选择器,支持请求函数和本地缓存。
基础用法
<template>
<BasicSelect
v-model="formData.deptId"
:request="getDeptOptions"
:cache="true"
cacheKey="dept-options"
placeholder="请选择部门"
/>
</template>
<script setup lang="ts">
async function getDeptOptions() {
const res = await getDeptListApi();
return res.data.map(item => ({ label: item.name, value: item.id }));
}
</script>
PageWrapper 页面容器
页面容器组件,提供标题卡片和固定底部区域。
基础用法
<template>
<PageWrapper title="用户管理" content="管理系统用户信息">
<BasicTable :columns="columns" :request="loadData" />
<template #leftFooter>
<a-button @click="handleCancel">取消</a-button>
</template>
<template #rightFooter>
<a-button type="primary" @click="handleSubmit">提交</a-button>
</template>
</PageWrapper>
</template>
Authority 权限容器
按权限码控制子元素是否渲染,类似 v-perm 指令的组件形式。
基础用法
<template>
<Authority :value="['sys:user:add']">
<a-button type="primary">新增用户</a-button>
</Authority>
</template>
:::tip v-perm 指令
除了 Authority 组件,项目还提供 v-perm 指令,功能相同但更简洁:
<a-button v-perm="['sys:user:add']">新增用户</a-button>
:::
Icon 图标
统一图标组件,支持 AntDesign 图标和 SVG 图标。
基础用法
<template>
<!-- AntDesign 图标 -->
<PlusOutlined />
<DeleteOutlined />
<EditOutlined />
<!-- SVG 图标 -->
<Icon name="svg-icon-user" />
</template>
ImpExcel Excel 导入/导出
Excel 文件导入和导出组件。
导出函数
import { jsonToSheetXlsx, aoaToSheetXlsx } from '@/components/Excel';
// JSON 数据导出
jsonToSheetXlsx({
data: [{ name: '张三', age: 25 }],
header: { name: '姓名', age: '年龄' },
filename: '用户列表.xlsx',
});
// 二维数组导出
aoaToSheetXlsx({
data: [['姓名', '年龄'], ['张三', 25]],
filename: '用户列表.xlsx',
});
导入组件
<template>
<ImpExcel @success="handleImportSuccess" />
</template>
<script setup lang="ts">
function handleImportSuccess(data) {
// data: 导入的表格数据
console.log(data);
}
</script>
全局注册
所有组件在 src/plugins/setupCustomComponents.ts 中通过 setupCustomComponents 全局注册:
// main.ts
import { setupCustomComponents } from '/@/plugins';
setupCustomComponents(app); // 注册所有自定义组件
全局注册后,任何组件中直接使用,无需单独 import。
权限控制体系
项目提供三种权限控制方式,覆盖不同场景:
| 方式 | 语法 | 适用场景 |
|---|---|---|
v-perm 指令 |
v-perm="['sys:user:add']" |
按钮级权限,最常用 |
Authority 组件 |
<Authority :value="['sys:user:add']"> |
容器级权限,控制多个元素 |
auth 列属性 |
{ auth: ['sys:user:delete'] } |
TableAction 操作列权限 |
:::warning 注意
v-perm 和 Authority 的权限码必须与后端 @permission_required("sys:xxx:xxx") 一致。用户 ID 1(admin)拥有所有权限。
:::
总结
公共组件库基于 AntDesign Vue 二次封装,核心三件套 BasicTable(表格)、BasicForm(表单)、BasicModal(弹窗)覆盖列表页标准场景。辅以 Upload、Editor、Cropper 等业务组件,以及 Authority、v-perm 权限体系。通过全局注册,任何页面直接使用,无需单独 import。优先使用公共组件,避免重复开发。