@mixd-id/web-scaffold 0.1.230406312 → 0.1.230406314
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/components/List.vue +48 -13
- package/src/components/VirtualTable.vue +112 -93
- package/src/utils/preset-selector.js +47 -23
- package/src/utils/preset-selector.mjs +21 -1
- package/src/widgets/PresetBar.vue +6 -0
package/package.json
CHANGED
package/src/components/List.vue
CHANGED
|
@@ -250,7 +250,7 @@ import VirtualGrid from "./VirtualGrid.vue";
|
|
|
250
250
|
import throttle from "lodash/throttle";
|
|
251
251
|
import PresetSelector from "../widgets/PresetSelector.vue";
|
|
252
252
|
import groupBy from "lodash/groupBy";
|
|
253
|
-
import { generatePivotColumns, sortsFn } from "../utils/preset-selector.mjs";
|
|
253
|
+
import { generatePivotColumns, generateTotalColumns, sortsFn } from "../utils/preset-selector.mjs";
|
|
254
254
|
import PresetBar from "../widgets/PresetBar.vue";
|
|
255
255
|
|
|
256
256
|
export default{
|
|
@@ -340,12 +340,11 @@ export default{
|
|
|
340
340
|
itemsPerPage: this.data.itemsPerPage,
|
|
341
341
|
})
|
|
342
342
|
.then(data => {
|
|
343
|
-
|
|
344
343
|
generatePivotColumns(this.preset, data.items)
|
|
344
|
+
generateTotalColumns(this.preset, data.items)
|
|
345
345
|
this.loadEnums(data.items)
|
|
346
346
|
Object.assign(this.data, data ?? {})
|
|
347
347
|
this.$emit('after-load')
|
|
348
|
-
|
|
349
348
|
})
|
|
350
349
|
.catch(err => {
|
|
351
350
|
this.toast(err)
|
|
@@ -586,24 +585,60 @@ export default{
|
|
|
586
585
|
const filters = []
|
|
587
586
|
|
|
588
587
|
for(let idx in this.preset.pivot.rows){
|
|
589
|
-
const
|
|
588
|
+
const pivotRow = this.preset.pivot.rows[idx]
|
|
589
|
+
const key = pivotRow.key
|
|
590
590
|
const presetColumn = this.preset.columns.find(_ => _.key === key)
|
|
591
|
-
let value = item[
|
|
591
|
+
let value = item[pivotRow.key]
|
|
592
592
|
switch(presetColumn.type){
|
|
593
593
|
|
|
594
594
|
case 'bool':
|
|
595
595
|
case 'boolean':
|
|
596
|
-
|
|
596
|
+
filters.push({
|
|
597
|
+
key,
|
|
598
|
+
value: [{
|
|
599
|
+
operator: '=',
|
|
600
|
+
value: value === 'true'
|
|
601
|
+
}]
|
|
602
|
+
})
|
|
603
|
+
break
|
|
604
|
+
|
|
605
|
+
case 'date':
|
|
606
|
+
switch(pivotRow.aggregrate){
|
|
607
|
+
|
|
608
|
+
case 'hour':
|
|
609
|
+
value = value.split('-')
|
|
610
|
+
filters.push({
|
|
611
|
+
key,
|
|
612
|
+
value: [{
|
|
613
|
+
operator: '>=',
|
|
614
|
+
value: value[0] + ':00',
|
|
615
|
+
fn: 'format_hour'
|
|
616
|
+
}]
|
|
617
|
+
})
|
|
618
|
+
filters.push({
|
|
619
|
+
key,
|
|
620
|
+
value: [{
|
|
621
|
+
operator: '<=',
|
|
622
|
+
value: value[1] + ':59',
|
|
623
|
+
fn: 'format_hour'
|
|
624
|
+
}]
|
|
625
|
+
})
|
|
626
|
+
break
|
|
627
|
+
|
|
628
|
+
}
|
|
629
|
+
break
|
|
630
|
+
|
|
631
|
+
default:
|
|
632
|
+
filters.push({
|
|
633
|
+
key,
|
|
634
|
+
value: [{
|
|
635
|
+
operator: '=',
|
|
636
|
+
value
|
|
637
|
+
}]
|
|
638
|
+
})
|
|
597
639
|
break
|
|
598
640
|
}
|
|
599
641
|
|
|
600
|
-
filters.push({
|
|
601
|
-
key,
|
|
602
|
-
value: [{
|
|
603
|
-
operator: '=',
|
|
604
|
-
value
|
|
605
|
-
}]
|
|
606
|
-
})
|
|
607
642
|
}
|
|
608
643
|
|
|
609
644
|
let [ kk, ...vv ] = column.key.split('-')
|
|
@@ -32,18 +32,23 @@
|
|
|
32
32
|
</tr>
|
|
33
33
|
</thead>
|
|
34
34
|
<tbody ref="tbody">
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
<tr v-for="(item, index) in visibleItems" :key="item"
|
|
36
|
+
@click="select(item, index)"
|
|
37
|
+
:class="trClass(item, index)">
|
|
38
|
+
<td v-for="(column, columnIndex) in visibleColumns"
|
|
39
|
+
:class="tdClass(item, column)"
|
|
40
|
+
@click="$emit('item-click', item, column)">
|
|
41
|
+
|
|
42
|
+
<div v-if="columnIndex === 0 && item._type === 'totalRow'" :class="$style.total">Total</div>
|
|
43
|
+
<div v-else-if="item._type === 'totalRow' && !column.key.startsWith('_')"></div>
|
|
44
|
+
|
|
45
|
+
<slot v-else-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
|
|
46
|
+
<slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
|
|
47
|
+
<div v-else :class="columnClass(column, item)" v-html="formatColumn(item, column)"></div>
|
|
48
|
+
|
|
49
|
+
</td>
|
|
50
|
+
<td :class="$style.spacer"></td>
|
|
51
|
+
</tr>
|
|
47
52
|
</tbody>
|
|
48
53
|
</table>
|
|
49
54
|
<div v-if="state === 2" ref="spinner" class="h-[44px] relative">
|
|
@@ -343,8 +348,16 @@ export default{
|
|
|
343
348
|
case 'currency':
|
|
344
349
|
align = 'right'
|
|
345
350
|
break
|
|
351
|
+
|
|
352
|
+
default:
|
|
353
|
+
if(column.key.startsWith('_'))
|
|
354
|
+
align = 'center'
|
|
355
|
+
break
|
|
346
356
|
}
|
|
347
357
|
}
|
|
358
|
+
else{
|
|
359
|
+
align = column.align
|
|
360
|
+
}
|
|
348
361
|
|
|
349
362
|
let appearanceClass = ''
|
|
350
363
|
if(item && Array.isArray(column.appearances)){
|
|
@@ -361,93 +374,95 @@ export default{
|
|
|
361
374
|
break
|
|
362
375
|
}
|
|
363
376
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
377
|
+
if(!('_type' in item)){
|
|
378
|
+
for(let appearance of column.appearances){
|
|
379
|
+
|
|
380
|
+
let conditionMatched = false
|
|
381
|
+
for(let condition of appearance.conditions ?? []){
|
|
382
|
+
switch(condition.operator){
|
|
383
|
+
|
|
384
|
+
case '>':
|
|
385
|
+
if(value > condition.value){
|
|
386
|
+
conditionMatched = true
|
|
387
|
+
}
|
|
388
|
+
break
|
|
389
|
+
|
|
390
|
+
case '>=':
|
|
391
|
+
if(value >= condition.value){
|
|
392
|
+
conditionMatched = true
|
|
393
|
+
}
|
|
394
|
+
break
|
|
395
|
+
|
|
396
|
+
case '=':
|
|
397
|
+
if(value === condition.value){
|
|
398
|
+
conditionMatched = true
|
|
399
|
+
}
|
|
400
|
+
break
|
|
401
|
+
|
|
402
|
+
case '<':
|
|
403
|
+
if(value < condition.value){
|
|
404
|
+
conditionMatched = true
|
|
405
|
+
}
|
|
406
|
+
break
|
|
407
|
+
|
|
408
|
+
case '<=':
|
|
409
|
+
if(value <= condition.value){
|
|
410
|
+
conditionMatched = true
|
|
411
|
+
}
|
|
412
|
+
break
|
|
413
|
+
|
|
414
|
+
case 'startsWith':
|
|
415
|
+
if(value.startsWith(condition.value)){
|
|
416
|
+
conditionMatched = true
|
|
417
|
+
}
|
|
418
|
+
break
|
|
419
|
+
|
|
420
|
+
case 'notStartsWith':
|
|
421
|
+
if(!value.startsWith(condition.value)){
|
|
422
|
+
conditionMatched = true
|
|
423
|
+
}
|
|
424
|
+
break
|
|
425
|
+
|
|
426
|
+
case 'contains':
|
|
427
|
+
if(value.indexOf(condition.value) >= 0){
|
|
428
|
+
conditionMatched = true
|
|
429
|
+
}
|
|
430
|
+
break
|
|
431
|
+
|
|
432
|
+
case 'notContains':
|
|
433
|
+
if(value.indexOf(condition.value) < 0){
|
|
434
|
+
conditionMatched = true
|
|
435
|
+
}
|
|
436
|
+
break
|
|
437
|
+
|
|
438
|
+
case 'endsWith':
|
|
439
|
+
if(value.endsWith(condition.value)){
|
|
440
|
+
conditionMatched = true
|
|
441
|
+
}
|
|
442
|
+
break
|
|
443
|
+
|
|
444
|
+
case 'notEndsWith':
|
|
445
|
+
if(!value.endsWith(condition.value)){
|
|
446
|
+
conditionMatched = true
|
|
447
|
+
}
|
|
448
|
+
break
|
|
449
|
+
|
|
450
|
+
case 'none':
|
|
372
451
|
conditionMatched = true
|
|
373
|
-
|
|
374
|
-
break
|
|
452
|
+
break
|
|
375
453
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
conditionMatched = true
|
|
379
|
-
}
|
|
380
|
-
break
|
|
454
|
+
case 'regex':
|
|
455
|
+
break
|
|
381
456
|
|
|
382
|
-
|
|
383
|
-
if(value === condition.value){
|
|
384
|
-
conditionMatched = true
|
|
385
|
-
}
|
|
386
|
-
break
|
|
387
|
-
|
|
388
|
-
case '<':
|
|
389
|
-
if(value < condition.value){
|
|
390
|
-
conditionMatched = true
|
|
391
|
-
}
|
|
392
|
-
break
|
|
393
|
-
|
|
394
|
-
case '<=':
|
|
395
|
-
if(value <= condition.value){
|
|
396
|
-
conditionMatched = true
|
|
397
|
-
}
|
|
398
|
-
break
|
|
399
|
-
|
|
400
|
-
case 'startsWith':
|
|
401
|
-
if(value.startsWith(condition.value)){
|
|
402
|
-
conditionMatched = true
|
|
403
|
-
}
|
|
404
|
-
break
|
|
405
|
-
|
|
406
|
-
case 'notStartsWith':
|
|
407
|
-
if(!value.startsWith(condition.value)){
|
|
408
|
-
conditionMatched = true
|
|
409
|
-
}
|
|
410
|
-
break
|
|
411
|
-
|
|
412
|
-
case 'contains':
|
|
413
|
-
if(value.indexOf(condition.value) >= 0){
|
|
414
|
-
conditionMatched = true
|
|
415
|
-
}
|
|
416
|
-
break
|
|
417
|
-
|
|
418
|
-
case 'notContains':
|
|
419
|
-
if(value.indexOf(condition.value) < 0){
|
|
420
|
-
conditionMatched = true
|
|
421
|
-
}
|
|
422
|
-
break
|
|
423
|
-
|
|
424
|
-
case 'endsWith':
|
|
425
|
-
if(value.endsWith(condition.value)){
|
|
426
|
-
conditionMatched = true
|
|
427
|
-
}
|
|
428
|
-
break
|
|
429
|
-
|
|
430
|
-
case 'notEndsWith':
|
|
431
|
-
if(!value.endsWith(condition.value)){
|
|
432
|
-
conditionMatched = true
|
|
433
|
-
}
|
|
434
|
-
break
|
|
435
|
-
|
|
436
|
-
case 'none':
|
|
437
|
-
conditionMatched = true
|
|
438
|
-
break
|
|
439
|
-
|
|
440
|
-
case 'regex':
|
|
441
|
-
break
|
|
457
|
+
}
|
|
442
458
|
|
|
459
|
+
if(conditionMatched) break
|
|
443
460
|
}
|
|
444
461
|
|
|
445
|
-
if(conditionMatched)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
appearanceClass = appearance.class
|
|
450
|
-
break
|
|
462
|
+
if(conditionMatched){
|
|
463
|
+
appearanceClass = appearance.class
|
|
464
|
+
break
|
|
465
|
+
}
|
|
451
466
|
}
|
|
452
467
|
}
|
|
453
468
|
}
|
|
@@ -675,4 +690,8 @@ export default{
|
|
|
675
690
|
@apply inline-block m-1 rounded-md bg-orange-600 text-white px-2 text-sm;
|
|
676
691
|
}
|
|
677
692
|
|
|
693
|
+
.total{
|
|
694
|
+
@apply px-3 uppercase font-bold;
|
|
695
|
+
}
|
|
696
|
+
|
|
678
697
|
</style>
|
|
@@ -2,11 +2,12 @@ const groupBy = require("lodash/groupBy");
|
|
|
2
2
|
const dayjs = require("dayjs");
|
|
3
3
|
const {Op, literal, fn, DataTypes} = require("sequelize");
|
|
4
4
|
const {ftWildcard} = require("./helpers");
|
|
5
|
+
const util = require("util");
|
|
5
6
|
|
|
6
7
|
const getValue = (filter, opt) => {
|
|
7
8
|
|
|
8
9
|
const { columns, withoutKey = false } = opt
|
|
9
|
-
const { key, operator } = filter
|
|
10
|
+
const { key, operator, fn } = filter
|
|
10
11
|
const keyColumns = groupBy(columns, 'key')
|
|
11
12
|
const type = ((keyColumns[key] ?? [])[0] ?? {}).type
|
|
12
13
|
|
|
@@ -213,17 +214,26 @@ const getValue = (filter, opt) => {
|
|
|
213
214
|
break
|
|
214
215
|
|
|
215
216
|
case '<=':
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
217
|
+
switch(fn){
|
|
218
|
+
|
|
219
|
+
case 'format_hour':
|
|
220
|
+
//console.log('<=', filter.value)
|
|
221
|
+
break
|
|
222
|
+
|
|
223
|
+
default:
|
|
224
|
+
withoutKey ?
|
|
225
|
+
whereObj = {
|
|
226
|
+
[Op.lte]: [
|
|
227
|
+
dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
|
|
228
|
+
]
|
|
229
|
+
} :
|
|
230
|
+
whereObj[key] = {
|
|
231
|
+
[Op.lte]: [
|
|
232
|
+
dayjs(filter.value).format('YYYY-MM-DD 23:59:59'),
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
227
237
|
break
|
|
228
238
|
|
|
229
239
|
case '=':
|
|
@@ -257,17 +267,25 @@ const getValue = (filter, opt) => {
|
|
|
257
267
|
break
|
|
258
268
|
|
|
259
269
|
case '>=':
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
270
|
+
switch(fn) {
|
|
271
|
+
|
|
272
|
+
case 'format_hour':
|
|
273
|
+
//console.log('>=', filter.value)
|
|
274
|
+
break
|
|
275
|
+
|
|
276
|
+
default:
|
|
277
|
+
withoutKey ?
|
|
278
|
+
whereObj = {
|
|
279
|
+
[Op.gte]: [
|
|
280
|
+
dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
|
|
281
|
+
]
|
|
282
|
+
} :
|
|
283
|
+
whereObj[key] = {
|
|
284
|
+
[Op.gte]: [
|
|
285
|
+
dayjs(filter.value).format('YYYY-MM-DD 00:00:00'),
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
}
|
|
271
289
|
break
|
|
272
290
|
|
|
273
291
|
}
|
|
@@ -724,6 +742,12 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
|
724
742
|
if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y'), 'asc' ])
|
|
725
743
|
break
|
|
726
744
|
|
|
745
|
+
case 'hour':
|
|
746
|
+
attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%H:00-%H:59'), row.key ])
|
|
747
|
+
group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%H:00-%H:59'), row.key ])
|
|
748
|
+
if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%H:00-%H:59'), 'asc' ])
|
|
749
|
+
break
|
|
750
|
+
|
|
727
751
|
default:
|
|
728
752
|
attributes.push([ literal(`${model.name}.${field}`), row.key ])
|
|
729
753
|
group.push(`${model.name}.${field}`)
|
|
@@ -157,9 +157,29 @@ const generatePivotColumns = (preset, items) => {
|
|
|
157
157
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
const generateTotalColumns = (preset, items) => {
|
|
161
|
+
if(!preset.pivot || !preset.pivot.enabled || !preset.pivot.useTotal || (items ?? []).length < 1) return
|
|
162
|
+
|
|
163
|
+
const totalItem = { _type:'totalRow' }
|
|
164
|
+
|
|
165
|
+
for(let item of items){
|
|
166
|
+
for(let key in item){
|
|
167
|
+
if(key.startsWith('_')){
|
|
168
|
+
if(!totalItem[key]){
|
|
169
|
+
totalItem[key] = 0
|
|
170
|
+
}
|
|
171
|
+
totalItem[key] += parseFloat(item[key])
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
items.push(totalItem)
|
|
177
|
+
}
|
|
178
|
+
|
|
160
179
|
export {
|
|
161
180
|
sortsFn,
|
|
162
181
|
pickColumns,
|
|
163
182
|
setupConfig,
|
|
164
|
-
generatePivotColumns
|
|
183
|
+
generatePivotColumns,
|
|
184
|
+
generateTotalColumns
|
|
165
185
|
}
|
|
@@ -490,6 +490,7 @@
|
|
|
490
490
|
v-model="item.aggregrate"
|
|
491
491
|
class="px-1 appearance-none bg-base-300 rounded-md border-[1px] border-text-50 outline-none"
|
|
492
492
|
@change="apply()">
|
|
493
|
+
<option value="hour">Hour</option>
|
|
493
494
|
<option value="date">Date</option>
|
|
494
495
|
<option value="month">Month</option>
|
|
495
496
|
<option value="year">Year</option>
|
|
@@ -578,6 +579,11 @@
|
|
|
578
579
|
<PresetBarPivotColumnEdit ref="presetBarPivotColumnEdit" />
|
|
579
580
|
</div>
|
|
580
581
|
|
|
582
|
+
<div class="flex flex-row items-center gap-3">
|
|
583
|
+
<label>Total</label>
|
|
584
|
+
<Switch v-model="presetPivot.useTotal" />
|
|
585
|
+
</div>
|
|
586
|
+
|
|
581
587
|
</div>
|
|
582
588
|
|
|
583
589
|
</div>
|