中后台开发有个反复出现的尴尬:一个搜索表单、一个带勾选的表格、一个日期范围筛选——逻辑不复杂,但每次都要写一堆模板代码,还要处理 Element Plus 的默认行为和业务需求的缝隙。EaseUI 的做法很直接:每个组件就是一个 .vue 单文件,没有外部样式依赖、没有工具函数依赖,直接复制到你的项目目录就能用。最近它又加了图片裁剪和签名板两个组件,覆盖的场景更实了。
「复制即用」到底意味着什么
大多数组件库的安装路径是 npm install → 配置插件 → 全局注册 → 按文档写模板。EaseUI 走了另一条路:
- 每个组件是独立的
.vue文件,内部样式 scoped、逻辑自包含 - 唯一外部依赖是 Element Plus,不再引入额外的 CSS 框架或工具库
- 使用方式是把文件复制到你的
src/components/目录,然后正常 import
这意味着几件事:
- 你可以随时改组件源码,它就是你的代码,不是第三方包的黑盒
- 不存在版本冲突——没有
package.json里的额外依赖条目 - 项目体积只增加你实际用到的那个
.vue文件的大小
对中后台项目来说,这种「可控的轻量」比「功能全面但不可改」更实用。
解决的具体痛点
EaseUI 的组件不是泛化的 UI 基础件,而是针对中后台高频场景的业务封装:
| 痛点 | 对应组件 | 做了什么 |
|---|---|---|
| 表格多选/单选逻辑混乱 | 表格选择组件 | 把勾选行、选中数据绑定、翻页保持选中等逻辑封装进组件 |
| 搜索表单代码膨胀 | 搜索表单组件 | 配置化生成搜索栏,一行配置代替几十行模板 |
| 日期范围绑定繁琐 | 日期范围组件 | 一个字段绑定起止日期,不用手动拆分合并 |
| 图片裁剪交互 | xly-image-cropper | 上传+裁剪一步完成,输出裁剪后的文件 |
| 手写签名采集 | xly-signature | Canvas 签名板,支持清除和导出图片 |
这些组件不是「又一个 UI 库的重新包装」,而是把中后台开发里每次都要写、每次都写得差不多、每次都略有不同的那段逻辑,收拢成一个可复制的单文件。
实践:把签名板组件接入表单
下面演示如何把 xly-signature 签名板组件复制到项目并在业务表单中使用。假设你的项目已经安装了 Element Plus。
第一步:复制组件文件
从 EaseUI 仓库找到 xly-signature.vue,复制到你的项目:
# 假设你把 EaseUI 组件统一放在 src/components/easeui/ 下
mkdir -p src/components/easeui
cp path/to/easeui/xly-signature.vue src/components/easeui/
第二步:在表单页面中使用
<template>
<el-form :model="form" label-width="100px" @submit.prevent="handleSubmit">
<el-form-item label="申请人">
<el-input v-model="form.applicant" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="手写签名">
<XlySignature
ref="signatureRef"
:width="400"
:height="200"
:line-width="3"
:line-color="#333"
background="#fafafa"
/>
<div style="margin-top: 8px;">
<el-button size="small" @click="clearSignature">清除签名</el-button>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSubmit">提交申请</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from 'vue'
import XlySignature from '@/components/easeui/xly-signature.vue'
const signatureRef = ref(null)
const form = reactive({
applicant: '',
signatureImage: ''
})
function clearSignature() {
signatureRef.value?.clear()
}
async function handleSubmit() {
// 从签名板导出 base64 图片
const imageData = signatureRef.value?.toBase64('image/png')
if (!imageData) {
// 组件内部可能返回空字符串表示未签名
console.warn('签名区域为空,请先签名')
return
}
form.signatureImage = imageData
// 实际项目中把 base64 转成 Blob 上传,这里演示提交数据结构
console.log('提交数据:', {
applicant: form.applicant,
signatureImage: form.signatureImage.substring(0, 50) + '...(truncated)'
})
}
</script>
几个注意点:
toBase64()和clear()是签名板组件暴露的方法,通过ref调用。具体方法名以你复制的那个.vue文件里的defineExpose为准——因为是源码直接复制,你可以随时打开文件确认或修改。- 如果业务要求签名必须转成文件上传而非 base64,可以在组件里加一个
toBlob()方法,改动几行源码即可,不需要去提 PR 等作者发版。
图片裁剪组件的接入思路
xly-image-cropper 的使用模式类似——复制文件、import、在模板中放置。典型流程是:
- 用户选择图片 → 组件加载图片到裁剪区域
- 用户拖拽裁剪框 → 组件实时输出裁剪坐标
- 用户确认 → 组件生成裁剪后的 Blob/File
<template>
<XlyImageCropper
ref="cropperRef"
:src="imageSrc"
:aspect-ratio="1"
output-type="blob"
@confirm="onCropConfirm"
/>
</template>
<script setup>
import { ref } from 'vue'
import XlyImageCropper from '@/components/easeui/xly-image-cropper.vue'
const cropperRef = ref(null)
const imageSrc = ref('')
// 选择文件后设置 imageSrc
function onFileChange(file) {
imageSrc.value = URL.createObjectURL(file.raw)
}
function onCropConfirm(croppedBlob) {
// croppedBlob 是裁剪后的 Blob 对象,可直接上传
const formData = new FormData()
formData.append('avatar', croppedBlob, 'avatar.png')
// fetch 或 axios 上传 formData...
}
</script>
采用之前想清楚的事
EaseUI 的「复制即用」不是银弹,有几个边界需要正视:
- 不享受 npm 包的自动更新:组件复制进来就是你的代码了,上游修复 bug 或加功能,你不会自动收到。需要你手动同步。对于中后台项目来说,这通常是好事——你掌控变更节奏,不会被意外升级打断。但如果你有十几个项目共用同一套组件,就要自己建立同步机制。
- 只适合 Vue 3 + Element Plus 生态:如果你的项目用 Ant Design Vue 或 Naive UI,这些组件的模板和逻辑依赖 Element Plus 的 API,不能直接用。你可以复制后手动替换 UI 层,但这就失去了「复制即用」的意义。
- 组件数量有限:EaseUI 目前覆盖的是高频中后台场景,不是全功能 UI 库。你的项目仍然需要 Element Plus 提供基础组件(按钮、弹窗、标签页等),EaseUI 只补上业务封装那一层。
适合采用的情况:
- 新起一个中后台项目,不想为搜索表单和表格选择再写一遍胶水代码
- 现有项目里有大量重复的表单/表格模板,想逐步收拢
- 团队对 Element Plus 已经熟悉,不需要额外的学习成本
不太适合的情况:
- 项目不用 Element Plus
- 需要超过 50 个组件的全功能库
- 团队习惯通过 npm 版本管理来控制依赖一致性,不想手动同步文件
一个实用的做法是:先挑一两个你最痛的场景(比如搜索表单或签名板)试水,复制进来用一周,看看是否真的减少了重复代码。如果确实有效,再逐步引入更多组件。毕竟复制进来容易,删掉也容易——这正是 EaseUI 的设计初衷。