Become a sponsor

说明
项目提供丰富的公共组件,位于 ui/src/components/ 目录,基于 ElementPlus 二次封装,覆盖表格、表单、弹窗、上传、富文本编辑等常见场景。组件通过 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/ | 图标(支持 ElementPlus 图标 + 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/ | 数字输入框 |
| priceInput | components/priceInput/ | 金额输入框 |
| Region | components/Region/ | 区域选择 |
| TableSelect | components/TableSelect/ | 表格选择器 |
| pagination | components/pagination/ | 分页组件 |
表格组件是使用频率最高的组件,基于 el-table + el-pagination 封装,详见 BasicTable 表格 章节。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
columns | BasicColumn[] | [](必填) | 列定义 |
request | Function | null | 数据请求函数,自动管理分页和 loading |
dataSource | Function | Array | [] | 数据源(与 request 二选一) |
actionColumn | BasicColumn | null | 操作列配置 |
title | string | null | 表格标题 |
titleTooltip | string | null | 标题提示 |
pagination | Object | Boolean | {} | 分页配置,false 隐藏分页 |
rowKey | string | Function | undefined | 行数据的唯一标识 |
border | Boolean | false | 是否显示边框 |
striped | Boolean | false | 斑马纹 |
size | string | 'default' | 表格尺寸:small / default / large |
tableHeight | Number | String | null | 固定表格高度 |
maxHeight | Number | - | 最大高度 |
canResize | Boolean | true | 是否自适应窗口高度 |
resizeHeightOffset | Number | 0 | 高度偏移量 |
showTableSetting | Boolean | true | 是否显示工具栏 |
tableSetting | TableSetting | 见下方 | 工具栏配置 |
isKeepRowKeys | Boolean | false | 翻页时是否保留选中行 |
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
redo | Boolean | true | 刷新按钮 |
size | Boolean | true | 密度切换 |
setting | Boolean | true | 列设置 |
fullscreen | Boolean | true | 全屏切换 |
striped | Boolean | true | 斑马纹开关 |
| 属性 | 类型 | 说明 |
|---|---|---|
label | string | 列标题 |
prop | string | 字段名 |
width | number | 列宽 |
align | string | 对齐方式 |
type | string | 类型:index / selection |
render | Function | 自定义渲染函数 (column, row, index) => VNode |
isSlot | boolean | 是否使用插槽 |
ellipsis | boolean | 超出省略(tooltip) |
auth | string[] | 权限编码,控制列是否显示 |
ifShow | boolean | Function | 业务控制是否显示 |
| 事件 | 参数 | 说明 |
|---|---|---|
fetch-success | (data, result) | 数据请求成功 |
fetch-error | (error) | 数据请求失败 |
selection-change | (rows) | 多选变化 |
columns-change | (columns) | 列设置变化 |
checked-row-change | (rowKeys) | 选中行变化 |
| 插槽 | 说明 |
|---|---|
tableTitle | 表格标题左侧区域(常放添加按钮) |
toolbar | 工具栏右侧区域 |
{prop} | 列名对应插槽,自定义列渲染 |
| 方法 | 说明 |
|---|---|
reload(opt?) | 刷新表格(保留分页) |
restReload(opt?) | 重置到第一页并刷新 |
reloadTable(opt?) | 内部刷新(清空选中行) |
getDataSource() | 获取当前数据 |
setTableData(data) | 设置表格数据 |
updateTableDataRecord(index, record) | 更新指定行 |
deleteTableDataRecord(index) | 删除指定行 |
getColumns() | 获取列配置 |
setColumns(columns) | 设置列配置 |
clearSelection() | 清空多选 |
setCheckedRowKeys(keys) | 设置选中行 |
redoHeight() | 重新计算高度 |
<template>
<BasicTable :columns="columns" :request="loadDataTable" ref="tableRef">
<template #tableTitle>
<el-button v-perm="['sys:level:add']" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>新增
</el-button>
</template>
</BasicTable>
</template>
<script setup lang="ts">
const columns = [
{ label: '名称', prop: 'name', width: 200 },
{ label: '状态', prop: 'status', width: 100 },
{ label: '创建时间', prop: 'createTime', width: 180 },
];
const actionColumn = {
width: 200,
actions: [
{ label: '编辑', auth: ['sys:level:update'], onClick: handleEdit },
{
label: '删除',
auth: ['sys:level:delete'],
isConfirm: true,
popConfirm: { title: '确认删除?' },
onClick: handleDelete,
},
],
};
async function loadDataTable({ page, pageSize }) {
const res = await getListApi({ pageNo: page, pageSize });
return { items: res.data, total: res.count };
}
</script>操作列按钮组件,支持权限控制和下拉折叠。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
actions | ActionItem[] | (必填) | 操作按钮列表 |
dropDownActions | ActionItem[] | null | 下拉折叠操作列表 |
dropDownProps | ActionItem | { label: '更多' } | 下拉按钮配置 |
select | Function | () => {} | 下拉选择回调 |
| 属性 | 类型 | 说明 |
|---|---|---|
label | string | 按钮文字 |
auth | string[] | 权限编码 |
onClick | Function | 点击回调 |
icon | Component | 图标组件 |
type | string | 按钮类型 |
disabled | boolean | 是否禁用 |
ifShow | boolean | Function | 是否显示 |
isConfirm | boolean | 是否需要确认 |
popConfirm | object | 气泡确认框配置 |
表单组件通过 schemas 配置化定义表单字段,支持动态渲染、折叠展开,详见 BasicForm 表单 章节。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
schemas | FormSchema[] | [] | 表单字段配置 |
model | Object | - | 表单数据对象 |
rules | FormRules | - | 表单验证规则 |
inline | Boolean | false | 行内表单 |
layout | string | 'vertical' | 布局方式 |
labelPosition | string | 'right' | 标签位置 |
labelWidth | Number | String | 80 | 标签宽度 |
size | string | 'default' | 表单尺寸 |
disabled | Boolean | false | 禁用全部组件 |
isFull | Boolean | true | 组件是否 100% 宽度 |
showActionButtonGroup | Boolean | true | 显示操作按钮组 |
showSubmitButton | Boolean | true | 显示查询按钮 |
showResetButton | Boolean | true | 显示重置按钮 |
showAdvancedButton | Boolean | true | 显示展开/收起按钮 |
submitButtonText | string | '查询' | 查询按钮文字 |
resetButtonText | string | '重置' | 重置按钮文字 |
collapsed | Boolean | true | 是否默认折叠 |
collapsedRows | Number | 1 | 折叠时显示行数 |
| 属性 | 类型 | 说明 |
|---|---|---|
field | string | 字段名(必填) |
label | string | 标签文字 |
component | ComponentType | 渲染组件:Input / Select / DatePicker 等 |
componentProps | object | 组件属性 |
defaultValue | any | 默认值 |
rules | object | object[] | 字段校验规则 |
slot | string | 自定义插槽名 |
hidden | boolean | Function | 是否隐藏 |
colProps | ColProps | 栅格配置 |
labelMessage | string | 标签提示信息 |
suffix | string | 后缀文字 |
| 事件 | 说明 |
|---|---|
submit | 点击查询按钮 |
reset | 点击重置按钮 |
| 方法 | 说明 |
|---|---|
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: [
{
field: 'name',
label: '名称',
component: 'Input',
componentProps: { placeholder: '请输入名称' },
},
{
field: 'status',
label: '状态',
component: 'Select',
componentProps: {
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 },
],
},
},
{
field: 'dateRange',
label: '日期范围',
component: 'DatePicker',
componentProps: { type: 'daterange' },
},
],
});
</script>弹窗组件基于 el-dialog 封装,支持拖拽、自定义按钮,详见 Modal 弹窗 章节。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | '' | 弹窗标题 |
width | Number | 446 | 弹窗宽度 |
draggable | Boolean | true | 是否可拖拽 |
hideFooter | Boolean | false | 隐藏底部按钮 |
confirmButText | string | '确认' | 确认按钮文字 |
cancelButText | string | '取消' | 取消按钮文字 |
confirmButProps | Object | - | 确认按钮属性 |
cancelButProps | Object | - | 取消按钮属性 |
| 事件 | 说明 |
|---|---|
ok | 点击确认 |
register | 弹窗注册回调 |
| 方法 | 说明 |
|---|---|
setModalProps(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, setModalProps }] = useModal();
function handleEdit(record) {
setModalProps({ title: '编辑用户' });
openModal();
// 回填表单数据
nextTick(() => {
setFieldsValue(record);
});
}
</script>文件/图片上传组件,基于 el-upload 封装,支持卡片/列表模式、数量限制、图片预览。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
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 | '' | 帮助提示文字 |
width | Number | 104 | 卡片宽度(px) |
| 事件 | 参数 | 说明 |
|---|---|---|
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="上传附件" />基于 wangeditor 封装,支持图片/视频上传、工具栏配置。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
getHtml | string | - | 双向绑定(HTML 内容) |
getText | string | - | 双向绑定(纯文本) |
height | string | '310' | 编辑器高度 |
width | string | 'auto' | 编辑器宽度 |
mode | string | 'default' | 模式:default / simple |
disable | Boolean | false | 是否禁用 |
placeholder | string | '请输入内容...' | 占位文字 |
uploadFileUrl | string | '/file/upload' | 图片上传接口 |
<template>
<Editor v-model:getHtml="formData.content" height="400px" />
</template>注意
编辑器内部自动处理图片上传,上传接口由 uploadFileUrl 配置。后端图片迁移和 XSS 清洗由 Service 层的 rich_text_fields 声明自动处理。
基于 cropperjs 封装,支持圆形/矩形裁剪、实时预览。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
src | string | (必填) | 图片地址 |
alt | string | - | 图片描述 |
circled | Boolean | false | 圆形裁剪 |
realTimePreview | Boolean | true | 实时预览 |
height | string | Number | '360px' | 裁剪区域高度 |
imageStyle | CSSProperties | {} | 图片样式 |
options | Object | {} | cropperjs 配置 |
| 事件 | 参数 | 说明 |
|---|---|---|
cropend | { imgBase64, imgInfo } | 裁剪完成 |
ready | (cropper) | 裁剪器就绪 |
cropendError | - | 裁剪出错 |
<template>
<CropperImage
:src="formData.avatar"
:circled="true"
height="300px"
@cropend="handleCropend"
/>
</template>
<script setup lang="ts">
function handleCropend({ imgBase64, imgInfo }) {
// imgBase64: base64 格式的裁剪结果
// imgInfo: 裁剪区域坐标和尺寸
formData.value.avatar = imgBase64;
}
</script>基于 qrcode 封装,支持 canvas/img 渲染、内嵌 logo。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | Array | (必填) | 二维码内容 |
width | Number | 200 | 宽度(px) |
tag | string | 'canvas' | 渲染方式:canvas / img |
logo | string | Object | '' | 中间 logo |
options | Object | {} | qrcode 配置项 |
| 事件 | 参数 | 说明 |
|---|---|---|
done | { url, ctx } | 生成完成 |
error | (error) | 生成失败 |
| 方法 | 说明 |
|---|---|
download(fileName?) | 下载二维码图片 |
<template>
<QrCode value="https://www.example.com" :width="200" :logo="logoUrl" />
</template>省市区三级联动选择器,基于后端城市接口懒加载。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue | String | Array | - | 双向绑定(区划编码数组) |
type | Number | 3 | 联动级别:2(省市) / 3(省市区) |
disabled | Boolean | false | 是否禁用 |
| 事件 | 参数 | 说明 |
|---|---|---|
update:modelValue | (codes) | 值变化 |
change | (text) | 选中文本(如"浙江省杭州市西湖区") |
<template>
<ChinaArea v-model="formData.cityCode" :type="3" @change="handleCityChange" />
</template>远程数据下拉选择器,支持请求函数和本地缓存。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
request | Function | (必填) | 获取选项数据的异步函数 |
options | Array | [] | 静态选项(与 request 二选一) |
modelValue | String | Number | - | 双向绑定 |
cache | Boolean | false | 是否缓存到 localStorage |
cacheKey | string | '' | 缓存 key(开启缓存时必填) |
width | Number | 150 | 宽度(px) |
block | Boolean | false | 100% 父容器宽度 |
| 方法 | 说明 |
|---|---|
fetch() | 重新请求数据 |
getData() | 获取当前选项数据 |
<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>数字动态递增/递减动画,支持前缀/后缀、千分位、缓动曲线。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
startVal | Number | 0 | 起始值 |
endVal | Number | 2021 | 结束值 |
duration | Number | 1500 | 动画时长(ms) |
autoplay | Boolean | true | 自动播放 |
decimals | Number | 0 | 小数位数 |
prefix | string | '' | 前缀 |
suffix | string | '' | 后缀 |
separator | string | ',' | 千分位分隔符 |
decimal | string | '.' | 小数点符号 |
color | string | - | 字体颜色 |
useEasing | Boolean | true | 是否使用缓动 |
transition | string | 'linear' | 缓动曲线 |
| 事件 | 说明 |
|---|---|
onStarted | 动画开始 |
onFinished | 动画结束 |
<template>
<CountTo :endVal="12345" :duration="2000" prefix="¥" separator="," />
</template>密码输入框,内置强度指示器、重复确认、复杂度校验。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | - | 密码值 |
repeat | Boolean | false | 是否显示重复确认框 |
required | Boolean | true | 是否必填 |
minLength | Number | 6 | 最小长度 |
maxLength | Number | 32 | 最大长度 |
complexity | Boolean | false | 是否启用复杂度校验 |
complexityTip | string | - | 复杂度不满足时的提示 |
width | Number | 300 | 弹出框宽度 |
block | Boolean | false | 100% 宽度 |
| 等级 | 条件 | 说明 |
|---|---|---|
| 0 | 无 | 极弱 |
| 1 | 含小写或大写 | 弱 |
| 2 | 含小写 + 大写,或含数字 | 中 |
| 3 | 含小写 + 大写 + 数字 | 强 |
| 4 | 含小写 + 大写 + 数字 + 特殊字符 | 极强 |
| 方法 | 说明 |
|---|---|
isValidator() | 是否通过校验 |
showValidator() | 触发校验显示 |
<template>
<Password v-model:value="formData.password" :repeat="true" :complexity="true" />
</template>页面容器组件,提供标题卡片和固定底部区域。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | '' | 页面标题 |
content | string | '' | 标题下方描述文字 |
contentStyle | Object | {} | 主体区域样式 |
contentClass | string | '' | 主体区域 class |
showFooter | Boolean | true | 是否显示底部 |
| 插槽 | 说明 |
|---|---|
headerContent | 标题卡片内容区域 |
default | 页面主体内容 |
leftFooter | 底部左侧(常放操作按钮) |
rightFooter | 底部右侧 |
<template>
<PageWrapper title="用户管理" content="管理系统用户信息">
<BasicTable :columns="columns" :request="loadData" />
<template #leftFooter>
<el-button @click="handleCancel">取消</el-button>
</template>
<template #rightFooter>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</template>
</PageWrapper>
</template>按权限码控制子元素是否渲染,类似 v-perm 指令的组件形式。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string[] | [] | 权限编码数组 |
<template>
<Authority :value="['sys:user:add']">
<el-button type="primary">新增用户</el-button>
</Authority>
</template>v-perm 指令
除了 Authority 组件,项目还提供 v-perm 指令,功能相同但更简洁:
<el-button v-perm="['sys:user:add']">新增用户</el-button>统一图标组件,支持 ElementPlus 图标和 SVG 图标。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | string | (必填) | 图标名称 |
size | string | Number | '14px' | 图标大小 |
color | string | 'inherit' | 图标颜色 |
<template>
<!-- ElementPlus 图标 -->
<Icon name="el-icon-Plus" size="18px" />
<!-- SVG 图标 -->
<Icon name="svg-icon-user" />
</template>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 操作列权限 |
注意
v-perm 和 Authority 的权限码必须与后端 @permission_required("sys:xxx:xxx") 一致。用户 ID 1(admin)拥有所有权限。
公共组件库基于 ElementPlus 二次封装,核心三件套 BasicTable(表格)、BasicForm(表单)、BasicModal(弹窗)覆盖列表页标准场景。辅以 Upload、Editor、Cropper 等业务组件,以及 Authority、v-perm 权限体系。通过全局注册,任何页面直接使用,无需单独 import。优先使用公共组件,避免重复开发。