ly-eui
v1.4.9
Published
基于 Element Plus 的 Vue 3 组件库,提供表格、表单、对话框等常用业务组件。
Downloads
54
Readme
LyEui - Vue 3 组件库
基于 Element Plus 的 Vue 3 组件库,提供表格、表单、对话框等常用业务组件。
特性
- 🚀 基于 Vue 3 + Element Plus
- 📦 支持按需加载和 Tree-shaking
- 🎨 完整的 TypeScript 支持
- ⚡ 兼容 Vite、Webpack 等构建工具
- 🎯 专注于企业级中后台应用
在线资源
- 在线文档: http://wdweixin.szlanyou.com/doc-ly-eui/lyeui
- 示例代码: https://codesandbox.io/p/sandbox/vue-code-highlight-example-63h5m
安装与使用
安装
npm install ly-eui element-plus
# 或
yarn add ly-eui element-plus全局注册
import { createApp } from 'vue'
import App from './App.vue'
import lyEui from 'ly-eui'
import 'ly-eui/style.css'
const app = createApp(App)
app.use(lyEui, {
DpTable: {
methods: {
$requestAPI({ url, headers, formParams, ...options }) {
console.log('接口注入依赖===', { url, headers, formParams })
return useRequest(url, formParams, { method: 'post', ...headers, ...options })
},
// 配置自动查询字段输出
configQueryfieldOutput() {
return {
total: 'records',
pageSize: 'pages',
current: 'pageindex',
tableData: 'rows'
}
}
}
},
LyForm: {
methods: {
$requestAPI({ url, headers, formParams, ...options }) {
console.log('接口注入依赖===', { url, headers, formParams, ...options })
return useRequest(url, formParams, { method: 'post', ...headers, ...options })
},
// 配置自动查询字段输出
fieldFieldOutput() {
return {
total: 'records',
pageSize: 'pages',
current: 'pageindex',
listData: 'rows'
}
}
}
}
})
app.mount('#app')按需引入
import { DpTable, LyForm, useDialog } from 'ly-eui'
import 'ly-eui/dist/style.css'
export default {
components: {
DpTable,
LyForm
},
setup() {
const dialog = useDialog()
return { dialog }
}
}1. dp-table 表格组件
dp-table 是一个功能强大的表格组件,支持分页、排序、选择、固定列、复合表头、自定义渲染等功能,并内置了数据请求和分页自动处理能力。
基础用法
<template>
<dp-table
:options="tableOptions"
:columns="tableColumns"
v-model:pagination="pagination"
v-model:dataSource="dataSource"
ref="tableRef"
/>
</template>
<script setup>
import { ref, reactive } from 'vue'
const tableRef = ref()
const dataSource = ref([])
const pagination = reactive({
total: 0,
pageSize: 10,
current: 1
})
const tableOptions = reactive({
size: 'small',
border: true,
heightType: 'height',
tabHeight: 400
})
const tableColumns = reactive([
{
type: 'index',
label: '序号',
width: 80,
align: 'center'
},
{
label: '姓名',
key: 'name',
emptyPlaceholder: '-暂无-'
},
{
label: '年龄',
key: 'age',
render: (text, row) => {
return h('el-button', {
type: 'primary',
link: true,
onClick: () => alert(`年龄: ${row.age}`)
}, row.age)
}
},
{
label: '操作',
key: 'operate',
btns: [
{
name: '查看详情',
clickEvent: (row) => {
console.log('查看详情', row)
}
},
{
name: '删除',
type: 'danger',
clickEvent: (row) => {
console.log('删除', row)
}
}
]
}
])
</script>参数配置
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| options | Object | 是 | {} | 表格全局配置,包含分页、接口请求等信息 |
| columns | Array<Object> | 是 | [] | 表格列配置数组,定义表格的列内容及样式 |
| v-model:pagination | Object | 是 | {} | 分页数据绑定,包含总条数、当前页、每页条数等信息 |
| v-model:dataSource | Array | 是 | [] | 表格数据源绑定,存储表格展示的数据 |
| ref | String | 否 | - | 表格实例引用,用于调用表格方法或访问表格状态 |
| initTabHeight | Number | 否 | 400 | 表格初始高度 |
| headBarClass | String/Object | 否 | '' | 表格头部栏样式类 |
| showHeaderBar | Boolean | 否 | true | 是否显示头部栏 |
options 配置项详解
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| size | String | 否 | 'small' | 表格尺寸,可选值为 'large'、'default'、'small' |
| border | Boolean | 否 | false | 是否显示表格边框 |
| heightType | String | 否 | 'auto' | 表格高度类型,可选值为 'auto' 或 'height' |
| tabHeight | Number | 否 | 400 | 表格容器高度(当 heightType 为 'height' 时生效) |
| isInitRun | Boolean | 否 | true | 初始化时是否自动请求接口数据 |
| cachePageSelection | Boolean | 否 | true | 是否缓存分页选择状态 |
| rowkey | String | 否 | - | 行数据的唯一标识字段,用于缓存选择状态 |
| apiParams | Object | 否 | - | 接口请求参数配置 |
| configTableOut | Object | 否 | - | 接口返回数据字段映射配置 |
| listenToCallBack | Object | 否 | - | 请求前后的回调函数配置 |
| httpRequest | Function | 否 | - | 自定义请求方法 |
| actionUrl | String | 否 | - | 接口请求地址(已废弃,推荐使用 apiParams) |
apiParams 配置
apiParams: {
url: '/api/data/list', // 接口地址
headers: { // 请求头
'Content-Type': 'application/json'
},
model: { // 请求参数
status: 1
},
options: { // 其他请求选项
method: 'POST'
}
}configTableOut 配置
configTableOut: {
total: 'records', // 总条数字段名
pageSize: 'pageSize', // 每页条数字段名
current: 'pageNo', // 当前页字段名
tableData: 'rows' // 数据列表字段名
}listenToCallBack 配置
listenToCallBack: {
brcb: (params) => {
// 请求前参数处理
return { ...params, timestamp: Date.now() }
},
qrcb: (response) => {
// 请求后响应处理
console.log('响应数据:', response)
}
}columns 配置项详解
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| type | String | 否 | - | 列类型,如 'selection'(复选框)、'index'(序号列)、'expand'(展开行) |
| label | String | 是 | - | 列标题 |
| key/prop | String | 是 | - | 数据字段名,对应数据源中的字段 |
| width | Number/String | 否 | - | 列宽度,可以是数字或字符串(如 '200px') |
| align | String | 否 | 'left' | 内容对齐方式,可选值为 'left'、'center'、'right' |
| fixed | String/Boolean | 否 | false | 是否固定列,可选值为 'left'、'right' 或 true/false |
| render | Function | 否 | - | 自定义渲染函数 |
| btns | Array | 否 | - | 操作按钮配置(仅在 key/prop 为 'operate' 时生效) |
| groups | Array | 否 | - | 复合表头配置 |
| hidCol | Boolean | 否 | false | 是否隐藏该列 |
| emptyPlaceholder | String | 否 | '-' | 空值占位符 |
| scopedSlots | Object | 否 | - | 作用域插槽配置 |
复合表头配置示例
{
label: '任务奖励',
key: 'reward',
groups: [
{
label: '奖励成长值',
key: 'growthValue',
width: 200,
render: (text, record) => 20
},
{
label: '奖励积分',
key: 'points',
width: 200,
render: (text, record) => 50
}
]
}表格实例方法
通过 ref 获取表格实例后,可以调用以下方法:
// 获取选中的行数据
const selectedRows = tableRef.value.multipleSelection
// 清空选择
tableRef.value.clearAllSelection()
// 手动触发表格布局重计算
tableRef.value.tableRef?.doLayout()
// 获取表格内部实例
const tableInstance = tableRef.value.getTableInstantce()事件
| 事件名 | 参数 | 描述 |
|--------|------|------|
| update:dataSource | dataSource | 数据源更新时触发 |
| update:pagination | pagination | 分页信息更新时触发 |
| pagination-current-change | pagination | 当前页码改变时触发 |
| size-change | pagination, size | 每页条数改变时触发 |
| change-table-sort | column | 表格排序改变时触发 |
高级用法
1. 表格与表单组合使用
<template>
<dp-table
:options="tableOptions"
:columns="tableColumns"
v-model:pagination="pagination"
v-model:dataSource="dataSource"
>
<ly-form
ref="formRef"
:model="formModel"
:layout-form-props="formLayout"
:form-item-list="formItems"
:config-btn="formButtons"
/>
</dp-table>
</template>2. 自动化分页处理
当表格嵌套表单时,表格会自动处理分页请求:
const tableOptions = {
isInitRun: true,
apiParams: {
url: '/api/orders',
model: { status: 1 }
},
configTableOut: {
total: 'total',
pageSize: 'size',
current: 'page',
tableData: 'list'
}
}3. 自定义请求方法
const tableOptions = {
httpRequest: async (params) => {
const { url, formParams, ...rest } = params
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formParams)
})
return await response.json()
}
}2. ly-form 表单组件
ly-form 是一个高度灵活的表单组件,支持动态表单项生成、多种内置表单类型、自定义渲染、表单验证、条件显示等功能。它采用声明式配置的方式,通过 JSON 配置快速构建复杂的表单界面。
基础用法
<template>
<ly-form
ref="formRef"
:model="formModel"
:layout-form-props="formLayout"
:form-item-list="formItems"
:config-btn="formButtons"
/>
</template>
<script setup>
import { ref, reactive } from 'vue'
const formRef = ref()
const formModel = reactive({
sourceChannel: '',
orderNo: '',
customerName: '',
customerPhone: '',
productName: '',
performanceMode: '',
orderStatus: '',
time: [],
time2: [],
batchNo: ''
})
const formLayout = reactive({
fromLayProps: {
labelWidth: '95px',
size: 'small',
minfoldRows: 2, // 最多展示2行,超出部分可折叠
gutter: 16
}
})
const formItems = reactive([
{
label: '来源渠道',
span: 6,
prop: 'sourceChannel',
formtype: 'Select',
dataOptions: [
{ label: '全部', value: '' },
{ label: '领克APP', value: '16' },
{ label: '几何APP', value: '15' },
{ label: '吉利APP', value: '14' },
{ label: '运营后台', value: '17' }
],
attrs: {
clearable: true,
placeholder: '请选择'
}
},
{
label: '订单编号',
span: 6,
prop: 'orderNo',
formtype: 'Input',
formItemOptions: {
rules: {
required: true,
trigger: 'blur'
}
}
},
{
label: '客户姓名',
span: 6,
prop: 'customerName',
formtype: 'Input',
isHiden: (formData) => formData.sourceChannel === '16'
}
])
const formButtons = reactive([
{
name: '查询',
icon: 'Search',
key: 'query',
type: 'primary',
click: (model, formInstance) => {
formInstance.validate((valid) => {
if (valid) {
console.log('表单验证通过', model)
}
})
}
},
{
name: '重置',
icon: 'RefreshLeft',
key: 'reset',
click: (model, formInstance) => {
formInstance.resetFields()
}
}
])
</script>参数配置
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| model | Object | 是 | {} | 表单数据绑定对象,存储表单字段值 |
| layoutFormProps | Object | 是 | {} | 表单布局属性配置 |
| formItemList | Array<Object> | 是 | [] | 表单项配置数组 |
| configBtn | Array<Object> | 否 | [] | 操作按钮配置数组 |
| renderBtn | Function/Boolean | 否 | false | 自定义按钮渲染函数 |
| btnColSpanRow | Boolean | 否 | true | 按钮是否独占一行 |
| ref | String | 否 | - | 表单实例引用 |
layoutFormProps 配置项详解
fromLayProps 配置
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| isBtnHiden | Boolean | 否 | false | 是否隐藏操作按钮 |
| labelWidth | String | 否 | - | 标签宽度(如 '95px') |
| labelBtnWidth | String | 否 | - | 按钮标签宽度 |
| btnColSpan | Number | 否 | 0 | 按钮栅格占据列数 |
| minfoldRows | Number | 否 | 0 | 最小折叠行数,超过此行数会显示展开/收起按钮 |
| size | String | 否 | - | 表单尺寸('large'、'default'、'small') |
| labelPosition | String | 否 | 'left' | 标签位置('left'、'right'、'top') |
| showMessage | Boolean | 否 | true | 是否显示校验错误信息 |
rowLayProps 配置
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| gutter | Number | 否 | 0 | 栅格间隔 |
| justify | String | 否 | 'start' | 水平排列方式 |
| align | String | 否 | 'top' | 垂直对齐方式 |
setOptions 配置
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| setOptions | Boolean | 否 | false | 是否启用表单和表格自定义选项开关(需要与 dp-table 组合使用) |
formItemList 配置项详解
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| label | String | 是 | - | 表单项标签 |
| span | Number | 是 | 6 | 栅格占据列数(1-24) |
| prop | String | 是 | - | 数据绑定字段名 |
| formtype | String | 否 | - | 内置表单类型 |
| render | Function | 否 | - | 自定义渲染函数 |
| formItemOptions | Object | 否 | - | 表单项配置(校验规则等) |
| isHiden | Function | 否 | - | 动态隐藏条件函数 |
| dataOptions | Array | 否 | [] | 数据选项(用于 Select、Radio 等) |
| components | Object | 否 | - | 自定义组件 |
| apiParams | Object | 否 | - | 接口参数(用于异步加载数据) |
| callOptionListFormat | Function | 否 | - | 数据格式化函数 |
| attrs | Object | 否 | - | 表单项额外属性 |
| on | Object | 否 |
内置表单类型 (formtype)
'Input': 输入框'Select': 选择器'datePicker': 日期选择器'timePicker': 时间选择器'Cascader':级联选择器'Radio': 单选框'Checkbox': 复选框'Switch': 开关'Rate': 评分'Upload': 上传
自定义渲染示例
{
label: '履约方式',
span: 6,
prop: 'performanceMode',
render: (formData, row) => {
return h('el-select', {
modelValue: formData.performanceMode,
'onUpdate:modelValue': (value) => {
formData.performanceMode = value
},
clearable: true,
style: 'width: 100%'
}, row.dataOptions.map(item =>
h('el-option', {
key: item.value,
value: item.value,
label: item.label
})
))
}
}异步数据加载
{
label: '商品分类',
span: 6,
prop: 'categoryId',
formtype: 'Select',
apiParams: {
url: '/api/categories',
model: { status: 1 }
},
callOptionListFormat: (data) => {
return data.map(item => ({
value: item.id,
label: item.name
}))
}
}configBtn 配置项详解
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| name | String | 是 | - | 按钮名称 |
| icon | String | 否 | - | 按钮图标名称 |
| key | String | 否 | - | 按钮标识 |
| type | String | 否 | 'default' | 按钮类型 |
| disabled | Boolean/Function | 否 | false | 是否禁用按钮 |
| click | Function | 是 | - | 按钮点击事件处理函数 |
| direction | String | 否 | 'left' | 按钮位置('left' 或 'right') |
| triggerEvent | String | 否 | - | 触发事件类型('query' 或 'rest') |
按钮点击事件参数
click: (model, formInstance, tableInstance) => {
// model: 表单数据对象
// formInstance: 表单实例(包含 validate、resetFields 等方法)
// tableInstance: 表格实例(当与 dp-table 组合使用时)
}表单实例方法
通过 ref 获取表单实例后,可以调用以下方法:
// 表单验证
formRef.value.formInstance.validate((valid, fields) => {
if (valid) {
console.log('验证通过')
} else {
console.log('验证失败', fields)
}
})
// 重置表单
formRef.value.formInstance.resetFields()
// 清空验证
formRef.value.formInstance.clearValidate()高级用法
1. 复杂表单验证
{
label: '客户手机号',
span: 6,
prop: 'customerPhone',
formtype: 'Input',
formItemOptions: {
rules: [
{
required: true,
message: '请输入手机号',
trigger: 'blur'
},
{
validator: (rule, value, callback) => {
if (value && !/^[1][3-9][0-9]{9}$/.test(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
},
trigger: 'blur'
}
]
}
}2. 条件显示表单项
{
label: '客户姓名',
span: 6,
prop: 'customerName',
formtype: 'Input',
isHiden: (formData, item, fromProps) => {
// 根据表单数据动态隐藏表单项
return formData.sourceChannel === '16'
}
}3. 表单与表格组合
当 ly-form 作为 dp-table 的子组件时,表单会自动与表格联动:
<dp-table :options="tableOptions" :columns="tableColumns">
<ly-form
:model="formModel"
:layout-form-props="formLayout"
:form-item-list="formItems"
/>
</dp-table>在这种情况下,表单的查询按钮会自动触发表格的数据请求。
3. useDialog 弹窗组件
useDialog 是一个函数式弹窗组件,提供简单直观的 API 来创建和管理弹窗。它支持拖拽、全屏、自定义按钮、内容渲染等功能,并提供两种模式:单例模式和多实例模式。
基础用法
import { useDialog } from 'ly-eui'
// 创建弹窗实例
const dialog = useDialog()
// 打开弹窗
const openDialog = () => {
dialog({
width: '40%',
title: '提醒',
isDraggable: true,
closeOnClickModal: true,
configBtn: [
{
name: '确定',
icon: 'CircleCheck',
type: 'primary',
click: (renderBodyRefs, lyFormInstance, dialogInstance) => {
// 关闭弹窗
dialogInstance.exposed.closed()
}
},
{
name: '取消',
icon: 'CircleClose',
click: () => {
// 关闭弹窗
dialog.close()
}
}
],
render: (renderBodyRefs, h, { DpTable, LyForm }) => {
return h('div', '这是一个弹窗内容')
},
onSubmit: (closeFn) => {
// 提交回调
console.log('弹窗提交')
closeFn() // 关闭弹窗
},
onClosed: () => {
// 关闭回调
console.log('弹窗已关闭')
}
})
}参数配置
useDialog 函数参数
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| Component | Component | 否 | DpDialog | 自定义弹窗组件 |
| opt | Object | 否 | { onlyInstance: false } | 选项配置 |
DialogComponent 函数参数 (options 对象)
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| visible | Boolean | 否 | true | 是否可见 |
| width | String/Number | 否 | '50%' | 弹窗宽度(百分比或像素值) |
| title | String | 否 | - | 弹窗标题 |
| isDraggable | Boolean | 否 | false | 是否允许拖动弹窗 |
| closeOnClickModal | Boolean | 否 | true | 点击蒙层是否关闭弹窗 |
| hiddenFullBtn | Boolean | 否 | false | 是否隐藏全屏按钮 |
| fullscreen | Boolean | 否 | false | 是否以全屏模式打开弹窗 |
| destroyOnClose | Boolean | 否 | true | 关闭时是否销毁组件 |
| configBtn | Array<Object> | 否 | [] | 弹窗底部操作按钮配置 |
| render | Function | 是 | - | 自定义渲染函数 |
| onSubmit | Function | 否 | - | 弹窗提交事件处理函数 |
| onClosed | Function | 否 | - | 弹窗关闭事件处理函数 |
| appendTo | String/HTMLElement | 否 | 'body' | 弹窗挂载的 DOM 元素 |
configBtn 配置项详解
| 参数名 | 类型 | 是否必填 | 默认值 | 描述 |
|--------|------|----------|--------|------|
| name | String | 是 | - | 按钮显示的文本 |
| icon | String | 否 | - | 按钮图标名称 |
| key | String | 否 | - | 按钮的唯一标识 |
| type | String | 否 | - | 按钮类型('primary'、'success'、'warning'、'danger'、'info') |
| disabled | Boolean/Function | 否 | false | 是否禁用按钮,可以是返回布尔值的函数 |
| click | Function | 是 | - | 按钮点击事件处理函数 |
| direction | String | 否 | - | 按钮方向('left' 或 'right') |
按钮点击事件参数
click: (renderBodyRefs, lyFormInstance, dialogInstance) => {
// renderBodyRefs: 渲染内容的 refs
// lyFormInstance: ly-form 实例(如果渲染内容包含 ly-form)
// dialogInstance: 弹窗实例
}弹窗实例方法
单例模式 (onlyInstance: false,默认)
const dialog = useDialog()
// 关闭弹窗
dialog.close()
// 销毁弹窗
dialog.destroy()多实例模式 (onlyInstance: true)
const dialog = useDialog(null, { onlyInstance: true })
// 关闭当前弹窗实例
dialog.close()高级用法
1. 嵌套弹窗
const openNestedDialog = () => {
const dialog1 = useDialog()
const dialog2 = useDialog()
dialog1({
width: '30%',
title: '嵌套对话框',
configBtn: [
{
name: '打开子弹窗',
click: () => {
dialog2({
width: '40%',
title: '子弹窗',
render: () => h('div', '这是子弹窗')
})
}
}
],
render: () => h('div', '这是父弹窗')
})
}2. 弹窗与表单组合
const openFormDialog = () => {
const dialog = useDialog()
dialog({
width: '65%',
title: '新增',
isDraggable: true,
closeOnClickModal: false,
configBtn: [
{
name: '保存',
type: 'primary',
click: (renderBodyRefs, lyFormInstance) => {
lyFormInstance.validate((valid) => {
if (valid) {
console.log('表单验证通过')
// 保存逻辑
}
})
}
}
],
render: (renderBodyRefs, h, { LyForm }) => {
return h(LyForm, {
ref: 'formRef',
model: formModel,
layoutFormProps: formLayout,
formItemList: formItems
})
}
})
}3. 弹窗与表格组合
const openTableDialog = () => {
const dialog = useDialog()
dialog({
width: '80%',
title: '表格弹窗',
render: (renderBodyRefs, h, { DpTable }) => {
return h(DpTable, {
options: tableOptions,
columns: tableColumns,
pagination: pagination,
dataSource: dataSource
})
}
})
}4. 自定义弹窗组件
// 自定义弹窗组件
const CustomDialog = {
name: 'CustomDialog',
props: ['visible', 'title', 'onSubmit', 'onClosed'],
emits: ['update:visible'],
setup(props, { emit }) {
const handleClose = () => {
emit('update:visible', false)
props.onClosed?.()
}
const handleSubmit = () => {
props.onSubmit?.(handleClose)
}
return () => h('div', {
class: 'custom-dialog'
}, [
h('h3', props.title),
h('div', '自定义内容'),
h('button', { onClick: handleSubmit }, '确定'),
h('button', { onClick: handleClose }, '取消')
])
}
}
// 使用自定义弹窗
const dialog = useDialog(CustomDialog)
dialog({
title: '自定义弹窗',
onSubmit: (closeFn) => {
console.log('自定义提交')
closeFn()
}
})最佳实践
1. 性能优化
- 表格性能: 对于大数据量表格,建议使用虚拟滚动或分页加载
- 表单性能: 避免在
render函数中进行复杂计算,可使用computed缓存 - 弹窗性能: 对于频繁打开的弹窗,考虑使用单例模式 (
onlyInstance: true)
2. 错误处理
- 表格错误处理: 在
httpRequest中添加错误处理逻辑 - 表单验证: 使用完整的 async-validator 规则进行表单验证
- 弹窗异常: 在弹窗的
onSubmit和onClosed回调中处理异常
3. 代码组织
- 配置分离: 将表格列配置、表单项配置等提取到单独的文件中
- 组件复用: 创建可复用的表单组件和弹窗组件
- 状态管理: 对于复杂的状态,考虑使用 Vuex 或 Pinia 进行管理
4. 类型安全
// TypeScript 类型定义示例
interface Order {
id: number
orderNo: string
customerName: string
customerPhone: string
orderStatus: 'WAIT_PAYMENT' | 'WAIT_INSTALL' | 'INSTALLED_WAIT_CONFIRM' | 'WAIT_DELIVER' | 'DEAL_CLOSED' | 'DEAL_FINISH'
}
interface TableColumn {
label: string
key: keyof Order
width?: number | string
render?: (text: any, row: Order) => VNode
}
interface FormItem {
label: string
prop: keyof Order
span: number
formtype: string
formItemOptions?: {
rules?: Rule[]
}
}常见问题解答
Q1: 如何自定义表格列的样式?
A: 可以通过 columns 配置中的 render 函数返回自定义的 JSX 元素,或者使用 scopedSlots 配置作用域插槽。
Q2: 表单如何与表格联动?
A: 当 ly-form 作为 dp-table 的子组件时,表单的查询按钮会自动触发表格的数据请求。也可以通过 ref 手动调用表格的 httpRequestInstance 方法。
Q3: 弹窗中的表单如何验证?
A: 在弹窗的按钮点击事件中,通过第二个参数 lyFormInstance 调用 validate 方法进行表单验证。
Q4: 如何处理异步数据加载?
A: 在 formItemList 中使用 apiParams 配置异步接口,组件会自动处理数据加载和选项渲染。
开发环境
# 启动开发环境
node start.js --imgname=vite-ly-eui --mode=dev1ly-eui 组件库综合说明
1. 架构设计特点
优势:
- 声明式编程范式: 采用配置驱动的开发模式,通过 JSON/对象配置快速构建复杂界面,极大提升开发效率
- JSX/TSX 支持: 充分利用 Vue 3 的 Composition API 和 JSX 能力,提供灵活的渲染控制
- 组件解耦设计: 三大核心组件(LyForm、DpTable、useDialog)职责清晰,可独立使用也可组合使用
- 插件化架构: 支持全局插件配置和按需引入,便于项目集成和定制
劣势:
- 学习曲线较陡: 需要开发者熟悉 JSX/TSX 语法和声明式编程思想
- 过度依赖配置: 复杂的配置对象可能导致代码可读性下降
2. 核心功能评估
LyForm 表单组件:
- ✅ 支持动态表单渲染,内置多种表单类型(Input、Select、DatePicker 等)
- ✅ 提供灵活的布局控制(栅格系统、折叠展开、自定义渲染)
- ✅ 支持异步数据加载和接口自动调用
- ✅ 内置表单验证和条件显示逻辑
DpTable 表格组件:
- ✅ 完整的表格功能(分页、排序、选择、固定列等)
- ✅ 支持分组表头和复杂自定义渲染
- ✅ 内置数据请求和分页自动处理
- ✅ 提供缓存分页选择状态功能
- ✅ 自适应高度计算和响应式布局
useDialog 弹窗组件:
- ✅ 函数式调用,使用简单直观
- ✅ 支持拖拽、全屏、自定义按钮等高级功能
- ✅ 提供两种模式:单例模式和多实例模式
- ✅ 完善的生命周期管理
3. 技术栈兼容性
兼容性良好:
- ✅ 基于 Vue 3 + Element Plus 构建,充分利用现代 Vue 特性
- ✅ TypeScript 支持完善,提供完整的类型定义
- ✅ 支持现代浏览器和开发工具链
依赖约束:
- ⚠️ 强依赖 Element Plus 2.0+,升级 Element Plus 可能带来兼容性风险
- ⚠️ 需要 Vue 3.0+ 环境,
不兼容 Vue 2.x 项目
4. 性能表现
优势:
- ✅ 虚拟滚动支持,大数据量表格性能良好
- ✅ 按需加载机制,减少初始包体积
- ✅ 响应式数据绑定,状态更新高效
待优化:
- ⚠️ 复杂配置对象的深拷贝可能影响性能
- ⚠️ 大量自定义渲染函数可能增加内存占用
5. 开发体验
开发友好:
- ✅ 配置驱动,减少样板代码
- ✅ 完善的 TypeScript 支持
- ✅ 丰富的示例和文档
- ✅ 组件组合灵活,扩展性强
学习成本:
- ⚠️ 需要理解 JSX/TSX 语法
- ⚠️ 配置对象结构较复杂,需要熟悉 API
6. 适用场景
强烈推荐:
- 中后台管理系统快速开发
- 数据密集型应用(表格、表单为主)
- 需要快速原型开发的场景
- 团队技术栈统一为 Vue 3 + Element Plus
谨慎使用:
- 对性能要求极高的应用
- 需要大量自定义 UI 的项目
- 团队不熟悉 JSX/TSX 的情况
7. 总结
ly-eui 是一个功能强大、设计现代的 Vue 3 组件库,特别适合中后台管理系统的快速开发。它通过声明式配置大大简化了复杂界面的开发过程,提供了完整的表格、表单、弹窗解决方案。虽然在学习曲线和性能方面有一定挑战,但其带来的开发效率提升和代码质量改善是显著的。对于采用 Vue 3 + Element Plus 技术栈的团队,ly-eui 是一个值得考虑的选择。
完整CRUD示例
下面是一个完整的增删改查示例,展示了如何使用 ly-eui 的三大核心组件协同工作:
<template>
<div class="crud-demo">
<!-- 表格区域 -->
<dp-table
ref="tableRef"
:options="tableOptions"
:columns="tableColumns"
:pagination="pagination"
:http-request="httpRequest"
>
<!-- 查询表单 -->
<ly-form
ref="searchFormRef"
:model="searchForm"
:layout-form-props="searchLayout"
:form-item-list="searchItems"
/>
</dp-table>
</div>
</template>
<script setup>
import { ref, reactive, h } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useDialog } from 'ly-eui'
// 表格引用
const tableRef = ref()
const searchFormRef = ref()
// 搜索表单数据
const searchForm = reactive({
orderNo: '',
customerName: '',
orderStatus: '',
dateRange: []
})
// 搜索表单布局
const searchLayout = reactive({
inline: true,
configBtn: [
{
name: '查询',
icon: 'Search',
type: 'primary',
triggerEvent: 'query',
click: () => {
tableRef.value.httpRequestInstance()
}
},
{
name: '重置',
icon: 'Refresh',
triggerEvent: 'rest',
click: () => {
searchFormRef.value.formInstance.resetFields()
tableRef.value.httpRequestInstance()
}
}
]
})
// 搜索表单项
const searchItems = reactive([
{
label: '订单编号',
span: 6,
prop: 'orderNo',
formtype: 'Input',
formItemOptions: {
placeholder: '请输入订单编号'
}
},
{
label: '客户姓名',
span: 6,
prop: 'customerName',
formtype: 'Input',
formItemOptions: {
placeholder: '请输入客户姓名'
}
},
{
label: '订单状态',
span: 6,
prop: 'orderStatus',
formtype: 'Select',
formItemOptions: {
placeholder: '请选择订单状态',
clearable: true
},
options: [
{ value: '', label: '全部' },
{ value: 'WAIT_PAYMENT', label: '待付款' },
{ value: 'WAIT_INSTALL', label: '待安装' },
{ value: 'INSTALLED_WAIT_CONFIRM', label: '已安装待确认' },
{ value: 'WAIT_DELIVER', label: '待发货' },
{ value: 'DEAL_CLOSED', label: '交易关闭' },
{ value: 'DEAL_FINISH', label: '交易完成' }
]
},
{
label: '下单时间',
span: 6,
prop: 'dateRange',
formtype: 'datePicker',
formItemOptions: {
type: 'daterange',
rangeSeparator: '至',
startPlaceholder: '开始日期',
endPlaceholder: '结束日期',
valueFormat: 'YYYY-MM-DD'
}
}
])
// 表格配置
const tableOptions = reactive({
stripe: true,
border: true,
fit: true,
showHeader: true,
highlightCurrentRow: true,
emptyText: '暂无数据',
showSummary: false,
selectOnIndeterminate: true,
indent: 16,
lazy: false,
load: undefined,
treeProps: { hasChildren: 'hasChildren', children: 'children' },
defaultExpandAll: false,
defaultExpandedKeys: undefined,
expandRowKeys: undefined,
defaultSort: undefined,
tooltipEffect: 'dark',
showOverflowTooltip: true,
spanMethod: undefined,
selectOnClick: true,
reserveSelection: true,
rowKey: 'id'
})
// 表格列配置
const tableColumns = reactive([
{
type: 'selection',
width: 55,
fixed: 'left'
},
{
label: '订单编号',
key: 'orderNo',
width: 180,
fixed: 'left',
showOverflowTooltip: true
},
{
label: '客户姓名',
key: 'customerName',
width: 120
},
{
label: '客户手机号',
key: 'customerPhone',
width: 130
},
{
label: '订单状态',
key: 'orderStatus',
width: 120,
render: (text, row) => {
const statusMap = {
'WAIT_PAYMENT': { text: '待付款', type: 'warning' },
'WAIT_INSTALL': { text: '待安装', type: 'info' },
'INSTALLED_WAIT_CONFIRM': { text: '已安装待确认', type: 'primary' },
'WAIT_DELIVER': { text: '待发货', type: 'info' },
'DEAL_CLOSED': { text: '交易关闭', type: 'danger' },
'DEAL_FINISH': { text: '交易完成', type: 'success' }
}
const status = statusMap[text] || { text: '未知', type: 'info' }
return h('el-tag', { type: status.type }, status.text)
}
},
{
label: '订单金额',
key: 'orderAmount',
width: 120,
align: 'right',
render: (text) => `¥${Number(text).toFixed(2)}`
},
{
label: '下单时间',
key: 'createTime',
width: 160,
render: (text) => new Date(text).toLocaleString()
},
{
label: '操作',
key: 'action',
width: 200,
fixed: 'right',
render: (text, row) => {
return h('div', { class: 'action-buttons' }, [
h('el-button', {
type: 'primary',
size: 'small',
onClick: () => handleEdit(row)
}, '编辑'),
h('el-button', {
type: 'info',
size: 'small',
onClick: () => handleView(row)
}, '查看'),
h('el-button', {
type: 'danger',
size: 'small',
onClick: () => handleDelete(row)
}, '删除')
])
}
}
])
// 分页配置
const pagination = reactive({
pageSize: 10,
currentPage: 1,
pageSizes: [10, 20, 50, 100],
layout: 'total, sizes, prev, pager, next, jumper',
background: true
})
// 弹窗实例
const dialog = useDialog()
// 表单数据
const formModel = reactive({
id: undefined,
orderNo: '',
customerName: '',
customerPhone: '',
orderStatus: 'WAIT_PAYMENT',
orderAmount: 0,
remark: ''
})
// 表单布局
const formLayout = reactive({
labelWidth: '100px',
configBtn: [
{
name: '保存',
icon: 'Check',
type: 'primary',
click: (model, formInstance, dialogInstance) => {
formInstance.validate((valid) => {
if (valid) {
saveOrder(model).then(() => {
ElMessage.success('保存成功')
dialogInstance.exposed.closed()
tableRef.value.httpRequestInstance()
})
}
})
}
},
{
name: '取消',
icon: 'Close',
click: (model, formInstance, dialogInstance) => {
dialogInstance.exposed.closed()
}
}
]
})
// 表单项
const formItems = reactive([
{
label: '订单编号',
span: 12,
prop: 'orderNo',
formtype: 'Input',
formItemOptions: {
disabled: true,
placeholder: '系统自动生成'
}
},
{
label: '客户姓名',
span: 12,
prop: 'customerName',
formtype: 'Input',
formItemOptions: {
placeholder: '请输入客户姓名'
},
rules: [
{ required: true, message: '请输入客户姓名', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
]
},
{
label: '客户手机号',
span: 12,
prop: 'customerPhone',
formtype: 'Input',
formItemOptions: {
placeholder: '请输入客户手机号'
},
rules: [
{ required: true, message: '请输入客户手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
]
},
{
label: '订单状态',
span: 12,
prop: 'orderStatus',
formtype: 'Select',
formItemOptions: {
placeholder: '请选择订单状态'
},
options: [
{ value: 'WAIT_PAYMENT', label: '待付款' },
{ value: 'WAIT_INSTALL', label: '待安装' },
{ value: 'INSTALLED_WAIT_CONFIRM', label: '已安装待确认' },
{ value: 'WAIT_DELIVER', label: '待发货' },
{ value: 'DEAL_CLOSED', label: '交易关闭' },
{ value: 'DEAL_FINISH', label: '交易完成' }
],
rules: [{ required: true, message: '请选择订单状态', trigger: 'change' }]
},
{
label: '订单金额',
span: 12,
prop: 'orderAmount',
formtype: 'Input',
formItemOptions: {
type: 'number',
placeholder: '请输入订单金额'
},
rules: [
{ required: true, message: '请输入订单金额', trigger: 'blur' },
{ type: 'number', min: 0, message: '订单金额不能小于0', trigger: 'blur' }
]
},
{
label: '备注',
span: 24,
prop: 'remark',
formtype: 'Input',
formItemOptions: {
type: 'textarea',
rows: 3,
placeholder: '请输入备注信息'
}
}
])
// 表格数据请求
const httpRequest = async (params) => {
try {
const queryParams = {
...params,
...searchForm,
startDate: searchForm.dateRange?.[0],
endDate: searchForm.dateRange?.[1]
}
const response = await fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(queryParams)
})
const data = await response.json()
return {
data: data.list,
total: data.total
}
} catch (error) {
ElMessage.error('获取数据失败')
return {
data: [],
total: 0
}
}
}
// 新增订单
const handleAdd = () => {
// 重置表单数据
Object.keys(formModel).forEach(key => {
if (key === 'orderStatus') {
formModel[key] = 'WAIT_PAYMENT'
} else if (key === 'orderAmount') {
formModel[key] = 0
} else {
formModel[key] = undefined
}
})
dialog({
width: '60%',
title: '新增订单',
isDraggable: true,
closeOnClickModal: false,
render: (renderBodyRefs, h, { LyForm }) => {
return h(LyForm, {
ref: 'formRef',
model: formModel,
layoutFormProps: formLayout,
formItemList: formItems
})
}
})
}
// 编辑订单
const handleEdit = (row) => {
// 填充表单数据
Object.keys(formModel).forEach(key => {
formModel[key] = row[key]
})
dialog({
width: '60%',
title: '编辑订单',
isDraggable: true,
closeOnClickModal: false,
render: (renderBodyRefs, h, { LyForm }) => {
return h(LyForm, {
ref: 'formRef',
model: formModel,
layoutFormProps: formLayout,
formItemList: formItems
})
}
})
}
// 查看订单
const handleView = (row) => {
dialog({
width: '60%',
title: '订单详情',
isDraggable: true,
configBtn: [
{
name: '关闭',
icon: 'Close',
click: (renderBodyRefs, dialogInstance) => {
dialogInstance.exposed.closed()
}
}
],
render: (renderBodyRefs, h) => {
return h('div', { class: 'order-detail' }, [
h('el-descriptions', {
title: '订单信息',
column: 2,
border: true
}, () => [
h('el-descriptions-item', { label: '订单编号' }, row.orderNo),
h('el-descriptions-item', { label: '客户姓名' }, row.customerName),
h('el-descriptions-item', { label: '客户手机号' }, row.customerPhone),
h('el-descriptions-item', { label: '订单状态' }, () => {
const statusMap = {
'WAIT_PAYMENT': { text: '待付款', type: 'warning' },
'WAIT_INSTALL': { text: '待安装', type: 'info' },
'INSTALLED_WAIT_CONFIRM': { text: '已安装待确认', type: 'primary' },
'WAIT_DELIVER': { text: '待发货', type: 'info' },
'DEAL_CLOSED': { text: '交易关闭', type: 'danger' },
'DEAL_FINISH': { text: '交易完成', type: 'success' }
}
const status = statusMap[row.orderStatus] || { text: '未知', type: 'info' }
return h('el-tag', { type: status.type }, status.text)
}),
h('el-descriptions-item', { label: '订单金额' }, `¥${Number(row.orderAmount).toFixed(2)}`),
h('el-descriptions-item', { label: '下单时间' }, new Date(row.createTime).toLocaleString()),
h('el-descriptions-item', { label: '备注', span: 2 }, row.remark || '无')
])
])
}
})
}
// 删除订单
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除订单 ${row.orderNo} 吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
return deleteOrder(row.id)
}).then(() => {
ElMessage.success('删除成功')
tableRef.value.httpRequestInstance()
}).catch(() => {
ElMessage.info('已取消删除')
})
}
// 批量删除
const handleBatchDelete = () => {
const selectedRows = tableRef.value.getSelectionRows()
if (selectedRows.length === 0) {
ElMessage.warning('请选择要删除的订单')
return
}
ElMessageBox.confirm(
`确定要删除选中的 ${selectedRows.length} 个订单吗?`,
'批量删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
const ids = selectedRows.map(row => row.id)
return batchDeleteOrders(ids)
}).then(() => {
ElMessage.success('批量删除成功')
tableRef.value.clearSelection()
tableRef.value.httpRequestInstance()
}).catch(() => {
ElMessage.info('已取消批量删除')
})
}
// API 方法(模拟)
const saveOrder = async (data) => {
// 模拟 API 调用
return new Promise(resolve => {
setTimeout(() => {
console.log('保存订单:', data)
resolve({ success: true })
}, 1000)
})
}
const deleteOrder = async (id) => {
// 模拟 API 调用
return new Promise(resolve => {
setTimeout(() => {
console.log('删除订单:', id)
resolve({ success: true })
}, 500)
})
const batchDeleteOrders = async (ids) => {
// 模拟 API 调用
return new Promise(resolve => {
setTimeout(() => {
console.log('批量删除订单:', ids)
resolve({ success: true })
}, 500)
})
}
// 添加新增按钮到表格工具栏
tableOptions.configBtn = [
{
name: '新增',
icon: 'Plus',
type: 'primary',
click: handleAdd
},
{
name: '批量删除',
icon: 'Delete',
type: 'danger',
click: handleBatchDelete
}
]
</script>
<style scoped>
.crud-demo {
padding: 20px;
}
.action-buttons {
display: flex;
gap: 8px;
}
.order-detail {
padding: 20px;
}
</style>这个完整的CRUD示例展示了:
- 表格与表单联动: 搜索表单自动与表格数据请求联动
- 完整的增删改查: 包含新增、编辑、查看、删除、批量删除功能
- 弹窗与表单组合: 使用
useDialog创建表单弹窗 - 数据验证: 表单内置完整的验证规则
- 状态管理: 使用响应式数据管理表单状态
- 用户体验: 包含加载状态、错误处理、确认提示等
总结
ly-eui 组件库通过声明式配置的方式,极大地简化了中后台管理系统的开发过程。三大核心组件各有特色:
- dp-table: 功能强大的表格组件,支持复杂的数据展示和交互
- ly-form: 灵活的表单组件,支持动态渲染和验证
- useDialog: 简洁的弹窗组件,提供函数式调用方式
通过合理的组合使用这些组件,可以快速构建出功能完善、用户体验良好的管理后台系统。组件库的设计遵循现代前端开发的最佳实践,提供了良好的类型支持和扩展能力。
快速开始建议
- 从简单开始: 先掌握基础用法,再逐步学习高级功能
- 参考示例: 多参考官方示例和文档,理解配置结构
- 逐步集成: 先在项目中试用单个组件,再逐步替换现有代码
- 关注性能: 对于大数据量场景,注意使用分页和虚拟滚动
- 保持更新: 关注组件库的更新,及时获取新功能和修复
希望这份文档能够帮助你快速上手 ly-eui 组件库,构建出优秀的中后台管理系统!
相关资源
最后更新时间: 2025年12月1日
文档版本: v1.4.7
组件库版本: 基于最新版本
**更新开发文档和修复Select 组件中 el-option 不显示问题
**更新打包esbuild适配Liux平台
## 许可证
MIT License
## 联系方式
如有问题或建议,请提交 Issue 或 Pull Request。