w-ui-v1 1.0.42 → 1.0.43
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/index.ts +3 -1
- package/package.json +1 -1
- package/w-menu/w-menu.vue +18 -7
- package/w-report-table/w-report-table.vue +465 -0
- package/w-select-picker/types.ts +1 -1
- package/w-select-picker/w-select-picker.vue +480 -490
package/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import wEdit from './w-edit/w-edit.vue'
|
|
|
13
13
|
import WSelectTable from './w-select-table/w-select-table.vue'
|
|
14
14
|
import WFormMessageBox from './w-form-message-box/w-form-message-box.vue'
|
|
15
15
|
import WAudio from './w-audio/w-audio.vue'
|
|
16
|
+
import WReportTable from './w-report-table/w-report-table.vue'
|
|
16
17
|
const coms: any[] = [
|
|
17
18
|
wTest,
|
|
18
19
|
wLogin,
|
|
@@ -27,7 +28,8 @@ const coms: any[] = [
|
|
|
27
28
|
wEdit,
|
|
28
29
|
WSelectTable,
|
|
29
30
|
WFormMessageBox,
|
|
30
|
-
WAudio
|
|
31
|
+
WAudio,
|
|
32
|
+
WReportTable
|
|
31
33
|
]
|
|
32
34
|
// 批量组件注册
|
|
33
35
|
function install(Vue: App) {
|
package/package.json
CHANGED
package/w-menu/w-menu.vue
CHANGED
|
@@ -33,7 +33,6 @@ onMounted(() => {
|
|
|
33
33
|
|
|
34
34
|
// 点击菜单跳转页面
|
|
35
35
|
function gotoPage(item: any) {
|
|
36
|
-
console.log(item)
|
|
37
36
|
// 跳转页面
|
|
38
37
|
if (goto(item))
|
|
39
38
|
return
|
|
@@ -53,13 +52,25 @@ function sheetGotoPage(item: any) {
|
|
|
53
52
|
|
|
54
53
|
// 跳转页面
|
|
55
54
|
function goto(item: any) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
console.log(item.pageType)
|
|
56
|
+
switch (item.pageType) {
|
|
57
|
+
case "列表":
|
|
58
|
+
uni.navigateTo({
|
|
59
|
+
url: `/pages/table/table?sourceId=${item.id}&pageTitle=${item.title}`,
|
|
60
|
+
})
|
|
61
|
+
return true
|
|
62
|
+
break;
|
|
63
|
+
case "报表":
|
|
64
|
+
uni.navigateTo({
|
|
65
|
+
url: `/pages/report-table/report-table?sourceId=${item.id}&pageTitle=${item.title}`,
|
|
66
|
+
})
|
|
67
|
+
return true
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
default:
|
|
71
|
+
return false
|
|
72
|
+
break;
|
|
61
73
|
}
|
|
62
|
-
return false
|
|
63
74
|
}
|
|
64
75
|
|
|
65
76
|
// 打开wd-action-sheet动作面板
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="w-report-table">
|
|
3
|
+
<!-- 表格容器 -->
|
|
4
|
+
<view class="table-container" :style="{height: tableHeight}">
|
|
5
|
+
<!-- 表格主体 -->
|
|
6
|
+
<scroll-view scroll-y="true" class="table-body">
|
|
7
|
+
<!-- 表头 -->
|
|
8
|
+
<view class="table-header">
|
|
9
|
+
<view class="table-row">
|
|
10
|
+
<view v-if="showCheckbox" class="checkbox-col header-cell">
|
|
11
|
+
<checkbox-group @change="toggleAllSelection">
|
|
12
|
+
<checkbox :checked="isAllSelected" />
|
|
13
|
+
</checkbox-group>
|
|
14
|
+
</view>
|
|
15
|
+
|
|
16
|
+
<template v-for="(col, colIndex) in columns" :key="colIndex">
|
|
17
|
+
<template v-if="col.children">
|
|
18
|
+
<view
|
|
19
|
+
:class="['header-cell', 'group-header', col.className]"
|
|
20
|
+
:style="{
|
|
21
|
+
flex: col.children.reduce((sum, c) => sum + (parseInt(c.width) || 1), 0),
|
|
22
|
+
'text-align': 'center'
|
|
23
|
+
}"
|
|
24
|
+
:colspan="col.children.length"
|
|
25
|
+
>
|
|
26
|
+
{{ col.title }}
|
|
27
|
+
</view>
|
|
28
|
+
</template>
|
|
29
|
+
<template v-else>
|
|
30
|
+
<view
|
|
31
|
+
:class="['header-cell', col.className]"
|
|
32
|
+
:style="{
|
|
33
|
+
flex: col.width || 1,
|
|
34
|
+
'text-align': col.align || 'center'
|
|
35
|
+
}"
|
|
36
|
+
>
|
|
37
|
+
{{ col.title }}
|
|
38
|
+
</view>
|
|
39
|
+
</template>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<view v-if="showAction" class="action-col header-cell">操作</view>
|
|
43
|
+
</view>
|
|
44
|
+
</view>
|
|
45
|
+
|
|
46
|
+
<!-- 表格内容 -->
|
|
47
|
+
<view class="table-body">
|
|
48
|
+
<view
|
|
49
|
+
v-for="(row, rowIndex) in paginatedData"
|
|
50
|
+
:key="rowIndex"
|
|
51
|
+
class="table-row"
|
|
52
|
+
:class="{'selected-row': isSelected(row)}"
|
|
53
|
+
>
|
|
54
|
+
<view v-if="showCheckbox" class="checkbox-col body-cell">
|
|
55
|
+
<checkbox-group @change="toggleSelection($event, row)">
|
|
56
|
+
<checkbox :checked="isSelected(row)" />
|
|
57
|
+
</checkbox-group>
|
|
58
|
+
</view>
|
|
59
|
+
|
|
60
|
+
<template v-for="(col, colIndex) in columns" :key="colIndex">
|
|
61
|
+
<template v-if="col.children">
|
|
62
|
+
<template v-for="(childCol, childIndex) in col.children" :key="childIndex">
|
|
63
|
+
<view
|
|
64
|
+
:class="['body-cell', childCol.className]"
|
|
65
|
+
:style="{
|
|
66
|
+
flex: childCol.width || 1,
|
|
67
|
+
'text-align': childCol.align || 'center',
|
|
68
|
+
display: getRowSpan(row, childCol) === 0 ? 'none' : 'flex'
|
|
69
|
+
}"
|
|
70
|
+
:rowspan="getRowSpan(row, childCol)"
|
|
71
|
+
:colspan="getColSpan(row, childCol)"
|
|
72
|
+
>
|
|
73
|
+
{{ row[childCol.key] }}
|
|
74
|
+
</view>
|
|
75
|
+
</template>
|
|
76
|
+
</template>
|
|
77
|
+
<template v-else>
|
|
78
|
+
<view
|
|
79
|
+
:class="['body-cell', col.className]"
|
|
80
|
+
:style="{
|
|
81
|
+
flex: col.width || 1,
|
|
82
|
+
'text-align': col.align || 'center',
|
|
83
|
+
display: getRowSpan(row, col) === 0 ? 'none' : 'flex'
|
|
84
|
+
}"
|
|
85
|
+
:rowspan="getRowSpan(row, col)"
|
|
86
|
+
:colspan="getColSpan(row, col)"
|
|
87
|
+
>
|
|
88
|
+
{{ row[col.key] }}
|
|
89
|
+
</view>
|
|
90
|
+
</template>
|
|
91
|
+
</template>
|
|
92
|
+
|
|
93
|
+
<view v-if="showAction" class="action-col body-cell">
|
|
94
|
+
<view class="action-btns">
|
|
95
|
+
<text v-for="(action, i) in actions" :key="i" @tap="handleAction(action, row)">
|
|
96
|
+
{{ action.label }}
|
|
97
|
+
</text>
|
|
98
|
+
</view>
|
|
99
|
+
</view>
|
|
100
|
+
</view>
|
|
101
|
+
</view>
|
|
102
|
+
|
|
103
|
+
<!-- 合计行 -->
|
|
104
|
+
<view v-if="showSummary" class="table-footer">
|
|
105
|
+
<view class="table-row summary-row">
|
|
106
|
+
<view v-if="showCheckbox" class="checkbox-col"></view>
|
|
107
|
+
<template v-for="(col, colIndex) in columns" :key="colIndex">
|
|
108
|
+
<template v-if="col.children">
|
|
109
|
+
<template v-for="(childCol, childIndex) in col.children" :key="childIndex">
|
|
110
|
+
<view
|
|
111
|
+
:class="['summary-cell', childCol.className]"
|
|
112
|
+
:style="{
|
|
113
|
+
flex: childCol.width || 1,
|
|
114
|
+
'text-align': childCol.align || 'center'
|
|
115
|
+
}"
|
|
116
|
+
>
|
|
117
|
+
{{ getSummary(childCol) }}
|
|
118
|
+
</view>
|
|
119
|
+
</template>
|
|
120
|
+
</template>
|
|
121
|
+
<template v-else>
|
|
122
|
+
<view
|
|
123
|
+
:class="['summary-cell', col.className]"
|
|
124
|
+
:style="{
|
|
125
|
+
flex: col.width || 1,
|
|
126
|
+
'text-align': col.align || 'center'
|
|
127
|
+
}"
|
|
128
|
+
>
|
|
129
|
+
{{ getSummary(col) }}
|
|
130
|
+
</view>
|
|
131
|
+
</template>
|
|
132
|
+
</template>
|
|
133
|
+
<view v-if="showAction"></view>
|
|
134
|
+
</view>
|
|
135
|
+
</view>
|
|
136
|
+
</scroll-view>
|
|
137
|
+
</view>
|
|
138
|
+
|
|
139
|
+
<!-- 分页 -->
|
|
140
|
+
<view v-if="showPagination" class="pagination">
|
|
141
|
+
<view class="page-info">
|
|
142
|
+
共 {{ total }} 条记录
|
|
143
|
+
</view>
|
|
144
|
+
<view class="page-controls">
|
|
145
|
+
<button @click="prevPage" :disabled="currentPage === 1">上一页</button>
|
|
146
|
+
<text class="page-current">{{ currentPage }}/{{ pageCount }}</text>
|
|
147
|
+
<button @click="nextPage" :disabled="currentPage === pageCount">下一页</button>
|
|
148
|
+
</view>
|
|
149
|
+
</view>
|
|
150
|
+
</view>
|
|
151
|
+
</template>
|
|
152
|
+
|
|
153
|
+
<script setup lang="ts">
|
|
154
|
+
import { ref, computed } from 'vue'
|
|
155
|
+
|
|
156
|
+
interface Column {
|
|
157
|
+
title: string
|
|
158
|
+
key: string
|
|
159
|
+
width?: string
|
|
160
|
+
align?: string
|
|
161
|
+
className?: string
|
|
162
|
+
children?: Column[]
|
|
163
|
+
/**
|
|
164
|
+
* 单元格合并规则
|
|
165
|
+
* @param row 当前行数据
|
|
166
|
+
* @param index 当前行索引
|
|
167
|
+
* @returns 返回 true 表示参与合并,false 表示不合并
|
|
168
|
+
*/
|
|
169
|
+
mergeRule?: (row: any, index: number) => boolean
|
|
170
|
+
summaryMethod?: (data: any[]) => string | number
|
|
171
|
+
colspan?: number
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
interface Action {
|
|
175
|
+
label: string
|
|
176
|
+
handler: (row: any) => void
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const props = defineProps({
|
|
180
|
+
data: {
|
|
181
|
+
type: Array,
|
|
182
|
+
default: () => []
|
|
183
|
+
},
|
|
184
|
+
columns: {
|
|
185
|
+
type: Array as () => Column[],
|
|
186
|
+
default: () => []
|
|
187
|
+
},
|
|
188
|
+
actions: {
|
|
189
|
+
type: Array as () => Action[],
|
|
190
|
+
default: () => []
|
|
191
|
+
},
|
|
192
|
+
showCheckbox: {
|
|
193
|
+
type: Boolean,
|
|
194
|
+
default: false
|
|
195
|
+
},
|
|
196
|
+
showSummary: {
|
|
197
|
+
type: Boolean,
|
|
198
|
+
default: false
|
|
199
|
+
},
|
|
200
|
+
showPagination: {
|
|
201
|
+
type: Boolean,
|
|
202
|
+
default: false
|
|
203
|
+
},
|
|
204
|
+
showAction: {
|
|
205
|
+
type: Boolean,
|
|
206
|
+
default: true
|
|
207
|
+
},
|
|
208
|
+
pageSize: {
|
|
209
|
+
type: Number,
|
|
210
|
+
default: 10
|
|
211
|
+
},
|
|
212
|
+
tableHeight: {
|
|
213
|
+
type: String,
|
|
214
|
+
default: '500px'
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
const currentPage = ref(1)
|
|
219
|
+
const selectedRows = ref<any[]>([])
|
|
220
|
+
|
|
221
|
+
const paginatedData = computed(() => {
|
|
222
|
+
const start = (currentPage.value - 1) * props.pageSize
|
|
223
|
+
const end = start + props.pageSize
|
|
224
|
+
return props.data.slice(start, end)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
const pageCount = computed(() => {
|
|
228
|
+
return Math.ceil(props.data.length / props.pageSize)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
const total = computed(() => props.data.length)
|
|
232
|
+
|
|
233
|
+
const isAllSelected = computed(() => {
|
|
234
|
+
return selectedRows.value.length === props.data.length
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
const prevPage = () => {
|
|
238
|
+
if (currentPage.value > 1) {
|
|
239
|
+
currentPage.value--
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const nextPage = () => {
|
|
244
|
+
if (currentPage.value < pageCount.value) {
|
|
245
|
+
currentPage.value++
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const toggleSelection = (event: any, row: any) => {
|
|
250
|
+
const checked = event.detail.value.includes(row)
|
|
251
|
+
if (checked) {
|
|
252
|
+
selectedRows.value.push(row)
|
|
253
|
+
} else {
|
|
254
|
+
const index = selectedRows.value.findIndex(item => item === row)
|
|
255
|
+
if (index > -1) {
|
|
256
|
+
selectedRows.value.splice(index, 1)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const toggleAllSelection = (event: any) => {
|
|
262
|
+
if (event.detail.value.length > 0) {
|
|
263
|
+
selectedRows.value = [...props.data]
|
|
264
|
+
} else {
|
|
265
|
+
selectedRows.value = []
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const isSelected = (row: any) => {
|
|
270
|
+
return selectedRows.value.includes(row)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const getRowSpan = (row: any, col: Column) => {
|
|
274
|
+
if (!col.mergeRule) return 1
|
|
275
|
+
|
|
276
|
+
const index = props.data.indexOf(row)
|
|
277
|
+
if (!col.mergeRule(row, index)) return 1
|
|
278
|
+
|
|
279
|
+
// 计算相同内容的连续行数
|
|
280
|
+
let span = 1
|
|
281
|
+
const currentValue = row[col.key]
|
|
282
|
+
|
|
283
|
+
for (let i = index + 1; i < props.data.length; i++) {
|
|
284
|
+
if (props.data[i][col.key] === currentValue &&
|
|
285
|
+
(!col.mergeRule || col.mergeRule(props.data[i], i))) {
|
|
286
|
+
span++
|
|
287
|
+
} else {
|
|
288
|
+
break
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return span
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const getColSpan = (row: any, col: Column) => {
|
|
296
|
+
if (col.colspan) return col.colspan
|
|
297
|
+
|
|
298
|
+
// 计算相同内容的连续列数
|
|
299
|
+
if (!col.mergeRule) return 1
|
|
300
|
+
|
|
301
|
+
let index = -1
|
|
302
|
+
for (let i = 0; i < props.columns.length; i++) {
|
|
303
|
+
if (props.columns[i] === col) {
|
|
304
|
+
index = i
|
|
305
|
+
break
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (index === -1) return 1
|
|
309
|
+
|
|
310
|
+
let span = 1
|
|
311
|
+
const currentValue = row[col.key]
|
|
312
|
+
|
|
313
|
+
for (let i = index + 1; i < props.columns.length; i++) {
|
|
314
|
+
const nextCol = props.columns[i]
|
|
315
|
+
if (row[nextCol.key] === currentValue &&
|
|
316
|
+
(!nextCol.mergeRule || nextCol.mergeRule(row, i))) {
|
|
317
|
+
span++
|
|
318
|
+
} else {
|
|
319
|
+
break
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return span
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const getSummary = (col: Column) => {
|
|
327
|
+
if (col.summaryMethod) {
|
|
328
|
+
return col.summaryMethod(props.data)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (typeof props.data[0]?.[col.key] === 'number') {
|
|
332
|
+
return props.data.reduce((sum, row) => sum + (row[col.key] || 0), 0)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return ''
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const handleAction = (action: Action, row: any) => {
|
|
339
|
+
action.handler(row)
|
|
340
|
+
}
|
|
341
|
+
</script>
|
|
342
|
+
|
|
343
|
+
<style lang="scss" scoped>
|
|
344
|
+
.w-report-table {
|
|
345
|
+
width: 100%;
|
|
346
|
+
display: flex;
|
|
347
|
+
flex-direction: column;
|
|
348
|
+
|
|
349
|
+
.table-container {
|
|
350
|
+
width: 100%;
|
|
351
|
+
overflow: hidden;
|
|
352
|
+
|
|
353
|
+
.table-body {
|
|
354
|
+
width: 100%;
|
|
355
|
+
height: 100%;
|
|
356
|
+
|
|
357
|
+
.table-header, .table-body, .table-footer {
|
|
358
|
+
width: 100%;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.table-row {
|
|
362
|
+
display: flex;
|
|
363
|
+
width: 100%;
|
|
364
|
+
border-bottom: 1px solid #ebeef5;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.header-cell, .body-cell, .summary-cell {
|
|
368
|
+
padding: 12px 8px;
|
|
369
|
+
word-break: break-word;
|
|
370
|
+
border-right: 1px solid #ebeef5;
|
|
371
|
+
display: flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
justify-content: center;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.group-header {
|
|
377
|
+
font-weight: bold;
|
|
378
|
+
background-color: #eef1f6;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.table-header {
|
|
382
|
+
background-color: #f5f7fa;
|
|
383
|
+
|
|
384
|
+
.header-cell {
|
|
385
|
+
font-weight: bold;
|
|
386
|
+
color: #333;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.table-body {
|
|
391
|
+
.table-row:hover {
|
|
392
|
+
background-color: #f5f7fa;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.selected-row {
|
|
396
|
+
background-color: #e6f7ff;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.checkbox-col {
|
|
401
|
+
flex: 0 0 50px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.action-col {
|
|
405
|
+
flex: 0 0 120px;
|
|
406
|
+
|
|
407
|
+
.action-btns {
|
|
408
|
+
display: flex;
|
|
409
|
+
justify-content: space-around;
|
|
410
|
+
|
|
411
|
+
text {
|
|
412
|
+
color: #409eff;
|
|
413
|
+
cursor: pointer;
|
|
414
|
+
|
|
415
|
+
&:hover {
|
|
416
|
+
color: #66b1ff;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.pagination {
|
|
425
|
+
display: flex;
|
|
426
|
+
justify-content: space-between;
|
|
427
|
+
align-items: center;
|
|
428
|
+
margin-top: 16px;
|
|
429
|
+
padding: 8px 16px;
|
|
430
|
+
background-color: #f5f7fa;
|
|
431
|
+
|
|
432
|
+
.page-controls {
|
|
433
|
+
display: flex;
|
|
434
|
+
align-items: center;
|
|
435
|
+
|
|
436
|
+
button {
|
|
437
|
+
margin: 0 8px;
|
|
438
|
+
padding: 4px 12px;
|
|
439
|
+
background-color: #fff;
|
|
440
|
+
border: 1px solid #dcdfe6;
|
|
441
|
+
border-radius: 4px;
|
|
442
|
+
cursor: pointer;
|
|
443
|
+
|
|
444
|
+
&:disabled {
|
|
445
|
+
color: #c0c4cc;
|
|
446
|
+
cursor: not-allowed;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.page-current {
|
|
451
|
+
margin: 0 8px;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.summary-row {
|
|
457
|
+
background-color: #f5f7fa;
|
|
458
|
+
font-weight: bold;
|
|
459
|
+
|
|
460
|
+
.summary-cell {
|
|
461
|
+
color: #333;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
</style>
|
package/w-select-picker/types.ts
CHANGED
|
@@ -39,7 +39,7 @@ export const selectPickerProps = {
|
|
|
39
39
|
/** 设置 picker 内部的选项组尺寸大小 (单/复选框) */
|
|
40
40
|
selectSize: String,
|
|
41
41
|
/** 加载中 */
|
|
42
|
-
loading: makeBooleanProp(false),
|
|
42
|
+
// loading: makeBooleanProp(false),
|
|
43
43
|
/** 加载的颜色,只能使用十六进制的色值写法,且不能使用缩写 */
|
|
44
44
|
loadingColor: makeStringProp('#4D80F0'),
|
|
45
45
|
/** 点击遮罩是否关闭 */
|