高级功能
本节介绍 SunlightTable 组件的高级使用方法,包括自定义渲染、树形表格、合并单元格、可编辑表格等功能。
自定义渲染
SunlightTable 组件支持通过插槽自定义列的渲染内容,可以实现复杂的自定义显示效果。
示例
禁用
vue
<template>
<SunlightTable :data="tableData" :columns="columns">
<!-- 自定义头像渲染 -->
<template #avatar="{ row }">
<el-avatar :size="32" :src="row.avatar" :alt="row.name" />
</template>
<!-- 自定义状态渲染 -->
<template #status="{ row }">
<el-tag :type="row.status === 'active' ? 'success' : 'danger'" size="small">
{{ row.status === "active" ? "激活" : "禁用" }}
</el-tag>
</template>
<!-- 自定义操作按钮 -->
<template #action="{ row }">
<div class="action-buttons">
<el-button size="small" type="primary" @click="handleEdit(row)">编辑11</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
</div>
</template>
</SunlightTable>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { SunlightTable } from "sunlight-ui";
import { ElMessage } from "element-plus";
// 表格数据
const tableData = ref([
{
id: 1,
name: "张三",
age: 25,
status: "active",
avatar: "https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
},
{
id: 2,
name: "李四",
age: 30,
status: "inactive",
avatar: "https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
},
{
id: 3,
name: "王五",
age: 28,
status: "active",
avatar: "https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
},
]);
// 列配置
const columns = [
{ prop: "id", label: "ID", align: "center" },
{ prop: "avatar", label: "头像", align: "center", slotName: "avatar" },
{ prop: "name", label: "姓名" },
{ prop: "age", label: "年龄", align: "center" },
{ prop: "status", label: "状态", align: "center", slotName: "status" },
{ prop: "action", label: "操作", align: "center", slotName: "action" },
];
// 编辑操作
function handleEdit(row: any) {
ElMessage({
message: `编辑用户: ${row.name}`,
type: "success",
});
}
// 删除操作
function handleDelete(row: any) {
ElMessage({
message: `删除用户: ${row.name}`,
type: "success",
});
}
</script>
<style scoped>
.action-buttons {
display: flex;
gap: 8px;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
隐藏源代码
配置说明
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| slotName | 自定义渲染插槽名称 | string | - |
树形表格
SunlightTable 组件支持树形表格功能,可以展示层级结构的数据,支持懒加载、自定义展开图标、行点击事件等高级功能。
示例
高级树形表格示例
展示树形表格的高级功能,包括懒加载、自定义展开图标、行点击事件等
禁用
vue
<template>
<div>
<h3>高级树形表格示例</h3>
<p>展示树形表格的高级功能,包括懒加载、自定义展开图标、行点击事件等</p>
<div class="demo-container">
<SunlightTable
:data="tableData"
:columns="columns"
:table-props="tableProps"
:show-selection="true"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
>
<!-- 自定义展开图标 -->
<template #default-expand-icon="{ row, expanded }">
<el-icon :size="16">
<el-icon-check v-if="expanded" />
<el-icon-plus v-else />
</el-icon>
</template>
<!-- 自定义状态列 -->
<template #status="{ row }">
<el-tag :type="row.status === 'active' ? 'success' : 'danger'" size="small">
{{ row.status === "active" ? "启用" : "禁用" }}
</el-tag>
</template>
</SunlightTable>
<div class="selected-info" v-if="selectedRows.length > 0">
<p>已选择 {{ selectedRows.length }} 项:</p>
<ul>
<li v-for="row in selectedRows" :key="row.id">{{ row.name }}</li>
</ul>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { SunlightTable } from "sunlight-ui";
// 表格数据
const tableData = ref([
{
id: 1,
name: "公司总部",
level: "1",
status: "active",
manager: "张总",
children: [
{
id: 2,
name: "技术部",
level: "2",
status: "active",
manager: "李经理",
children: [
{
id: 3,
name: "前端开发组",
level: "3",
status: "active",
manager: "王组长",
},
{
id: 4,
name: "后端开发组",
level: "3",
status: "active",
manager: "赵组长",
},
{
id: 5,
name: "测试组",
level: "3",
status: "active",
manager: "孙组长",
},
],
},
{
id: 6,
name: "产品部",
level: "2",
status: "active",
manager: "周经理",
children: [
{
id: 7,
name: "产品规划组",
level: "3",
status: "active",
manager: "吴组长",
},
{
id: 8,
name: "产品设计组",
level: "3",
status: "active",
manager: "郑组长",
},
],
},
{
id: 9,
name: "设计部",
level: "2",
status: "active",
manager: "王经理",
children: [
{
id: 10,
name: "UI设计组",
level: "3",
status: "active",
manager: "冯组长",
},
{
id: 11,
name: "UX设计组",
level: "3",
status: "active",
manager: "陈组长",
},
],
},
],
},
{
id: 12,
name: "市场部",
level: "1",
status: "active",
manager: "刘总",
children: [
{
id: 13,
name: "营销组",
level: "2",
status: "active",
manager: "杨经理",
},
{
id: 14,
name: "销售组",
level: "2",
status: "active",
manager: "朱经理",
},
],
},
]);
// 选中的行
const selectedRows = ref([]);
// 列配置
const columns = [
{ prop: "id", label: "ID", width: 80, align: "center" },
{ prop: "name", label: "部门名称", minWidth: 180 },
{ prop: "level", label: "层级", width: 80, align: "center" },
{ prop: "status", label: "状态", width: 100, align: "center", slotName: "status" },
{ prop: "manager", label: "负责人", width: 120 },
];
// 表格属性配置
const tableProps = {
border: true,
treeProps: {
children: "children",
hasChildren: "hasChildren",
},
defaultExpandAll: true,
indent: 20,
// 懒加载配置(这里只是示例,实际项目中需要配置load方法)
// lazy: true,
// load: loadNode,
};
// 选择变化事件处理
const handleSelectionChange = selection => {
selectedRows.value = selection;
console.log("选择变化:", selection);
};
// 行点击事件处理
const handleRowClick = (row, column, event) => {
console.log("行点击:", row);
};
// 懒加载节点方法(示例)
const loadNode = (node, resolve) => {
// 模拟异步加载
setTimeout(() => {
const newChildren = [
{
id: Date.now() + 1,
name: `动态节点 ${node.data.name}-1`,
level: `${parseInt(node.data.level) + 1}`,
status: "active",
manager: "动态负责人",
hasChildren: true,
},
{
id: Date.now() + 2,
name: `动态节点 ${node.data.name}-2`,
level: `${parseInt(node.data.level) + 1}`,
status: "active",
manager: "动态负责人",
},
];
resolve(newChildren);
}, 500);
};
</script>
<style scoped>
.selected-info {
margin-top: 20px;
padding: 15px;
background: #e6f7ff;
border: 1px solid #91d5ff;
border-radius: 4px;
}
.selected-info ul {
margin: 10px 0 0 20px;
padding: 0;
}
.selected-info li {
margin: 5px 0;
list-style-type: disc;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
隐藏源代码
配置说明
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| tableProps.treeProps | 树形表格的属性配置 | { children?: string; hasChildren?: string } | { children: 'children' } |
| tableProps.defaultExpandAll | 是否默认展开所有行 | boolean | false |
| tableProps.expandRowKeys | 展开行的 key 数组 | (string | number)[] | - |
| tableProps.defaultExpandedKeys | 默认展开的行的 key 数组 | (string | number)[] | - |
合并单元格
SunlightTable 组件支持合并单元格功能,可以通过 spanMethod 属性实现复杂的合并逻辑,包括跨行合并、跨列合并和动态合并规则。
示例
禁用
vue
<template>
<div>
<div>
<SunlightTable :data="tableData" :columns="columns" :table-props="tableProps" :show-selection="false">
<!-- 自定义状态列 -->
<template #status="{ row }">
<el-tag :type="row.status === 'active' ? 'success' : 'danger'" size="small">
{{ row.status === "active" ? "启用" : "禁用" }}
</el-tag>
</template>
</SunlightTable>
</div>
<!-- <div class="description">
<h4>合并规则说明:</h4>
<ul>
<li>
<strong>ID列</strong>
:相同ID的行进行纵向合并
</li>
<li>
<strong>姓名列</strong>
:相同姓名的行进行纵向合并
</li>
<li>
<strong>部门列</strong>
:相同部门的行进行纵向合并
</li>
<li>
<strong>职位列</strong>
:"高级"职位进行纵向合并
</li>
<li>
<strong>薪资列</strong>
:薪资大于10000的行进行纵向合并
</li>
<li>
<strong>状态列</strong>
:"启用"状态的行进行纵向合并
</li>
</ul>
</div> -->
</div>
</template>
<script setup>
import { ref } from "vue";
import { SunlightTable } from "sunlight-ui";
import { ElTag } from "element-plus";
// 表格数据
const tableData = ref([
{
id: 1,
name: "张三",
department: "技术部",
position: "高级前端开发",
salary: 15000,
status: "active",
},
{
id: 1,
name: "张三",
department: "技术部",
position: "高级前端开发",
salary: 15000,
status: "active",
},
{
id: 1,
name: "张三",
department: "技术部",
position: "高级前端开发",
salary: 15000,
status: "active",
},
{
id: 2,
name: "李四",
department: "技术部",
position: "前端开发",
salary: 9000,
status: "active",
},
{
id: 3,
name: "王五",
department: "技术部",
position: "高级后端开发",
salary: 14000,
status: "active",
},
{
id: 3,
name: "王五",
department: "技术部",
position: "高级后端开发",
salary: 14000,
status: "active",
},
{
id: 4,
name: "赵六",
department: "产品部",
position: "产品经理",
salary: 16000,
status: "active",
},
{
id: 4,
name: "赵六",
department: "产品部",
position: "产品经理",
salary: 16000,
status: "disabled",
},
{
id: 5,
name: "孙七",
department: "设计部",
position: "UI设计师",
salary: 10000,
status: "disabled",
},
{
id: 6,
name: "周八",
department: "运营部",
position: "运营专员",
salary: 8000,
status: "disabled",
},
]);
// 列配置
const columns = [
{ prop: "id", label: "ID", align: "center" },
{ prop: "name", label: "姓名" },
{ prop: "department", label: "部门" },
{ prop: "position", label: "职位" },
{ prop: "salary", label: "薪资", align: "right" },
{ prop: "status", label: "状态", align: "center", slotName: "status" },
];
// 表格属性配置
const tableProps = {
border: true,
// 复杂的合并单元格逻辑
spanMethod: ({ row, rowIndex, columnIndex }) => {
// 处理ID列合并(第一列)
if (columnIndex === 0) {
return mergeCellsByField(row, rowIndex, columnIndex, "id");
}
// 处理姓名列合并(第二列)
if (columnIndex === 1) {
return mergeCellsByField(row, rowIndex, columnIndex, "name");
}
// 处理部门列合并(第三列)
if (columnIndex === 2) {
return mergeCellsByField(row, rowIndex, columnIndex, "department");
}
// 处理职位列合并(第四列)- 只合并高级职位
if (columnIndex === 3) {
if (row.position.includes("高级")) {
return mergeCellsByField(row, rowIndex, columnIndex, "position");
}
return { rowspan: 1, colspan: 1 };
}
// 处理薪资列合并(第五列)- 只合并薪资大于10000的行
if (columnIndex === 4) {
if (row.salary > 10000) {
return mergeCellsByField(row, rowIndex, columnIndex, "salary");
}
return { rowspan: 1, colspan: 1 };
}
// 处理状态列合并(第六列)- 只合并启用状态
if (columnIndex === 5) {
if (row.status === "active") {
return mergeCellsByField(row, rowIndex, columnIndex, "status");
}
return { rowspan: 1, colspan: 1 };
}
// 默认不合并
return { rowspan: 1, colspan: 1 };
},
};
// 根据字段值合并单元格的通用方法
const mergeCellsByField = (row, rowIndex, columnIndex, field) => {
// 向上查找相同值的起始行
let startRow = rowIndex;
while (startRow > 0 && tableData.value[startRow - 1][field] === row[field]) {
startRow--;
}
// 向下查找相同值的结束行
let endRow = rowIndex;
while (endRow < tableData.value.length - 1 && tableData.value[endRow + 1][field] === row[field]) {
endRow++;
}
// 计算合并的行数
const rowspan = endRow - startRow + 1;
// 如果是起始行则合并,否则隐藏
if (startRow === rowIndex) {
return { rowspan, colspan: 1 };
} else {
return { rowspan: 0, colspan: 0 };
}
};
</script>
<style scoped>
.demo-container {
margin: 20px 0;
padding: 20px;
background: #f5f7fa;
border-radius: 8px;
}
.description {
margin-top: 20px;
padding: 15px;
background: #e6f7ff;
border-radius: 8px;
}
description h4 {
margin-top: 0;
}
.description ul {
margin-bottom: 0;
}
.description li {
margin: 5px 0;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
隐藏源代码
配置说明
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| tableProps.spanMethod | 单元格合并方法 | (params: { row: any; rowIndex: number; columnIndex: number }) => { rowspan: number; colspan: number } | - |
可编辑表格
SunlightTable 组件支持可编辑表格功能,可以直接在表格中编辑数据,支持多种编辑组件类型。
示例
请选择状态
请选择类型
请选择城市
暂无数据
共 20 条
- 1
- 2
页
vue
<template>
<div>
<!-- 配置化行内编辑:通过 columns 配置 editable 属性即可实现编辑功能 -->
<SunlightTable
:data="tableData"
:columns="columns"
:table-props="{ border: true }"
:show-pagination="true"
:current-page="currentPage"
:page-size="pageSize"
:total="total"
@update:current-page="currentPage = $event"
@update:page-size="pageSize = $event"
>
<template #action="{ row }">
<el-button type="primary" size="small" @click="handleSave(row)">保存</el-button>
<el-button size="small" @click="handleReset(row)">重置</el-button>
</template>
</SunlightTable>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { SunlightTable } from "sunlight-ui";
import { ElMessage } from "element-plus";
const departments = [
"技术部",
"产品部",
"运营部",
"市场部",
"人事部",
"财务部",
"销售部",
"客服部",
"设计部",
"测试部",
];
const managers = ["张总", "刘总", "陈总", "王总", "李总", "赵总", "孙总", "周总", "吴总", "郑总"];
const cities = ["北京", "上海", "广州", "深圳", "杭州", "南京", "武汉", "成都", "西安", "重庆"];
const statusOptions = ["active", "inactive"];
const types = ["研发", "运营", "销售", "管理", "支持"];
const allData = [
{
id: 1,
name: "技术部",
manager: "张总",
employeeCount: 20,
status: "active",
createDate: "2023-01-01",
city: "北京",
type: "研发",
budget: 500000,
phone: "13800138001",
email: "tech@company.com",
address: "北京市朝阳区科技园",
description: "负责公司技术研发工作",
},
{
id: 2,
name: "产品部",
manager: "刘总",
employeeCount: 15,
status: "active",
createDate: "2023-01-02",
city: "上海",
type: "管理",
budget: 400000,
phone: "13800138002",
email: "product@company.com",
address: "上海市浦东新区商务区",
description: "负责公司产品规划工作",
},
{
id: 3,
name: "运营部",
manager: "陈总",
employeeCount: 18,
status: "active",
createDate: "2023-01-03",
city: "广州",
type: "运营",
budget: 350000,
phone: "13800138003",
email: "operation@company.com",
address: "广州市天河区工业园",
description: "负责公司运营管理工作",
},
{
id: 4,
name: "市场部",
manager: "王总",
employeeCount: 12,
status: "active",
createDate: "2023-01-04",
city: "深圳",
type: "市场",
budget: 300000,
phone: "13800138004",
email: "marketing@company.com",
address: "深圳市南山区科技园",
description: "负责公司市场推广工作",
},
{
id: 5,
name: "人事部",
manager: "李总",
employeeCount: 8,
status: "active",
createDate: "2023-01-05",
city: "杭州",
type: "管理",
budget: 250000,
phone: "13800138005",
email: "hr@company.com",
address: "杭州市西湖区商务区",
description: "负责人力资源管理工作",
},
{
id: 6,
name: "财务部",
manager: "赵总",
employeeCount: 10,
status: "active",
createDate: "2023-01-06",
city: "南京",
type: "管理",
budget: 200000,
phone: "13800138006",
email: "finance@company.com",
address: "南京市玄武区工业园",
description: "负责财务管理工作",
},
{
id: 7,
name: "销售部",
manager: "孙总",
employeeCount: 25,
status: "active",
createDate: "2023-01-07",
city: "武汉",
type: "销售",
budget: 450000,
phone: "13800138007",
email: "sales@company.com",
address: "武汉市武昌区科技园",
description: "负责公司销售工作",
},
{
id: 8,
name: "客服部",
manager: "周总",
employeeCount: 16,
status: "active",
createDate: "2023-01-08",
city: "成都",
type: "支持",
budget: 280000,
phone: "13800138008",
email: "service@company.com",
address: "成都市锦江区商务区",
description: "负责客户服务工作",
},
{
id: 9,
name: "设计部",
manager: "吴总",
employeeCount: 14,
status: "active",
createDate: "2023-01-09",
city: "西安",
type: "研发",
budget: 320000,
phone: "13800138009",
email: "design@company.com",
address: "西安市雁塔区工业园",
description: "负责公司设计工作",
},
{
id: 10,
name: "测试部",
manager: "郑总",
employeeCount: 12,
status: "active",
createDate: "2023-01-10",
city: "重庆",
type: "支持",
budget: 260000,
phone: "13800138010",
email: "test@company.com",
address: "重庆市渝中区科技园",
description: "负责软件测试工作",
},
{
id: 11,
name: "研发部",
manager: "张总",
employeeCount: 30,
status: "active",
createDate: "2023-01-11",
city: "北京",
type: "研发",
budget: 550000,
phone: "13800138011",
email: "rd@company.com",
address: "北京市海淀区科技园",
description: "负责核心技术研发",
},
{
id: 12,
name: "数据部",
manager: "刘总",
employeeCount: 10,
status: "active",
createDate: "2023-01-12",
city: "上海",
type: "研发",
budget: 350000,
phone: "13800138012",
email: "data@company.com",
address: "上海市静安区商务区",
description: "负责数据管理和分析",
},
{
id: 13,
name: "运维部",
manager: "陈总",
employeeCount: 8,
status: "active",
createDate: "2023-01-13",
city: "广州",
type: "支持",
budget: 220000,
phone: "13800138013",
email: "ops@company.com",
address: "广州市番禺区工业园",
description: "负责系统运维工作",
},
{
id: 14,
name: "采购部",
manager: "王总",
employeeCount: 6,
status: "active",
createDate: "2023-01-14",
city: "深圳",
type: "管理",
budget: 180000,
phone: "13800138014",
email: "procurement@company.com",
address: "深圳市福田区商务区",
description: "负责采购管理工作",
},
{
id: 15,
name: "法务部",
manager: "李总",
employeeCount: 5,
status: "active",
createDate: "2023-01-15",
city: "杭州",
type: "管理",
budget: 150000,
phone: "13800138015",
email: "legal@company.com",
address: "杭州市余杭区科技园",
description: "负责法律事务工作",
},
{
id: 16,
name: "行政部",
manager: "赵总",
employeeCount: 7,
status: "active",
createDate: "2023-01-16",
city: "南京",
type: "管理",
budget: 160000,
phone: "13800138016",
email: "admin@company.com",
address: "南京市建邺区商务区",
description: "负责行政后勤工作",
},
{
id: 17,
name: "培训部",
manager: "孙总",
employeeCount: 4,
status: "active",
createDate: "2023-01-17",
city: "武汉",
type: "支持",
budget: 120000,
phone: "13800138017",
email: "training@company.com",
address: "武汉市洪山区工业园",
description: "负责员工培训工作",
},
{
id: 18,
name: "品牌部",
manager: "周总",
employeeCount: 9,
status: "active",
createDate: "2023-01-18",
city: "成都",
type: "市场",
budget: 240000,
phone: "13800138018",
email: "brand@company.com",
address: "成都市武侯区科技园",
description: "负责品牌建设工作",
},
{
id: 19,
name: "国际部",
manager: "吴总",
employeeCount: 11,
status: "active",
createDate: "2023-01-19",
city: "西安",
type: "销售",
budget: 380000,
phone: "13800138019",
email: "international@company.com",
address: "西安市未央区商务区",
description: "负责国际业务工作",
},
{
id: 20,
name: "创新部",
manager: "郑总",
employeeCount: 13,
status: "active",
createDate: "2023-01-20",
city: "重庆",
type: "研发",
budget: 400000,
phone: "13800138020",
email: "innovation@company.com",
address: "重庆市江北区工业园",
description: "负责创新项目研发",
},
];
const tableData = ref([]);
// 分页相关
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(20);
const originalData = ref({});
const columns = [
{
prop: "id",
label: "ID",
width: 80,
// editable 未设置,默认为不可编辑(只读)
},
{
prop: "name",
label: "部门名称",
width: 150,
editable: true, // 设置为可编辑
editType: "input", // 使用文本输入框
editProps: {
placeholder: "请输入部门名称",
maxlength: 50,
},
},
{
prop: "manager",
label: "负责人",
width: 120,
editable: true, // 设置为可编辑
editType: "input", // 使用文本输入框
editProps: {
placeholder: "请输入负责人",
},
},
{
prop: "employeeCount",
label: "人数",
width: 100,
editable: true, // 设置为可编辑
editType: "input-number", // 使用数字输入框
editProps: {
min: 0,
max: 9999,
precision: 0,
},
},
{
prop: "status",
label: "状态",
width: 120,
editable: true, // 设置为可编辑
editType: "select", // 使用下拉选择器
editOptions: [
{ label: "启用", value: "active" },
{ label: "禁用", value: "inactive" },
],
editProps: {
placeholder: "请选择状态",
},
},
{
prop: "type",
label: "类型",
width: 120,
editable: true, // 设置为可编辑
editType: "select", // 使用下拉选择器
editOptions: [
{ label: "研发", value: "研发" },
{ label: "运营", value: "运营" },
{ label: "销售", value: "销售" },
{ label: "管理", value: "管理" },
{ label: "支持", value: "支持" },
],
editProps: {
placeholder: "请选择类型",
},
},
{
prop: "city",
label: "城市",
width: 120,
editable: true, // 设置为可编辑
editType: "select", // 使用下拉选择器
editOptions: cities.map(city => ({ label: city, value: city })),
editProps: {
placeholder: "请选择城市",
},
},
{
prop: "budget",
label: "预算(元)",
width: 150,
editable: true, // 设置为可编辑
editType: "input-number", // 使用数字输入框
editProps: {
min: 0,
max: 10000000,
precision: 0,
step: 1000,
},
},
{
prop: "createDate",
label: "创建日期",
width: 150,
editable: true, // 设置为可编辑
editType: "date-picker", // 使用日期选择器
editProps: {
type: "date",
format: "YYYY-MM-DD",
valueFormat: "YYYY-MM-DD",
placeholder: "请选择日期",
},
},
{
prop: "phone",
label: "联系电话",
width: 150,
editable: true, // 设置为可编辑
editType: "input", // 使用文本输入框
editProps: {
placeholder: "请输入联系电话",
maxlength: 11,
},
},
{
prop: "email",
label: "邮箱",
width: 180,
editable: true, // 设置为可编辑
editType: "input", // 使用文本输入框
editProps: {
placeholder: "请输入邮箱地址",
type: "email",
},
},
{
prop: "address",
label: "地址",
width: 200,
editable: true, // 设置为可编辑
editType: "textarea", // 使用多行文本输入框
editProps: {
placeholder: "请输入地址",
rows: 2,
maxlength: 200,
},
},
{
prop: "description",
label: "描述",
width: 200,
// 不可编辑,只读显示
},
{
prop: "action",
label: "操作",
width: 180,
fixed: "right",
align: "center",
slotName: "action", // 操作列使用自定义插槽
},
];
// 模拟分页数据获取
function fetchTableData(page = 1, pageSizeVal = 10) {
return new Promise(resolve => {
setTimeout(() => {
const start = (page - 1) * pageSizeVal;
const end = start + pageSizeVal;
resolve({
data: allData.slice(start, end),
total: allData.length,
});
}, 300);
});
}
// 加载数据
const loadData = async () => {
const result = await fetchTableData(currentPage.value, pageSize.value);
tableData.value = result.data;
total.value = result.total;
// 每次加载数据后,重新保存原始数据
initOriginalData();
};
// 初始化时保存原始数据
const initOriginalData = () => {
tableData.value.forEach(row => {
originalData.value[row.id] = { ...row };
});
};
// 保存操作
const handleSave = row => {
if (!row.name || !row.name.trim()) {
ElMessage.warning("部门名称不能为空");
return;
}
if (!row.manager || !row.manager.trim()) {
ElMessage.warning("负责人不能为空");
return;
}
if (row.employeeCount < 0) {
ElMessage.warning("人数不能为负数");
return;
}
if (row.budget < 0) {
ElMessage.warning("预算不能为负数");
return;
}
// 这里可以调用 API 保存数据
console.log("保存数据:", row);
ElMessage.success("保存成功");
// 更新原始数据
originalData.value[row.id] = { ...row };
};
// 重置操作
const handleReset = row => {
if (originalData.value[row.id]) {
Object.assign(row, originalData.value[row.id]);
ElMessage.info("已重置为原始数据");
} else {
ElMessage.warning("没有可重置的数据");
}
};
// 监听分页变化
watch([currentPage, pageSize], () => {
loadData();
});
// 初始化加载数据
loadData();
</script>
<style scoped></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
隐藏源代码
配置说明
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| editable | 是否可编辑 | boolean | false |
| editType | 编辑组件类型 | 'input' | 'input-number' | 'select' | 'date-picker' | 'textarea' | 'input' |
| editProps | 编辑组件的属性配置 | Record<string, any> | - |
| editOptions | 选择器选项 | Array<{ label: string; value: any; disabled?: boolean }> | - |
支持的编辑类型
| 编辑类型 | 说明 | 适用场景 |
|---|---|---|
| input | 文本输入框 | 普通文本输入 |
| input-number | 数字输入框 | 数字类型输入 |
| select | 下拉选择器 | 从固定选项中选择 |
| date-picker | 日期选择器 | 日期类型输入 |
| textarea | 多行文本输入框 | 长文本输入 |
多级表头
SunlightTable 组件支持多级表头功能,可以实现多级表头的展示,支持嵌套表头、自定义宽度、对齐方式等高级功能。
示例
高级复杂表头示例
展示多层级复杂表头功能,包括嵌套表头、自定义宽度、对齐方式和合并单元格
vue
<template>
<div>
<h3>高级复杂表头示例</h3>
<p>展示多层级复杂表头功能,包括嵌套表头、自定义宽度、对齐方式和合并单元格</p>
<div>
<SunlightTable :data="tableData" :columns="columns" :table-props="tableProps" :show-selection="false">
<!-- 自定义操作列 -->
<template #action="{ row }">
<div class="action-buttons">
<el-button size="small" type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
</div>
</template>
</SunlightTable>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { SunlightTable } from "sunlight-ui";
import { ElButton } from "element-plus";
// 表格数据
const tableData = ref([
{
id: "2023001",
name: "张三",
gender: "男",
age: 18,
class: "高三(1)班",
chinese: 95,
math: 98,
english: 92,
physics: 90,
chemistry: 88,
history: 85,
geography: 82,
biology: 86,
politics: 89,
running: 80,
longJump: 78,
highJump: 75,
shotPut: 72,
music: 90,
painting: 85,
calligraphy: 88,
socialPractice: 92,
experiment: 89,
morality: 94,
behavior: 96,
teacherEvaluation: "该生学习认真,成绩优异,综合素质全面发展,是一名优秀的学生。",
studentSelfEvaluation: "我在学习上认真努力,积极参加各项活动,希望能够不断进步,成为更好的自己。",
},
{
id: "2023002",
name: "李四",
gender: "女",
age: 17,
class: "高三(1)班",
chinese: 92,
math: 95,
english: 90,
physics: 88,
chemistry: 86,
history: 90,
geography: 88,
biology: 92,
politics: 94,
running: 85,
longJump: 82,
highJump: 78,
shotPut: 75,
music: 92,
painting: 90,
calligraphy: 85,
socialPractice: 90,
experiment: 92,
morality: 95,
behavior: 97,
teacherEvaluation: "该生学习态度端正,成绩优秀,乐于助人,是同学们的好榜样。",
studentSelfEvaluation: "我会继续努力学习,提高自己的综合素质,为班级和学校争光。",
},
{
id: "2023003",
name: "王五",
gender: "男",
age: 18,
class: "高三(2)班",
chinese: 88,
math: 92,
english: 86,
physics: 90,
chemistry: 92,
history: 85,
geography: 83,
biology: 87,
politics: 88,
running: 88,
longJump: 85,
highJump: 80,
shotPut: 78,
music: 85,
painting: 82,
calligraphy: 80,
socialPractice: 88,
experiment: 90,
morality: 92,
behavior: 94,
teacherEvaluation: "该生学习刻苦,成绩良好,体育特长突出,希望能够平衡发展。",
studentSelfEvaluation: "我会在保持体育优势的同时,努力提高文化课成绩,争取全面发展。",
},
{
id: "2023004",
name: "赵六",
gender: "女",
age: 17,
class: "高三(2)班",
chinese: 90,
math: 88,
english: 92,
physics: 85,
chemistry: 83,
history: 92,
geography: 90,
biology: 88,
politics: 90,
running: 78,
longJump: 75,
highJump: 72,
shotPut: 70,
music: 95,
painting: 92,
calligraphy: 90,
socialPractice: 94,
experiment: 86,
morality: 93,
behavior: 95,
teacherEvaluation: "该生艺术天赋突出,学习成绩稳定,是一名有特长的优秀学生。",
studentSelfEvaluation: "我会继续发挥艺术特长,同时努力提高理科成绩,争取全面发展。",
},
]);
// 复杂表头配置
const columns = [
// 基础信息
{
label: "基础信息",
align: "center",
children: [
{ prop: "id", label: "学号", width: 120, align: "center" },
{ prop: "name", label: "姓名", width: 100, align: "center" },
{ prop: "gender", label: "性别", width: 80, align: "center" },
{ prop: "age", label: "年龄", width: 80, align: "center" },
{ prop: "class", label: "班级", width: 120, align: "center" },
],
},
// 学习成绩
{
label: "学习成绩",
align: "center",
children: [
{
label: "主要课程",
align: "center",
children: [
{ prop: "chinese", label: "语文", width: 80, align: "center" },
{ prop: "math", label: "数学", width: 80, align: "center" },
{ prop: "english", label: "英语", width: 80, align: "center" },
{ prop: "physics", label: "物理", width: 80, align: "center" },
{ prop: "chemistry", label: "化学", width: 80, align: "center" },
],
},
{
label: "选修课程",
align: "center",
children: [
{ prop: "history", label: "历史", width: 80, align: "center" },
{ prop: "geography", label: "地理", width: 80, align: "center" },
{ prop: "biology", label: "生物", width: 80, align: "center" },
{ prop: "politics", label: "政治", width: 80, align: "center" },
],
},
],
},
// 综合素质
{
label: "综合素质",
align: "center",
children: [
{
label: "体育",
align: "center",
children: [
{ prop: "running", label: "跑步", width: 80, align: "center" },
{ prop: "longJump", label: "跳远", width: 80, align: "center" },
{ prop: "highJump", label: "跳高", width: 80, align: "center" },
{ prop: "shotPut", label: "铅球", width: 80, align: "center" },
],
},
{
label: "艺术",
align: "center",
children: [
{ prop: "music", label: "音乐", width: 80, align: "center" },
{ prop: "painting", label: "绘画", width: 80, align: "center" },
{ prop: "calligraphy", label: "书法", width: 80, align: "center" },
],
},
{
label: "实践",
align: "center",
children: [
{ prop: "socialPractice", label: "社会实践", width: 100, align: "center" },
{ prop: "experiment", label: "实验操作", width: 100, align: "center" },
],
},
{
label: "德育",
align: "center",
children: [
{ prop: "morality", label: "思想品德", width: 100, align: "center" },
{ prop: "behavior", label: "行为规范", width: 100, align: "center" },
],
},
],
},
{
label: "评价",
align: "center",
children: [
{ prop: "teacherEvaluation", label: "教师评价", minWidth: 200, align: "left" },
{ prop: "studentSelfEvaluation", label: "学生自评", minWidth: 200, align: "left" },
],
},
// 操作列
{ prop: "action", label: "操作", width: 150, align: "center", slotName: "action" },
];
// 表格属性配置
const tableProps = {
border: true,
stripe: true,
height: 600,
};
// 编辑操作
const handleEdit = row => {
console.log("编辑:", row);
// 这里可以实现编辑逻辑
};
// 删除操作
const handleDelete = row => {
console.log("删除:", row);
// 这里可以实现删除逻辑
};
</script>
<style scoped>
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
隐藏源代码
配置说明
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| children | 子列,用于复杂表头合并 | TableItem[] | - |
高级搜索
SunlightTable 组件支持高级搜索功能,可以通过配置搜索表单实现复杂的搜索条件,包括多条件搜索、异步选项加载、搜索列配置等功能。
示例
高级搜索示例
展示 SunlightTable 组件的高级搜索功能,包括多条件搜索、异步选项加载、搜索列配置等
未激活
暂无数据
共 100 条
- 1
- 2
- 3
- 4
- 5
- 6
- 10
页
vue
<template>
<div>
<h3>高级搜索示例</h3>
<p>展示 SunlightTable 组件的高级搜索功能,包括多条件搜索、异步选项加载、搜索列配置等</p>
<div>
<SunlightTable
:data="tableData"
:columns="columns"
:table-props="tableProps"
:show-pagination="true"
v-model:current-page="currentPage"
v-model:page-size="pageSize"
v-model:hide-columns="hideColumns"
:total="total"
:search-columns="searchColumns"
v-model:search-param="searchParam"
v-model:visible-search-columns="visibleSearchColumns"
:search-collapsed="searchCollapsed"
:search-theme-style="searchThemeStyle"
@search="handleSearch"
@reset="handleReset"
>
<template #isActive="{ row }">
<el-tag :type="row.isActive ? 'success' : 'danger'" size="small">
{{ row.isActive ? "已激活" : "未激活" }}
</el-tag>
</template>
<template #tags="{ row }">
<el-tag v-for="tag in row.tags" :key="tag" size="small" class="tag-item">
{{ tag }}
</el-tag>
</template>
</SunlightTable>
</div>
</div>
</template>
<script setup>
import { ref, reactive, computed } from "vue";
import { SunlightTable } from "sunlight-ui";
import { ElTag } from "element-plus";
const toolbarConfig = reactive({
showDensity: true,
showColumnConfig: true,
showRefresh: true,
showSearchConfig: true,
});
// 搜索主题样式
const searchThemeStyle = computed(() => ({
borderColor: "#409eff",
focusBorderColor: "#409eff",
focusBoxShadow: `0 0 0 3px #409eff26`,
}));
// 可见的搜索列
const visibleSearchColumns = ref(["name", "status", "department", "city", "position", "hireDate", "isActive"]);
// 搜索是否折叠
const searchCollapsed = ref(true);
// 搜索参数
const searchParam = reactive({
name: "",
status: null,
department: null,
city: null,
position: [],
hireDate: null,
isActive: null,
});
// 异步加载城市选项
async function fetchCityOptions() {
await new Promise(resolve => setTimeout(resolve, 500));
return [
{ label: "北京", value: "北京" },
{ label: "上海", value: "上海" },
{ label: "广州", value: "广州" },
{ label: "深圳", value: "深圳" },
{ label: "杭州", value: "杭州" },
{ label: "南京", value: "南京" },
{ label: "武汉", value: "武汉" },
{ label: "成都", value: "成都" },
{ label: "西安", value: "西安" },
{ label: "重庆", value: "重庆" },
];
}
// 搜索列配置
const searchColumns = [
{
prop: "name",
label: "用户姓名",
type: "input",
placeholder: "请输入姓名",
span: 6,
attrs: {
clearable: true,
},
},
{
prop: "status",
label: "员工状态",
type: "select",
placeholder: "请选择状态",
span: 6,
options: [
{ label: "在职", value: "在职" },
{ label: "离职", value: "离职" },
{ label: "休假中", value: "休假中" },
{ label: "试用期", value: "试用期" },
],
clearable: true,
},
{
prop: "department",
label: "部门",
type: "select",
placeholder: "请选择部门",
span: 6,
options: [
{ label: "技术部", value: "技术部" },
{ label: "市场部", value: "市场部" },
{ label: "销售部", value: "销售部" },
{ label: "人事部", value: "人事部" },
{ label: "财务部", value: "财务部" },
],
clearable: true,
},
{
prop: "city",
label: "城市",
type: "select",
placeholder: "请选择城市(异步加载)",
span: 6,
options: () => fetchCityOptions(),
clearable: true,
},
{
prop: "position",
label: "职位",
type: "select",
placeholder: "请选择职位",
span: 6,
multiple: true,
options: [
{ label: "员工", value: "员工" },
{ label: "主管", value: "主管" },
{ label: "经理", value: "经理" },
{ label: "总监", value: "总监" },
],
clearable: true,
},
{
prop: "isActive",
label: "账户状态",
type: "select",
placeholder: "请选择账户状态",
span: 6,
multiple: true,
options: [
{ label: "已激活", value: true },
{ label: "未激活", value: false },
],
clearable: true,
},
{
prop: "hireDate",
label: "入职日期",
type: "date-picker",
placeholder: "请选择入职日期",
span: 6,
config: {
type: "date",
format: "YYYY-MM-DD",
valueFormat: "YYYY-MM-DD",
},
},
];
// 表格数据
const tableData = ref([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(100);
const hideColumns = ref([]);
// 表格属性
const tableProps = {
border: true,
stripe: true,
};
// 列配置
const columns = [
{ prop: "id", label: "ID", width: 80 },
{ prop: "name", label: "姓名" },
{ prop: "age", label: "年龄", width: 100 },
{ prop: "city", label: "城市", width: 120 },
{ prop: "department", label: "部门", width: 120 },
{ prop: "position", label: "职位", width: 120 },
{ prop: "salary", label: "薪资", width: 120 },
{ prop: "hireDate", label: "入职日期", width: 120 },
{
prop: "isActive",
label: "账户状态",
width: 100,
slotName: "isActive",
},
{
prop: "tags",
label: "技能标签",
width: 200,
slotName: "tags",
},
];
// 模拟数据生成
const surnames = ["张", "王", "李", "赵", "刘", "陈", "杨", "黄", "周", "吴"];
const names = ["伟", "芳", "娜", "敏", "静", "丽", "强", "磊", "军", "洋"];
const cities = ["北京", "上海", "广州", "深圳", "杭州", "南京", "武汉", "成都", "西安", "重庆"];
const departments = ["技术部", "市场部", "销售部", "人事部", "财务部", "运营部"];
const positions = ["员工", "主管", "经理", "总监"];
const allSkills = ["JavaScript", "TypeScript", "Vue", "React", "Node.js", "Python", "Java", "C++"];
// 生成模拟数据
const allData = [];
for (let i = 1; i <= 100; i++) {
const surname = surnames[Math.floor(Math.random() * surnames.length)];
const name = names[Math.floor(Math.random() * names.length)];
const dept = departments[Math.floor(Math.random() * departments.length)];
const salary = 5000 + Math.floor(Math.random() * 20000);
const age = 22 + Math.floor(Math.random() * 20);
let status;
const statusRand = Math.random();
if (statusRand < 0.6) {
status = "在职";
} else if (statusRand < 0.75) {
status = "离职";
} else if (statusRand < 0.85) {
status = "休假中";
} else {
status = "试用期";
}
const hireYear = 2020 + Math.floor(Math.random() * 5);
const hireMonth = 1 + Math.floor(Math.random() * 12);
const hireDay = 1 + Math.floor(Math.random() * 28);
const hireDate = `${hireYear}-${String(hireMonth).padStart(2, "0")}-${String(hireDay).padStart(2, "0")}`;
const skillCount = 1 + Math.floor(Math.random() * 4);
const skills = [];
const shuffledSkills = [...allSkills].sort(() => Math.random() - 0.5);
for (let j = 0; j < skillCount; j++) {
skills.push(shuffledSkills[j]);
}
allData.push({
id: i,
name: `${surname}${name}`,
age,
city: cities[Math.floor(Math.random() * cities.length)],
department: dept,
position: positions[Math.floor(Math.random() * positions.length)],
salary,
status,
hireDate,
tags: skills,
isActive: Math.random() > 0.2,
});
}
// 模拟API请求
function fetchTableData(page = 1, pageSizeVal = 10, searchData = {}) {
return new Promise(resolve => {
setTimeout(() => {
let filteredData = [...allData];
// 应用搜索过滤
if (searchData.name) {
filteredData = filteredData.filter(item => item.name.includes(searchData.name));
}
if (searchData.status) {
filteredData = filteredData.filter(item => item.status === searchData.status);
}
if (searchData.department) {
filteredData = filteredData.filter(item => item.department === searchData.department);
}
if (searchData.city) {
filteredData = filteredData.filter(item => item.city === searchData.city);
}
if (searchData.position && searchData.position.length > 0) {
filteredData = filteredData.filter(item => searchData.position.includes(item.position));
}
if (searchData.hireDate) {
filteredData = filteredData.filter(item => item.hireDate >= searchData.hireDate);
}
if (searchData.isActive !== null && searchData.isActive !== undefined) {
filteredData = filteredData.filter(item => item.isActive === searchData.isActive);
}
// 分页处理
const start = (page - 1) * pageSizeVal;
const end = start + pageSizeVal;
resolve({
data: filteredData.slice(start, end),
total: filteredData.length,
});
}, 300);
});
}
// 加载数据
const loadData = async () => {
const result = await fetchTableData(currentPage.value, pageSize.value, searchParam);
tableData.value = result.data;
total.value = result.total;
};
// 搜索事件处理
const handleSearch = searchData => {
currentPage.value = 1;
fetchTableData(currentPage.value, pageSize.value, searchData).then(result => {
tableData.value = result.data;
total.value = result.total;
});
};
// 重置事件处理
const handleReset = () => {
// 重置搜索参数
Object.keys(searchParam).forEach(key => {
if (Array.isArray(searchParam[key])) {
searchParam[key] = [];
} else if (typeof searchParam[key] === "string") {
searchParam[key] = "";
} else {
searchParam[key] = null;
}
});
// 重新加载数据
currentPage.value = 1;
loadData();
};
// 初始加载数据
loadData();
</script>
<style scoped>
.tag-item {
margin-right: 8px;
margin-bottom: 8px;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
隐藏源代码
配置说明
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| visibleSearchColumns | 可见的搜索列 | string[] | - |
| searchCollapsed | 搜索表单是否收起 | boolean | false |
| searchThemeStyle | 搜索表单主题样式 | { borderColor?: string; focusBorderColor?: string; focusBoxShadow?: string; backgroundColor?: string; boxShadow?: string } | - |
| search-columns | 搜索列配置 | SearchColumn[] | - |
| search-param | 搜索参数 | Record<string, any> | - |
搜索列配置说明
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| prop | 字段名 | string | - |
| label | 标签名 | string | - |
| type | 输入类型 | 'input' | 'select' | 'date-picker' | 'input-number' | 'textarea' | 'input' |
| placeholder | 占位符 | string | string[] | - |
| span | 所占列数 | number | 6 |
| options | 选项列表或异步函数 | Array<{ label: string; value: any }> | () => Promise<any[]> | - |
| multiple | 是否支持多选 | boolean | false |
| clearable | 是否支持清空 | boolean | false |
| disableToggle | 是否禁用隐藏 | boolean | false |
| attrs | 额外属性 | Record<string, any> | - |
| config | 组件配置 | Record<string, any> | - |