npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

gykj-vue-crud-pro

v1.0.8

Published

基于 Vue3 + Element Plus 的企业级 CRUD 组件库

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. 调试流程

  1. 修改代码 - 编辑 src/ 目录下的文件
  2. 重新构建 - npm run build
  3. 测试验证 - 打开 dev-test.html 查看效果
  4. 发布更新 - 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

发布前检查清单

  1. 代码测试

    • 运行 npm run build 确保构建成功
    • 打开 dev-test.html 验证功能正常
    • 检查控制台是否有错误
  2. 版本号管理

    • 确认当前版本号:npm version
    • 根据修改内容选择合适的版本类型:
      • patch: 修复 bug 或小改动
      • minor: 新增功能,向后兼容
      • major: 重大更新,可能不兼容
  3. 发布前准备

    • 确保已登录 npm:npm login
    • 检查包名是否可用:npm view gykj-vue-crud-pro
    • 确认 package.json 中的信息正确
  4. 发布后验证

    • 检查 npm 官网是否显示新版本
    • 在测试项目中安装验证:npm install gykj-vue-crud-pro@latest
    • 确认功能正常后更新版本号

6. 注意事项

  1. 版本号管理: 每次发布前都要更新版本号
  2. 测试验证: 发布前务必进行充分测试
  3. 文档更新: 修改功能后及时更新 README.md
  4. 向后兼容: 注意保持 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 install

2. npm dedupe

# 删除重复的依赖包
npm dedupe

3. yarn dedupe (如果使用 yarn)

# 删除重复的依赖包
yarn dedupe

4. 手动检查依赖树

# 查看依赖树
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 页面语言同步

语言检测优先级

组件库会按以下优先级自动检测用户语言:

  1. localStorage: languagelang 键值
  2. HTML lang 属性: <html lang="zh-cn">
  3. 浏览器语言: navigator.language
  4. 环境变量: VITE_LANGLANG
  5. 默认语言: 中文简体 (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 dev

3. 通过 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