vue2server7 7.0.108 → 7.0.110
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/docs/superpowers/plans/2026-05-13-table-column-config-demo-plan.md +275 -0
- package/docs/superpowers/plans/2026-05-13-table-column-settings-plan.md +443 -0
- package/docs/superpowers/specs/2026-05-13-table-column-config-demo-design.md +74 -0
- package/docs/superpowers/specs/2026-05-13-table-column-settings-component.md +69 -0
- package/frontEnd/src/components/BaseTable.vue +296 -0
- package/frontEnd/src/components/TableColumnSettings.vue +296 -0
- package/frontEnd/src/pages/BaseTableDemoPage.vue +236 -0
- package/frontEnd/src/pages/ColumnConfigDemoPage.vue +166 -0
- package/frontEnd/src/router/routes.js +20 -0
- package/package.json +1 -1
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# 表格列配置演示页面 Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** 在前端项目中新增一个"表格列配置演示"路由页面,提供基础的表格框架和预留列配置扩展点
|
|
6
|
+
|
|
7
|
+
**Architecture:** 遵循现有项目结构,创建新的 Vue 页面组件并添加路由配置,保持与现有页面一致的代码风格
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Vue 3 + Composition API + TypeScript + Element Plus
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 文件结构
|
|
14
|
+
|
|
15
|
+
| 文件 | 操作 | 说明 |
|
|
16
|
+
|------|------|------|
|
|
17
|
+
| `frontEnd/src/pages/ColumnConfigDemoPage.vue` | 创建 | 表格列配置演示页面 |
|
|
18
|
+
| `frontEnd/src/router/routes.js` | 修改 | 添加新路由配置 |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
### Task 1: 创建列配置演示页面组件
|
|
23
|
+
|
|
24
|
+
**Files:**
|
|
25
|
+
- Create: `frontEnd/src/pages/ColumnConfigDemoPage.vue`
|
|
26
|
+
|
|
27
|
+
- [ ] **Step 1: 创建基础页面模板和脚本**
|
|
28
|
+
|
|
29
|
+
```vue
|
|
30
|
+
<template>
|
|
31
|
+
<section class="page column-config-demo-page">
|
|
32
|
+
<h1 class="title">表格列配置演示</h1>
|
|
33
|
+
|
|
34
|
+
<div class="toolbar">
|
|
35
|
+
<el-button type="primary">查询</el-button>
|
|
36
|
+
<el-button>重置</el-button>
|
|
37
|
+
|
|
38
|
+
<!-- 列设置按钮 - 预留位置,用户自行实现弹窗逻辑 -->
|
|
39
|
+
<el-popover placement="bottom" trigger="click" width="300">
|
|
40
|
+
<template #reference>
|
|
41
|
+
<el-button>列设置</el-button>
|
|
42
|
+
</template>
|
|
43
|
+
<!-- 列配置内容区域 - 用户自行开发 -->
|
|
44
|
+
<div class="column-settings-panel">
|
|
45
|
+
<p>列配置区域</p>
|
|
46
|
+
<p>请在此处实现列配置功能</p>
|
|
47
|
+
</div>
|
|
48
|
+
</el-popover>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<el-table
|
|
52
|
+
:key="tableKey"
|
|
53
|
+
:data="tableData"
|
|
54
|
+
border
|
|
55
|
+
stripe
|
|
56
|
+
size="small"
|
|
57
|
+
style="width: 100%"
|
|
58
|
+
>
|
|
59
|
+
<el-table-column type="index" label="序号" width="60" align="center" />
|
|
60
|
+
<el-table-column
|
|
61
|
+
v-for="col in visibleColumns"
|
|
62
|
+
:key="col.key"
|
|
63
|
+
:prop="col.prop"
|
|
64
|
+
:label="col.label"
|
|
65
|
+
:width="col.width"
|
|
66
|
+
:min-width="col.minWidth"
|
|
67
|
+
:align="col.align || 'left'"
|
|
68
|
+
:fixed="col.fixed || false"
|
|
69
|
+
>
|
|
70
|
+
</el-table-column>
|
|
71
|
+
</el-table>
|
|
72
|
+
|
|
73
|
+
<div class="pagination">
|
|
74
|
+
<el-pagination
|
|
75
|
+
layout="total, prev, pager, next"
|
|
76
|
+
:current-page="page"
|
|
77
|
+
:page-size="pageSize"
|
|
78
|
+
:total="total"
|
|
79
|
+
@current-change="onPageChange"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
</section>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<script setup lang="ts">
|
|
86
|
+
import { ref, reactive, computed, onMounted } from 'vue'
|
|
87
|
+
|
|
88
|
+
// 列配置接口
|
|
89
|
+
interface ColumnConfig {
|
|
90
|
+
key: string
|
|
91
|
+
prop: string
|
|
92
|
+
label: string
|
|
93
|
+
width?: number
|
|
94
|
+
minWidth?: number
|
|
95
|
+
align?: 'left' | 'center' | 'right'
|
|
96
|
+
fixed?: boolean | 'left' | 'right'
|
|
97
|
+
visible?: boolean
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 表格数据接口
|
|
101
|
+
interface TableRow {
|
|
102
|
+
id: number
|
|
103
|
+
name: string
|
|
104
|
+
department: string
|
|
105
|
+
position: string
|
|
106
|
+
phone: string
|
|
107
|
+
email: string
|
|
108
|
+
joinDate: string
|
|
109
|
+
status: string
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 响应式数据
|
|
113
|
+
const tableKey = ref(0)
|
|
114
|
+
const page = ref(1)
|
|
115
|
+
const pageSize = ref(10)
|
|
116
|
+
const total = ref(50)
|
|
117
|
+
const tableData = ref<TableRow[]>([])
|
|
118
|
+
|
|
119
|
+
// 列配置 - 可在此基础上扩展更多属性
|
|
120
|
+
const columns = reactive<ColumnConfig[]>([
|
|
121
|
+
{ key: 'name', prop: 'name', label: '姓名', width: 100 },
|
|
122
|
+
{ key: 'department', prop: 'department', label: '部门', width: 120 },
|
|
123
|
+
{ key: 'position', prop: 'position', label: '职位', width: 120 },
|
|
124
|
+
{ key: 'phone', prop: 'phone', label: '电话', width: 140 },
|
|
125
|
+
{ key: 'email', prop: 'email', label: '邮箱', minWidth: 180 },
|
|
126
|
+
{ key: 'joinDate', prop: 'joinDate', label: '入职日期', width: 120, align: 'center' },
|
|
127
|
+
{ key: 'status', prop: 'status', label: '状态', width: 100, align: 'center' }
|
|
128
|
+
])
|
|
129
|
+
|
|
130
|
+
// 可见列计算 - 预留,用户可自行控制显示/隐藏
|
|
131
|
+
const visibleColumns = computed(() => {
|
|
132
|
+
return columns.filter(col => col.visible !== false)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// 分页切换
|
|
136
|
+
const onPageChange = (p: number) => {
|
|
137
|
+
page.value = p
|
|
138
|
+
loadData()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 加载模拟数据
|
|
142
|
+
const loadData = () => {
|
|
143
|
+
const data: TableRow[] = []
|
|
144
|
+
for (let i = 0; i < pageSize.value; i++) {
|
|
145
|
+
const index = (page.value - 1) * pageSize.value + i + 1
|
|
146
|
+
data.push({
|
|
147
|
+
id: index,
|
|
148
|
+
name: `员工${index}`,
|
|
149
|
+
department: ['技术部', '产品部', '运营部', '市场部'][index % 4],
|
|
150
|
+
position: ['开发工程师', '产品经理', '运营专员', '市场专员'][index % 4],
|
|
151
|
+
phone: `138${String(index).padStart(8, '0')}`,
|
|
152
|
+
email: `employee${index}@example.com`,
|
|
153
|
+
joinDate: `2024-${String((index % 12) + 1).padStart(2, '0')}-${String((index % 28) + 1).padStart(2, '0')}`,
|
|
154
|
+
status: ['在职', '离职', '休假'][index % 3]
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
tableData.value = data
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
onMounted(() => {
|
|
161
|
+
loadData()
|
|
162
|
+
})
|
|
163
|
+
</script>
|
|
164
|
+
|
|
165
|
+
<style scoped>
|
|
166
|
+
.page.column-config-demo-page {
|
|
167
|
+
padding: 16px;
|
|
168
|
+
}
|
|
169
|
+
.title {
|
|
170
|
+
font-size: 18px;
|
|
171
|
+
margin-bottom: 12px;
|
|
172
|
+
}
|
|
173
|
+
.toolbar {
|
|
174
|
+
margin-bottom: 12px;
|
|
175
|
+
}
|
|
176
|
+
.pagination {
|
|
177
|
+
margin-top: 12px;
|
|
178
|
+
text-align: right;
|
|
179
|
+
}
|
|
180
|
+
.column-settings-panel {
|
|
181
|
+
padding: 8px 0;
|
|
182
|
+
}
|
|
183
|
+
</style>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- [ ] **Step 2: 验证文件创建成功**
|
|
187
|
+
|
|
188
|
+
检查文件是否存在:
|
|
189
|
+
```bash
|
|
190
|
+
ls -la frontEnd/src/pages/ColumnConfigDemoPage.vue
|
|
191
|
+
```
|
|
192
|
+
Expected: 文件存在,大小约 3-4KB
|
|
193
|
+
|
|
194
|
+
- [ ] **Step 3: 提交**
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
git add frontEnd/src/pages/ColumnConfigDemoPage.vue
|
|
198
|
+
git commit -m "feat: add column config demo page skeleton"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### Task 2: 添加路由配置
|
|
204
|
+
|
|
205
|
+
**Files:**
|
|
206
|
+
- Modify: `frontEnd/src/router/routes.js:8-87`
|
|
207
|
+
|
|
208
|
+
- [ ] **Step 1: 修改 routes.js 添加导入和路由配置**
|
|
209
|
+
|
|
210
|
+
在文件顶部导入语句区域添加(第8行后):
|
|
211
|
+
```javascript
|
|
212
|
+
import ColumnConfigDemoPage from '../pages/ColumnConfigDemoPage.vue'
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
在 routes 数组末尾添加新路由(第86行前):
|
|
216
|
+
```javascript
|
|
217
|
+
{
|
|
218
|
+
path: '/column-config-demo',
|
|
219
|
+
name: 'ColumnConfigDemo',
|
|
220
|
+
component: ColumnConfigDemoPage,
|
|
221
|
+
meta: {
|
|
222
|
+
title: '表格列配置演示',
|
|
223
|
+
showInMenu: true
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
- [ ] **Step 2: 验证路由配置**
|
|
229
|
+
|
|
230
|
+
检查文件内容:
|
|
231
|
+
```bash
|
|
232
|
+
grep -n "ColumnConfigDemo" frontEnd/src/router/routes.js
|
|
233
|
+
```
|
|
234
|
+
Expected: 至少两行输出(导入语句和路由配置)
|
|
235
|
+
|
|
236
|
+
- [ ] **Step 3: 提交**
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
git add frontEnd/src/router/routes.js
|
|
240
|
+
git commit -m "feat: add column config demo route"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### Task 3: 验证页面功能
|
|
246
|
+
|
|
247
|
+
**Files:**
|
|
248
|
+
- Test: 手动运行前端项目验证
|
|
249
|
+
|
|
250
|
+
- [ ] **Step 1: 启动前端开发服务器**
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
npm run front-dev
|
|
254
|
+
```
|
|
255
|
+
Expected: Vite 服务器启动成功,无编译错误
|
|
256
|
+
|
|
257
|
+
- [ ] **Step 2: 手动验证**
|
|
258
|
+
1. 浏览器访问页面
|
|
259
|
+
2. 检查侧边栏菜单是否显示"表格列配置演示"
|
|
260
|
+
3. 点击菜单项,页面是否正常加载
|
|
261
|
+
4. 检查表格数据是否显示
|
|
262
|
+
5. 检查分页是否正常工作
|
|
263
|
+
6. 检查"列设置"按钮是否可点击
|
|
264
|
+
|
|
265
|
+
- [ ] **Step 3: 提交验证完成(无代码变更)**
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 计划完成检查清单
|
|
270
|
+
|
|
271
|
+
- [x] 所有文件路径正确
|
|
272
|
+
- [x] 代码示例完整无占位符
|
|
273
|
+
- [x] 命令和预期输出明确
|
|
274
|
+
- [x] 与现有项目风格一致
|
|
275
|
+
- [x] 预留了用户自定义扩展点
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
# 表格列配置组件 Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** 集成 TableColumnSettings 列配置组件到项目中,并在演示页面使用
|
|
6
|
+
|
|
7
|
+
**Architecture:** 创建独立组件文件,在演示页面中引入,实现动态列显示/隐藏功能
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Vue 3 + Composition API + Element Plus
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 文件结构
|
|
14
|
+
|
|
15
|
+
| 文件 | 操作 | 说明 |
|
|
16
|
+
|------|------|------|
|
|
17
|
+
| `frontEnd/src/components/TableColumnSettings.vue` | 创建 | 表格列配置组件 |
|
|
18
|
+
| `frontEnd/src/pages/ColumnConfigDemoPage.vue` | 修改/创建 | 演示页面集成组件 |
|
|
19
|
+
| `frontEnd/src/router/routes.js` | 修改 | 添加路由(如未添加) |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### Task 1: 创建 TableColumnSettings 组件
|
|
24
|
+
|
|
25
|
+
**Files:**
|
|
26
|
+
- Create: `frontEnd/src/components/TableColumnSettings.vue`
|
|
27
|
+
|
|
28
|
+
- [ ] **Step 1: 创建组件文件**
|
|
29
|
+
|
|
30
|
+
```vue
|
|
31
|
+
<template>
|
|
32
|
+
<el-button @click="visible = true">
|
|
33
|
+
<el-icon><Setting /></el-icon>
|
|
34
|
+
列配置
|
|
35
|
+
</el-button>
|
|
36
|
+
|
|
37
|
+
<el-dialog
|
|
38
|
+
v-model="visible"
|
|
39
|
+
title="表格列配置"
|
|
40
|
+
width="760px"
|
|
41
|
+
append-to-body
|
|
42
|
+
>
|
|
43
|
+
<div class="desc">请选择需要在表格中显示的数据列</div>
|
|
44
|
+
|
|
45
|
+
<div class="column-box">
|
|
46
|
+
<div class="check-all-row">
|
|
47
|
+
<el-checkbox
|
|
48
|
+
v-model="checkAll"
|
|
49
|
+
:indeterminate="isIndeterminate"
|
|
50
|
+
@change="handleCheckAllChange"
|
|
51
|
+
>
|
|
52
|
+
全选
|
|
53
|
+
</el-checkbox>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<el-checkbox-group
|
|
57
|
+
v-model="checkedKeys"
|
|
58
|
+
class="column-list"
|
|
59
|
+
@change="updateCheckAllStatus"
|
|
60
|
+
>
|
|
61
|
+
<el-checkbox
|
|
62
|
+
v-for="item in columns"
|
|
63
|
+
:key="item.prop"
|
|
64
|
+
:label="item.prop"
|
|
65
|
+
:disabled="item.disabled"
|
|
66
|
+
>
|
|
67
|
+
{{ item.label }}
|
|
68
|
+
</el-checkbox>
|
|
69
|
+
</el-checkbox-group>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<template #footer>
|
|
73
|
+
<el-button @click="visible = false">取消</el-button>
|
|
74
|
+
<el-button type="primary" @click="handleConfirm">
|
|
75
|
+
确认
|
|
76
|
+
</el-button>
|
|
77
|
+
</template>
|
|
78
|
+
</el-dialog>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<script setup>
|
|
82
|
+
import { ref, computed, watch } from 'vue'
|
|
83
|
+
import { Setting } from '@element-plus/icons-vue'
|
|
84
|
+
|
|
85
|
+
const props = defineProps({
|
|
86
|
+
columns: {
|
|
87
|
+
type: Array,
|
|
88
|
+
default: () => []
|
|
89
|
+
},
|
|
90
|
+
selectedKeys: {
|
|
91
|
+
type: Array,
|
|
92
|
+
default: () => []
|
|
93
|
+
},
|
|
94
|
+
buttonText: {
|
|
95
|
+
type: String,
|
|
96
|
+
default: '列配置'
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const emit = defineEmits(['confirm'])
|
|
101
|
+
|
|
102
|
+
const visible = ref(false)
|
|
103
|
+
const checkedKeys = ref([])
|
|
104
|
+
const checkAll = ref(false)
|
|
105
|
+
const isIndeterminate = ref(false)
|
|
106
|
+
|
|
107
|
+
const enabledKeys = computed(() => {
|
|
108
|
+
return props.columns
|
|
109
|
+
.filter(item => !item.disabled)
|
|
110
|
+
.map(item => item.prop)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
watch(visible, val => {
|
|
114
|
+
if (val) {
|
|
115
|
+
checkedKeys.value = [...props.selectedKeys]
|
|
116
|
+
updateCheckAllStatus()
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
function handleCheckAllChange(val) {
|
|
121
|
+
const disabledCheckedKeys = props.columns
|
|
122
|
+
.filter(item => item.disabled && checkedKeys.value.includes(item.prop))
|
|
123
|
+
.map(item => item.prop)
|
|
124
|
+
|
|
125
|
+
checkedKeys.value = val
|
|
126
|
+
? [...new Set([...disabledCheckedKeys, ...enabledKeys.value])]
|
|
127
|
+
: disabledCheckedKeys
|
|
128
|
+
|
|
129
|
+
updateCheckAllStatus()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function updateCheckAllStatus() {
|
|
133
|
+
const count = checkedKeys.value.filter(key =>
|
|
134
|
+
enabledKeys.value.includes(key)
|
|
135
|
+
).length
|
|
136
|
+
|
|
137
|
+
checkAll.value = count === enabledKeys.value.length
|
|
138
|
+
isIndeterminate.value = count > 0 && count < enabledKeys.value.length
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function handleConfirm() {
|
|
142
|
+
emit('confirm', checkedKeys.value)
|
|
143
|
+
visible.value = false
|
|
144
|
+
}
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<style scoped>
|
|
148
|
+
.desc {
|
|
149
|
+
margin-bottom: 22px;
|
|
150
|
+
color: #666;
|
|
151
|
+
font-size: 16px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.column-box {
|
|
155
|
+
border: 1px solid #dcdfe6;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.check-all-row {
|
|
159
|
+
padding: 18px 24px;
|
|
160
|
+
border-bottom: 1px solid #dcdfe6;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.column-list {
|
|
164
|
+
padding: 22px 24px;
|
|
165
|
+
display: flex;
|
|
166
|
+
flex-wrap: wrap;
|
|
167
|
+
gap: 22px 28px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
:deep(.el-checkbox) {
|
|
171
|
+
margin-right: 0;
|
|
172
|
+
}
|
|
173
|
+
</style>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- [ ] **Step 2: 验证文件创建成功**
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
ls -la frontEnd/src/components/TableColumnSettings.vue
|
|
180
|
+
```
|
|
181
|
+
Expected: 文件存在
|
|
182
|
+
|
|
183
|
+
- [ ] **Step 3: 提交**
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
git add frontEnd/src/components/TableColumnSettings.vue
|
|
187
|
+
git commit -m "feat: add TableColumnSettings component"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### Task 2: 创建/更新 ColumnConfigDemoPage 页面
|
|
193
|
+
|
|
194
|
+
**Files:**
|
|
195
|
+
- Create/Modify: `frontEnd/src/pages/ColumnConfigDemoPage.vue`
|
|
196
|
+
|
|
197
|
+
- [ ] **Step 1: 创建完整的演示页面(如果文件不存在则创建)**
|
|
198
|
+
|
|
199
|
+
```vue
|
|
200
|
+
<template>
|
|
201
|
+
<section class="page column-config-demo-page">
|
|
202
|
+
<h1 class="title">表格列配置演示</h1>
|
|
203
|
+
|
|
204
|
+
<div class="toolbar">
|
|
205
|
+
<el-button type="primary">查询</el-button>
|
|
206
|
+
<el-button>重置</el-button>
|
|
207
|
+
|
|
208
|
+
<!-- 列配置组件 -->
|
|
209
|
+
<TableColumnSettings
|
|
210
|
+
:columns="columnSettings"
|
|
211
|
+
:selected-keys="selectedKeys"
|
|
212
|
+
@confirm="onColumnConfirm"
|
|
213
|
+
/>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<el-table
|
|
217
|
+
:key="tableKey"
|
|
218
|
+
:data="tableData"
|
|
219
|
+
border
|
|
220
|
+
stripe
|
|
221
|
+
size="small"
|
|
222
|
+
style="width: 100%"
|
|
223
|
+
>
|
|
224
|
+
<el-table-column type="index" label="序号" width="60" align="center" />
|
|
225
|
+
<el-table-column
|
|
226
|
+
v-for="col in visibleColumns"
|
|
227
|
+
:key="col.prop"
|
|
228
|
+
:prop="col.prop"
|
|
229
|
+
:label="col.label"
|
|
230
|
+
:width="col.width"
|
|
231
|
+
:min-width="col.minWidth"
|
|
232
|
+
:align="col.align || 'left'"
|
|
233
|
+
:fixed="col.fixed || false"
|
|
234
|
+
>
|
|
235
|
+
</el-table-column>
|
|
236
|
+
</el-table>
|
|
237
|
+
|
|
238
|
+
<div class="pagination">
|
|
239
|
+
<el-pagination
|
|
240
|
+
layout="total, prev, pager, next"
|
|
241
|
+
:current-page="page"
|
|
242
|
+
:page-size="pageSize"
|
|
243
|
+
:total="total"
|
|
244
|
+
@current-change="onPageChange"
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
</section>
|
|
248
|
+
</template>
|
|
249
|
+
|
|
250
|
+
<script setup lang="ts">
|
|
251
|
+
import { ref, reactive, computed, onMounted } from 'vue'
|
|
252
|
+
import TableColumnSettings from '../components/TableColumnSettings.vue'
|
|
253
|
+
|
|
254
|
+
// 表格数据接口
|
|
255
|
+
interface TableRow {
|
|
256
|
+
id: number
|
|
257
|
+
name: string
|
|
258
|
+
department: string
|
|
259
|
+
position: string
|
|
260
|
+
phone: string
|
|
261
|
+
email: string
|
|
262
|
+
joinDate: string
|
|
263
|
+
status: string
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 响应式数据
|
|
267
|
+
const tableKey = ref(0)
|
|
268
|
+
const page = ref(1)
|
|
269
|
+
const pageSize = ref(10)
|
|
270
|
+
const total = ref(50)
|
|
271
|
+
const tableData = ref<TableRow[]>([])
|
|
272
|
+
|
|
273
|
+
// 列配置 - 用于表格渲染
|
|
274
|
+
const columns = reactive([
|
|
275
|
+
{ key: 'name', prop: 'name', label: '姓名', width: 100 },
|
|
276
|
+
{ key: 'department', prop: 'department', label: '部门', width: 120 },
|
|
277
|
+
{ key: 'position', prop: 'position', label: '职位', width: 120 },
|
|
278
|
+
{ key: 'phone', prop: 'phone', label: '电话', width: 140 },
|
|
279
|
+
{ key: 'email', prop: 'email', label: '邮箱', minWidth: 180 },
|
|
280
|
+
{ key: 'joinDate', prop: 'joinDate', label: '入职日期', width: 120, align: 'center' },
|
|
281
|
+
{ key: 'status', prop: 'status', label: '状态', width: 100, align: 'center' }
|
|
282
|
+
])
|
|
283
|
+
|
|
284
|
+
// 列配置 - 用于列配置组件
|
|
285
|
+
const columnSettings = computed(() => {
|
|
286
|
+
return columns.map(col => ({
|
|
287
|
+
prop: col.prop,
|
|
288
|
+
label: col.label,
|
|
289
|
+
disabled: col.prop === 'name' // 姓名列强制显示,不可取消
|
|
290
|
+
}))
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
// 当前选中的列
|
|
294
|
+
const selectedKeys = ref<string[]>([])
|
|
295
|
+
|
|
296
|
+
// 可见列
|
|
297
|
+
const visibleColumns = computed(() => {
|
|
298
|
+
return columns.filter(col => selectedKeys.value.includes(col.prop))
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
// 列配置确认
|
|
302
|
+
const onColumnConfirm = (keys: string[]) => {
|
|
303
|
+
selectedKeys.value = keys
|
|
304
|
+
tableKey.value += 1 // 强制刷新表格
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// 分页切换
|
|
308
|
+
const onPageChange = (p: number) => {
|
|
309
|
+
page.value = p
|
|
310
|
+
loadData()
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 加载模拟数据
|
|
314
|
+
const loadData = () => {
|
|
315
|
+
const data: TableRow[] = []
|
|
316
|
+
for (let i = 0; i < pageSize.value; i++) {
|
|
317
|
+
const index = (page.value - 1) * pageSize.value + i + 1
|
|
318
|
+
data.push({
|
|
319
|
+
id: index,
|
|
320
|
+
name: `员工${index}`,
|
|
321
|
+
department: ['技术部', '产品部', '运营部', '市场部'][index % 4],
|
|
322
|
+
position: ['开发工程师', '产品经理', '运营专员', '市场专员'][index % 4],
|
|
323
|
+
phone: `138${String(index).padStart(8, '0')}`,
|
|
324
|
+
email: `employee${index}@example.com`,
|
|
325
|
+
joinDate: `2024-${String((index % 12) + 1).padStart(2, '0')}-${String((index % 28) + 1).padStart(2, '0')}`,
|
|
326
|
+
status: ['在职', '离职', '休假'][index % 3]
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
tableData.value = data
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
onMounted(() => {
|
|
333
|
+
// 默认选中所有列
|
|
334
|
+
selectedKeys.value = columns.map(col => col.prop)
|
|
335
|
+
loadData()
|
|
336
|
+
})
|
|
337
|
+
</script>
|
|
338
|
+
|
|
339
|
+
<style scoped>
|
|
340
|
+
.page.column-config-demo-page {
|
|
341
|
+
padding: 16px;
|
|
342
|
+
}
|
|
343
|
+
.title {
|
|
344
|
+
font-size: 18px;
|
|
345
|
+
margin-bottom: 12px;
|
|
346
|
+
}
|
|
347
|
+
.toolbar {
|
|
348
|
+
margin-bottom: 12px;
|
|
349
|
+
}
|
|
350
|
+
.pagination {
|
|
351
|
+
margin-top: 12px;
|
|
352
|
+
text-align: right;
|
|
353
|
+
}
|
|
354
|
+
</style>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
- [ ] **Step 2: 验证文件**
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
ls -la frontEnd/src/pages/ColumnConfigDemoPage.vue
|
|
361
|
+
```
|
|
362
|
+
Expected: 文件存在
|
|
363
|
+
|
|
364
|
+
- [ ] **Step 3: 提交**
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
git add frontEnd/src/pages/ColumnConfigDemoPage.vue
|
|
368
|
+
git commit -m "feat: update ColumnConfigDemoPage with TableColumnSettings"
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
### Task 3: 添加路由配置(如未添加)
|
|
374
|
+
|
|
375
|
+
**Files:**
|
|
376
|
+
- Modify: `frontEnd/src/router/routes.js`
|
|
377
|
+
|
|
378
|
+
- [ ] **Step 1: 检查并添加路由**
|
|
379
|
+
|
|
380
|
+
如果 routes.js 中还没有 ColumnConfigDemo 路由,添加:
|
|
381
|
+
|
|
382
|
+
在导入部分添加:
|
|
383
|
+
```javascript
|
|
384
|
+
import ColumnConfigDemoPage from '../pages/ColumnConfigDemoPage.vue'
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
在 routes 数组中添加:
|
|
388
|
+
```javascript
|
|
389
|
+
{
|
|
390
|
+
path: '/column-config-demo',
|
|
391
|
+
name: 'ColumnConfigDemo',
|
|
392
|
+
component: ColumnConfigDemoPage,
|
|
393
|
+
meta: {
|
|
394
|
+
title: '表格列配置演示',
|
|
395
|
+
showInMenu: true
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
- [ ] **Step 2: 验证路由配置**
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
grep -n "ColumnConfigDemo" frontEnd/src/router/routes.js
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
- [ ] **Step 3: 提交**
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
git add frontEnd/src/router/routes.js
|
|
410
|
+
git commit -m "feat: add column config demo route"
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
### Task 4: 验证功能
|
|
416
|
+
|
|
417
|
+
**Files:**
|
|
418
|
+
- Test: 手动运行测试
|
|
419
|
+
|
|
420
|
+
- [ ] **Step 1: 启动前端开发服务器**
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
npm run front-dev
|
|
424
|
+
```
|
|
425
|
+
Expected: 无编译错误,服务器正常启动
|
|
426
|
+
|
|
427
|
+
- [ ] **Step 2: 手动验证功能**
|
|
428
|
+
1. 访问页面,点击"列配置"按钮
|
|
429
|
+
2. 确认弹窗显示所有列选项
|
|
430
|
+
3. 确认"姓名"列是禁用状态(不可取消)
|
|
431
|
+
4. 测试全选/取消全选
|
|
432
|
+
5. 测试部分选中,确认半选状态显示
|
|
433
|
+
6. 点击确认,表格列动态更新
|
|
434
|
+
7. 再次打开弹窗,确认状态保持
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 计划完成检查清单
|
|
439
|
+
|
|
440
|
+
- [x] 组件代码完整
|
|
441
|
+
- [x] 演示页面集成完整
|
|
442
|
+
- [x] 文件路径正确
|
|
443
|
+
- [x] 功能描述清晰
|