z-crud-table 0.0.64 → 0.0.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +495 -153
- package/dist/components/CrudTable.vue.d.ts +38 -4
- package/dist/z-crud-table.js +1444 -1341
- package/dist/z-crud-table.umd.cjs +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,200 +1,542 @@
|
|
|
1
|
-
|
|
1
|
+
## 介绍
|
|
2
|
+
CrudTable 是一个高度抽象的业务组件,旨在统一项目中表格增删改查的操作体验和开发规范。
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
它通过“配置驱动”的方式,将一个完整表格页面的功能点抽象为以下几个核心 Prop:
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
API 配置: 通过 apiUrlQuery, apiUrlDetail, apiUrlCreate, apiUrlUpdate, apiUrlDelete 五个属性自动管理所有后端交互。
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- **高度可定制**: 提供丰富的插槽,用于自定义查询条件、表格列、操作按钮等。
|
|
9
|
-
- **配置驱动表单**: 支持通过 JSON 数组配置动态渲染新增/编辑弹窗内的表单。
|
|
10
|
-
- **完整的 `el-table` 功能**: 继承 `el-table` 所有原生属性和事件,无缝衔接现有使用习惯。
|
|
11
|
-
- **强大的生命周期**: 提供完整的 `onBefore` 和 `onAfter` 钩子,方便在操作前后进行数据处理和逻辑注入。
|
|
12
|
-
- **灵活的列控制**: 可通过 props 单独控制多选框、序号列、操作列以及编辑/删除按钮的显示与隐藏。
|
|
13
|
-
- **开箱即用**: 内置了分页、单行删除、批量删除等常用功能。
|
|
8
|
+
表格列配置: columns 数组用于动态渲染 el-table 的列,并支持插槽和表头提示。
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
弹窗表单配置: dialogFormConfig 数组与内置的 DynamicForm 组件联动,自动渲染新增和编辑时的弹窗表单。
|
|
16
11
|
|
|
12
|
+
此外,它还提供了丰富的插槽 (Slots) 和生命周期钩子 (Hooks),允许开发者在不修改组件源码的情况下,轻松注入自定义的搜索条件、行内操作和提交前/后逻辑,在实现高度复用的同时保持了灵活性。
|
|
13
|
+
:::tip 提示
|
|
14
|
+
表格的请求、查询、编辑、删除接口类型分别为"GET"、"POST"、"PUT"、"DELETE"类型,否则会报错!
|
|
15
|
+
:::
|
|
16
|
+
## 安装
|
|
17
|
+
```NPM
|
|
18
|
+
npm install z-crud-table
|
|
17
19
|
```
|
|
18
|
-
npm install your-crud-table-package-name
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
## 引用
|
|
21
22
|
|
|
22
|
-
## 🚀 快速上手
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
## 案例
|
|
25
|
+
### 配置驱动
|
|
26
|
+
```vue
|
|
27
|
+
<template>
|
|
28
|
+
<crud-table
|
|
29
|
+
:api-url-query="apiUrls.query"
|
|
30
|
+
:api-url-detail="apiUrls.detail"
|
|
31
|
+
:api-url-create="apiUrls.create"
|
|
32
|
+
:api-url-update="apiUrls.update"
|
|
33
|
+
:api-url-delete="apiUrls.delete"
|
|
34
|
+
:columns="tableColumns"
|
|
35
|
+
:dialog-form-config="formConfig"
|
|
36
|
+
:dialog-form-rules="formRules"
|
|
37
|
+
/>
|
|
38
|
+
</template>
|
|
25
39
|
|
|
40
|
+
<script setup lang="ts">
|
|
41
|
+
import { ref } from 'vue';
|
|
42
|
+
|
|
43
|
+
// 1. API 地址
|
|
44
|
+
const apiUrls = {
|
|
45
|
+
query: '/api/user/list',
|
|
46
|
+
detail: '/api/user/detail',
|
|
47
|
+
create: '/api/user/create',
|
|
48
|
+
update: '/api/user/update',
|
|
49
|
+
delete: '/api/user/delete',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// 2. 表格列配置
|
|
53
|
+
const tableColumns = ref([
|
|
54
|
+
{ prop: 'username', label: '用户名', width: 150 },
|
|
55
|
+
{ prop: 'nickname', label: '昵称', width: 150 },
|
|
56
|
+
{ prop: 'email', label: '邮箱' },
|
|
57
|
+
{ prop: 'role', label: '角色', width: 150 },
|
|
58
|
+
{ prop: 'createTime', label: '创建时间', width: 150 },
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
// 3. 弹窗表单配置 (使用 DynamicForm)
|
|
62
|
+
const formConfig = ref([
|
|
63
|
+
{ type: 'input', prop: 'username', label: '用户名' },
|
|
64
|
+
{ type: 'input', prop: 'nickname', label: '昵称' },
|
|
65
|
+
{ type: 'input', prop: 'email', label: '邮箱' },
|
|
66
|
+
{
|
|
67
|
+
type: 'select',
|
|
68
|
+
prop: 'role',
|
|
69
|
+
label: '角色',
|
|
70
|
+
options: [
|
|
71
|
+
{ label: '管理员', value: 'admin' },
|
|
72
|
+
{ label: '普通用户', value: 'user' },
|
|
73
|
+
],
|
|
74
|
+
// 自定义字段
|
|
75
|
+
// componentProps: {
|
|
76
|
+
// placeholder: '请选择角色',
|
|
77
|
+
// // ✨ 在这里指定自定义的字段映射
|
|
78
|
+
// props: {
|
|
79
|
+
// label: 'dictName', // 将 label 映射到 dictLabel
|
|
80
|
+
// value: 'dictCode' // 将 value 映射到 dictValue
|
|
81
|
+
// }
|
|
82
|
+
// },
|
|
83
|
+
},
|
|
84
|
+
{ type: 'textarea', prop: 'description', label: '备注' },
|
|
85
|
+
{
|
|
86
|
+
label: '创建时间',
|
|
87
|
+
prop: 'createTime',
|
|
88
|
+
type: 'date-picker', // 对应 DynamicForm 中的 v-if
|
|
89
|
+
componentProps: {
|
|
90
|
+
type: 'date', // Element Plus 原生属性,指定为日期选择
|
|
91
|
+
valueFormat: 'YYYY-MM-DD', // 指定绑定值的格式
|
|
92
|
+
placeholder: '请选择日期',
|
|
93
|
+
clearable: true
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
// 4. 弹窗表单校验规则
|
|
99
|
+
const formRules = ref({
|
|
100
|
+
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
|
101
|
+
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
|
102
|
+
});
|
|
103
|
+
</script>
|
|
26
104
|
```
|
|
105
|
+
|
|
106
|
+
### 自定义搜索区 & 表格列内容
|
|
107
|
+
```vue
|
|
27
108
|
<template>
|
|
28
109
|
<crud-table
|
|
29
|
-
|
|
30
|
-
:api-url-
|
|
31
|
-
:api-url-
|
|
32
|
-
:api-url-
|
|
33
|
-
:api-url-
|
|
34
|
-
:
|
|
35
|
-
:
|
|
36
|
-
:dialog-form-
|
|
110
|
+
:api-url-query="apiUrls.query"
|
|
111
|
+
:api-url-detail="apiUrls.detail"
|
|
112
|
+
:api-url-create="apiUrls.create"
|
|
113
|
+
:api-url-update="apiUrls.update"
|
|
114
|
+
:api-url-delete="apiUrls.delete"
|
|
115
|
+
:columns="tableColumns"
|
|
116
|
+
:dialog-form-config="formConfig"
|
|
117
|
+
:dialog-form-rules="formRules"
|
|
37
118
|
>
|
|
38
|
-
<
|
|
39
|
-
|
|
119
|
+
<template #query-conditions="{ searchForm }">
|
|
120
|
+
<el-form-item label="用户名">
|
|
121
|
+
<el-input v-model="searchForm.username" placeholder="请输入用户名" clearable />
|
|
122
|
+
</el-form-item>
|
|
123
|
+
<el-form-item label="状态">
|
|
124
|
+
<el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
|
|
125
|
+
<el-option label="启用" :value="1" />
|
|
126
|
+
<el-option label="禁用" :value="0" />
|
|
127
|
+
</el-select>
|
|
128
|
+
</el-form-item>
|
|
129
|
+
</template>
|
|
130
|
+
|
|
131
|
+
<template #status="{ row }">
|
|
132
|
+
<el-tag v-if="row.status === 1" type="success">启用</el-tag>
|
|
133
|
+
<el-tag v-else type="danger">禁用</el-tag>
|
|
134
|
+
</template>
|
|
135
|
+
|
|
136
|
+
<template #role="{ row }">
|
|
137
|
+
<el-tag type="primary">{{ row.roleName }}</el-tag>
|
|
138
|
+
</template>
|
|
40
139
|
</crud-table>
|
|
41
140
|
</template>
|
|
42
141
|
|
|
43
142
|
<script setup lang="ts">
|
|
44
143
|
import { ref } from 'vue';
|
|
45
|
-
import CrudTable from 'your-crud-table-package-name';
|
|
46
144
|
|
|
47
|
-
|
|
145
|
+
// 1. API 地址
|
|
146
|
+
const apiUrls = {
|
|
147
|
+
query: '/api/user/list',
|
|
148
|
+
detail: '/api/user/detail',
|
|
149
|
+
create: '/api/user/create',
|
|
150
|
+
update: '/api/user/update',
|
|
151
|
+
delete: '/api/user/delete',
|
|
152
|
+
};
|
|
48
153
|
|
|
49
|
-
|
|
50
|
-
|
|
154
|
+
// 2. 表格列配置
|
|
155
|
+
const tableColumns = ref([
|
|
156
|
+
{ prop: 'username', label: '用户名' },
|
|
157
|
+
{ prop: 'role', label: '角色', slot: 'role' }, // ✨ 指定使用名为 'role' 的插槽
|
|
158
|
+
{ prop: 'status', label: '状态', slot: 'status' }, // ✨ 指定使用名为 'status' 的插槽
|
|
159
|
+
{ prop: 'createTime', label: '创建时间' },
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
// 3. 弹窗表单配置 (使用 DynamicForm)
|
|
163
|
+
const formConfig = ref([
|
|
164
|
+
{ type: 'input', prop: 'username', label: '用户名' },
|
|
165
|
+
{ type: 'input', prop: 'nickname', label: '昵称' },
|
|
51
166
|
{ type: 'input', prop: 'email', label: '邮箱' },
|
|
167
|
+
{
|
|
168
|
+
type: 'select',
|
|
169
|
+
prop: 'role',
|
|
170
|
+
label: '角色',
|
|
171
|
+
options: [
|
|
172
|
+
{ label: '管理员', value: 'admin' },
|
|
173
|
+
{ label: '普通用户', value: 'user' },
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
{ type: 'textarea', prop: 'description', label: '备注' },
|
|
52
177
|
]);
|
|
53
|
-
</script>
|
|
54
178
|
|
|
179
|
+
// 4. 弹窗表单校验规则
|
|
180
|
+
const formRules = ref({
|
|
181
|
+
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
|
182
|
+
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
|
183
|
+
});
|
|
184
|
+
</script>
|
|
55
185
|
```
|
|
56
186
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
### **属性 (Props)**
|
|
60
|
-
|
|
61
|
-
| 属性名 | 描述 | 类型 | 是否必需 | 默认值 |
|
|
62
|
-
| --- | --- | --- | --- | --- |
|
|
63
|
-
| **API 配置** | | | | |
|
|
64
|
-
| `apiUrlQuery` | 获取表格列表数据的接口地址。 | `String` | `true` | - |
|
|
65
|
-
| `apiUrlDetail` | 获取单条数据详情的接口地址(用于编辑时回填表单)。 | `String` | `true` | - |
|
|
66
|
-
| `apiUrlCreate` | 创建新条目的接口地址。 | `String` | `true` | - |
|
|
67
|
-
| `apiUrlUpdate` | 更新条目的接口地址。 | `String` | `true` | - |
|
|
68
|
-
| `apiUrlDelete` | 删除条目的接口地址。 | `String` | `true` | - |
|
|
69
|
-
| **功能控制** | | | | |
|
|
70
|
-
| `showSelectionColumn` | 是否显示表格的多选框列。 | `Boolean` | `false` | `true` |
|
|
71
|
-
| `showIndexColumn` | 是否显示表格的序号列。 | `Boolean` | `false` | `true` |
|
|
72
|
-
| `showActionsColumn` | 是否显示操作列。 | `Boolean` | `false` | `true` |
|
|
73
|
-
| `showEditButton` | 是否在操作列中显示默认的“编辑”按钮。 | `Boolean` | `false` | `true` |
|
|
74
|
-
| `showDeleteButton` | 是否在操作列中显示默认的“删除”按钮。 | `Boolean` | `false` | `true` |
|
|
75
|
-
| `actionsColumnWidth` | 操作列的宽度。 | `Number` | `false` | `120` |
|
|
76
|
-
| **表单与弹窗** | | | | |
|
|
77
|
-
| `dialogWidth` | 新增/编辑弹窗的宽度。 | `String` | `false` | `'50%'` |
|
|
78
|
-
| `dialogFormConfig` | **动态表单配置数组**,用于快速生成弹窗内的表单。 | `Array` | `false` | `[]` |
|
|
79
|
-
| `dialogFormRules` | 弹窗表单的 [Element Plus 验证规则](https://element-plus.org/zh-CN/component/form.html#%E8%A1%A8%E5%8D%95%E6%A0%A1%E9%AA%8C)。 | `Object` | `false` | `{}` |
|
|
80
|
-
| **分页配置** | | | | |
|
|
81
|
-
| `initialSearchForm` | 查询表单的初始值,**包含初始分页参数 `pageNum` 和 `pageSize`**。 | `Object` | `false` | `{ pageNum: 1, pageSize: 10 }` |
|
|
82
|
-
| `showPagination` | 是否显示分页组件。 | `Boolean` | `false` | `true` |
|
|
83
|
-
| `pageSizes` | 每页显示个数选择器的选项设置。 | `Array` | `false` | `[10, 20, 50, 100]` |
|
|
84
|
-
| `paginationLayout` | 分页组件布局。 | `String` | `false` | `'total, sizes, prev, pager, next, jumper'` |
|
|
85
|
-
| `paginationBackground` | 是否为分页按钮添加背景色。 | `Boolean` | `false` | `true` |
|
|
86
|
-
| `paginationSmall` | 是否使用小型分页样式。 | `Boolean` | `false` | `false` |
|
|
87
|
-
| `paginationHideOnSinglePage` | 只有一页时是否隐藏分页。 | `Boolean` | `false` | `false` |
|
|
88
|
-
| **生命周期钩子** | | | | |
|
|
89
|
-
| `onBeforeQuery` | 查询请求**前**执行。可用于修改请求参数。**必须返回**处理后的参数对象。 | `Function` | `false` | - |
|
|
90
|
-
| `onAfterQuery` | 查询请求**后**执行。可用于格式化返回的列表数据。**必须返回**处理后的数据数组。 | `Function` | `false` | - |
|
|
91
|
-
| `onBeforeOpenDialog` | 打开弹窗**前**执行。可用于预处理表单数据。 | `Function` | `false` | - |
|
|
92
|
-
| `onAfterOpenDialog` | 打开弹窗**后**执行。可用于在弹窗渲染后执行某些操作。 | `Function` | `false` | - |
|
|
93
|
-
| `onBeforeSubmit` | 表单提交**前**执行。可用于序列化提交的数据。**必须返回**处理后的数据对象。 | `Function` | `false` | - |
|
|
94
|
-
| `onAfterSubmit` | 表单提交**后**执行。可用于执行提交成功后的副作用。 | `Function` | `false` | - |
|
|
95
|
-
| `onBeforeDelete` | 删除操作**前**执行。可用于进行额外的确认。**返回 `false` 可中止删除**。 | `Function` | `false` | - |
|
|
96
|
-
| `onAfterDelete` | 删除操作**后**执行。可用于执行删除成功后的副作用。 | `Function` | `false` | - |
|
|
97
|
-
|
|
98
|
-
### **插槽 (Slots)**
|
|
99
|
-
|
|
100
|
-
| 插槽名称 | 作用域 (Props) | 描述 |
|
|
101
|
-
| --- | --- | --- |
|
|
102
|
-
| `default` | - | **(核心)** 用于定义表格的列 (`<el-table-column>`),除了序号列、复选框列和操作列。 |
|
|
103
|
-
| `header` | - | 在整个组件最顶部插入内容,如页面大标题。 |
|
|
104
|
-
| `query-conditions` | `{ searchForm }` | 自定义查询区域的表单项。`searchForm` 是响应式的查询对象。 |
|
|
105
|
-
| `query-left` | - | 在“搜索”按钮左侧添加自定义按钮或内容。 |
|
|
106
|
-
| `query-right` | - | 在“清空”按钮右侧添加自定义按钮或内容。 |
|
|
107
|
-
| `action-left` | `{ selections }` | 在“新增”按钮左侧添加自定义按钮,可通过 `selections` 访问当前勾选的数据。 |
|
|
108
|
-
| `action-right` | - | 在“新增”按钮右侧添加自定义按钮。 |
|
|
109
|
-
| `actions` | `{ row }` | **(重要)** 自定义操作列的内容,可用于添加、修改或完全替换默认的编辑/删除按钮。 |
|
|
110
|
-
| `dialog-form-content` | `{ formData, mode }` | **完全自定义**新增/编辑弹窗的表单内容。如果使用此插槽,`dialogFormConfig` 将被忽略。 |
|
|
111
|
-
|
|
112
|
-
### **事件 (Events)**
|
|
113
|
-
|
|
114
|
-
| 事件名称 | 载荷 (Payload) | 描述 |
|
|
115
|
-
| --- | --- | --- |
|
|
116
|
-
| `@open-dialog` | `{ mode: string, data: object }` | 在新增或编辑弹窗打开后触发。 |
|
|
117
|
-
| `@submit` | `{ mode: string, data: object }` | 在表单提交(新增或更新)成功**后**触发。 |
|
|
118
|
-
| `@delete` | `number[]` | 在删除操作成功**后**触发,载荷为被删除的 ID 数组。 |
|
|
119
|
-
|
|
120
|
-
### **暴露的方法 (Exposed Methods)**
|
|
121
|
-
|
|
122
|
-
通过 `ref` 可以获取到组件实例,并调用其暴露的方法。
|
|
123
|
-
|
|
124
|
-
| 方法名称 | 参数 | 描述 |
|
|
125
|
-
| --- | --- | --- |
|
|
126
|
-
| `refresh()` | - | 强制刷新表格数据,使用当前的查询条件和分页状态。 |
|
|
127
|
-
| `search()` | - | 重置到第一页并刷新数据,相当于点击“搜索”按钮。 |
|
|
128
|
-
| `handleDelete(ids: number[])` | `ids` (ID 数组) | 触发删除操作,可用于父组件中自定义的批量删除逻辑。 |
|
|
129
|
-
| `openDialog(mode, data)` | `mode`, `data` | 手动打开新增/编辑弹窗。 |
|
|
130
|
-
|
|
131
|
-
## **实践案例**
|
|
132
|
-
|
|
133
|
-
```
|
|
187
|
+
### 生命周期函数使用
|
|
188
|
+
```vue
|
|
134
189
|
<template>
|
|
135
190
|
<crud-table
|
|
136
|
-
|
|
137
|
-
:
|
|
138
|
-
:
|
|
139
|
-
:api-url-create="'/api/users'"
|
|
140
|
-
:api-url-update="'/api/users'"
|
|
141
|
-
:api-url-delete="'/api/users'"
|
|
142
|
-
:show-delete-button="false"
|
|
143
|
-
actions-column-width="140"
|
|
144
|
-
:initial-search-form="{ role: null, pageNum: 1, pageSize: 5 }"
|
|
145
|
-
@submit="onSubmit"
|
|
191
|
+
...省略了基础配置(api-url与表头、表单内容等)
|
|
192
|
+
:initial-search-form="{ dateRange: [] }"
|
|
193
|
+
:on-before-query="handleBeforeQuery"
|
|
146
194
|
:on-after-query="handleAfterQuery"
|
|
195
|
+
:on-before-submit="handleBeforeSubmit"
|
|
196
|
+
:on-before-delete="handleBeforeDelete"
|
|
147
197
|
>
|
|
148
|
-
<template #
|
|
149
|
-
<
|
|
198
|
+
<template #query-conditions="{ searchForm }">
|
|
199
|
+
<el-form-item label="创建日期">
|
|
200
|
+
<el-date-picker
|
|
201
|
+
v-model="searchForm.dateRange"
|
|
202
|
+
type="daterange"
|
|
203
|
+
range-separator="至"
|
|
204
|
+
start-placeholder="开始日期"
|
|
205
|
+
end-placeholder="结束日期"
|
|
206
|
+
value-format="YYYY-MM-DD"
|
|
207
|
+
/>
|
|
208
|
+
</el-form-item>
|
|
150
209
|
</template>
|
|
210
|
+
</crud-table>
|
|
211
|
+
</template>
|
|
151
212
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
213
|
+
<script setup lang="ts">
|
|
214
|
+
import { ref } from 'vue';
|
|
215
|
+
import { ElMessage } from 'element-plus';
|
|
216
|
+
|
|
217
|
+
const roleMap = { 1: '管理员', 2: '普通用户', 3: '访客' };
|
|
218
|
+
const currentUser = { name: 'admin_user' }; // 假设这是当前登录用户
|
|
219
|
+
|
|
220
|
+
// 1. 查询前:转换日期参数
|
|
221
|
+
const handleBeforeQuery = (params) => {
|
|
222
|
+
const newParams = { ...params };
|
|
223
|
+
if (newParams.dateRange && newParams.dateRange.length === 2) {
|
|
224
|
+
newParams.beginTime = newParams.dateRange[0] + ' 00:00:00';
|
|
225
|
+
newParams.endTime = newParams.dateRange[1] + ' 23:59:59';
|
|
226
|
+
}
|
|
227
|
+
delete newParams.dateRange; // 删除原始数组
|
|
228
|
+
return newParams;
|
|
229
|
+
};
|
|
161
230
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
231
|
+
// 2. 查询后:处理表格数据
|
|
232
|
+
const handleAfterQuery = (tableData, queryParams) => {
|
|
233
|
+
console.log('本次查询的参数是:', queryParams);
|
|
234
|
+
// 映射角色 ID 为角色名称
|
|
235
|
+
return tableData.map(item => ({
|
|
236
|
+
...item,
|
|
237
|
+
roleName: roleMap[item.roleId] || '未知角色',
|
|
238
|
+
}));
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// 3. 提交前:附加额外数据
|
|
242
|
+
const handleBeforeSubmit = (formData, mode) => {
|
|
243
|
+
if (mode === 'add') {
|
|
244
|
+
formData.createBy = currentUser.name;
|
|
245
|
+
} else {
|
|
246
|
+
formData.updateBy = currentUser.name;
|
|
247
|
+
}
|
|
248
|
+
// 必须返回处理后的 formData
|
|
249
|
+
return formData;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// 4. 删除前:进行业务校验
|
|
253
|
+
const handleBeforeDelete = async (ids, rows) => {
|
|
254
|
+
// rows 是即将被删除的完整行数据对象
|
|
255
|
+
const hasAdmin = rows.some(row => row.roleName === '管理员');
|
|
256
|
+
if (hasAdmin) {
|
|
257
|
+
ElMessage.error('不能删除“管理员”角色的用户!');
|
|
258
|
+
return false; // 返回 false 中止删除
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// 可以返回一个 Promise
|
|
262
|
+
// return ElMessageBox.confirm('确定要删除吗?');
|
|
263
|
+
|
|
264
|
+
return true; // 返回 true 继续执行
|
|
265
|
+
};
|
|
266
|
+
</script>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 自定义“操作列”
|
|
270
|
+
```vue
|
|
271
|
+
<template>
|
|
272
|
+
<crud-table
|
|
273
|
+
...省略了基础配置(api-url与表头、表单内容等)
|
|
274
|
+
>
|
|
275
|
+
<template #action-before-edit="{ row }">
|
|
276
|
+
<el-button size="small" type="primary" link @click="viewDetails(row)">
|
|
277
|
+
详情
|
|
278
|
+
</el-button>
|
|
166
279
|
</template>
|
|
280
|
+
|
|
281
|
+
<template #action-after-delete="{ row }">
|
|
282
|
+
<el-button size="small" type="warning" link @click="resetPassword(row)">
|
|
283
|
+
重置密码
|
|
284
|
+
</el-button>
|
|
285
|
+
</template>
|
|
286
|
+
|
|
287
|
+
</crud-table>
|
|
288
|
+
</template>
|
|
289
|
+
|
|
290
|
+
<script setup lang="ts">
|
|
291
|
+
// ...
|
|
292
|
+
const viewDetails = (row) => { console.log('查看详情', row.id); };
|
|
293
|
+
const resetPassword = (row) => { console.log('重置密码', row.id); };
|
|
294
|
+
// const handleApprove = (row) => { ... };
|
|
295
|
+
// const handleReject = (row) => { ... };
|
|
296
|
+
</script>
|
|
297
|
+
```
|
|
167
298
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
299
|
+
### 复杂表单
|
|
300
|
+
```vue
|
|
301
|
+
<template>
|
|
302
|
+
<crud-table
|
|
303
|
+
...省略了基础配置(api-url与表头、表单内容等)
|
|
304
|
+
:dialog-form-rules="formRules"
|
|
305
|
+
>
|
|
306
|
+
<template #dialog-form="{ formData, formRef }">
|
|
307
|
+
<el-form :ref="formRef" :model="formData" :rules="formRules" label-width="100px">
|
|
308
|
+
<el-form-item label="用户名" prop="username">
|
|
309
|
+
<el-input v-model="formData.username" />
|
|
310
|
+
</el-form-item>
|
|
311
|
+
<el-form-item label="昵称" prop="nickname">
|
|
312
|
+
<el-input v-model="formData.nickname" />
|
|
313
|
+
</el-form-item>
|
|
314
|
+
<el-form-item label="用户类型" prop="userType">
|
|
315
|
+
<el-select v-model="formData.userType" @change="onUserTypeChange(formData)">
|
|
316
|
+
<el-option label="普通用户" value="normal" />
|
|
317
|
+
<el-option label="VIP 用户" value="vip" />
|
|
318
|
+
</el-select>
|
|
319
|
+
</el-form-item>
|
|
320
|
+
|
|
321
|
+
<el-form-item v-if="formData.userType === 'vip'" label="VIP 等级" prop="vipLevel">
|
|
322
|
+
<el-input-number v-model="formData.vipLevel" :min="1" />
|
|
323
|
+
</el-form-item>
|
|
324
|
+
</el-form>
|
|
171
325
|
</template>
|
|
172
326
|
</crud-table>
|
|
173
327
|
</template>
|
|
174
328
|
|
|
175
|
-
<script setup>
|
|
329
|
+
<script setup lang="ts">
|
|
176
330
|
import { ref } from 'vue';
|
|
177
|
-
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
178
|
-
import CrudTable from './components/CrudTable.vue';
|
|
179
331
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
332
|
+
// 校验规则
|
|
333
|
+
const formRules = ref({
|
|
334
|
+
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
|
335
|
+
userType: [{ required: true, message: '请选择用户类型', trigger: 'change' }],
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// 表单内部联动逻辑
|
|
339
|
+
const onUserTypeChange = (formData) => {
|
|
340
|
+
if (formData.userType !== 'vip') {
|
|
341
|
+
formData.vipLevel = undefined; // 清空联动字段
|
|
342
|
+
}
|
|
188
343
|
};
|
|
344
|
+
</script>
|
|
345
|
+
```
|
|
189
346
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
347
|
+
### 文件上传表单
|
|
348
|
+
```vue
|
|
349
|
+
<template>
|
|
350
|
+
<crud-table
|
|
351
|
+
...省略了基础配置(api-url与表头内容等)
|
|
352
|
+
:dialog-form-rules="formRules"
|
|
353
|
+
:submit-as-form-data="true"
|
|
354
|
+
>
|
|
355
|
+
<template #dialog-form="{ formData, formRef }">
|
|
356
|
+
<el-form :ref="formRef" :model="formData" :rules="formRules" label-width="100px">
|
|
357
|
+
<el-form-item label="用户名" prop="username">
|
|
358
|
+
<el-input v-model="formData.username" />
|
|
359
|
+
</el-form-item>
|
|
360
|
+
|
|
361
|
+
<el-form-item label="用户头像" prop="avatarFile">
|
|
362
|
+
<el-upload
|
|
363
|
+
action="#"
|
|
364
|
+
:auto-upload="false"
|
|
365
|
+
:limit="1"
|
|
366
|
+
:on-change="handleAvatarChange"
|
|
367
|
+
>
|
|
368
|
+
<el-button type="primary">选择文件</el-button>
|
|
369
|
+
</el-upload>
|
|
370
|
+
</el-form-item>
|
|
371
|
+
</el-form>
|
|
372
|
+
</template>
|
|
373
|
+
</crud-table>
|
|
374
|
+
</template>
|
|
375
|
+
|
|
376
|
+
<script setup lang="ts">
|
|
377
|
+
import { ref } from 'vue';
|
|
378
|
+
|
|
379
|
+
// ...
|
|
380
|
+
const formRules = ref({
|
|
381
|
+
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
|
382
|
+
avatarFile: [{ required: true, message: '请上传头像', trigger: 'change' }],
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// 在 handleBeforeSubmit 钩子中处理文件
|
|
386
|
+
const handleBeforeSubmit = (formData, mode) => {
|
|
387
|
+
// `submit` 函数会自动将 `formData` 转换为 FormData 对象
|
|
388
|
+
// 我们只需要确保 `avatarFile` 字段是原始的 File 对象
|
|
389
|
+
console.log('提交前的表单数据:', formData);
|
|
390
|
+
return formData;
|
|
193
391
|
};
|
|
194
392
|
|
|
195
|
-
//
|
|
196
|
-
const
|
|
197
|
-
|
|
393
|
+
// el-upload 的 change 事件
|
|
394
|
+
const handleAvatarChange = (file, fileList) => {
|
|
395
|
+
// 上传逻辑
|
|
198
396
|
};
|
|
199
397
|
</script>
|
|
200
|
-
```
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## 属性
|
|
401
|
+
### API 与数据配置
|
|
402
|
+
|
|
403
|
+
| Prop | 类型 | 必需 | 默认值 | 描述 |
|
|
404
|
+
| ---- | ---- | ---- | ---- | ---- |
|
|
405
|
+
| `apiUrlQuery` | `String` | 否 | - | (R) 查询列表数据的API地址。 |
|
|
406
|
+
| `apiUrlDetail` | `String` | 否 | - | (R) 获取单条数据详情的API地址(编辑时使用)。 |
|
|
407
|
+
| `apiUrlCreate` | `String` | 否 | - | (C) 新增数据的API地址。 |
|
|
408
|
+
| `apiUrlUpdate` | `String` | 否 | - | (U) 更新数据的API地址。 |
|
|
409
|
+
| `apiUrlDelete` | `String` | 否 | - | (D) 删除数据的API地址。 |
|
|
410
|
+
| `columns` | `Column[]` | 否 | `[]` | **表格列配置**。详细结构见下文 [数据结构](#4-数据结构)。 |
|
|
411
|
+
| `initialSearchForm` | `Object` | 否 | `{ pageNum: 1, pageSize: 10 }` | 搜索表单的初始默认值,包含分页。 |
|
|
412
|
+
| `submitAsFormData` | `Boolean` | 否 | `false` | 是否以 `multipart/form-data` 格式提交表单,适用于文件上传。 |
|
|
413
|
+
|
|
414
|
+
### UI 功能开关
|
|
415
|
+
|
|
416
|
+
| Prop | 类型 | 必需 | 默认值 | 描述 |
|
|
417
|
+
| ---- | ---- | ---- | ---- | ---- |
|
|
418
|
+
| `showSearchSection` | `Boolean` | 否 | `true` | 是否显示顶部的搜索区域。 |
|
|
419
|
+
| `showSearchActionButtons` | `Boolean` | 否 | `true` | 是否显示“搜索”和“清空”按钮。 |
|
|
420
|
+
| `showSearchButton` | `Boolean` | 否 | `true` | 是否显示“搜索”按钮。 |
|
|
421
|
+
| `showClearButton` | `Boolean` | 否 | `true` | 是否显示“清空”按钮。 |
|
|
422
|
+
| `showNewBtn` | `Boolean` | 否 | `true` | 是否显示“新增”按钮。 |
|
|
423
|
+
| `showSelectionColumn` | `Boolean` | 否 | `true` | 是否显示表格的复选框列。 |
|
|
424
|
+
| `showIndexColumn` | `Boolean` | 否 | `true` | 是否显示表格的“序号”列。 |
|
|
425
|
+
| `showActionsColumn` | `Boolean` | 否 | `true` | 是否显示表格的“操作”列。 |
|
|
426
|
+
| `showEditButton` | `Boolean` | 否 | `true` | 是否在操作列中显示“编辑”按钮。 |
|
|
427
|
+
| `showDeleteButton` | `Boolean` | 否 | `true` | 是否在操作列中显示“删除”按钮。 |
|
|
428
|
+
| `showPagination` | `Boolean` | 否 | `true` | 是否显示分页组件。 |
|
|
429
|
+
|
|
430
|
+
### 弹窗与表单配置
|
|
431
|
+
|
|
432
|
+
| Prop | 类型 | 必需 | 默认值 | 描述 |
|
|
433
|
+
|----------------------|--------------------| ---- |---------|--------------------------------------------|
|
|
434
|
+
| `dialogWidth` | `String` | 否 | `'50%'` | 新增/编辑弹窗的宽度。 |
|
|
435
|
+
| `actionsColumnWidth` | `String \| Number` | 否 | `120` | “操作”列的宽度。 |
|
|
436
|
+
| `dialogFormConfig` | `FormItem[]` | 否 | `[]` | **弹窗表单配置**。用于动态生成表单,详细结构见下文 [数据结构](#4-数据结构)。 |
|
|
437
|
+
| `dialogFormRules` | `Object` | 否 | `{}` | 弹窗表单的 `element-plus` 校验规则。 |
|
|
438
|
+
| `dialogFullscreen` | `Boole` | 否 | `false` | 弹窗表单是否全屏展示。 |
|
|
439
|
+
| `addDialogTitle` | `String` | 否 | `新增` | 新增弹窗标题。 |
|
|
440
|
+
| `editDialogTitle` | `String` | 否 | `编辑` | 修改弹窗标题。 |
|
|
441
|
+
|
|
442
|
+
### 分页配置
|
|
443
|
+
|
|
444
|
+
| Prop | 类型 | 必需 | 默认值 | 描述 |
|
|
445
|
+
| ---- | ---- | ---- | ---- | ---- |
|
|
446
|
+
| `pageSizes` | `Array` | 否 | `[10, 20, 50, 100]` | 每页显示条目数选项。 |
|
|
447
|
+
| `paginationLayout` | `String` | 否 | `'total, sizes, prev, pager, next, jumper'` | 分页组件布局。 |
|
|
448
|
+
| `paginationBackground` | `Boolean` | 否 | `true` | 是否为分页按钮添加背景色。 |
|
|
449
|
+
| `paginationSmall` | `Boolean` | 否 | `false` | 是否使用小型分页。 |
|
|
450
|
+
| `paginationHideOnSinglePage` | `Boolean` | 否 | `false` | 只有一页时是否隐藏分页。 |
|
|
451
|
+
|
|
452
|
+
### 样式与加载
|
|
453
|
+
|
|
454
|
+
| Prop | 类型 | 必需 | 默认值 | 描述 |
|
|
455
|
+
| ---- | ---- | ---- | ---- | ---- |
|
|
456
|
+
| `theme` | `'default' \| 'large-screen'` | 否 | `'default'` | 组件的主题风格,会影响弹窗样式。 |
|
|
457
|
+
| `customClass` | `String` | 否 | `''` | 应用于组件根元素的自定义类名。 |
|
|
458
|
+
| `loadingText` | `String` | 否 | `'加载中…'` | 表格加载时的提示文字。 |
|
|
459
|
+
| `loadingBackground` | `String` | 否 | `'rgba(0, 0, 0, 0.3)'` | 表格加载时的遮罩背景色。 |
|
|
460
|
+
|
|
461
|
+
### 生命周期钩子 (Hooks)
|
|
462
|
+
|
|
463
|
+
| Prop | 类型 | 描述 |
|
|
464
|
+
| ---- | ---- | ---- |
|
|
465
|
+
| `onBeforeQuery` | `(params) => Promise<any> \| any` | 发起列表查询前调用。允许修改`params`。 |
|
|
466
|
+
| `onAfterQuery` | `(data, params) => Promise<any[] \| any[]` | 列表数据请求成功后、渲染表格前调用。允许修改`data`。 |
|
|
467
|
+
| `onBeforeOpenDialog` | `(mode, data) => Promise<any> \| any` | 打开弹窗前调用。允许修改表单的初始`data`。 |
|
|
468
|
+
| `onAfterOpenDialog` | `(mode, data) => void` | 打开弹窗后调用(编辑模式下在详情请求后)。 |
|
|
469
|
+
| `onBeforeSubmit` | `(data, mode) => Promise<any> \| any` | 提交表单(校验通过后)前调用。允许修改提交的`data`。 |
|
|
470
|
+
| `onAfterSubmit` | `(mode, data) => void` | 表单提交成功后调用。 |
|
|
471
|
+
| `onBeforeDelete` | `(ids, rows) => Promise<boolean> \| boolean` | 执行删除操作前调用。返回 `false` 可中止删除。 |
|
|
472
|
+
| `onAfterDelete` | `(ids, rows) => void` | 删除成功后调用。 |
|
|
473
|
+
|
|
474
|
+
### Events (自定义事件)
|
|
475
|
+
|
|
476
|
+
| 事件名 | 回调参数 | 描述 |
|
|
477
|
+
| ---- | ---- | ---- |
|
|
478
|
+
| `open-dialog` | `{ mode: 'add' \| 'edit', data: any }` | 弹窗打开后触发。 |
|
|
479
|
+
| `submit` | `{ mode: 'add' \| 'edit', data: any }` | 新增或编辑提交成功后触发。 |
|
|
480
|
+
| `delete` | `ids: number[]` | 删除成功后触发。 |
|
|
481
|
+
|
|
482
|
+
### Exposed Methods (暴露方法)
|
|
483
|
+
|
|
484
|
+
| 方法名 | 参数 | 描述 |
|
|
485
|
+
| ---- |------------------------------------------------------|------------------------------|
|
|
486
|
+
| `refresh()` | - | 手动刷新表格数据(使用当前 `searchForm`)。 |
|
|
487
|
+
| `search()` | `(ids: number[])` | 手动触发表格搜索(重置 `pageNum` 到 1)。 |
|
|
488
|
+
| `handleDelete` | `(ids: number[])` | 手动触发删除操作。 |
|
|
489
|
+
| `openDialog` | `(mode: 'add' \| 'edit', dataPayload?: any)` | 手动打开新增或编辑弹窗。 |
|
|
490
|
+
| `submit` | `(mode: 'add' \| 'edit', data: Record<string, any>)` | 手动触发提交逻辑。 |
|
|
491
|
+
| `closeDialog` | - | [新增] 手动触发提交关闭弹窗。 |
|
|
492
|
+
|
|
493
|
+
### 搜索区插槽
|
|
494
|
+
|
|
495
|
+
| Slot 名称 | 作用域 (Scope) | 描述 |
|
|
496
|
+
| ---- | ---- | ---- |
|
|
497
|
+
| `header` | - | 位于搜索区最顶部,`el-form` 之前。 |
|
|
498
|
+
| `query-conditions` | `{ searchForm: any }` | 搜索表单项的插入位置。 |
|
|
499
|
+
| `query-left` | `{ searchForm: any }` | 位于“搜索”按钮左侧。 |
|
|
500
|
+
| `query-right` | `{ searchForm: any }` | 位于“清空”按钮右侧。 |
|
|
501
|
+
|
|
502
|
+
### 操作区插槽
|
|
503
|
+
|
|
504
|
+
| Slot 名称 | 作用域 (Scope) | 描述 |
|
|
505
|
+
| ---- | ---- | ---- |
|
|
506
|
+
| `action-left` | `{ selections: any[] }` | 位于“新增”按钮左侧。 |
|
|
507
|
+
| `add-button-content` | `{ selections: any[] }` | 用于完全替换“新增”按钮。 |
|
|
508
|
+
| `action-right` | `{ selections: any[] }` | 位于“新增”按钮右侧。 |
|
|
509
|
+
|
|
510
|
+
### 表格区插槽
|
|
511
|
+
|
|
512
|
+
| Slot 名称 | 作用域 (Scope) | 描述 |
|
|
513
|
+
| ---- |----------------|------------------------------------------------------|
|
|
514
|
+
| `[column.slot]` | `{ row: any }` | **动态单元格插槽**。当 `columns` 配置项中提供了 `slot` 属性时,会以此名称渲染插槽。 |
|
|
515
|
+
| `actions` | `{ row: any }` | 完全替换默认的“编辑/删除”按钮组。 |
|
|
516
|
+
| `action-before-edit` | `{ row: any }` | 位于“编辑”按钮之前。 |
|
|
517
|
+
| `action-after-delete` | `{ row: any }` | 位于“删除”按钮之后。 |
|
|
518
|
+
| `dialog-form` | `{ row: any }` | 弹窗表单插槽 |
|
|
519
|
+
| `dialog-footer` | - | 弹窗表单页脚插槽 |
|
|
520
|
+
|
|
521
|
+
### `Column` (用于 `columns` Prop)
|
|
522
|
+
|
|
523
|
+
| 键 | 类型 | 必需 | 描述 |
|
|
524
|
+
| ---- | ---- | ---- | ---- |
|
|
525
|
+
| `prop` | `String` | 是 | "对应 `tableData` 中行的字段名。" |
|
|
526
|
+
| `label` | `String` | 是 | 列标题。 |
|
|
527
|
+
| `width` | `String \| Number` | 否 | 列宽度。 |
|
|
528
|
+
| `sortable` | `Boolean` | 否 | 是否可排序,默认为 `false`。 |
|
|
529
|
+
| `slot` | `String` | 否 | 自定义单元格渲染的插槽名称。 |
|
|
530
|
+
| `headerTooltip` | `Boolean` | 否 | 标题过长时是否显示 Tooltip(依赖 `TableHeaderWithTooltip.vue`)。 |
|
|
531
|
+
| `placement` | `String` | 否 | Tooltip 显示位置 (当 `headerTooltip` 为 `true` 时有效)。 |
|
|
532
|
+
| `attrs` | `Object` | 否 | 透传给 `el-table-column` 的其他属性 (如 `fixed`, `align` 等)。 |
|
|
533
|
+
|
|
534
|
+
### `FormItem` (用于 `dialogFormConfig` Prop)
|
|
535
|
+
|
|
536
|
+
| 键 | 类型 | 必需 | 描述 |
|
|
537
|
+
| ---- | ---- | ---- | ---- |
|
|
538
|
+
| `prop` | `String` | 是 | 表单字段名,对应 `v-model`。 |
|
|
539
|
+
| `label` | `String` | 是 | 表单项标签。 |
|
|
540
|
+
| `type` | `String` | 是 | "表单项类型。支持: `'input'`, `'textarea'`, `'select'`, `'radio-group'`, `'input-disabled'`。" |
|
|
541
|
+
| `options` | `Array<{ label, value }>` | 否 | 适用于 `select` 和 `radio-group` 的选项数组。 |
|
|
542
|
+
| `componentProps` | `Object` | 否 | 透传给内部 `el-` 组件的属性 (如 `placeholder`, `rows`, `clearable` 等)。 |
|