mwl-components
v0.2.1
Published
## 一、技术栈概述
Readme
mwl-components 基于vue 3.0 + ts + vite + element-plus 组件库
一、技术栈概述
核心技术
| 技术 | 版本 | 说明 | | ------------ | ------- | -------------------- | | Vue | ^3.5.17 | 渐进式JavaScript框架 | | TypeScript | ~5.8.0 | 类型系统 | | Element Plus | ^2.10.4 | UI组件库 | | Vite | ^7.0.0 | 构建工具 |
开发工具
| 技术 | 版本 | 说明 | | -------- | ------- | ------------------ | | ESLint | ^9.29.0 | 代码检查 | | Prettier | 3.5.3 | 代码格式化 | | vue-tsc | ^2.2.10 | TypeScript类型检查 | | tsup | ^8.5.0 | TypeScript打包工具 |
二、组件库安装与使用
安装
npm install mwl-components
# 或
pnpm add mwl-components全局注册
import { createApp } from 'vue'
import App from './App.vue'
import mwlComponents from 'mwl-components'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
app.use(mwlComponents)
app.mount('#app')按需引入
import {
EasyButton,
ContentWarp,
Dialog,
EasyForm,
EasyTable,
MenuTree,
Pagination,
EasyContextMenu,
ContextMenu,
} from 'mwl-components'⚠️注意: 如果你不想全局注册 element-plus,你需要单独注册组件库使用的 element-plus 组件
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/dark/css-vars.css' // 暗黑模式
import mwlComponents from 'mwl-components'
import 'mwl-components/dist/mwl-components.css'
import {
ElForm,
ElFormItem,
ElInput,
ElInputNumber,
ElSelect,
ElSelectV2,
ElTreeSelect,
ElOption,
ElDatePicker,
ElSwitch,
ElCascader,
ElCheckbox,
ElCheckboxGroup,
ElRadio,
ElRadioGroup,
ElSlider,
ElUpload,
ElTable,
ElTableColumn,
ElButton,
ElMenu,
ElSubMenu,
ElMenuItem,
ElPagination,
ElDialog,
ElScrollbar,
ElCard,
ElTooltip,
ElIcon,
} from 'element-plus'
const components = [
ElForm,
ElFormItem,
ElInput,
ElInputNumber,
ElSelect,
ElSelectV2,
ElTreeSelect,
ElOption,
ElDatePicker,
ElSwitch,
ElCascader,
ElCheckbox,
ElCheckboxGroup,
ElRadio,
ElRadioGroup,
ElSlider,
ElUpload,
ElTable,
ElTableColumn,
ElButton,
ElMenu,
ElSubMenu,
ElMenuItem,
ElPagination,
ElDialog,
ElScrollbar,
ElCard,
ElTooltip,
ElIcon,
]
export const install = (app: any) => {
components.forEach((component) => {
app.component(component.name, component)
})
app.use(mwlComponents)
}三、组件详解
1. EasyButton 按钮组件
基于 Element Plus 的 el-button 封装的带加载状态的按钮组件。
源码位置: src/components/EasyButton/index.vue
1.1 基础用法
<template>
<EasyButton type="primary" @click="handleClick"> 点击按钮 </EasyButton>
</template>
<script setup>
import { EasyButton } from 'mwl-components'
const handleClick = async () => {
console.log('按钮点击')
}
</script>1.2 Props
支持 Element Plus el-button 的所有属性,以下是常用属性:
| 属性名 | 类型 | 默认值 | 说明 | | ----------- | ------- | ------ | ----------------------------------------------------------- | | type | String | - | 按钮类型 primary / success / warning / danger / info / text | | size | String | - | 按钮尺寸 large / default / small | | plain | Boolean | false | 是否朴素按钮 | | round | Boolean | false | 是否圆角按钮 | | circle | Boolean | false | 是否圆形按钮 | | loading | Boolean | false | 是否加载中 | | disabled | Boolean | false | 是否禁用 | | autofocus | Boolean | false | 是否自动聚焦 | | native-type | String | button | 原生 type 属性 |
1.3 Slots
| 插槽名 | 说明 | | ------- | -------- | | default | 按钮内容 |
1.4 Events
| 事件名 | 参数 | 说明 | | ------ | --------------- | -------------- | | click | (e: MouseEvent) | 点击按钮时触发 |
1.5 特性
- 自动处理 loading 状态:点击按钮后自动显示 loading,等待异步操作完成后关闭
- 支持所有 el-button 属性(通过 v-bind="$attrs" 透传)
2. ContentWarp 内容容器组件
一个基于 ElCard 的内容包装容器,提供统一的卡片样式和布局结构。
源码位置: src/components/ContentWarp/index.vue
2.1 基础用法
<template>
<ContentWarp title="标题" message="副标题">
<div>内容区域</div>
<template #footer>
<div>底部区域</div>
</template>
</ContentWarp>
</template>
<script setup>
import { ContentWarp } from 'mwl-components'
</script>2.2 Props
| 属性名 | 类型 | 默认值 | 说明 | | ------- | ------ | ------ | ---------------- | | title | String | - | 卡片标题 | | message | String | - | 标题旁的提示信息 |
2.3 Slots
| 插槽名 | 说明 | | ------- | -------------------------- | | default | 默认内容区域 | | header | 自定义头部(覆盖默认标题) | | footer | 底部区域 |
2.4 特性
- 自动根据父元素 class 是否包含
content-wrap来计算表格最大高度 - 包裹 el-form 时自动调整内边距
3. Dialog 对话框组件
基于 Element Plus 的 el-dialog 封装的增强型对话框组件。
源码位置: src/components/Dialog/index.vue
3.1 基础用法
<template>
<Dialog v-model="dialogVisible" title="对话框标题" width="50%" :fullscreen="false" :scroll="true">
<div>对话框内容</div>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</template>
</Dialog>
</template>
<script setup>
import { ref } from 'vue'
import { Dialog } from 'mwl-components'
const dialogVisible = ref(false)
const handleConfirm = () => {
dialogVisible.value = false
}
</script>3.2 Props
| 属性名 | 类型 | 默认值 | 说明 | | -------------------- | --------------- | ------- | ---------------- | | modelValue / v-model | Boolean | false | 是否显示对话框 | | title | String | '' | 对话框标题 | | fullscreen | Boolean | false | 是否全屏显示 | | scroll | Boolean | false | 是否开启内容滚动 | | width | String | '61.8%' | 对话框宽度 | | maxHeight | String / Number | '' | 最大高度 |
支持 Element Plus el-dialog 的所有属性,通过 v-bind="$attrs" 透传:
| 属性名 | 类型 | 默认值 | 说明 | | --------------------- | ------- | ------ | ------------------ | | close-on-click-modal | Boolean | false | 点击遮罩是否关闭 | | close-on-press-escape | Boolean | true | 按 ESC 是否关闭 | | show-close | Boolean | true | 是否显示关闭按钮 | | destroy-on-close | Boolean | true | 关闭时是否销毁内容 | | draggable | Boolean | true | 是否可拖拽 | | center | Boolean | false | 是否居中对齐 | | align-center | Boolean | false | 是否垂直居中 | | modal-class | String | - | 遮罩类名 |
3.3 Slots
| 插槽名 | 说明 | | ------- | -------------- | | default | 对话框内容 | | title | 自定义标题区域 | | footer | 底部操作区 |
3.4 特性
- 支持全屏切换(点击标题栏右侧图标)
- 可配置内容滚动(scroll 为 true 时启用 ElScrollbar)
- 自动计算全屏模式下的最大高度
- 销毁时自动关闭(destroy-on-close)
- 默认点击遮罩不关闭、不可拖拽、按 ESC 可关闭
4. EasyForm 表单组件
强大的动态表单生成组件,基于 Element Plus 的表单项封装。
源码位置: src/components/EasyForm/index.vue
4.1 基础用法
<template>
<EasyForm v-model="formData" :form-items="formItems" :inline="true" label-width="120px">
<template #append>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</template>
</EasyForm>
</template>
<script setup>
import { ref } from 'vue'
import { EasyForm, type FormItem } from 'mwl-components'
const formData = ref({})
const formItems = ref<FormItem[]>([
{
label: '用户名',
type: 'text',
prop: 'username',
placeholder: '请输入用户名'
},
{
label: '状态',
type: 'select',
prop: 'status',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
}
])
</script>4.2 Props
| 属性名 | 类型 | 默认值 | 说明 | | -------------------- | --------------- | ------- | -------------- | | v-model / modelValue | Object | {} | 表单数据对象 | | formItems | FormItem[] | [] | 表单项配置数组 | | inline | Boolean | true | 是否行内表单 | | labelWidth | String / Number | '120px' | 表单标签宽度 | | itemWidth | String | '200px' | 表单项宽度 |
支持 Element Plus el-form 的所有属性,通过 v-bind="$attrs" 透传。
4.3 FormItem 完整配置
interface FormItem {
// 基础属性
type: string // 表单类型(见下表)
prop: string // 字段名(必填)
label: string | (() => any) // 标签文本
hidden?: boolean // 是否隐藏
// 校验规则
rule?: Rule[] // 校验规则数组
required?: boolean // 是否必填(自动生成校验规则)
// 禁用与只读
disabled?: boolean // 是否禁用
readonly?: boolean // 是否只读
// 占位符与提示
placeholder?: string // 占位符(自动生成)
tooltip?: string // 标签旁边的提示文字
// 样式相关
width?: string // 控件宽度
labelWidth?: string // 单独设置标签宽度
// 清空与搜索
clearable?: boolean // 是否可清空
filterable?: boolean // 是否可搜索(select/cascader)
// 选项配置(select/checkbox/radio/tree-select)
options?: Options // 选项数组 [{ label, value }]
data?: Array<any> // 数据数组(备用)
props?: { label: string; value: string } // 选项字段映射
multiple?: boolean // 是否多选
// 日期相关
valueFormat?: string // 时间值格式化(如 'YYYY-MM-DD HH:mm:ss')
format?: string // 时间显示格式化
disabledDate?: (time: Date) => boolean // 禁用日期函数
shortcuts?: Array<{ text: string; value: Date | (() => Date) }> // 快捷选项
// 级联选择
showAllLevels?: boolean // 是否显示完整路径
checkStrictly?: boolean // 是否严格遵循父子不关联
// 输入框相关
maxlength?: number // 最大输入长度
min?: number // 最小值(number 类型)
max?: number // 最大值(number 类型)
precision?: number // 数值精度
step?: number // 步进值
stepStrictly?: boolean // 是否只能输入步进倍数
// 下拉菜单
placement?: Placement // 下拉菜单展开方向
popperClass?: String // 下拉菜单类名
teleported?: boolean // 是否将下弹框放置于 body
allowCreate?: boolean // 是否允许创建新选项(select)
collapseTags?: boolean // 多选时是否折叠标签
collapseTagsTooltip?: boolean // 是否hover显示所有标签
// 树形选择
checkOnClickNode?: boolean // 点击节点时是否选中
// 上传
fileLimit?: number // 上传文件数量限制
// 插槽
slots?: Slots // 组件内部插槽配置
[key: string]: any // 其他属性透传到底层组件
}4.4 支持的表单项类型 (type)
| type 值 | 对应组件 | 默认属性 | 说明 | | ------------- | ----------------- | -------------------------------- | ---------------- | | text | el-input | clearable: true | 单行文本输入 | | textarea | el-input | type: textarea | 多行文本输入 | | number | el-input-number | controlsPosition: right | 数字输入 | | switch | el-switch | - | 开关 | | slider | el-slider | - | 滑块 | | checkbox | el-checkbox-group | - | 多选框组 | | radio | el-radio-group | - | 单选框组 | | select | el-select | - | 下拉选择 | | select-v2 | el-select-v2 | - | 虚拟化下拉选择 | | tree-select | el-tree-select | checkStrictly: true | 树形选择 | | date | el-date-picker | valueFormat: YYYY-MM-DD | 日期选择 | | dates | el-date-picker | valueFormat: YYYY-MM-DD | 多日期选择 | | datetime | el-date-picker | valueFormat: YYYY-MM-DD HH:mm:ss | 日期时间选择 | | week | el-date-picker | valueFormat: YYYY 第WW周 | 周选择 | | month | el-date-picker | valueFormat: YYYY-MM | 月份选择 | | year | el-date-picker | valueFormat: YYYY | 年份选择 | | daterange | el-date-picker | rangeSeparator: 至 | 日期范围选择 | | datetimerange | el-date-picker | rangeSeparator: 至 | 日期时间范围选择 | | monthrange | el-date-picker | rangeSeparator: 至 | 月份范围选择 | | yearrange | el-date-picker | rangeSeparator: 至 | 年份范围选择 | | cascader | el-cascader | clearable: true | 级联选择 | | upload | el-upload | - | 文件上传 | | custom | el-form-item | - | 自定义组件 |
4.5 预设属性 (componentsMap)
组件库为每种类型预设了常用属性,这些属性可被 formItems 中的配置覆盖:
// 通用预设
const commonAttrs = {
clearable: true,
}
// text / textarea
{
type: 'text' | 'textarea',
component: 'el-input',
attrs: { clearable: true }
}
// number
{
type: 'number',
component: 'el-input-number',
attrs: { controlsPosition: 'right' }
}
// switch
{
type: 'switch',
component: 'el-switch'
}
// checkbox
{
type: 'checkbox',
component: 'el-checkbox-group',
slots: (item) => ({
default: () => item.options?.map(option => h(elCheckbox, { value: option.value }, () => option.label))
})
}
// radio
{
type: 'radio',
component: 'el-radio-group',
slots: (item) => ({
default: () => item.options?.map(option => h(elRadio, { value: option.value }, () => option.label))
})
}
// select
{
type: 'select',
component: 'el-select',
slots: presetSelectSlot // 自动渲染 el-option
}
// tree-select
{
type: 'tree-select',
component: 'el-tree-select',
attrs: { checkStrictly: true, renderAfterExpand: true }
}
// 日期类型
{
type: 'date,dates',
component: 'el-date-picker',
attrs: { showConfirm: false, valueFormat: 'YYYY-MM-DD', format: 'YYYY-MM-DD' }
}
{
type: 'datetime,week',
component: 'el-date-picker',
attrs: { showConfirm: false, valueFormat: 'YYYY-MM-DD HH:mm:ss', format: 'YYYY-MM-DD HH:mm:ss' }
}
{
type: 'month,months',
component: 'el-date-picker',
attrs: { showConfirm: false, valueFormat: 'YYYY-MM', format: 'YYYY-MM' }
}
{
type: 'year,years',
component: 'el-date-picker',
attrs: { showConfirm: false, valueFormat: 'YYYY', format: 'YYYY' }
}
{
type: 'datetimerange,daterange,monthrange,yearrange',
component: 'el-date-picker',
attrs: { rangeSeparator: '至', startPlaceholder: '开始日期', endPlaceholder: '结束日期' }
}
// cascader
{
type: 'cascader',
component: 'el-cascader',
attrs: { clearable: true }
}4.6 工具函数
getPlaceholder: 自动生成占位符
import { getPlaceholder } from 'mwl-components'
const placeholder = getPlaceholder(formItem)
// text/number 类型: "请输入{label}"
// select/cascader/date 类型: "请选择{label}"4.7 Slots(模板插槽)
EasyForm 支持以下模板插槽:
| 插槽名 | 作用域参数 | 说明 | | ------ | ---------- | ------------------------------------------- | | {prop} | { item } | 单个表单项的自定义插槽(prop 为表单字段名) | | append | - | 表单底部的操作按钮区域 |
<template>
<EasyForm v-model="formData" :form-items="formItems">
<!-- 自定义某个表单项 -->
<template #username="{ item }">
<el-input v-model="formData.username" :placeholder="item.placeholder">
<template #prefix>
<el-icon><User /></el-icon>
</template>
</el-input>
</template>
<!-- 底部操作按钮 -->
<template #append>
<el-button type="primary">提交</el-button>
</template>
</EasyForm>
</template>4.8 自定义类型 (custom)
使用 type: 'custom' 可以完全自定义表单项:
const formItems = [
{
type: 'custom',
prop: 'customField',
label: '自定义',
width: '300px',
// 方式一:通过 slots 配置
slots: {
default: () => h('div', '自定义内容'),
},
},
{
type: 'custom',
prop: 'customField2',
label: '自定义2',
// 方式二:通过模板插槽
},
]<template>
<EasyForm v-model="formData" :form-items="formItems">
<template #customField2="{ item }">
<el-date-picker v-model="formData.customField2" type="datetime" placeholder="选择日期时间" />
</template>
</EasyForm>
</template>5. EasyTable 表格组件
基于 Element Plus 的 el-table 封装的增强型表格组件。
源码位置: src/components/EasyTable/index.vue
5.1 基础用法
<template>
<EasyTable :data="tableData" :table-columns="tableColumns" :buttons="buttons" button-width="120">
<!-- 自定义列内容 -->
<template #methodName="{ row }"> {{ row.methodName }}({{ row.methodParams }}) </template>
</EasyTable>
</template>
<script setup>
import { ref } from 'vue'
import { EasyTable, type TableColumn, type buttonType } from 'mwl-components'
const tableData = ref([
{
id: 1,
methodName: '新增',
methodParams: '参数1',
operateTime: '2023-01-01'
}
])
const tableColumns = ref<TableColumn[]>([
{
type: 'index',
label: '序号',
width: '80'
},
{
label: '操作内容',
prop: 'methodName'
},
{
label: '操作时间',
prop: 'operateTime'
}
])
const buttons = ref<buttonType[]>([
{
label: '编辑',
type: 'edit',
click: (row) => handleEdit(row)
},
{
label: '删除',
type: 'del',
click: (row) => handleDelete(row)
}
])
</script>5.2 Props
| 属性名 | 类型 | 默认值 | 说明 | | ------------ | --------------- | -------- | ------------------------------------ | | data | Array | required | 表格数据 | | tableColumns | TableColumn[] | [] | 列配置数组 | | buttons | buttonType[] | [] | 操作按钮配置数组 | | buttonWidth | Number / String | 120 | 操作列宽度 | | align | String | 'center' | 文字对齐方式 left / center / right | | height | Number / String | - | 表格高度 | | maxHeight | Number / String | - | 表格最大高度 | | footerHeight | Number | 65 | 底部分页高度(用于自动计算表格高度) |
支持 Element Plus el-table 的所有属性,通过 v-bind="$attrs" 透传:
| 属性名 | 类型 | 默认值 | 说明 | | --------------------- | ----------------- | ---------- | -------------------------------- | | stripe | Boolean | false | 是否斑马纹 | | border | Boolean | false | 是否带边框 | | size | String | - | 表格尺寸 large / default / small | | fit | Boolean | true | 列宽度是否自适应 | | show-header | Boolean | true | 是否显示表头 | | highlight-current-row | Boolean | false | 是否高亮当前行 | | row-key | String / Function | - | 行数据的 Key | | empty-text | String | '暂无数据' | 空数据时显示的文本 | | default-expand-all | Boolean | false | 默认展开所有行 | | expand-row-keys | Array | - | 展开行的 keys |
5.3 TableColumn 配置
interface TableColumn {
// 基础属性
label: string // 列标题(必填)
prop?: string // 字段名
width?: String / Number // 列宽度
minWidth?: String / Number // 最小列宽
fixed?: String / Boolean // 固定列 left / right / true
// 列类型
type?: 'index' | 'selection' | 'expand' // 序号列 / 多选列 / 展开列
// 样式
align?: String // 对齐方式 left / center / right
header-align?: String // 表头对齐方式
sortable?: Boolean / 'custom' // 是否可排序
sort-method?: Function // 排序方法
sort-by?: String // 按哪个字段排序
sort-orders?: Array // 排序顺序数组
// 显示与隐藏
show-overflow-tooltip?: Boolean // 是否显示省略号tooltip
resizable?: Boolean // 是否可拖拽调整宽度
// 渲染
formatter?: Function // 格式化函数 (row, column, cellValue, index)
selectable?: Function // 选择框是否禁用 (row, index)
// 插槽(配置插槽,非模板插槽)
slots?: {
default?: (scope: { row: any; $index: number }) => VNode // 自定义列内容
header?: (scope: { column: any; $index: number }) => VNode // 自定义表头
}
// 嵌套表头
children?: TableColumn[] // 子列配置(用于多级表头)
// 其他属性透传
[key: string]: any // 其他 el-table-column 属性
}5.4 buttonType 操作按钮配置
interface buttonType {
label: string // 按钮文本(必填)
type: String // 按钮类型(必填,用于样式和判断:edit/del 等)
permi?: any[] | String // 权限标识(预留)
hide?: Boolean // 是否隐藏
disabled?: (row: any) => Boolean // 禁用条件函数
show?: (row: any) => Boolean // 显示条件函数
click?: (row: any) => void // 点击回调函数(必填)
link?: (row: any) => Boolean // 是否显示为文字链接
// 其他属性(会透传到 el-button)
[key: string]: any
}5.5 表格事件(通过 $attrs 透传)
| 事件名 | 参数 | 说明 | | ---------------- | ------------------------ | -------------------- | | select | selection, row | 用户手动勾选时触发 | | select-all | selection | 用户点击全选时触发 | | selection-change | selection | 选择项发生变化时触发 | | cell-click | row, column, cell, event | 单元格点击时触发 | | cell-dblclick | row, column, cell, event | 单元格双击时触发 | | row-click | row, column, event | 行点击时触发 | | row-dblclick | row, column, event | 行双击时触发 | | header-click | column, event | 表头点击时触发 | | sort-change | { column, prop, order } | 排序发生变化时触发 | | filter-change | filters | 筛选发生变化时触发 |
5.6 Slots(模板插槽)
EasyTable 支持通过模板自定义列内容,插槽名为列的 prop 属性:
| 插槽名 | 作用域参数 | 说明 | | ------ | --------------- | ----------------------------------- | | {prop} | { row, $index } | 自定义列内容(prop 为列的 prop 值) |
<template>
<EasyTable :data="tableData" :table-columns="tableColumns">
<!-- 自定义 username 列 -->
<template #username="{ row }">
<el-tag>{{ row.username }}</el-tag>
</template>
<!-- 自定义操作列 -->
<template #operate="{ row }">
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</EasyTable>
</template>5.7 嵌套表头
const tableColumns = ref<TableColumn[]>([
{
label: '用户信息',
children: [
{
label: '姓名',
prop: 'name',
width: 120,
},
{
label: '联系方式',
children: [
{ label: '电话', prop: 'phone', width: 120 },
{ label: '邮箱', prop: 'email', width: 180 },
],
},
],
},
{
label: '操作',
fixed: 'right',
width: 150,
buttons: [
{ label: '编辑', type: 'edit', click: (row) => {} },
{ label: '删除', type: 'del', click: (row) => {} },
],
},
])5.8 特性
- 自动计算高度:当父容器 class 包含
content-wrap时,自动计算表格最大高度 - 响应式:监听窗口 resize 事件自动调整表格高度
- 操作按钮:支持配置多个操作按钮,自动处理删除按钮为 danger 类型
- 插槽优先级:配置 slots > 模板插槽 > 默认渲染
6. MenuTree 菜单树组件
基于 Element Plus 的 el-menu 封装的菜单导航组件。
源码位置: src/components/MenuTree/index.vue
6.1 基础用法
<template>
<MenuTree :menu-list="menuList" @handleMenuItemClick="handleMenuItemClick" />
</template>
<script setup>
import { MenuTree } from 'mwl-components'
const menuList = ref([
{
path: '/home',
name: 'Home',
meta: { title: '首页', icon: 'HomeFilled' },
children: [],
},
{
path: '/user',
name: 'User',
meta: { title: '用户管理', icon: 'User' },
children: [
{ path: '/user/list', meta: { title: '用户列表', icon: 'List' } },
{ path: '/user/add', meta: { title: '新增用户', icon: 'Plus' } },
],
},
])
const handleMenuItemClick = (item) => {
console.log('点击菜单:', item)
}
</script>6.2 Props
| 属性名 | 类型 | 默认值 | 说明 | | -------- | ----- | ------ | ------------ | | menuList | Array | [] | 菜单数据列表 |
支持 Element Plus el-menu 的所有属性,通过 v-bind="$attrs" 透传:
| 属性名 | 类型 | 默认值 | 说明 | | ----------------- | ------- | ---------- | ------------------------------ | | default-active | String | - | 当前激活菜单的 index | | default-openeds | Array | - | 当前打开的菜单 | | mode | String | 'vertical' | 菜单模式 vertical / horizontal | | collapse | Boolean | false | 是否折叠 | | background-color | String | - | 背景色 | | text-color | String | - | 文字颜色 | | active-text-color | String | - | 激活菜单文字颜色 | | unique-opened | Boolean | false | 是否只保持一个子菜单展开 | | menu-trigger | String | 'hover' | 触发子菜单的方式 hover / click |
6.3 Events
| 事件名 | 参数 | 说明 | | ------------------- | ---- | -------------- | | handleMenuItemClick | item | 菜单项点击事件 |
6.4 菜单数据格式
interface MenuItem {
path: string // 路由路径
name?: string // 路由名称
meta?: {
title: string // 菜单标题
icon?: string // 图标名称
[key: string]: any // 其他元信息
}
children?: MenuItem[] // 子菜单
[key: string]: any // 其他属性
}6.5 特性
- 支持无限级嵌套
- 自动渲染有子菜单的为 SubMenu,无子菜单的为 MenuItem
- 支持图标配置(需配合 el-icon 使用)
7. Pagination 分页组件
基于 Element Plus 的 el-pagination 封装的分页组件。
源码位置: src/components/Pagination/index.vue
7.1 基础用法
<template>
<Pagination
v-model:page="pageData.page"
v-model:limit="pageData.pageSize"
:total="total"
@pagination="handlePagination"
/>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { Pagination } from 'mwl-components'
const total = ref(100)
const pageData = reactive({
page: 1,
pageSize: 20,
})
const handlePagination = ({ page, limit }) => {
console.log(`当前页: ${page}, 每页条数: ${limit}`)
// 重新获取数据
}
</script>7.2 Props
| 属性名 | 类型 | 默认值 | 说明 | | ---------- | ------ | -------- | -------------------------------- | | total | Number | required | 总条目数 | | page | Number | 1 | 当前页数(v-model:page) | | limit | Number | 20 | 每页显示条数(v-model:limit) | | pagerCount | Number | 5/7 | 最大页码按钮数(移动端5,PC端7) |
支持 Element Plus el-pagination 的所有属性,以下是常用属性:
| 属性名 | 类型 | 默认值 | 说明 | | ---------- | ------- | ----------------------------------------- | ------------------- | | background | Boolean | true | 是否为背景色按钮 | | page-size | Number | - | 每页条数(默认 20) | | page-sizes | Array | [10, 20, 30, 50, 100] | 每页条数选择器 | | layout | String | 'total, sizes, prev, pager, next, jumper' | 组件布局 | | prev-text | String | - | 替代上一页图标文字 | | next-text | String | - | 替代下一页图标文字 | | small | Boolean | false | 是否使用小型分页 |
7.3 Events
| 事件名 | 参数 | 说明 | | ------------ | --------------- | ------------------ | | pagination | { page, limit } | 分页变化时触发 | | update:page | page | 页码变化时触发 | | update:limit | limit | 每页条数变化时触发 |
8. EasyContextMenu 右键菜单组件(Vue 组件)
基于 Vue 实现的右键上下文菜单组件。
源码位置: src/components/EasyContextMenu/index.vue
8.1 基础用法
<template>
<div>
<div class="target-area">右键此区域</div>
<EasyContextMenu :menu-list="menuList" :max-height="200" background-color="#fff" />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { EasyContextMenu } from 'mwl-components'
const menuList = ref([
{ label: '刷新', onClick: () => console.log('刷新') },
{ label: '导出', onClick: () => console.log('导出') },
{ label: '设置', onClick: () => console.log('设置') },
])
</script>8.2 Props
| 属性名 | 类型 | 默认值 | 说明 | | --------------- | --------------- | --------------------------- | ---------------- | | menuList | Array | [] | 菜单列表 | | maxHeight | String / Number | 200 | 菜单最大高度 | | backgroundColor | String | '#fff' | 菜单背景颜色 | | hoverItemStyle | Object | { backgroundColor: '#ddd' } | hover 时的样式 | | animationCurve | String | 'linear' | 菜单显示动画曲线 |
8.3 MenuItem 类型
interface MenuItem {
label?: string // 菜单文本
onClick?: (e: MouseEvent) => void // 点击回调
disabled?: boolean // 是否禁用
divider?: boolean // 是否为分隔线
}8.4 特性
- 监听父元素的 contextmenu 事件显示菜单
- 点击其他区域自动隐藏
- 支持自定义背景色和 hover 样式
- 菜单显示时有动画效果
9. ContextMenu 右键菜单类
通过面向对象方式创建右键菜单的类,适用于需要更灵活控制的场景。
源码位置: src/components/ContextMenu/index.ts
9.1 基础用法
<script setup>
import { onMounted, onUnmounted } from 'vue'
import ContextMenu, { contextMenu } from '@/components/ContextMenu/index.ts'
const menuList = [
{ label: '刷新', onClick: () => location.reload() },
{ label: '导出', onClick: () => console.log('导出') },
{ divider: true },
{ label: '帮助', onClick: () => console.log('帮助'), disabled: true },
]
onMounted(() => {
// 创建右键菜单
new ContextMenu().create({
target: document.getElementById('target'),
menuList,
event: 'contextmenu',
})
})
onUnmounted(() => {
contextMenu.destroy()
})
</script>9.2 MenuOptions 配置
interface MenuOptions {
target?: HTMLElement // 绑定目标元素(必填)
menuList: Array<MenuItem> // 菜单列表(必填)
style?: Partial<CSSStyleDeclaration> // 自定义样式
hoverStyle?: { [key: string]: string } // hover 样式
event?: 'contextmenu' | 'click' | 'dblclick' // 触发事件类型
appendToBody?: boolean // 是否添加到 body
bindEvent?: boolean // 是否自动绑定事件
classNames?: string // 自定义类名
}
interface MenuItem {
label?: string // 菜单文本
onClick?: (e: Event) => void // 点击回调
divider?: boolean // 是否为分隔线
disabled?: boolean // 是否禁用
}9.3 实例方法
| 方法 | 参数 | 说明 | | --------------------------------- | -------- | ---------------- | | create(options: MenuOptions) | 配置对象 | 创建右键菜单 | | destroy() | - | 销毁右键菜单 | | setMenuList(menuList: MenuItem[]) | 菜单数组 | 动态设置菜单列表 | | showMenu(e: MouseEvent) | 事件对象 | 手动显示菜单 | | hideMenu() | - | 手动隐藏菜单 |
9.4 默认样式
const defaultStyle = {
position: 'fixed',
'z-index': 1000,
'border-radius': '4px',
color: '#666',
'font-size': '14px',
'min-width': '100px',
'box-shadow': '1px 1px 11px 0px #a6b5ba',
'background-color': '#fff',
padding: '4px',
overflow: 'hidden',
}
const hoverStyle = {
'background-color': '#8f9f80ff',
}9.5 导出对象
组件库导出了两种右键菜单使用方式:
ContextMenu 类:每次创建都是新实例
import { ContextMenu } from 'mwl-components' new ContextMenu().create({ ... })contextMenu 单例:全局共享实例
import { contextMenu } from 'mwl-components' contextMenu.create({ ... })
四、类型导出
组件库导出了以下类型定义:
import type {
// 通用类型
Options, // 下拉选项类型:Array<{ label: string; value: string | number; [key: string]: any }>
Placement, // 弹出位置:'auto' | 'top' | 'bottom' | 'left' | 'right' | ...
// 表单相关
FormItem, // 表单项配置
Rule, // 表单校验规则:{ required?, message, pattern?, validator?, trigger? }
ComponentMapType, // 组件映射配置
// 表格相关
TableColumn, // 表格列配置
buttonType, // 表格操作按钮配置
Slots, // 插槽配置类型
TableColumnSlots, // 表格列插槽类型
// 右键菜单
MenuOptions, // 右键菜单配置
MenuItem, // 右键菜单项
} from 'mwl-components'五、主题定制
组件库使用 Element Plus 的主题系统,可以通过以下方式进行定制:
方式一:SCSS 变量覆盖
// src/assets/styles/element/index.scss
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': #0f9c11,
),
)
);方式二:CSS 变量覆盖
:root {
--el-color-primary: #0f9c11;
}六、注意事项
v-model 使用:
- EasyForm 使用
v-model双向绑定表单数据 - Dialog 使用
v-model控制显示隐藏 - Pagination 使用
v-model:page和v-model:limit
- EasyForm 使用
TypeScript 支持:组件库完全使用 TypeScript 编写,提供完整的类型提示
Tree-shaking 支持:支持按需引入,可配合自动导入插件使用
Element Plus 依赖:组件库基于 Element Plus,需要全局或按需引入相关组件
样式透传:大部分组件支持通过
$attrs透传属性到底层 Element Plus 组件
七、版本信息
- 当前版本:0.1.6
- 构建输出:ES Module / CommonJS / UMD
八 打包配置问题
如果想让组件库 在主项目使 el-config-provider 的配置生效,在组件库中的组件中不要单独引入 element-plus 的组件,并且 在vite.config.ts 中配置如下 自动导入方法AutoImport resolvers: [ElementPlusResolver()] 一定要注销掉 ,否则打包也会把element-plus 的组件打包进去
总结: 组件库 不用单独引入 element-plus 的组件,也不能注册,使组件库与主项目使用的 element-plus 保持(源)相同
export default defineConfig({
plugins: [
vue(),
// viteCompression({ algorithm: 'gzip' }), // 压缩
dts({
tsconfigPath: './tsconfig.json',
insertTypesEntry: true,
include: ['src/**/*.ts', 'src/**/*.vue']
}), // 生成dts
// AutoImport({ // 自动引入 打包一定要开启 否则 不会打包组件未引入而使用的ref等方法 或者 在组件中手动引入使用的方法
// imports: ['vue', 'vue-router'],
// dts: 'src/auto-imports.d.ts',
// // ignore: ['h'], // 关键配置 忽略h函数
// // resolvers: [ElementPlusResolver()]
// }),
// Components({
// dts: 'src/components.d.ts',
// resolvers: [ElementPlusResolver()],
// dirs: ['src/components/'],
// exclude: [/node_modules/],
// extensions: ['vue']
// })
],
})