业务组件总览
介绍
RuoYi-Plus-UniApp 前端管理端提供了一套完整的业务组件库,涵盖表单输入、数据展示、弹窗交互、图表可视化、AI 辅助等多个领域。这些组件基于 Element Plus 进行二次封装,遵循统一的设计规范和 API 约定。
核心特性:
- 高度模块化 - 70+ 个业务组件,按功能分类清晰
- 统一 API 设计 - 所有组件遵循一致的属性命名和事件处理规范
- 响应式布局 - 内置智能响应式系统,支持多种屏幕尺寸自适应
- 国际化支持 - 全面集成 i18n,所有文本支持多语言切换
- TypeScript 类型 - 完整的类型定义,提供优秀的开发体验
- 权限控制 - 与 v-permi 指令无缝集成
组件分类
1. 表单组件 (13个)
| 组件名称 | 组件标识 | 主要功能 |
|---|---|---|
| 文本输入框 | AFormInput | 单行/多行文本输入 |
| 下拉选择器 | AFormSelect | 单选/多选下拉框 |
| 复选框组 | AFormCheckbox | 复选框选择 |
| 单选框组 | AFormRadio | 单选框选择 |
| 日期选择器 | AFormDate | 日期/日期范围选择 |
| 开关 | AFormSwitch | 布尔值切换 |
| 树形选择器 | AFormTreeSelect | 树形结构选择 |
| 级联选择器 | AFormCascader | 级联数据选择 |
| 文件上传 | AFormFileUpload | 文件上传 |
| 图片上传 | AFormImgUpload | 图片上传 |
| 富文本编辑器 | AFormEditor | 富文本编辑 |
| 地图选择器 | AFormMap | 地理位置选择 |
| AI辅助输入框 | AFormInputWithAi | AI增强输入 |
2. 搜索和表格组件 (3个)
| 组件名称 | 组件标识 | 主要功能 |
|---|---|---|
| 搜索表单 | ASearchForm | 搜索条件容器 |
| 表格工具栏 | TableToolbar | 表格操作工具栏 |
| 分页器 | Pagination | 数据分页 |
3. 弹窗组件 (2个)
| 组件名称 | 组件标识 | 主要功能 |
|---|---|---|
| 通用弹窗 | AModal | 对话框/抽屉容器 |
| 详情弹窗 | ADetail | 数据详情展示 |
4. 业务选择器 (2个)
| 组件名称 | 组件标识 | 主要功能 |
|---|---|---|
| 用户选择器 | UserSelect | 用户选择 |
| 字典标签 | DictTag | 字典值显示 |
5. 卡片组件 (23个)
| 类别 | 组件数量 | 典型组件 |
|---|---|---|
| 统计卡片 | 3 | AStatsCard, ABarStatsCard, ALineStatsCard |
| 图表卡片 | 5 | ABarChartCard, ALineChartCard, APieChartCard |
| 内容卡片 | 4 | AFormCard, ATableCard, AImageCard, AInfoCard |
| 业务卡片 | 4 | AUserCard, AProfileCard, APricingCard, ASocialCard |
| 列表卡片 | 5 | ADataListCard, ATimelineListCard, AActivityCard |
| 特殊卡片 | 2 | ADataCard, AEmptyCard |
6. 图表组件 (10个)
| 组件名称 | 图表类型 | 使用场景 |
|---|---|---|
| AChart | 通用图表 | ECharts 基础封装 |
| ALineChart | 折线图 | 趋势分析 |
| ABarChart | 竖向柱状图 | 数据对比 |
| ABarHorizontalChart | 横向柱状图 | 排名展示 |
| ABarBidirectionalChart | 双向柱状图 | 对比分析 |
| APieChart | 饼图/环形图 | 占比展示 |
| ARadarChart | 雷达图 | 多维度评估 |
| AScatterChart | 散点图 | 分布关系 |
| ACandlestickChart | K线图 | 股票数据 |
| AMapChart | 地图 | 地理分布 |
7. AI 组件 (4个)
| 组件名称 | 功能 |
|---|---|
| AAiAssistant | AI 助手面板 |
| AAiTextOptimizer | 文本优化工具 |
| AAiContentReviewer | 内容审查工具 |
| AAiDataGenerator | 数据生成工具 |
8. 其他组件 (13个)
| 组件名称 | 功能 |
|---|---|
| AImportExcel | Excel 导入 |
| AOssMediaManager | 媒体管理器 |
| AThemeColorPicker | 主题配色选择器 |
| AThemeSvg | SVG 渲染 |
| AGeometricBackground | 几何背景 |
| AWatermark | 水印 |
| Icon | 图标渲染 |
| IconSelect | 图标选择器 |
| ImagePreview | 图片预览 |
| ASelectionTags | 选中标签 |
| AResizablePanels | 可拖拽面板 |
| IFrameContainer | IFrame 容器 |
| ARecharge | 充值组件 |
核心功能详解
响应式布局系统
所有表单组件都支持响应式布局,通过 span 属性实现。
vue
<template>
<!-- 固定 span -->
<AFormInput label="用户名" v-model="form.userName" :span="12" />
<!-- 响应式对象 -->
<AFormInput
label="邮箱"
v-model="form.email"
:span="{ xs: 24, sm: 24, md: 12, lg: 8, xl: 6 }"
/>
<!-- 预设响应式 -->
<AFormInput label="手机" v-model="form.phone" span="auto" />
</template>响应式模式:
| 模式 | 说明 | 场景 |
|---|---|---|
| screen | 基于屏幕尺寸 | 默认,页面级表单 |
| container | 基于容器尺寸 | 弹窗内表单 |
| modal-size | 基于 AModal size | 弹窗场景推荐 |
智能数据类型转换
AFormSelect 组件具备智能的数据类型检测和转换功能。
vue
<script setup lang="ts">
const form = ref({
roleIds: '1,2,3' // 字符串输入
})
// 组件自动转换为数组 [1, 2, 3] 用于内部选择
// 确认后自动转回字符串 "1,2,3" 保持类型一致
</script>类型检测规则:
- 如果数组中有任何字符串元素,输出字符串数组
- 如果全是数字且在安全整数范围内,输出数字数组
- 如果数字超过 15 位,自动转为字符串防止精度丢失
选项禁用条件配置
AFormSelect 支持灵活的选项禁用配置。
vue
<template>
<!-- 默认禁用 (status = '0') -->
<AFormSelect v-model="form.postIds" :options="postOptions" :multiple="true" />
<!-- 自定义禁用字段 -->
<AFormSelect
v-model="form.roleId"
:options="roleList"
disabled-field="isActive"
:disabled-value="false"
/>
<!-- 函数判断 -->
<AFormSelect
v-model="form.goodsId"
:options="productList"
:disabled-value="(item) => item.status === '0' || item.stock < 10"
/>
</template>表单组件详解
AFormInput - 文本输入框
vue
<template>
<el-form :model="form">
<AFormInput label="用户名" v-model="form.userName" prop="userName" :span="12" />
<AFormInput label="备注" v-model="form.remark" type="textarea" :maxlength="200" :rows="4" />
<AFormInput label="年龄" v-model="form.age" type="number" :min="0" :max="150" :span="8" />
<AFormInput label="密码" v-model="form.password" type="password" show-password prevent-autofill />
</el-form>
</template>Props 属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 绑定值 | string | number | - |
| label | 标签文本 | string | - |
| prop | 表单域字段名 | string | - |
| type | 输入框类型 | 'text' | 'textarea' | 'number' | 'password' | 'text' |
| span | 栅格占据列数 | number | SpanType | - |
| maxlength | 最大长度 | number | 255 |
| showWordLimit | 显示字数统计 | boolean | true |
| showPassword | 显示密码可见性切换 | boolean | false |
| preventAutofill | 防止自动填充 | boolean | false |
| clearable | 显示清除按钮 | boolean | true |
| disabled | 是否禁用 | boolean | false |
| responsiveMode | 响应式模式 | 'screen' | 'container' | 'modal-size' | 'screen' |
Events 事件
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| update:modelValue | 值变化时触发 | (value: string | number) => void |
| input | 输入时触发 | (value: string | number) => void |
| change | 值改变时触发 | (value: string | number) => void |
| blur | 失去焦点时触发 | (event: FocusEvent) => void |
| enter | 按下回车键时触发 | (value: string | number) => void |
Slots 插槽
| 插槽名 | 说明 |
|---|---|
| prepend | 输入框前置内容 |
| append | 输入框后置内容 |
| prefix | 输入框头部图标 |
| suffix | 输入框尾部图标 |
AFormSelect - 下拉选择器
vue
<template>
<el-form :model="form">
<AFormSelect label="类型" v-model="form.type" :options="sys_enable_status" :span="12" />
<AFormSelect
label="角色"
v-model="form.roleIds"
:options="roleList"
value-field="roleId"
label-field="roleName"
:multiple="true"
/>
<AFormSelect label="代码" v-model="form.code" :options="codeList" :show-value="true" />
</el-form>
</template>
<script setup lang="ts">
const { sys_enable_status } = useDict(DictTypes.sys_enable_status)
</script>Props 属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 绑定值 | string | number | Array | - |
| label | 标签文本 | string | - |
| options | 选项数据 | any[] | [] |
| span | 栅格占据列数 | number | SpanType | - |
| multiple | 是否多选 | boolean | false |
| filterable | 是否可搜索 | boolean | true |
| clearable | 是否可清空 | boolean | true |
| valueField | value 字段名 | string | 'value' |
| labelField | label 字段名 | string | 'label' |
| disabledField | 禁用判断字段名 | string | 'status' |
| disabledValue | 禁用条件值 | DisabledCondition | '0' |
| showValue | 是否显示选项值 | boolean | - |
| multipleLimit | 多选最多可选项数 | number | 0 |
| collapseTags | 多选是否折叠标签 | boolean | false |
AFormDate - 日期选择器
vue
<template>
<el-form :model="form">
<AFormDate label="出生日期" v-model="form.birthday" :span="12" />
<AFormDate label="创建时间" v-model="form.dateRange" type="daterange" :span="12" />
<AFormDate label="预约时间" v-model="form.appointmentTime" type="datetime" />
</el-form>
</template>AFormTreeSelect - 树形选择器
vue
<template>
<AFormTreeSelect
label="所属部门"
v-model="form.deptId"
:options="deptTree"
:props="{ label: 'deptName', value: 'deptId', children: 'children' }"
:span="12"
/>
</template>AFormUpload - 文件上传
vue
<template>
<el-form :model="form">
<AFormFileUpload label="附件" v-model="form.fileList" :limit="5" :accept="'.pdf,.doc,.docx'" />
<AFormImgUpload label="头像" v-model="form.avatar" :limit="1" :max-size="2" />
</el-form>
</template>搜索和表格组件
ASearchForm - 搜索表单
vue
<template>
<ASearchForm v-model="queryParams" title="搜索条件">
<AFormInput label="用户名" prop="userName" v-model="queryParams.userName" />
<AFormSelect label="状态" prop="status" v-model="queryParams.status" :options="statusOptions" />
<AFormDate label="创建时间" v-model="queryParams.dateRange" type="daterange" />
</ASearchForm>
</template>Props 属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 表单数据模型 | Record<string, any> | {} |
| visible | 显示/隐藏 | boolean | true |
| inline | 是否行内表单 | boolean | true |
| labelWidth | 标签宽度 | string | 'auto' |
| title | 卡片标题 | string | - |
| collapsible | 是否可展开收起 | boolean | true |
| defaultExpanded | 默认是否展开 | boolean | false |
TableToolbar - 表格工具栏
vue
<template>
<TableToolbar
:columns="tableColumns"
:show-search="showSearch"
@resetQuery="resetQuery"
@queryTable="getList"
>
<template #left>
<el-button type="primary" @click="handleAdd">新增</el-button>
</template>
</TableToolbar>
</template>Pagination - 分页器
vue
<template>
<Pagination
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</template>弹窗组件详解
AModal - 通用弹窗
支持对话框和抽屉两种模式。
vue
<template>
<!-- 对话框模式 -->
<AModal v-model="dialogVisible" title="新增用户" @confirm="handleSubmit">
<el-form :model="form">
<AFormInput label="用户名" v-model="form.userName" />
</el-form>
</AModal>
<!-- 抽屉模式 -->
<AModal v-model="drawerVisible" title="用户详情" mode="drawer" direction="rtl" :show-footer="false">
<UserDetail :user="selectedUser" />
</AModal>
</template>Props 属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 显示/隐藏状态 | boolean | false |
| mode | 模式 | 'dialog' | 'drawer' | 'dialog' |
| title | 标题 | string | - |
| size | 预设尺寸 | 'small' | 'medium' | 'large' | 'xl' | 'medium' |
| width | 自定义宽度 | string | number | - |
| fullscreen | 是否全屏 | boolean | false |
| movable | 是否可拖动 | boolean | false |
| direction | 抽屉方向 | 'ltr' | 'rtl' | 'ttb' | 'btt' | 'rtl' |
| showFooter | 显示底部 | boolean | true |
| footerType | 底部按钮类型 | 'default' | 'close-only' | 'default' |
| loading | 加载状态 | boolean | false |
| destroyOnClose | 关闭时销毁 | boolean | true |
Events 事件
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| update:modelValue | 状态变化 | (value: boolean) => void |
| confirm | 确认按钮点击 | () => void |
| cancel | 取消按钮点击 | () => void |
| open | 开始打开 | () => void |
| opened | 完全打开 | () => void |
| close | 开始关闭 | () => void |
| closed | 完全关闭 | () => void |
尺寸配置
typescript
const sizeMap = {
small: { dialog: '600px', drawer: '600px' },
medium: { dialog: '800px', drawer: '800px' },
large: { dialog: '1000px', drawer: '1000px' },
xl: { dialog: '1200px', drawer: '1200px' }
}ADetail - 详情弹窗
vue
<template>
<ADetail
v-model="detailVisible"
title="用户详情"
:data="detailData"
:fields="detailFields"
mode="drawer"
/>
</template>
<script setup lang="ts">
const detailFields = ref([
{ label: '用户ID', prop: 'userId', type: 'text' },
{ label: '用户名', prop: 'userName', type: 'copyable' },
{ label: '密码', prop: 'password', type: 'password' },
{ label: '头像', prop: 'avatar', type: 'image' },
{ label: '状态', prop: 'status', type: 'dict', dictType: 'sys_enable_status' },
{ label: '创建时间', prop: 'createTime', type: 'datetime' }
])
</script>字段类型
| 类型 | 说明 |
|---|---|
| text | 普通文本 |
| password | 密码(可切换显示) |
| copyable | 可复制文本 |
| dict | 字典标签 |
| image | 图片预览 |
| html | HTML 内容 |
| file | 文件链接 |
| date | 日期格式化 |
| datetime | 日期时间格式化 |
| currency | 货币格式化 |
| boolean | 是/否 |
| array | 数组显示 |
业务选择器详解
UserSelect - 用户选择器
vue
<template>
<UserSelect v-model="selectedUsers" :multiple="true" show-inline-tags />
<UserSelect v-model="selectedUser" :multiple="false" />
<UserSelect
v-model="userIds"
:initial-user-names="userNamesString"
:multiple="true"
button-text="选择成员"
/>
</template>Props 属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 绑定值 | string | number | SysUserVo | Array | - |
| multiple | 是否多选 | boolean | false |
| userIds | 限制用户 ID 范围 | string | number | Array | - |
| defaultReturnType | 默认返回类型 | 'object' | 'id' | 'object' |
| showInlineTags | 显示内置标签 | boolean | false |
| buttonText | 按钮文本 | string | '选择用户' |
| buttonType | 按钮类型 | string | 'primary' |
| disabled | 是否禁用 | boolean | false |
| initialUserNames | 初始用户名 | string | string[] | - |
智能返回类型
typescript
// 传入用户对象,返回用户对象
const user1 = ref<SysUserVo>({ userId: '1', userName: 'admin' })
// 传入用户 ID,返回用户 ID
const user2 = ref('1')
// 传入空值,根据 defaultReturnType 决定
const user3 = ref(null)DictTag - 字典标签
vue
<template>
<!-- dict 模式 -->
<DictTag :options="sys_enable_status" :value="user.status" />
<!-- region 模式 -->
<DictTag mode="region" :value="user.regionCode" />
<!-- cascader 模式 -->
<DictTag mode="cascader" :value="user.categoryId" :cascader-data="categoryTree" />
</template>Props 属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| mode | 模式 | 'dict' | 'region' | 'cascader' | 'dict' |
| options | 字典选项 | any[] | [] |
| value | 值 | string | number | - |
| cascaderData | 级联数据 | any[] | [] |
| valueField | value 字段名 | string | 'value' |
| labelField | label 字段名 | string | 'label' |
图表组件
基本用法
vue
<template>
<!-- 通用图表 -->
<AChart :option="chartOption" height="400px" />
<!-- 折线图 -->
<ALineChart title="访问量趋势" :xData="xData" :series="series" height="300px" />
<!-- 柱状图 -->
<ABarChart title="产品销量" :xData="products" :series="sales" height="350px" />
<!-- 饼图 -->
<APieChart title="流量来源" :data="trafficData" height="400px" />
</template>
<script setup lang="ts">
const xData = ref(['1月', '2月', '3月', '4月', '5月', '6月'])
const series = ref([
{ name: 'PV', data: [120, 132, 101, 134, 90, 230] },
{ name: 'UV', data: [45, 62, 48, 71, 53, 98] }
])
const trafficData = ref([
{ name: '直接访问', value: 335 },
{ name: '搜索引擎', value: 310 },
{ name: '外部链接', value: 234 }
])
</script>最佳实践
1. 列表页面开发流程
vue
<template>
<div class="app-container">
<!-- 1. 搜索表单 -->
<ASearchForm v-model="queryParams">
<AFormInput label="用户名" prop="userName" v-model="queryParams.userName" />
<AFormSelect label="状态" prop="status" v-model="queryParams.status" :options="sys_enable_status" />
</ASearchForm>
<!-- 2. 表格工具栏 -->
<TableToolbar :columns="tableColumns" @queryTable="getList">
<template #left>
<el-button type="primary" @click="handleAdd">新增</el-button>
</template>
</TableToolbar>
<!-- 3. 数据表格 -->
<el-table :data="tableData" v-loading="loading">
<el-table-column label="用户ID" prop="userId" />
<el-table-column label="状态" prop="status">
<template #default="{ row }">
<DictTag :options="sys_enable_status" :value="row.status" />
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 4. 分页 -->
<Pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
<!-- 5. 编辑弹窗 -->
<AModal v-model="editVisible" :title="editTitle" @confirm="handleSubmit">
<el-form :model="form" :rules="rules" ref="formRef">
<AFormInput label="用户名" prop="userName" v-model="form.userName" :span="12" />
</el-form>
</AModal>
</div>
</template>
<script setup lang="ts">
import { pageUsers, addUser, updateUser } from '@/api/system/core/user/userApi'
const { sys_enable_status } = useDict(DictTypes.sys_enable_status)
const queryParams = ref({ pageNum: 1, pageSize: 10, userName: '', status: '' })
const tableData = ref([])
const total = ref(0)
const loading = ref(false)
const editVisible = ref(false)
const form = ref({})
const getList = async () => {
loading.value = true
const [err, data] = await pageUsers(queryParams.value)
if (!err) {
tableData.value = data.records || []
total.value = data.total
}
loading.value = false
}
const handleSubmit = async () => {
const api = form.value.userId ? updateUser : addUser
const [err] = await api(form.value)
if (!err) {
ElMessage.success('操作成功')
editVisible.value = false
getList()
}
}
onMounted(() => getList())
</script>2. 表单验证集成
vue
<script setup lang="ts">
const rules = {
userName: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
],
phone: [
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
]
}
</script>3. 权限控制集成
vue
<template>
<el-button v-permi="['system:user:add']" type="primary" @click="handleAdd">新增</el-button>
<el-button v-permi="['system:user:edit']" type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button v-permi="['system:user:delete']" type="danger" link @click="handleDelete(row)">删除</el-button>
</template>4. 响应式布局最佳实践
vue
<template>
<!-- 弹窗内使用 modal-size 模式 -->
<AModal v-model="visible" size="large">
<el-form>
<AFormInput label="标题" v-model="form.title" :span="12" responsiveMode="modal-size" :modalSize="'large'" />
</el-form>
</AModal>
<!-- 页面中使用 screen 模式 -->
<el-form>
<AFormInput label="用户名" v-model="form.userName" :span="{ xs: 24, sm: 24, md: 12, lg: 8, xl: 6 }" />
</el-form>
</template>常见问题
1. AFormSelect 多选时类型不一致?
解决方案:
typescript
// 方案1: 让组件自动处理类型转换
const form = ref({
roleIds: '1,2,3' // 字符串输入,组件自动转换
})
// 方案2: 统一使用数组格式
const form = ref({
roleIds: [1, 2, 3] // 数组输入
})2. AFormInput 防自动填充不生效?
解决方案:
vue
<template>
<AFormInput label="密码" v-model="form.password" type="password" show-password prevent-autofill />
</template>
<!-- 如果仍然不生效,可以在表单外层添加 -->
<el-form autocomplete="new-password">3. UserSelect 返回类型如何控制?
解决方案:
typescript
// 方式1: 通过 v-model 的初始值类型自动推断
const userId = ref('') // 返回字符串 ID
const user = ref<SysUserVo>() // 返回用户对象
const userIds = ref<string[]>([]) // 返回 ID 数组
// 方式2: 使用 defaultReturnType 指定(当 v-model 为空时)
// <UserSelect v-model="emptyValue" defaultReturnType="id" />4. ASearchForm 展开/收起按钮不显示?
原因: 表单项少于 2 行时不显示
解决方案: 确保表单项足够多(至少能形成 2 行),或检查 collapsible 属性是否为 true。
总结
RuoYi-Plus-UniApp 前端业务组件库提供了完整的后台管理系统开发解决方案:
- 13 个表单组件 - 完整的表单输入方案
- 3 个搜索表格组件 - 列表页核心组件
- 2 个弹窗组件 - 对话框和详情展示
- 2 个业务选择器 - 用户选择和字典显示
- 23 个卡片组件 - 丰富的展示卡片
- 10 个图表组件 - 完整的数据可视化
- 4 个 AI 组件 - 智能辅助工具
通过这些组件,开发者可以快速构建列表页、灵活定制表单、展示丰富数据。所有组件都经过生产环境验证,性能优秀,易于使用。
