gykj-vue-crud-pro
v1.0.8
Published
基于 Vue3 + Element Plus 的企业级 CRUD 组件库
Maintainers
Readme
Vue CRUD Pro
一个基于 Vue 3 + Element Plus + VXE Table 的通用 CRUD 组件库。
特性
- 🚀 基于 Vue 3 + TypeScript
- 🎨 集成 Element Plus 组件库
- 📊 使用 VXE Table 提供强大的表格功能
- 🔧 高度可配置的 CRUD 操作
- 🛡️ 灵活的权限系统集成
- 📱 响应式设计
- 🎯 开箱即用的分页、搜索、表单功能
快速开始
1. 安装依赖
# 安装 Vue CRUD Pro
npm install gykj-vue-crud-pro
# 安装必需的依赖
npm install vue@^3.4.0 element-plus@^2.4.0 vxe-table@^4.5.0调试流程
1. 本地开发调试
方法一:使用 npm link 进行本地调试
# 在 npm 包目录中
npm run test:local
# 在测试项目中
npm link gykj-vue-crud-pro方法二:实时构建监听(推荐)
# 在 npm 包目录中启动监听模式,文件变化时自动重新构建
npm run dev:watch
# 在宿主项目中正常运行
npm run dev --force方法三:手动创建软链接(兜底方案)
如果 npm link 遇到问题,可以手动创建软链接:
Windows 系统:
# 进入宿主项目目录
cd "E:\projects for work\gykj-cloud-ui"
# 删除可能存在的旧链接
rmdir /s /q node_modules\gykj-vue-crud-pro 2>nul
# 创建软链接
mklink /J node_modules\gykj-vue-crud-pro "E:\projects for work\npms\vue-crud-pro"Linux/Mac 系统:
# 进入宿主项目目录
cd /path/to/your/project
# 删除可能存在的旧链接
rm -rf node_modules/gykj-vue-crud-pro
# 创建软链接
ln -s /path/to/vue-crud-pro node_modules/gykj-vue-crud-pro在宿主项目的 package.json 中添加占位依赖(避免编辑器报错):
{
"devDependencies": {
"gykj-vue-crud-pro": "0.0.0-local"
}
}开发测试页面
在浏览器中打开 dev-test.html 进行本地调试。
2. 调试流程
- 修改代码 - 编辑
src/目录下的文件 - 重新构建 -
npm run build - 测试验证 - 打开
dev-test.html查看效果 - 发布更新 -
npm version patch && npm publish
3. 常见调试场景
- 组件功能调试: 修改
src/components/Crud/下的组件 - Hook 功能调试: 修改
src/hooks/crud/useCrudPage.ts - 权限系统调试: 修改
src/utils/permission.ts - 样式调试: 修改
src/assets/styles/crud.scss
4. 调试技巧
控制台调试
// 在组件中添加调试信息
console.log('组件数据:', this.data);
console.log('组件配置:', this.config);浏览器开发者工具
- 使用 Vue DevTools 调试组件
- 使用 Network 面板检查资源加载
- 使用 Console 面板查看错误信息
- 在 Sources 面板中设置断点
5. 发布流程
# 1. 构建包
npm run build
# 2. 发布到 npm
npm publish
# 3. 更新版本号(发布成功后)
npm version patch # 补丁版本 1.0.0 -> 1.0.1
npm version minor # 次要版本 1.0.0 -> 1.1.0
npm version major # 主要版本 1.0.0 -> 2.0.0
# 4. 验证发布
npm update gykj-vue-crud-pro发布前检查清单
代码测试
- 运行
npm run build确保构建成功 - 打开
dev-test.html验证功能正常 - 检查控制台是否有错误
- 运行
版本号管理
- 确认当前版本号:
npm version - 根据修改内容选择合适的版本类型:
patch: 修复 bug 或小改动minor: 新增功能,向后兼容major: 重大更新,可能不兼容
- 确认当前版本号:
发布前准备
- 确保已登录 npm:
npm login - 检查包名是否可用:
npm view gykj-vue-crud-pro - 确认
package.json中的信息正确
- 确保已登录 npm:
发布后验证
- 检查 npm 官网是否显示新版本
- 在测试项目中安装验证:
npm install gykj-vue-crud-pro@latest - 确认功能正常后更新版本号
6. 注意事项
- 版本号管理: 每次发布前都要更新版本号
- 测试验证: 发布前务必进行充分测试
- 文档更新: 修改功能后及时更新 README.md
- 向后兼容: 注意保持 API 的向后兼容性
2. 基础使用
<template>
<div>
<CrudPage :config="crudConfig" :api="api" />
</div>
</template>
<script setup>
import { CrudPage } from 'gykj-vue-crud-pro'
import 'gykj-vue-crud-pro/dist/style.css'
const crudConfig = {
search: {
fields: [
{ prop: 'name', label: '名称', component: 'el-input' }
]
},
table: {
rowKey: 'id',
columns: [
{ prop: 'seq', label: '序号', minWidth: 60 },
{ prop: 'name', label: '名称', minWidth: 150 },
{ prop: 'status', label: '状态', minWidth: 100 }
]
},
form: {
fields: [
{ prop: 'name', label: '名称', component: 'el-input', required: true },
{ prop: 'status', label: '状态', component: 'el-select', options: [
{ label: '启用', value: '1' },
{ label: '禁用', value: '0' }
]}
]
},
toolbar: {
actions: {
add: { label: '新增' },
export: { label: '导出' }
}
}
}
const api = {
getList: () => Promise.resolve({
rows: [
{ id: 1, name: '测试数据1', status: '1' },
{ id: 2, name: '测试数据2', status: '0' }
],
total: 2
}),
add: (data) => Promise.resolve({ code: 200, msg: '添加成功' }),
update: (data) => Promise.resolve({ code: 200, msg: '更新成功' }),
delete: (id) => Promise.resolve({ code: 200, msg: '删除成功' })
}
</script>安装
# 安装 Vue CRUD Pro
npm install gykj-vue-crud-pro
# 安装必需的依赖
npm install vue@^3.4.0 element-plus@^2.4.0 vxe-table@^4.5.0使用
1. 全局注册
import { createApp } from 'vue'
import VueCrudPro from 'gykj-vue-crud-pro'
import 'gykj-vue-crud-pro/dist/style.css'
const app = createApp(App)
// 基础使用
app.use(VueCrudPro)
// 配置crud-pro权限系统
app.use(VueCrudPro, {
permissionConfig: {
enabled: true,
validator: (perm: string) => {
if (!perm) return false
return Auth.hasPermi(perm) // 内部已处理 *:*:* 超级权限
},
directiveName: 'hasPermi'
}
})
app.mount('#app')2. 按需导入
<template>
<div>
<CrudPage
:config="crudConfig"
:api="api"
:hooks="hooks"
/>
</div>
</template>
<script setup lang="ts">
import { CrudPage, useCrudPage, type CrudPageConfig } from 'gykj-vue-crud-pro'
// CRUD 配置
const crudConfig: CrudPageConfig = {
pageKey: 'user',
title: '用户管理',
permission: 'system:user',
// 搜索配置
search: {
fields: [
{
type: 'input',
prop: 'username',
label: '用户名',
component: 'el-input',
placeholder: '请输入用户名'
},
{
type: 'select',
prop: 'status',
label: '状态',
component: 'el-select',
placeholder: '请选择状态',
options: [
{ label: '启用', value: '1' },
{ label: '禁用', value: '0' }
]
}
]
},
// 表格配置
table: {
rowKey: 'id',
columns: [
{
prop: 'seq',
label: '序号',
minWidth: 60,
align: 'center'
},
{
prop: 'username',
label: '用户名',
minWidth: 120
},
{
prop: 'email',
label: '邮箱',
minWidth: 200
},
{
prop: 'status',
label: '状态',
minWidth: 100,
align: 'center',
render: (row) => {
return h('el-tag', {
type: row.status === '1' ? 'success' : 'danger'
}, row.status === '1' ? '启用' : '禁用')
}
},
{
prop: 'operation',
label: '操作',
minWidth: 180,
fixed: 'right',
align: 'center',
render: (row, index, scope, crudActions) => {
return h('div', {}, [
h('el-button', {
type: 'primary',
size: 'small',
onClick: () => crudActions?.handleUpdate(row)
}, '编辑'),
h('el-button', {
type: 'danger',
size: 'small',
onClick: () => crudActions?.handleDelete(row)
}, '删除')
])
}
}
]
},
// 表单配置
form: {
fields: [
{
type: 'input',
prop: 'username',
label: '用户名',
component: 'el-input',
required: true,
rules: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
]
},
{
type: 'input',
prop: 'email',
label: '邮箱',
component: 'el-input',
required: true,
rules: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
]
},
{
type: 'select',
prop: 'status',
label: '状态',
component: 'el-select',
required: true,
options: [
{ label: '启用', value: '1' },
{ label: '禁用', value: '0' }
]
}
]
},
// 工具栏配置
toolbar: {
actions: {
add: { label: '新增用户' },
export: { label: '导出数据' },
custom: [
{
action: 'batchDelete',
label: '批量删除',
type: 'danger',
icon: 'Delete'
}
]
}
}
}
// API 接口
const api = {
list: async (params: any) => {
// 实现列表接口
const response = await fetch('/api/users', {
method: 'GET',
params
})
return response.json()
},
add: async (data: any) => {
// 实现新增接口
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(data)
})
return response.json()
},
update: async (data: any) => {
// 实现更新接口
const response = await fetch(`/api/users/${data.id}`, {
method: 'PUT',
body: JSON.stringify(data)
})
return response.json()
},
delete: async (id: any) => {
// 实现删除接口
const response = await fetch(`/api/users/${id}`, {
method: 'DELETE'
})
return response.json()
}
}
// 自定义钩子
const hooks = {
onAdd: async () => {
console.log('新增前的钩子函数')
},
onEdit: async (id: string, row: any) => {
console.log('编辑前的钩子函数', id, row)
}
}
</script>权限系统集成
1. 使用自定义权限验证器
import { setPermissionConfig } from 'gykj-vue-crud-pro'
// 配置权限验证器
setPermissionConfig({
enabled: true,
validator: (requiredPermissions: string[]) => {
// 从你的权限系统获取用户权限
const userPermissions = getUserPermissions()
return requiredPermissions.some(permission =>
userPermissions.includes(permission) || permission === '*:*:*'
)
}
})2. 使用自定义权限指令
<template>
<!-- 使用自定义权限指令 -->
<el-button v-hasPermi="['system:user:add']">新增用户</el-button>
<!-- 或者使用自定义指令名 -->
<el-button v-customPermi="['system:user:edit']">编辑用户</el-button>
</template>
<script setup>
import { createPermissionDirective } from 'gykj-vue-crud-pro'
// 创建自定义权限指令
const customPermi = createPermissionDirective('customPermi')
</script>通用组件 (Common Components)
Vue CRUD Pro 提供了一套完整的通用组件,可以独立使用或组合使用,满足各种业务场景需求。
CommonSearch - 通用搜索组件
提供灵活的搜索表单功能,支持多种字段类型、权限控制、自动搜索等特性。
基础使用
<template>
<CommonSearch
:config="searchConfig"
:visible="showSearch"
:cols="3"
:collapsible="true"
:default-collapsed="false"
@search="onSearch"
@reset="onReset"
@change="onFieldChange"
/>
</template>
<script setup lang="ts">
import { CommonSearch } from 'gykj-vue-crud-pro'
const showSearch = ref(true)
// 选项数据
const statusOptions = [
{ label: '启用', value: '1' },
{ label: '停用', value: '0' }
]
// 搜索配置
const searchConfig = {
labelWidth: '100px',
fields: [
// 输入框
{
type: 'input',
prop: 'keyword',
label: '关键字',
placeholder: '名称/编号',
defaultValue: ''
},
// 下拉选择
{
type: 'select',
prop: 'status',
label: '状态',
options: statusOptions,
clearable: true
},
// 日期范围
{
type: 'daterange',
prop: 'createdAt',
label: '创建时间',
valueFormat: 'YYYY-MM-DD'
},
// 级联选择
{
type: 'cascader',
prop: 'region',
label: '地区',
options: [
{
label: '华东',
value: 'east',
children: [{ label: '杭州', value: 'hz' }]
}
],
attrs: { clearable: true, 'collapse-tags': true }
},
// 自定义组件
{
type: 'custom',
prop: 'tenant',
label: '租户',
component: TenantPicker,
attrs: { clearable: true }
},
// 自动搜索
{
type: 'select',
prop: 'city',
label: '城市',
options: cityOptions,
autoSearch: true, // 值变更后自动触发搜索
changeEvent: (field, val, model) => {
// 联动逻辑
model.keyword = ''
}
},
// 权限控制
{
type: 'input',
prop: 'secret',
label: '内部编码',
permission: 'biz:secret:read'
},
// 动态显示
{
type: 'input',
prop: 'email',
label: '邮箱',
show: (model) => model.status === '1'
}
]
}
// 搜索事件处理
function onSearch(params: Record<string, any>) {
console.log('搜索参数:', params)
// 发起列表请求
}
function onReset() {
// 重置处理
}
function onFieldChange({ prop, value, model }) {
console.log('字段变更:', prop, value, model)
}
</script>字段类型说明
| 类型 | 说明 | 特殊属性 |
|------|------|----------|
| input | 输入框 | 兜底类型,无需特殊配置 |
| select | 下拉选择 | options: 选项数组 [{label, value}] |
| date | 日期选择 | valueFormat: 日期格式 |
| daterange | 日期范围 | 默认值为 [] |
| datetimerange | 日期时间范围 | 默认值为 [] |
| cascader | 级联选择 | options: 树形选项数据 |
| custom | 自定义组件 | component: 组件引用,必须支持 v-model |
Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| config | SearchConfig | - | 搜索配置对象 |
| loading | boolean | false | 加载状态 |
| visible | boolean | true | 是否可见 |
| enterToSearch | boolean | true | 回车是否触发搜索 |
| collapsible | boolean | true | 是否可折叠 |
| defaultCollapsed | boolean | false | 默认是否折叠 |
| cols | number | 3 | 每行显示列数 (1-4) |
| permission | Function | - | 权限校验函数 |
| hidden | string[] | [] | 隐藏字段列表 |
| debounce | number | 300 | 自动搜索防抖间隔 |
Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| search | params: any | 搜索触发,返回表单数据 |
| reset | - | 重置触发 |
| change | {prop, value, model} | 字段值变更 |
CommonTable - 通用表格组件
基于 VXE Table 的通用表格组件,支持自定义列渲染、分页、工具栏等功能。
基础使用
<template>
<CommonTable
:config="tableConfig"
:data="tableData"
:loading="loading"
:pagination="pagination"
:crud-actions="crudActions"
@page-change="handlePageChange"
@action="handleAction"
>
<template #actions>
<el-button type="primary" @click="handleAdd">新增</el-button>
<el-button @click="handleExport">导出</el-button>
</template>
</CommonTable>
</template>
<script setup lang="ts">
import { CommonTable } from 'gykj-vue-crud-pro'
import { h } from 'vue'
const tableConfig = {
rowKey: 'id',
columns: [
// 序号列
{
prop: 'seq',
label: '序号',
minWidth: 60,
align: 'center'
},
// 普通列
{
prop: 'name',
label: '名称',
minWidth: 150,
showOverflow: true
},
// 自定义渲染列
{
prop: 'status',
label: '状态',
minWidth: 100,
align: 'center',
render: (row) => {
return h('el-tag', {
type: row.status === '1' ? 'success' : 'danger'
}, row.status === '1' ? '启用' : '禁用')
}
},
// 操作列
{
prop: 'operation',
label: '操作',
minWidth: 180,
fixed: 'right',
align: 'center',
render: (row, index, scope, crudActions) => {
return h('div', {}, [
h('el-button', {
type: 'primary',
size: 'small',
onClick: () => crudActions?.handleUpdate(row)
}, '编辑'),
h('el-button', {
type: 'danger',
size: 'small',
onClick: () => crudActions?.handleDelete(row)
}, '删除')
])
}
}
]
}
const crudActions = {
handleUpdate: (row) => console.log('编辑:', row),
handleDelete: (row) => console.log('删除:', row),
handleDetail: (row) => console.log('详情:', row),
getList: () => console.log('刷新列表')
}
function handlePageChange(pagination) {
console.log('分页变化:', pagination)
}
function handleAction(action, row) {
console.log('表格操作:', action, row)
}
</script>Props
| 属性 | 类型 | 说明 |
|------|------|------|
| config | TableConfig | 表格配置对象 |
| data | any[] | 表格数据 |
| loading | boolean | 加载状态 |
| pagination | PaginationConfig | 分页配置 |
| showSearch | boolean | 是否显示搜索 |
| crudActions | object | CRUD 操作方法 |
Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| page-change | pagination | 分页变化 |
| action | action, row | 表格操作 |
| update:showSearch | boolean | 搜索显隐变化 |
| refresh | - | 刷新表格 |
| columnChange | columns | 列显隐变化 |
CommonForm - 通用表单组件
提供灵活的表单渲染功能,支持多种表单控件类型和自定义组件。
基础使用
<template>
<CommonForm
:config="formConfig"
:data="formData"
:loading="loading"
:read-only="readOnly"
@submit="handleSubmit"
/>
</template>
<script setup lang="ts">
import { CommonForm } from 'gykj-vue-crud-pro'
const formConfig = {
labelWidth: '120px',
fields: [
// 输入框
{
component: 'el-input',
prop: 'name',
label: '姓名',
required: true,
attrs: { maxlength: 20, clearable: true }
},
// 数字输入
{
component: 'el-input-number',
prop: 'age',
label: '年龄',
attrs: { min: 0, max: 150 }
},
// 下拉选择
{
component: 'el-select',
prop: 'gender',
label: '性别',
options: [
{ label: '男', value: 'male' },
{ label: '女', value: 'female' }
],
placeholder: '请选择性别',
attrs: { filterable: true, clearable: true }
},
// 单选组
{
component: 'el-radio-group',
prop: 'type',
label: '类型',
options: [
{ label: '个人', value: 'personal' },
{ label: '企业', value: 'enterprise' }
]
},
// 多选组
{
component: 'el-checkbox-group',
prop: 'hobbies',
label: '爱好',
options: [
{ label: '阅读', value: 'reading' },
{ label: '运动', value: 'sports' },
{ label: '音乐', value: 'music' }
]
},
// 日期选择
{
component: 'el-date-picker',
prop: 'birthday',
label: '生日',
attrs: { type: 'date', valueFormat: 'YYYY-MM-DD' }
},
// 开关
{
component: 'el-switch',
prop: 'enabled',
label: '启用状态'
},
// 文件上传
{
component: 'el-upload',
prop: 'avatar',
label: '头像',
attrs: {
action: '/api/upload',
'list-type': 'picture-card',
tip: '支持 jpg/png 文件,且不超过 500kb'
}
},
// 自定义组件
{
component: CustomComponent,
prop: 'custom',
label: '自定义字段',
attrs: { customProp: 'value' }
}
]
}
function handleSubmit(data) {
console.log('表单数据:', data)
// 提交处理
}
</script>支持的组件类型
| 组件名 | 说明 | 特殊配置 |
|--------|------|----------|
| el-input | 输入框 | - |
| el-input-number | 数字输入 | - |
| el-select | 下拉选择 | options |
| el-radio-group | 单选组 | options |
| el-checkbox-group | 多选组 | options |
| el-date-picker | 日期选择 | - |
| el-time-picker | 时间选择 | - |
| el-cascader | 级联选择 | - |
| el-switch | 开关 | - |
| el-slider | 滑块 | - |
| el-rate | 评分 | - |
| el-color-picker | 颜色选择 | - |
| el-upload | 文件上传 | - |
| el-transfer | 穿梭框 | - |
| el-autocomplete | 自动完成 | - |
Props
| 属性 | 类型 | 说明 |
|------|------|------|
| visible | boolean | 是否可见 |
| config | FormConfig | 表单配置 |
| data | any | 表单数据 |
| loading | boolean | 加载状态 |
| title | string | 表单标题 |
| readOnly | boolean | 是否只读 |
Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| update:visible | boolean | 可见性变化 |
| submit | data | 表单提交 |
| cancel | - | 取消操作 |
| action | action, data | 表单操作 |
CommonToolbar - 通用工具栏组件
提供表格工具栏功能,包括操作按钮、搜索显隐、刷新、列设置等。
基础使用
<template>
<CommonToolbar
v-model:showSearch="showSearch"
:columns="columnsTree"
@refresh="refresh"
@columnChange="applyColumnVisibility"
>
<template #actions>
<el-button type="primary" @click="openAdd">新增</el-button>
<el-button @click="handleExport">导出</el-button>
<el-button @click="handleBatchDelete">批量删除</el-button>
</template>
<template #rightActions>
<el-button @click="handleMore">更多操作</el-button>
</template>
</CommonToolbar>
</template>
<script setup lang="ts">
import { CommonToolbar } from 'gykj-vue-crud-pro'
const showSearch = ref(true)
// 列树结构(用于列显隐控制)
const columnsTree = [
{ key: 'name', label: '名称', visible: true },
{ key: 'status', label: '状态', visible: true },
{ key: 'operation', label: '操作', visible: true }
]
function refresh() {
console.log('刷新表格')
}
function applyColumnVisibility(columns) {
console.log('应用列显隐:', columns)
}
</script>Props
| 属性 | 类型 | 说明 |
|------|------|------|
| showSearch | boolean | 搜索显隐状态 |
| columns | ColumnNode[] | 列树结构 |
Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| update:showSearch | boolean | 搜索显隐变化 |
| refresh | - | 刷新事件 |
| columnChange | columns | 列显隐变化 |
CommonDrawer - 通用抽屉组件
提供抽屉弹窗功能,支持新增、编辑、详情三种模式。
基础使用
<template>
<CommonDrawer
v-model="drawerVisible"
:mode="drawerMode"
:read-only="drawerMode === 'detail'"
:loading="saving"
:before-close="guardClose"
width="720px"
@submit="handleSubmit"
@cancel="handleCancel"
>
<template #default="{ mode, readOnly }">
<CommonForm
v-if="mode !== 'detail'"
:config="formConfig"
:data="formData"
:read-only="readOnly"
/>
<DetailView v-else :data="currentRow" />
</template>
<template #footer="{ submit, cancel, loading, mode }">
<el-space>
<el-button @click="cancel">取 消</el-button>
<el-button
v-if="mode !== 'detail'"
type="primary"
:loading="loading"
@click="onSave(submit)"
>
保 存
</el-button>
</el-space>
</template>
</CommonDrawer>
</template>
<script setup lang="ts">
import { CommonDrawer } from 'gykj-vue-crud-pro'
const drawerVisible = ref(false)
const drawerMode = ref<'add' | 'edit' | 'detail'>('add')
const saving = ref(false)
function guardClose() {
// 检查是否有未保存的更改
return true
}
async function onSave(submit) {
saving.value = true
try {
// 保存逻辑
await saveData()
submit() // 触发 submit 事件
drawerVisible.value = false
} finally {
saving.value = false
}
}
function handleSubmit() {
console.log('抽屉提交')
}
function handleCancel() {
drawerVisible.value = false
}
</script>Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| modelValue | boolean | - | 显示状态 |
| mode | 'add' | 'edit' | 'detail' | 'add' | 抽屉模式 |
| title | string | - | 自定义标题 |
| titleMap | object | - | 模式标题映射 |
| readOnly | boolean | false | 是否只读 |
| width | string | number | '50%' | 抽屉宽度 |
| loading | boolean | false | 加载状态 |
| beforeClose | Function | - | 关闭前拦截 |
| destroyOnClose | boolean | true | 关闭时销毁 |
| appendToBody | boolean | true | 插入到 body |
| lockScroll | boolean | true | 锁定滚动 |
| closeOnClickModal | boolean | false | 点击遮罩关闭 |
| closeOnPressEscape | boolean | true | 按 ESC 关闭 |
| zIndex | number | - | 层级 |
Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| update:modelValue | boolean | 显示状态变化 |
| open | - | 打开时触发 |
| opened | - | 打开完成时触发 |
| close | - | 关闭时触发 |
| closed | - | 关闭完成时触发 |
| submit | - | 提交事件 |
| cancel | - | 取消事件 |
API 参考
CrudPageConfig
| 属性 | 类型 | 说明 | |------|------|------| | pageKey | string | 页面唯一标识 | | title | string | 页面标题 | | permission | string | 权限标识 | | search | SearchConfig | 搜索配置 | | table | TableConfig | 表格配置 | | form | FormConfig | 表单配置 | | toolbar | ToolbarConfig | 工具栏配置 |
CrudApi
| 方法 | 参数 | 返回值 | 说明 | |------|------|--------|------| | list | params: any | Promise | 获取列表数据 | | add | data: any | Promise | 新增数据 | | update | data: any | Promise | 更新数据 | | delete | id: any | Promise | 删除数据 | | detail | id: any | Promise | 获取详情 | | export | params: any | Promise | 导出数据 |
开发
# 安装依赖
npm install
# 开发模式
npm run dev
# 构建
npm run build
# 预览构建结果
npm run preview
# 实时构建监听(推荐用于开发调试)
npm run dev:watch注意事项
依赖要求
本组件库需要以下依赖:
- Vue: ^3.4.0
- Element Plus: ^2.4.0
- VXE Table: ^4.5.0
Peer Dependencies 说明
本组件库使用 peerDependencies 来避免与宿主项目产生重复依赖冲突:
{
"peerDependencies": {
"vue": "^3.0.0",
"element-plus": "^2.0.0",
"vxe-table": "^4.0.0"
}
}这意味着:
- 这些依赖由宿主项目提供,不会被打包到组件库中
- 避免重复安装相同版本的库,减少包体积
- 确保使用宿主项目的 Vue 实例,避免多实例问题
解决重复依赖问题
如果遇到依赖冲突或重复安装问题,可以使用以下工具:
1. npm-check-updates (推荐)
# 安装
npm install -g npm-check-updates
# 检查依赖更新
ncu
# 更新 package.json 中的依赖版本
ncu -u
# 重新安装依赖
npm install2. npm dedupe
# 删除重复的依赖包
npm dedupe3. yarn dedupe (如果使用 yarn)
# 删除重复的依赖包
yarn dedupe4. 手动检查依赖树
# 查看依赖树
npm ls vue
npm ls element-plus
npm ls vxe-table
# 查看重复的包
npm ls | grep -E "(vue|element-plus|vxe-table)"Element Plus 自动注册
本组件库默认会自动注册 Element Plus 组件,无需手动注册。如果你已经在项目中注册了 Element Plus,可以通过以下方式禁用自动注册:
app.use(VueCrudPro, {
autoRegisterElementPlus: false, // 禁用自动注册
// ... 其他配置
})权限系统集成
本组件库提供了灵活的权限系统,可以与任何外部权限系统集成:
// 配置权限验证器
app.use(VueCrudPro, {
permissionConfig: {
enabled: true,
validator: (requiredPermissions: string[]) => {
const userPermissions = getUserPermissions() // 从你的权限系统获取
return requiredPermissions.some(permission =>
userPermissions.includes(permission)
)
}
}
})样式文件
使用组件库时,需要引入样式文件:
import 'gykj-vue-crud-pro/dist/style.css'国际化 (i18n) 功能
Vue CRUD Pro 提供了完整的国际化支持,支持中文简体、中文繁体和英文三种语言,可以自动检测用户语言环境并提供相应的文本显示。
特性
- 🌍 支持中文简体 (zh-cn)、中文繁体 (zh-tw)、英文 (en) 三种语言
- 🔍 自动检测浏览器语言环境
- 💾 支持本地存储语言偏好
- 🔄 支持运行时动态切换语言
- 📱 响应式语言切换,无需刷新页面
- 🎯 跨 Tab 页面语言同步
语言检测优先级
组件库会按以下优先级自动检测用户语言:
- localStorage:
language或lang键值 - HTML lang 属性:
<html lang="zh-cn"> - 浏览器语言:
navigator.language - 环境变量:
VITE_LANG或LANG - 默认语言: 中文简体 (zh-cn)
基础使用
1. 在组件中使用国际化文本
<template>
<div>
<!-- 使用 pickText 函数进行文本国际化 -->
<el-button type="primary">{{ pickText('搜索', 'Search') }}</el-button>
<el-button>{{ pickText('重置', 'Reset') }}</el-button>
<!-- 支持中文繁体 -->
<span>{{ pickText('新增', 'Add', '新增') }}</span>
<!-- 手动指定语言 -->
<span>{{ pickText('保存', 'Save', '儲存', 'en') }}</span>
</div>
</template>
<script setup lang="ts">
import { pickText } from 'gykj-vue-crud-pro'
// 在组件中使用
const buttonText = pickText('确认', 'Confirm', '確認')
</script>2. 使用 pickByLang 函数处理任意类型数据
<script setup lang="ts">
import { pickByLang } from 'gykj-vue-crud-pro'
// 处理字符串
const title = pickByLang('用户管理', 'User Management', '用戶管理')
// 处理对象
const config = pickByLang(
{ label: '姓名', placeholder: '请输入姓名' },
{ label: 'Name', placeholder: 'Please enter name' },
{ label: '姓名', placeholder: '請輸入姓名' }
)
// 处理数组
const options = pickByLang(
[
{ label: '启用', value: '1' },
{ label: '禁用', value: '0' }
],
[
{ label: 'Enabled', value: '1' },
{ label: 'Disabled', value: '0' }
],
[
{ label: '啟用', value: '1' },
{ label: '禁用', value: '0' }
]
)
</script>3. 检查当前语言环境
<script setup lang="ts">
import { isEnglish, getLocale } from 'gykj-vue-crud-pro'
// 检查是否为英文环境
const isEn = isEnglish()
// 获取当前语言
const currentLang = getLocale() // 'zh-cn' | 'zh-tw' | 'en'
// 根据语言环境执行不同逻辑
if (isEnglish()) {
console.log('当前为英文环境')
} else {
console.log('当前为中文环境')
}
</script>语言切换
1. 编程式切换语言
<script setup lang="ts">
import { setLocale } from 'gykj-vue-crud-pro'
// 切换到英文
setLocale('en', true) // 第二个参数表示是否持久化到 localStorage
// 切换到中文简体
setLocale('zh-cn', true)
// 切换到中文繁体
setLocale('zh-tw', true)
// 不持久化的切换(仅当前会话有效)
setLocale('en', false)
</script>2. 监听语言变化
<script setup lang="ts">
import { onLocaleChange } from 'gykj-vue-crud-pro'
import { onMounted, onUnmounted } from 'vue'
const unsubscribe = onLocaleChange((newLocale) => {
console.log('语言已切换到:', newLocale)
// 执行语言切换后的逻辑
// 例如:重新加载组件、更新标题等
})
// 组件卸载时取消订阅
onUnmounted(() => {
unsubscribe()
})
</script>3. 语言切换组件示例
<template>
<el-dropdown @command="handleLanguageChange">
<span class="language-selector">
{{ currentLanguageLabel }}
<el-icon><ArrowDown /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="zh-cn">中文简体</el-dropdown-item>
<el-dropdown-item command="zh-tw">中文繁體</el-dropdown-item>
<el-dropdown-item command="en">English</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { setLocale, getLocale, pickText } from 'gykj-vue-crud-pro'
import { ArrowDown } from '@element-plus/icons-vue'
const currentLang = ref(getLocale())
const currentLanguageLabel = computed(() => {
const langMap = {
'zh-cn': '中文简体',
'zh-tw': '中文繁體',
'en': 'English'
}
return langMap[currentLang.value] || '中文简体'
})
const handleLanguageChange = (lang: string) => {
setLocale(lang, true) // 持久化到 localStorage
currentLang.value = lang
}
</script>配置语言环境
1. 通过 HTML lang 属性
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>Vue CRUD Pro</title>
</head>
<body>
<div id="app"></div>
</body>
</html>2. 通过环境变量
# .env 文件
VITE_LANG=en
# 或者在启动时设置
LANG=en npm run dev3. 通过 localStorage
// 在应用启动时设置
localStorage.setItem('language', 'en')
// 或者在用户操作时设置
localStorage.setItem('language', 'zh-tw')在 CRUD 配置中使用国际化
1. 表格列配置
<script setup lang="ts">
import { pickText } from 'gykj-vue-crud-pro'
const tableConfig = {
columns: [
{
prop: 'name',
label: pickText('姓名', 'Name', '姓名'),
minWidth: 120
},
{
prop: 'status',
label: pickText('状态', 'Status', '狀態'),
minWidth: 100,
render: (row) => {
const statusText = pickText(
row.status === '1' ? '启用' : '禁用',
row.status === '1' ? 'Enabled' : 'Disabled',
row.status === '1' ? '啟用' : '禁用'
)
return h('el-tag', {
type: row.status === '1' ? 'success' : 'danger'
}, statusText)
}
}
]
}
</script>2. 搜索表单配置
<script setup lang="ts">
import { pickText } from 'gykj-vue-crud-pro'
const searchConfig = {
fields: [
{
prop: 'keyword',
label: pickText('关键字', 'Keyword', '關鍵字'),
placeholder: pickText('请输入关键字', 'Please enter keyword', '請輸入關鍵字'),
component: 'el-input'
},
{
prop: 'status',
label: pickText('状态', 'Status', '狀態'),
placeholder: pickText('请选择状态', 'Please select status', '請選擇狀態'),
component: 'el-select',
options: [
{
label: pickText('启用', 'Enabled', '啟用'),
value: '1'
},
{
label: pickText('禁用', 'Disabled', '禁用'),
value: '0'
}
]
}
]
}
</script>3. 表单配置
<script setup lang="ts">
import { pickText } from 'gykj-vue-crud-pro'
const formConfig = {
fields: [
{
prop: 'username',
label: pickText('用户名', 'Username', '用戶名'),
component: 'el-input',
required: true,
rules: [
{
required: true,
message: pickText('请输入用户名', 'Please enter username', '請輸入用戶名'),
trigger: 'blur'
}
]
},
{
prop: 'email',
label: pickText('邮箱', 'Email', '郵箱'),
component: 'el-input',
rules: [
{
type: 'email',
message: pickText('请输入正确的邮箱格式', 'Please enter valid email format', '請輸入正確的郵箱格式'),
trigger: 'blur'
}
]
}
]
}
</script>工具栏按钮国际化
<script setup lang="ts">
import { pickText } from 'gykj-vue-crud-pro'
const toolbarConfig = {
actions: {
add: {
label: pickText('新增', 'Add', '新增')
},
export: {
label: pickText('导出', 'Export', '導出')
},
custom: [
{
action: 'batchDelete',
label: pickText('批量删除', 'Batch Delete', '批量刪除'),
type: 'danger'
}
]
}
}
</script>错误消息国际化
<script setup lang="ts">
import { pickText } from 'gykj-vue-crud-pro'
import { ElMessage } from 'element-plus'
const showSuccessMessage = (message: string) => {
ElMessage.success(pickText(message, message, message))
}
const showErrorMessage = (message: string) => {
ElMessage.error(pickText(message, message, message))
}
// 使用示例
const handleSuccess = () => {
showSuccessMessage('操作成功')
}
const handleError = () => {
showErrorMessage('操作失败')
}
</script>自定义语言包
如果需要支持更多语言或自定义翻译,可以创建自己的语言包:
// i18n/custom.ts
export const customTranslations = {
'zh-cn': {
'custom.key': '自定义键值',
'custom.button': '自定义按钮'
},
'en': {
'custom.key': 'Custom Key',
'custom.button': 'Custom Button'
},
'zh-tw': {
'custom.key': '自定義鍵值',
'custom.button': '自定義按鈕'
}
}
// 自定义翻译函数
export function customPickText(key: string, zhCn: string, en: string, zhTw?: string) {
const lang = getLocale()
const translations = customTranslations[lang]
if (translations && translations[key]) {
return translations[key]
}
// 回退到 pickText
return pickText(zhCn, en, zhTw)
}最佳实践
1. 统一管理翻译文本
// i18n/translations.ts
export const translations = {
// 通用按钮
buttons: {
add: { 'zh-cn': '新增', 'en': 'Add', 'zh-tw': '新增' },
edit: { 'zh-cn': '编辑', 'en': 'Edit', 'zh-tw': '編輯' },
delete: { 'zh-cn': '删除', 'en': 'Delete', 'zh-tw': '刪除' },
save: { 'zh-cn': '保存', 'en': 'Save', 'zh-tw': '儲存' },
cancel: { 'zh-cn': '取消', 'en': 'Cancel', 'zh-tw': '取消' },
search: { 'zh-cn': '搜索', 'en': 'Search', 'zh-tw': '搜索' },
reset: { 'zh-cn': '重置', 'en': 'Reset', 'zh-tw': '重置' }
},
// 表单字段
fields: {
name: { 'zh-cn': '姓名', 'en': 'Name', 'zh-tw': '姓名' },
email: { 'zh-cn': '邮箱', 'en': 'Email', 'zh-tw': '郵箱' },
status: { 'zh-cn': '状态', 'en': 'Status', 'zh-tw': '狀態' }
},
// 消息
messages: {
saveSuccess: { 'zh-cn': '保存成功', 'en': 'Saved successfully', 'zh-tw': '儲存成功' },
deleteSuccess: { 'zh-cn': '删除成功', 'en': 'Deleted successfully', 'zh-tw': '刪除成功' },
confirmDelete: { 'zh-cn': '确认删除?', 'en': 'Confirm deletion?', 'zh-tw': '確認刪除?' }
}
}
// 便捷函数
export function t(key: string) {
const lang = getLocale()
const keys = key.split('.')
let value = translations
for (const k of keys) {
value = value[k]
if (!value) break
}
return value?.[lang] || key
}2. 在组件中使用
<template>
<div>
<el-button type="primary">{{ t('buttons.add') }}</el-button>
<el-button>{{ t('buttons.edit') }}</el-button>
<el-button type="danger">{{ t('buttons.delete') }}</el-button>
</div>
</template>
<script setup lang="ts">
import { t } from '@/i18n/translations'
</script>API 参考
核心函数
| 函数 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| pickText | zhCn: string, en: string, zhTw?: string, lang?: string | string | 根据语言返回对应文本 |
| pickByLang | zhCn: T, en: T, zhTw?: T, lang?: string | T | 根据语言返回对应值(支持任意类型) |
| getLocale | - | 'zh-cn' \| 'zh-tw' \| 'en' | 获取当前语言 |
| setLocale | locale: string, persist?: boolean | void | 设置语言 |
| isEnglish | lang?: string | boolean | 检查是否为英文环境 |
| normalizeLocale | input?: string \| null | 'zh-cn' \| 'zh-tw' \| 'en' | 规范化语言标识 |
语言标识
| 标识 | 说明 | 示例 |
|------|------|------|
| zh-cn | 中文简体 | 中国大陆、新加坡 |
| zh-tw | 中文繁体 | 台湾、香港、澳门 |
| en | 英文 | 美国、英国、澳大利亚等 |
事件监听
| 函数 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| onLocaleChange | callback: (locale: string) => void | () => void | 监听语言变化,返回取消订阅函数 |
许可证
MIT
