yu-vue3-ui
v1.0.132
Published
基于 Vue 3 + Element Plus 的业务组件库,通过配置化的方式简化后台管理系统的 CRUD 页面开发。
Readme
yu-vue3-ui
基于 Vue 3 + Element Plus 的业务组件库,通过配置化的方式简化后台管理系统的 CRUD 页面开发。
特性
- 配置化开发:通过
columns一份配置,自动生成搜索、表单、表格、详情四个区域的字段 - 丰富的表单类型:支持 17 种输入类型,覆盖日常开发中的各种场景
- 完整的 CRUD 封装:列表、新增、编辑、删除、导入、导出、自定义表头
- 灵活可扩展:支持自定义校验、联动筛选、动态显示/隐藏字段
- 完整的 TypeScript 支持:提供完整的类型定义
安装
npm install yu-vue3-ui快速开始
1. 定义数据源 (dataSource.ts)
import { gCrud_IF } from 'yu-vue3-ui/interface/crud'
// 定义 columns 配置
const columns: gCrud_IF[] = [
{
label: '用户名',
dataIndex: 'username',
type: 'input',
showSearch: true,
isRequired: true
},
{
label: '状态',
dataIndex: 'status',
type: 'select',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
}
]
// 定义 API 接口地址
const url = {
list: '/api/user/list',
getById: '/api/user/',
add: '/api/user',
edit: '/api/user',
delete: '/api/user',
export: '/api/user/export'
}
export { columns, url }2. 列表页面 (index.vue)
<template>
<div class="g-crud">
<!-- 搜索区域 -->
<el-card shadow="hover">
<yuForm v-model="queryParam" :options="searchColumns">
<el-button type="primary" icon="search" @click="getList">查 询</el-button>
<el-button icon="RefreshRight" @click="handleReset">重 置</el-button>
</yuForm>
</el-card>
<!-- 数据表格区域 -->
<el-card shadow="hover" v-loading="loading">
<!-- 工具栏按钮 -->
<div class="g-table-button">
<el-button type="primary" icon="Plus" @click="handleAdd">新增</el-button>
<el-button icon="Download" @click="handleExport('文件名')">导出</el-button>
</div>
<!-- 数据表格 -->
<yuTable :columns="diyTableColumns" :data="dataSource">
<!-- 操作列插槽 -->
<template #action="{ record }">
<el-button type="text" @click="handleDetails(record)">查看</el-button>
<el-button type="text" @click="handleEdit(record)">修改</el-button>
<el-button type="text" @click="handleDelete(record)">删除</el-button>
</template>
</yuTable>
<!-- 分页组件 -->
<el-pagination
v-model:current-page="queryParam.pageNum"
v-model:page-size="queryParam.pageSize"
:page-sizes="[10, 20, 30, 40]"
layout="total, sizes, prev, pager, next, jumper"
:total="queryParam.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
<!-- 详情/新增/编辑弹窗 -->
<detailsDialog @ok="getList" :options="detailColumns" :url="url" ref="detailsRef" />
</div>
</template>
<script lang="ts" setup>
import { columns, url } from './dataSource'
import detailsDialog from './detailsDialog.vue'
import indexHooks from 'yu-vue3-ui/hooks/indexHooks'
const {
searchColumns, // 搜索表单配置
detailColumns, // 详情表单配置
detailsRef, // 详情弹窗实例
queryParam, // 查询参数(包含分页)
dataSource, // 表格数据
loading, // 加载状态
diyTableColumns, // 表格列配置(支持自定义表头)
getList, // 获取列表数据
handleDetails, // 查看详情
handleAdd, // 新增
handleEdit, // 编辑
handleDelete, // 删除
handleReset, // 重置搜索
handleExport, // 导出
handleSizeChange, // 分页大小变更
handleCurrentChange, // 页码变更
} = indexHooks({ columns, url })
</script>3. 详情弹窗 (detailsDialog.vue)
<template>
<el-dialog v-model="show" :title="title">
<!-- 表单组件,isDetails=true 时禁用编辑 -->
<yu-form ref="gFormRef" v-model="form" :options="options" :span="24" :disabled="isDetails" />
<template #footer>
<div class="dialog-footer" v-if="!isDetails">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" :loading="loading" @click="handleOk">确定</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import detailsHooks from 'yu-vue3-ui/hooks/detailsHooks'
const props = defineProps({
options: { type: Array, default: () => [] },
url: { type: Object, default: () => ({}) }
})
const emit = defineEmits(['ok'])
const {
show, // 弹窗显示状态
title, // 弹窗标题
isDetails, // 是否为详情查看模式
form, // 表单数据
loading, // 提交按钮加载状态
gFormRef, // 表单实例(用于调用校验等方法)
handleOpen, // 打开弹窗
handleOk, // 确认提交
handleCancel // 取消关闭
} = detailsHooks({ url: props.url, emit })
defineExpose({ show, handleOpen })
</script>核心概念
columns 配置详解
columns 是整个组件库的核心,通过一份配置自动适配搜索、表单、表格、详情四个场景:
const columns: gCrud_IF[] = [
{
label: '字段名称', // 字段显示名称
dataIndex: 'fieldName', // 绑定字段名
type: 'input', // 字段类型
showSearch: true, // 是否显示在搜索区域
isRequired: true // 是否必填
}
]字段类型 (crudType_TP)
| 类型 | 说明 | Element Plus 组件 |
|------|------|------------------|
| input | 单行文本输入框 | el-input |
| text | 多行文本输入框 | el-input (textarea) |
| number | 数字输入框 | el-input-number |
| select | 下拉选择框 | el-select |
| multipleSelect | 多选下拉框 | el-select延申自定义组件(仅适配字符串以逗号分割) |
| treeSelect | 树形下拉选择 | el-tree-select |
| checkbox | 多选框 | el-checkbox-group |
| radio | 单选框 | el-radio-group |
| date | 日期选择器 | el-date-picker |
| dateYear | 年份选择器 | el-date-picker |
| datetime | 日期时间选择器 | el-date-picker |
| switch | 开关 | el-switch |
| color | 颜色选择器 | el-color-picker |
| editor | 富文本编辑器 | 自定义组件(需自行引入editor) |
| fileUpload | 文件上传 | 自定义组件(需自行引入fileUpload) |
| imageUpload | 图片上传 | 自定义组件(需自行引入imageUpload) |
| divider | 分隔线 | 自定义组件 |
columns 完整属性
| 属性 | 类型 | 说明 |
|------|------|------|
| label | string | 字段显示名称 |
| dataIndex | string | 绑定字段名 |
| type | crudType_TP | 默认字段类型 |
| searchType | crudType_TP | 搜索区域专属类型 |
| detailType | crudType_TP | 详情区域专属类型 |
| showSearch | boolean | 是否显示在搜索区域 |
| noTable | boolean | 是否不在表格中展示 |
| noDetails | boolean | 是否不在详情表单中展示 |
| noShow | boolean | 是否不在表格列中显示 |
| isRequired | boolean | 是否为必填项 |
| isInnerHtml | boolean | 是否使用 v-html 展示(用于富文本) |
| dict | string | 若依字典标识 |
| unit | string | 数据单位,如 "100m" |
| options | options_TP \| funcOptions_TP | 选项数据(支持异步函数) |
| searchOptions | options_TP \| funcOptions_TP | 搜索区域专属选项 |
| detailOptions | options_TP \| funcOptions_TP | 详情区域专属选项 |
| config | anyObject_IF | 公共扩展配置 |
| searchConfig | anyObject_IF | 搜索区域扩展配置 |
| detailConfig | anyObject_IF | 详情区域扩展配置 |
| tableConfig | anyObject_IF | 表格区域扩展配置 |
| customValidator | any[] | 自定义校验规则 |
| customHide | (form) => boolean | 自定义隐藏逻辑 |
| customType | (form) => crudType_TP | 自定义字段类型 |
| customText | (value, prop) => string | 自定义展示文本 |
| filterCallback | (options, form) => options | 选项过滤回调 |
| onChange | (value, item, form) => void | 值变更回调 |
进阶功能
1. 联动筛选
下拉选项根据其他字段值动态过滤:
{
label: '省份',
dataIndex: 'province',
type: 'select',
options: () => getProvinceList(),
onChange: (value, item, form) => {
form.value.city = undefined // 清空城市选择
}
},
{
label: '城市',
dataIndex: 'city',
type: 'select',
options: [],
filterCallback: (selectOptions, form) => {
// 根据省份过滤城市列表
return selectOptions.filter(city =>
city.provinceId === form.value.province
)
}
}2. 动态显示/隐藏字段
根据表单其他字段值决定是否显示:
{
label: '类型',
dataIndex: 'type',
type: 'radio',
options: [
{ label: '固定', value: 'fixed' },
{ label: '动态', value: 'dynamic' }
]
},
{
label: '固定值',
dataIndex: 'fixedValue',
type: 'input',
customHide: (form) => {
return form.type !== 'fixed' // 类型不为固定时隐藏
}
}3. 动态切换字段类型
根据条件改变输入组件类型:
{
label: '数据值',
dataIndex: 'value',
customType: (form) => {
return form.type === 'number' ? 'number' : 'input'
}
}4. 自定义表格列渲染
使用插槽自定义表格单元格的显示:
<yuTable :columns="diyTableColumns" :data="dataSource">
<!-- 自定义状态列 -->
<template #status="{ record }">
<el-tag :type="record.status === 1 ? 'success' : 'danger'">
{{ record.status === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
<!-- 操作列 -->
<template #action="{ record }">
<el-button type="text" @click="handleEdit(record)">编辑</el-button>
</template>
</yuTable>columns 中定义插槽名:
{
label: '状态',
dataIndex: 'status',
config: { slotName: 'status' }
}5. 异步加载选项
选项数据从 API 异步获取:
{
label: '角色',
dataIndex: 'roleId',
type: 'select',
options: async () => {
const res = await fetch('/api/roles')
return res.data.map(item => ({
label: item.name,
value: item.id
}))
}
}6. 自定义校验规则
{
label: '手机号',
dataIndex: 'phone',
type: 'input',
isRequired: true,
customValidator: [
{
validator: (rule, value, callback) => {
if (!/^1[3-9]\d{9}$/.test(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
},
trigger: 'blur'
}
]
}7. 自定义展示文本
{
label: '状态',
dataIndex: 'status',
type: 'select',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
],
customText: (value, prop) => {
return `<span style="color: ${value === '启用' ? 'green' : 'red'}">${value}</span>`
},
isInnerHtml: true // 开启 HTML 渲染
}8. 表格列配置扩展
{
label: '金额',
dataIndex: 'amount',
type: 'number',
unit: '元',
tableConfig: {
width: 120,
sortable: true,
align: 'right'
}
}工具函数
columns 处理工具
import {
getSearchColumns, // 从 columns 提取搜索配置
getTableColumns, // 从 columns 提取表格配置
getDetailColumns, // 从 columns 提取详情表单配置
getDealColumns // 处理异步 options
} from 'yu-vue3-ui/utils/columns'表单校验
import { getRules } from 'yu-vue3-ui/utils/formRules'
const rules = getRules(options)数据格式化
import { treeFormat, getOneDimensional } from 'yu-vue3-ui/utils/dataFormat'
// 将树形数据转换为 options 格式
const options = treeFormat(data, 'name', 'id', 'children')
// 将树形数据扁平化为一维数组
const flatData = getOneDimensional(treeData)大屏自适应
import { useIndex } from 'yu-vue3-ui/utils/adaptiveScaling'
const appRef = ref()
onMounted(() => {
useIndex(appRef.value, 1920, 1080) // 设计稿尺寸
})Hooks API
indexHooks - 列表页面 Hook
interface hooksOptions {
columns: gCrud_IF[] // 列配置
url: { // API 接口
list: string
add?: string
edit?: string
delete?: string
export?: string
}
name?: string // 自定义表头缓存 key
key?: string // 主键字段名,默认 'id'
defaultParams?: object // 默认查询参数
noRunList?: boolean // 是否不自动加载列表
beforeSetData?: (data) => data // 数据处理前回调
afterGetList?: (dataSource) => void // 数据获取后回调
beforeSetParams?: (params) => params // 查询参数处理前回调
getDealColumns?: (columns) => Promise // 自定义 columns 处理
}
const result = indexHooks(options)
// 返回值
{
searchColumns, // 搜索表单配置
detailColumns, // 详情表单配置
diyTableColumns, // 表格列配置(支持自定义表头)
detailsRef, // 详情弹窗实例引用
importRef, // 导入弹窗实例引用
pageParams, // 分页参数 { pageNum, pageSize }
queryParam, // 查询参数(包含分页和搜索条件)
dataSource, // 表格数据数组
loading, // 加载状态
openTableHeader, // 自定义表头弹窗显示状态
selectList, // 表格多选选中的行数据
getList, // 获取列表数据
handleAdd, // 打开新增弹窗
handleEdit, // 打开编辑弹窗
handleDetails, // 打开详情弹窗
handleDelete, // 删除单条数据
handleReset, // 重置搜索条件
handleExport, // 导出数据
handleImport, // 打开导入弹窗
handleSizeChange, // 分页大小变更
handleCurrentChange,// 页码变更
handleTableHeader, // 打开自定义表头弹窗
changeColumn, // 自定义表头变更回调
setDealColumns, // 重新处理 columns
getParams, // 获取当前查询参数
selectTable, // 表格选择回调
enter // 回车搜索
}detailsHooks - 详情弹窗 Hook
interface detailsHooksOptions {
url: { // API 接口
add: string
edit: string
getById: string
}
key?: string // 主键字段名,默认 'id'
defaultForm?: object // 表单默认值
afterOpen?: () => void // 弹窗打开后回调
beforeSetValue?: (data) => data // 数据回填前处理
beforeSetParams?: (params) => params // 提交参数处理前回调
afterSubmit?: (res) => void // 提交成功后回调
getAction: (url, params) => Promise // GET 请求方法
httpAction: (url, params, method) => Promise // 通用请求方法
}
const result = detailsHooks(options)
// 返回值
{
show, // 弹窗显示状态
title, // 弹窗标题
isDetails, // 是否为详情查看模式
form, // 表单数据对象
loading, // 提交按钮加载状态
gFormRef, // 表单实例(用于调用 validate/resetFields)
handleOpen, // 打开弹窗
handleOk, // 确认提交
handleCancel, // 取消关闭
resetFields, // 重置表单
clearValidate // 清除校验状态
}组件列表
yuTable - 增强表格组件
基于 Element Plus Table 封装,支持选项翻译显示。
<yuTable :columns="columns" :data="dataSource" border>
<template #action="{ record }">
<el-button @click="handleEdit(record)">编辑</el-button>
</template>
</yuTable>暴露方法:
setTable(callback)- 获取 table 实例toggleSelection(rows)- 选中指定行clearSelection()- 清空选择
yuForm - 动态表单组件
根据配置自动渲染表单项。
<yuForm
v-model="formData"
:options="formColumns"
:span="24"
:disabled="isView"
/>暴露方法:
validate()- 校验表单resetFields()- 重置表单clearValidate()- 清除校验状态
yuMain - 主页布局组件
带可折叠搜索面板的主布局。
<yuMain :showSearch="true" :searchWidth="300">
<template #search>
<!-- 搜索表单 -->
</template>
<!-- 主内容 -->
</yuMain>yuScrollTable - 滚动表格组件
数据自动滚动展示,适合公告、消息等场景。
<yuScrollTable :changeTime="3000">
<div class="scroll-box">
<div class="scroll-item" v-for="item in list">
{{ item.title }}
</div>
</div>
</yuScrollTable>Props:
scrollBox- 滚动容器选择器,默认.scroll-boxscrollItem- 滚动项选择器,默认.scroll-itemchangeTime- 滚动间隔时间(ms),默认 3000
暴露方法:
init()- 重新初始化startAnimate()- 开始滚动stopAnimate()- 停止滚动clear()- 清除
yuMap - 天地图组件
集成天地图的地图展示。
<yuMap ref="mapRef" @changeZoom="handleZoomChange" />暴露方法:
initMap(lng, lat, zoom)- 初始化地图
yuWaveformChart - 波形图表组件
基于 Canvas 的实时波形绘制。
<yuWaveformChart
:data="waveData"
:chartDuration="18"
:dataUpdateInterval="100"
lineStyle="color: #ffa34f; lineWidth: 2"
/>依赖
- Vue 3.4+
- Element Plus 2.7+
License
MIT
