verce-vue-test 0.0.0 → 0.0.2
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/package.json +1 -1
- package/src/App.vue +3 -0
- package/src/components/ElTablePro.vue +28 -6
- package/src/router/index.ts +5 -0
- package/src/views/TableProForView.vue +144 -0
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -27,6 +27,9 @@ import { RouterView } from 'vue-router'
|
|
|
27
27
|
<el-menu-item index="/table-pro">
|
|
28
28
|
<span>高级表格</span>
|
|
29
29
|
</el-menu-item>
|
|
30
|
+
<el-menu-item index="/table-pro-for">
|
|
31
|
+
<span>循环表格</span>
|
|
32
|
+
</el-menu-item>
|
|
30
33
|
<el-menu-item index="/about">
|
|
31
34
|
<span>关于</span>
|
|
32
35
|
</el-menu-item>
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
useAttrs,
|
|
10
10
|
useSlots,
|
|
11
11
|
cloneVNode,
|
|
12
|
+
Fragment,
|
|
12
13
|
type VNode,
|
|
13
14
|
} from 'vue'
|
|
14
15
|
import { useRoute } from 'vue-router'
|
|
@@ -90,6 +91,7 @@ type ColumnOption = {
|
|
|
90
91
|
key: string // 列唯一标识
|
|
91
92
|
label: string // 列显示名称
|
|
92
93
|
visible: boolean // 是否可见
|
|
94
|
+
disabled: boolean // 是否禁用选择
|
|
93
95
|
vnode: VNode // 列的 VNode 节点
|
|
94
96
|
}
|
|
95
97
|
|
|
@@ -143,10 +145,15 @@ const currentPageSize = computed({
|
|
|
143
145
|
|
|
144
146
|
// 是否全选(弹窗中的临时状态)
|
|
145
147
|
const isAllSelected = computed({
|
|
146
|
-
get: () =>
|
|
148
|
+
get: () => {
|
|
149
|
+
const selectable = pendingColumnOptions.value.filter(col => !col.disabled)
|
|
150
|
+
return selectable.length > 0 && selectable.every(col => col.visible)
|
|
151
|
+
},
|
|
147
152
|
set: value => {
|
|
148
153
|
pendingColumnOptions.value.forEach(col => {
|
|
149
|
-
col.
|
|
154
|
+
if (!col.disabled) {
|
|
155
|
+
col.visible = value
|
|
156
|
+
}
|
|
150
157
|
})
|
|
151
158
|
},
|
|
152
159
|
})
|
|
@@ -210,6 +217,7 @@ const restoreColumnConfig = () => {
|
|
|
210
217
|
|
|
211
218
|
// 遍历列选项,更新可见性状态
|
|
212
219
|
columnOptions.value.forEach(column => {
|
|
220
|
+
if (column.disabled) return
|
|
213
221
|
const match = cacheColumns.find(item => item.key === column.key)
|
|
214
222
|
if (match) {
|
|
215
223
|
column.visible = match.visible
|
|
@@ -271,20 +279,33 @@ const cancelColumnConfig = () => {
|
|
|
271
279
|
* 初始化列配置
|
|
272
280
|
*/
|
|
273
281
|
const initColumns = () => {
|
|
274
|
-
|
|
275
|
-
|
|
282
|
+
const rawChildren = slots.default?.() || []
|
|
283
|
+
|
|
284
|
+
const flattenVNodes = (vnodes: VNode[]): VNode[] => {
|
|
285
|
+
const result: VNode[] = []
|
|
286
|
+
for (const vnode of vnodes) {
|
|
287
|
+
if (vnode.type === Fragment && Array.isArray(vnode.children)) {
|
|
288
|
+
result.push(...flattenVNodes(vnode.children as VNode[]))
|
|
289
|
+
} else {
|
|
290
|
+
result.push(vnode)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return result
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const children = flattenVNodes(rawChildren)
|
|
276
297
|
|
|
277
|
-
// 转换为列选项列表
|
|
278
298
|
columnOptions.value = children.map((vnode, index) => {
|
|
299
|
+
const columnProps = vnode.props as Record<string, any> | null
|
|
279
300
|
return {
|
|
280
301
|
key: getColumnKey(vnode, index),
|
|
281
302
|
label: getColumnLabel(vnode, index),
|
|
282
303
|
visible: true,
|
|
304
|
+
disabled: columnProps?.disabled ?? false,
|
|
283
305
|
vnode,
|
|
284
306
|
}
|
|
285
307
|
})
|
|
286
308
|
|
|
287
|
-
// 从本地存储恢复配置
|
|
288
309
|
restoreColumnConfig()
|
|
289
310
|
}
|
|
290
311
|
|
|
@@ -387,6 +408,7 @@ initColumns()
|
|
|
387
408
|
>
|
|
388
409
|
<el-checkbox
|
|
389
410
|
v-model="column.visible"
|
|
411
|
+
:disabled="column.disabled"
|
|
390
412
|
>
|
|
391
413
|
{{ column.label }}
|
|
392
414
|
</el-checkbox>
|
package/src/router/index.ts
CHANGED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ElTablePro
|
|
3
|
+
:data="tableData"
|
|
4
|
+
:total="total"
|
|
5
|
+
v-model:page="query.page"
|
|
6
|
+
v-model:page-size="query.pageSize"
|
|
7
|
+
table-key="product"
|
|
8
|
+
row-key="id"
|
|
9
|
+
border
|
|
10
|
+
stripe
|
|
11
|
+
v-loading="loading"
|
|
12
|
+
@pagination-change="handlePaginationChange"
|
|
13
|
+
>
|
|
14
|
+
<template #toolbar>
|
|
15
|
+
<el-button type="primary" @click="handleAdd">新增</el-button>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<el-table-column
|
|
19
|
+
v-for="col in visibleColumns"
|
|
20
|
+
:key="col.prop"
|
|
21
|
+
:prop="col.prop"
|
|
22
|
+
:label="col.label"
|
|
23
|
+
:width="col.width"
|
|
24
|
+
:min-width="col.minWidth"
|
|
25
|
+
>
|
|
26
|
+
<template v-if="col.slot" #default="{ row }">
|
|
27
|
+
<el-tag
|
|
28
|
+
v-if="col.prop === 'status'"
|
|
29
|
+
:type="row.status ? 'success' : 'danger'"
|
|
30
|
+
>
|
|
31
|
+
{{ row.status ? '启用' : '禁用' }}
|
|
32
|
+
</el-tag>
|
|
33
|
+
<el-tag
|
|
34
|
+
v-else-if="col.prop === 'category'"
|
|
35
|
+
:type="categoryTagType(row.category)"
|
|
36
|
+
>
|
|
37
|
+
{{ row.category }}
|
|
38
|
+
</el-tag>
|
|
39
|
+
<template v-else-if="col.prop === 'action'">
|
|
40
|
+
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
|
|
41
|
+
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
|
|
42
|
+
</template>
|
|
43
|
+
</template>
|
|
44
|
+
</el-table-column>
|
|
45
|
+
</ElTablePro>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
import { ref, reactive, onMounted } from 'vue'
|
|
50
|
+
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
51
|
+
import ElTablePro from '@/components/ElTablePro.vue'
|
|
52
|
+
|
|
53
|
+
interface ColumnConfig {
|
|
54
|
+
prop: string
|
|
55
|
+
label: string
|
|
56
|
+
width?: number | string
|
|
57
|
+
minWidth?: number | string
|
|
58
|
+
slot?: boolean
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const columns: ColumnConfig[] = [
|
|
62
|
+
{ prop: 'id', label: 'ID', width: 80 },
|
|
63
|
+
{ prop: 'name', label: '商品名称', minWidth: 150 },
|
|
64
|
+
{ prop: 'category', label: '分类', width: 120, slot: true },
|
|
65
|
+
{ prop: 'price', label: '价格', width: 100 },
|
|
66
|
+
{ prop: 'stock', label: '库存', width: 80 },
|
|
67
|
+
{ prop: 'status', label: '状态', width: 100, slot: true },
|
|
68
|
+
{ prop: 'action', label: '操作', width: 180, slot: true },
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
const visibleColumns = columns
|
|
72
|
+
|
|
73
|
+
const loading = ref(false)
|
|
74
|
+
const total = ref(0)
|
|
75
|
+
|
|
76
|
+
const query = reactive({
|
|
77
|
+
page: 1,
|
|
78
|
+
pageSize: 10,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const allData = [
|
|
82
|
+
{ id: 1, name: 'MacBook Pro 16"', category: '电子产品', price: 18999, stock: 50, status: true },
|
|
83
|
+
{ id: 2, name: 'iPhone 17 Pro', category: '电子产品', price: 8999, stock: 120, status: true },
|
|
84
|
+
{ id: 3, name: 'AirPods Max', category: '电子产品', price: 4399, stock: 80, status: false },
|
|
85
|
+
{ id: 4, name: '耐克运动鞋', category: '服装鞋帽', price: 899, stock: 200, status: true },
|
|
86
|
+
{ id: 5, name: '优衣库羽绒服', category: '服装鞋帽', price: 599, stock: 150, status: true },
|
|
87
|
+
{ id: 6, name: '三只松鼠坚果礼盒', category: '食品饮料', price: 128, stock: 300, status: true },
|
|
88
|
+
{ id: 7, name: '飞利浦电动牙刷', category: '家居用品', price: 399, stock: 90, status: true },
|
|
89
|
+
{ id: 8, name: '小米空气净化器', category: '家居用品', price: 1299, stock: 60, status: false },
|
|
90
|
+
{ id: 9, name: '星巴克咖啡豆', category: '食品饮料', price: 158, stock: 250, status: true },
|
|
91
|
+
{ id: 10, name: '华为 MatePad Pro', category: '电子产品', price: 3699, stock: 70, status: true },
|
|
92
|
+
{ id: 11, name: '阿迪达斯跑步鞋', category: '服装鞋帽', price: 799, stock: 180, status: true },
|
|
93
|
+
{ id: 12, name: '戴森吹风机', category: '家居用品', price: 3199, stock: 40, status: true },
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
const tableData = ref<any[]>([])
|
|
97
|
+
|
|
98
|
+
const categoryTagType = (category: string) => {
|
|
99
|
+
const map: Record<string, string> = {
|
|
100
|
+
'电子产品': 'primary',
|
|
101
|
+
'服装鞋帽': 'warning',
|
|
102
|
+
'食品饮料': 'success',
|
|
103
|
+
'家居用品': 'info',
|
|
104
|
+
}
|
|
105
|
+
return map[category] || 'info'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const getList = () => {
|
|
109
|
+
loading.value = true
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
const start = (query.page - 1) * query.pageSize
|
|
112
|
+
const end = start + query.pageSize
|
|
113
|
+
tableData.value = allData.slice(start, end)
|
|
114
|
+
total.value = allData.length
|
|
115
|
+
loading.value = false
|
|
116
|
+
}, 300)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const handlePaginationChange = (_value: { page: number; pageSize: number }) => {
|
|
120
|
+
getList()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const handleAdd = () => {
|
|
124
|
+
ElMessage.info('新增商品')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const handleEdit = (row: any) => {
|
|
128
|
+
ElMessage.info(`编辑商品: ${row.name}`)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const handleDelete = (row: any) => {
|
|
132
|
+
ElMessageBox.confirm(`确认删除商品 ${row.name}?`, '提示', {
|
|
133
|
+
confirmButtonText: '确认',
|
|
134
|
+
cancelButtonText: '取消',
|
|
135
|
+
type: 'warning',
|
|
136
|
+
}).then(() => {
|
|
137
|
+
ElMessage.success('删除成功')
|
|
138
|
+
}).catch(() => {})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
onMounted(() => {
|
|
142
|
+
getList()
|
|
143
|
+
})
|
|
144
|
+
</script>
|