@mixd-id/web-scaffold 0.1.230406061 → 0.1.230406062
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/Checkbox.vue +4 -1
- package/src/components/ListView.vue +251 -18
- package/src/components/ListViewBarSummary.vue +80 -0
- package/src/components/ListViewLineSummary.vue +75 -0
- package/src/components/ListViewMapSummary.vue +56 -0
- package/src/components/ListViewSettings.vue +44 -212
- package/src/components/ListViewTableSummary.vue +77 -0
- package/src/components/VirtualScroll.vue +1 -1
- package/src/index.js +4 -0
- package/src/utils/listview.js +25 -155
package/package.json
CHANGED
|
@@ -43,9 +43,12 @@ export default {
|
|
|
43
43
|
if(Array.isArray(this.modelValue)){
|
|
44
44
|
return this.modelValue.includes(this.value)
|
|
45
45
|
}
|
|
46
|
-
else{
|
|
46
|
+
else if(this.modelValue){
|
|
47
47
|
return this.value === this.modelValue
|
|
48
48
|
}
|
|
49
|
+
else{
|
|
50
|
+
return !!this.value
|
|
51
|
+
}
|
|
49
52
|
}
|
|
50
53
|
else if(this.trueValue && this.falseValue) {
|
|
51
54
|
return this.modelValue === this.trueValue
|
|
@@ -75,13 +75,12 @@
|
|
|
75
75
|
</Textbox>
|
|
76
76
|
</div>
|
|
77
77
|
|
|
78
|
-
<div class="
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
</div>
|
|
78
|
+
<div v-if="presetSummary && summary" class="h-[300px] max-h-[30vh] flex">
|
|
79
|
+
<Bar v-if="presetSummary.mode === 'bar'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
|
|
80
|
+
<Line v-else-if="presetSummary.mode === 'line'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
|
|
81
|
+
<VirtualTable v-else-if="presetSummary.mode === 'table'" :items="summaryItems" :columns="summaryColumns" class="flex-1"></VirtualTable>
|
|
82
|
+
<Gmaps v-else-if="presetSummary.mode === 'map'" :data="summary.items" :config="presetSummary.map"
|
|
83
|
+
class="flex-1 w-full p-2" :apiKey="mapApiKey" />
|
|
85
84
|
</div>
|
|
86
85
|
|
|
87
86
|
<div class="flex-1 flex" v-if="mediaPrefix">
|
|
@@ -168,8 +167,12 @@
|
|
|
168
167
|
<script>
|
|
169
168
|
|
|
170
169
|
import throttle from "lodash/throttle";
|
|
170
|
+
import { Bar, Line } from 'vue-chartjs'
|
|
171
|
+
import Chart from 'chart.js/auto'
|
|
172
|
+
import dayjs from "dayjs";
|
|
171
173
|
|
|
172
174
|
export default{
|
|
175
|
+
components: {Line, Bar},
|
|
173
176
|
|
|
174
177
|
emits: [ ],
|
|
175
178
|
|
|
@@ -281,15 +284,13 @@ export default{
|
|
|
281
284
|
if(!this.src) return
|
|
282
285
|
|
|
283
286
|
this.$refs.table1 ? this.$refs.table1.setState(2) : this.$refs.table2.setState(3)
|
|
284
|
-
this.socketEmit2(this.src, { preset:
|
|
287
|
+
this.socketEmit2(this.src, { columns:this.config.columns, preset:this.preset })
|
|
285
288
|
.then((res) => {
|
|
286
289
|
Object.assign(this.$data, res)
|
|
287
290
|
})
|
|
288
291
|
.finally(() => {
|
|
289
292
|
this.$refs.table1 ? this.$refs.table1.setState(1) : this.$refs.table2.setState(1)
|
|
290
293
|
})
|
|
291
|
-
|
|
292
|
-
this.loadSummary()
|
|
293
294
|
},
|
|
294
295
|
|
|
295
296
|
loadNext(){
|
|
@@ -314,6 +315,10 @@ export default{
|
|
|
314
315
|
|
|
315
316
|
if(!this.configStoreObj || 'reset' in ((this.$route ?? {}).query ?? {})){
|
|
316
317
|
this.configLoaded = true
|
|
318
|
+
|
|
319
|
+
if('reset' in ((this.$route ?? {}).query ?? {}))
|
|
320
|
+
await this.saveConfig()
|
|
321
|
+
|
|
317
322
|
return
|
|
318
323
|
}
|
|
319
324
|
|
|
@@ -339,8 +344,9 @@ export default{
|
|
|
339
344
|
preset: this.preset
|
|
340
345
|
})
|
|
341
346
|
.then((res) => {
|
|
342
|
-
|
|
343
|
-
|
|
347
|
+
console.log('res', res)
|
|
348
|
+
/*const data = res && res.data ? res.data : res
|
|
349
|
+
this.summary = data*/
|
|
344
350
|
})
|
|
345
351
|
.catch((err) => {
|
|
346
352
|
this.toast(err)
|
|
@@ -348,20 +354,46 @@ export default{
|
|
|
348
354
|
},
|
|
349
355
|
|
|
350
356
|
onHooks(model, event, items){
|
|
357
|
+
|
|
351
358
|
if(model === this.subscriptionObj.model){
|
|
352
359
|
switch(event){
|
|
353
360
|
|
|
354
361
|
case 'create':
|
|
355
362
|
case 'update':
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
363
|
+
if(Object.keys(items[0]).length <= 1 && Object.keys(items[0])[0] === 'uid'){
|
|
364
|
+
this.socketEmit2(this.src, {
|
|
365
|
+
columns:this.config.columns,
|
|
366
|
+
preset:{
|
|
367
|
+
...this.preset,
|
|
368
|
+
filters: [{
|
|
369
|
+
enabled: true,
|
|
370
|
+
key: "uid",
|
|
371
|
+
filters: [
|
|
372
|
+
{
|
|
373
|
+
operator: "in",
|
|
374
|
+
value: items.map((_) => _.uid)
|
|
375
|
+
}
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
.then(({ items }) => {
|
|
382
|
+
this.$util.unshift(this.items, ...items)
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
else{
|
|
386
|
+
this.$util.unshift(this.items, ...items)
|
|
387
|
+
}
|
|
359
388
|
break
|
|
360
389
|
|
|
361
390
|
case 'remove':
|
|
362
391
|
case 'destroy':
|
|
363
392
|
items.forEach((item) => {
|
|
364
|
-
const idx = this.items.findIndex((_) =>
|
|
393
|
+
const idx = this.items.findIndex((_) => {
|
|
394
|
+
return item.uid ? _.uid === item.uid :
|
|
395
|
+
(item.id ? _.id === item.id : false)
|
|
396
|
+
})
|
|
365
397
|
if(idx >= 0){
|
|
366
398
|
this.items.splice(idx, 1)
|
|
367
399
|
}
|
|
@@ -439,7 +471,54 @@ export default{
|
|
|
439
471
|
this.socketEmit2(method, { name:model }).then()
|
|
440
472
|
break
|
|
441
473
|
}
|
|
442
|
-
}
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
summaryColumns(){
|
|
477
|
+
|
|
478
|
+
const columns = [ ...this.summary.columns ]
|
|
479
|
+
|
|
480
|
+
if(this.presetSummary.mode === 'table' && this.presetSummary.table.showColumnTotal){
|
|
481
|
+
columns.push({ key:"_total", label:"Total", visible:true, type:"number" })
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return columns
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
summaryItems(){
|
|
488
|
+
|
|
489
|
+
const items = [ ...this.summary.items ]
|
|
490
|
+
|
|
491
|
+
//console.log('#1', this.presetSummary.showColumnTotal, this.presetSummary.showRowTotal, items.length)
|
|
492
|
+
|
|
493
|
+
if(this.presetSummary.mode === 'table' && this.presetSummary.table.showColumnTotal){
|
|
494
|
+
items.forEach((item) => {
|
|
495
|
+
let total = 0
|
|
496
|
+
for(let key in item){
|
|
497
|
+
if([ 'dfDate', '_total' ].includes(key)) continue
|
|
498
|
+
total += parseInt(item[key])
|
|
499
|
+
}
|
|
500
|
+
item._total = total
|
|
501
|
+
})
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
if(this.presetSummary.mode === 'table' && this.presetSummary.table.showRowTotal){
|
|
506
|
+
const totalItem = { dfDate:"Total" }
|
|
507
|
+
this.summary.items.forEach((item) => {
|
|
508
|
+
|
|
509
|
+
for(let key in item){
|
|
510
|
+
if([ 'dfDate' ].includes(key)) continue
|
|
511
|
+
|
|
512
|
+
if(!(key in totalItem))
|
|
513
|
+
totalItem[key] = 0
|
|
514
|
+
totalItem[key] += parseInt(item[key])
|
|
515
|
+
}
|
|
516
|
+
})
|
|
517
|
+
items.push(totalItem)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return items
|
|
521
|
+
},
|
|
443
522
|
|
|
444
523
|
},
|
|
445
524
|
|
|
@@ -447,6 +526,128 @@ export default{
|
|
|
447
526
|
|
|
448
527
|
computed: {
|
|
449
528
|
|
|
529
|
+
|
|
530
|
+
chartOptions(){
|
|
531
|
+
|
|
532
|
+
var style = getComputedStyle(document.body)
|
|
533
|
+
var gridColor = style.getPropertyValue('--text-50')
|
|
534
|
+
var gridColor2 = style.getPropertyValue('--text-200')
|
|
535
|
+
|
|
536
|
+
const baseColumn = this.summary.columns[0] ?? {}
|
|
537
|
+
const baseColumnType = baseColumn.type
|
|
538
|
+
|
|
539
|
+
let highGrids = []
|
|
540
|
+
if(baseColumnType === 'date'){
|
|
541
|
+
this.summary.items.forEach((item) => {
|
|
542
|
+
if(dayjs(item.dfDate).day() === 1){
|
|
543
|
+
highGrids.push(dayjs(item.dfDate).format('D MMM'))
|
|
544
|
+
}
|
|
545
|
+
})
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
responsive: true,
|
|
550
|
+
maintainAspectRatio: false,
|
|
551
|
+
plugins: {
|
|
552
|
+
legend: !this.presetSummary.hideLegends,
|
|
553
|
+
tooltip: {
|
|
554
|
+
callbacks: {
|
|
555
|
+
label: function(context) {
|
|
556
|
+
|
|
557
|
+
const labels = []
|
|
558
|
+
|
|
559
|
+
labels.push(context.dataset.label + ': ' + context.parsed.y)
|
|
560
|
+
|
|
561
|
+
if(context.parsed._stacks){
|
|
562
|
+
let total = 0
|
|
563
|
+
let percent = 0
|
|
564
|
+
for(let key in context.parsed._stacks.y){
|
|
565
|
+
if(!isNaN(parseInt(key))){
|
|
566
|
+
total += parseInt(context.parsed._stacks.y[key])
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
percent = Math.round(parseInt(context.parsed.y) / total * 100)
|
|
570
|
+
labels.push(`Total: ${total} (${percent}%)`)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return labels;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
scales: {
|
|
579
|
+
x: {
|
|
580
|
+
grid: {
|
|
581
|
+
color: function(context){
|
|
582
|
+
if(baseColumnType === 'date' && context.tick && highGrids.includes(context.tick.label)){
|
|
583
|
+
return `rgb(${gridColor2})`
|
|
584
|
+
}
|
|
585
|
+
return `rgb(${gridColor})`
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
stacked: this.presetSummary.mode === 'bar' ? this.presetSummary[this.presetSummary.mode].stacked : undefined
|
|
589
|
+
},
|
|
590
|
+
y: {
|
|
591
|
+
grid: {
|
|
592
|
+
color: function(context){
|
|
593
|
+
return `rgb(${gridColor})`
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
stacked: this.presetSummary.mode === 'bar' ? this.presetSummary[this.presetSummary.mode].stacked : undefined
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
|
|
602
|
+
chartData(){
|
|
603
|
+
|
|
604
|
+
const column = this.summary.columns.filter((_) => _.key === 'dfDate').pop() ?? {}
|
|
605
|
+
|
|
606
|
+
const labels = []
|
|
607
|
+
this.summary.items.forEach((item) => {
|
|
608
|
+
|
|
609
|
+
let label = item.dfDate
|
|
610
|
+
switch(column.type){
|
|
611
|
+
|
|
612
|
+
case 'date':
|
|
613
|
+
const dateFormat = column.format ?? 'D MMM YY HH:mm:ss'
|
|
614
|
+
const djs = dayjs(item.dfDate)
|
|
615
|
+
label = djs.isValid() ? djs.format(dateFormat) : item.dfDate
|
|
616
|
+
break
|
|
617
|
+
}
|
|
618
|
+
labels.push(label)
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
const datasets = []
|
|
622
|
+
this.summary.columns.forEach((column) => {
|
|
623
|
+
if(column.key === 'dfDate') return
|
|
624
|
+
|
|
625
|
+
const data = []
|
|
626
|
+
this.summary.items.forEach((item) => {
|
|
627
|
+
data.push(parseInt(item[column.key]))
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
const dataset = {
|
|
631
|
+
label: column.label,
|
|
632
|
+
data,
|
|
633
|
+
backgroundColor: this.chartOpt.backgroundColors[datasets.length % 9],
|
|
634
|
+
borderColor: this.chartOpt.borderColors[datasets.length % 9]
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
datasets.push(dataset)
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
return {
|
|
641
|
+
labels,
|
|
642
|
+
datasets
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/*return {
|
|
646
|
+
labels: [ 'January', 'February', 'March' ],
|
|
647
|
+
datasets: [ { data: [40, 20, 12] } ]
|
|
648
|
+
}*/
|
|
649
|
+
},
|
|
650
|
+
|
|
450
651
|
subscriptionObj(){
|
|
451
652
|
const splitted = ((this.subscription ?? '').toString()).split(':')
|
|
452
653
|
const splitted2 = (splitted[1] ?? '').split(',')
|
|
@@ -539,6 +740,10 @@ export default{
|
|
|
539
740
|
return c
|
|
540
741
|
},
|
|
541
742
|
|
|
743
|
+
presetSummary(){
|
|
744
|
+
return ((this.preset ?? {}).summaries ?? []).filter((_) => _.enabled).pop()
|
|
745
|
+
},
|
|
746
|
+
|
|
542
747
|
},
|
|
543
748
|
|
|
544
749
|
data(){
|
|
@@ -549,7 +754,35 @@ export default{
|
|
|
549
754
|
configLoaded: false,
|
|
550
755
|
selectedColumn: null,
|
|
551
756
|
copiedConfig: null,
|
|
552
|
-
mediaPrefix: null
|
|
757
|
+
mediaPrefix: null,
|
|
758
|
+
summary: null,
|
|
759
|
+
mapApiKey: null,
|
|
760
|
+
chartOpt: {
|
|
761
|
+
backgroundColors: [
|
|
762
|
+
'#5D9CEC',
|
|
763
|
+
'#A0D468',
|
|
764
|
+
'#FFCE54',
|
|
765
|
+
'#FC6E51',
|
|
766
|
+
'#48CFAD',
|
|
767
|
+
'#AC92EC',
|
|
768
|
+
'#4FC1E9',
|
|
769
|
+
'#FFCE54',
|
|
770
|
+
'#ED5565',
|
|
771
|
+
'#EC87C0'
|
|
772
|
+
],
|
|
773
|
+
borderColors: [
|
|
774
|
+
'#4A89DC',
|
|
775
|
+
'#8CC152',
|
|
776
|
+
'#F6BB42',
|
|
777
|
+
'#E9573F',
|
|
778
|
+
'#37BC9B',
|
|
779
|
+
'#967ADC',
|
|
780
|
+
'#3BAFDA',
|
|
781
|
+
'#F6BB42',
|
|
782
|
+
'#DA4453',
|
|
783
|
+
'#D770AD'
|
|
784
|
+
],
|
|
785
|
+
},
|
|
553
786
|
}
|
|
554
787
|
},
|
|
555
788
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.comp">
|
|
3
|
+
|
|
4
|
+
<div class="p-3">
|
|
5
|
+
<label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
|
|
6
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
7
|
+
<Dropdown v-model="summary.bar.rows[0].key" class="flex-1">
|
|
8
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
9
|
+
<option v-for="column in columns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
10
|
+
</Dropdown>
|
|
11
|
+
<Dropdown v-model="summary.bar.rows[0].format" class="w-[100px]">
|
|
12
|
+
<option value="">{{ $t('Default') }}</option>
|
|
13
|
+
<option value="date">{{ $t('Date') }}</option>
|
|
14
|
+
<option value="month">{{ $t('Month') }}</option>
|
|
15
|
+
<option value="quarter">{{ $t('Quarterly') }}</option>
|
|
16
|
+
<option value="year">{{ $t('Year') }}</option>
|
|
17
|
+
</Dropdown>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div class="p-3">
|
|
22
|
+
<label class="text-text-400 flex-1">{{ $t('Column') }}</label>
|
|
23
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
24
|
+
<Dropdown v-model="summary.bar.columns[0].key" class="flex-1">
|
|
25
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
26
|
+
<option value="(none)">{{ $t('None') }}</option>
|
|
27
|
+
<option v-for="column in columns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
28
|
+
</Dropdown>
|
|
29
|
+
<Dropdown v-model="summary.bar.columns[0].format" class="w-[100px]">
|
|
30
|
+
<option value="">{{ $t('Default') }}</option>
|
|
31
|
+
<option value="date">{{ $t('Date') }}</option>
|
|
32
|
+
</Dropdown>
|
|
33
|
+
</div>
|
|
34
|
+
<Checkbox class="mt-2" v-model="summary.bar.stacked">
|
|
35
|
+
Stacked
|
|
36
|
+
</Checkbox>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="p-3">
|
|
40
|
+
<label class="text-text-400 flex-1">Values</label>
|
|
41
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
42
|
+
<Dropdown v-model="summary.bar.values[0].aggregrate" class="flex-1">
|
|
43
|
+
<option value="" disabled selected>{{ $t('Select') }}</option>
|
|
44
|
+
<option value="count">{{ $t('Count') }}</option>
|
|
45
|
+
<option value="max">{{ $t('Max') }}</option>
|
|
46
|
+
<option value="min">{{ $t('Min') }}</option>
|
|
47
|
+
<option value="avg">{{ $t('Avg') }}</option>
|
|
48
|
+
</Dropdown>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script>
|
|
56
|
+
|
|
57
|
+
export default{
|
|
58
|
+
|
|
59
|
+
props: {
|
|
60
|
+
|
|
61
|
+
columns: Array,
|
|
62
|
+
|
|
63
|
+
summary: {
|
|
64
|
+
type: Object,
|
|
65
|
+
required: true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<style module>
|
|
75
|
+
|
|
76
|
+
.comp{
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
</style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.comp">
|
|
3
|
+
|
|
4
|
+
<div class="p-3">
|
|
5
|
+
<label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
|
|
6
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
7
|
+
<Dropdown v-model="openedPresetSummary.line.rows[0].key" class="flex-1">
|
|
8
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
9
|
+
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
10
|
+
</Dropdown>
|
|
11
|
+
<Dropdown v-model="openedPresetSummary.line.rows[0].format" class="w-[100px]">
|
|
12
|
+
<option value="">{{ $t('Default') }}</option>
|
|
13
|
+
<option value="date">{{ $t('Date') }}</option>
|
|
14
|
+
<option value="month">{{ $t('Month') }}</option>
|
|
15
|
+
<option value="quarter">{{ $t('Quarterly') }}</option>
|
|
16
|
+
<option value="year">{{ $t('Year') }}</option>
|
|
17
|
+
</Dropdown>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div class="p-3">
|
|
22
|
+
<label class="text-text-400 flex-1">{{$t('Column') }}</label>
|
|
23
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
24
|
+
<Dropdown v-model="openedPresetSummary.line.columns[0].key" class="flex-1">
|
|
25
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
26
|
+
<option value="(none)">{{ $t('None') }}</option>
|
|
27
|
+
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
28
|
+
</Dropdown>
|
|
29
|
+
<Dropdown v-model="openedPresetSummary.line.columns[0].format" class="w-[100px]">
|
|
30
|
+
<option value="">{{ $t('Default') }}</option>
|
|
31
|
+
<option value="date">{{ $t('Date') }}</option>
|
|
32
|
+
</Dropdown>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="p-3">
|
|
37
|
+
<label class="text-text-400 flex-1">Values</label>
|
|
38
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
39
|
+
<Dropdown v-model="openedPresetSummary.line.values[0].aggregrate" class="flex-1">
|
|
40
|
+
<option value="" disabled selected>{{ $t('Select') }}</option>
|
|
41
|
+
<option value="count">{{ $t('Count') }}</option>
|
|
42
|
+
<option value="max">{{ $t('Max') }}</option>
|
|
43
|
+
<option value="min">{{ $t('Min') }}</option>
|
|
44
|
+
<option value="avg">{{ $t('Avg') }}</option>
|
|
45
|
+
</Dropdown>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<script>
|
|
53
|
+
|
|
54
|
+
export default{
|
|
55
|
+
|
|
56
|
+
props: {
|
|
57
|
+
|
|
58
|
+
summary: {
|
|
59
|
+
type: Object,
|
|
60
|
+
required: true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style module>
|
|
70
|
+
|
|
71
|
+
.comp{
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
</style>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.comp">
|
|
3
|
+
|
|
4
|
+
<div class="p-3 flex flex-row gap-3">
|
|
5
|
+
<div class="flex-1">
|
|
6
|
+
<label class="text-text-400 flex-1">Map Type</label>
|
|
7
|
+
<Dropdown class="mt-2" v-model="openedPresetSummary.map.mapType">
|
|
8
|
+
<option value="" disabled selected>(Default)</option>
|
|
9
|
+
<option value="heatmap">Heatmap</option>
|
|
10
|
+
</Dropdown>
|
|
11
|
+
</div>
|
|
12
|
+
<div>
|
|
13
|
+
<label class="text-text-400 flex-1">Radius</label>
|
|
14
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
15
|
+
<Textbox v-model="openedPresetSummary.map.mapRadius" class="w-[60px]" />
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="p-3">
|
|
21
|
+
<label class="text-text-400 flex-1">{{$t('Property') }}</label>
|
|
22
|
+
<div>
|
|
23
|
+
<Dropdown v-model="openedPresetSummary.map.key" class="flex-1">
|
|
24
|
+
<option value="" disabled selected>{{ $t('Select Column') }}</option>
|
|
25
|
+
<option v-for="column in coordinateColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
26
|
+
</Dropdown>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script>
|
|
34
|
+
|
|
35
|
+
export default{
|
|
36
|
+
|
|
37
|
+
props: {
|
|
38
|
+
|
|
39
|
+
summary: {
|
|
40
|
+
type: Object,
|
|
41
|
+
required: true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<style module>
|
|
51
|
+
|
|
52
|
+
.comp{
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
</style>
|
|
@@ -67,13 +67,13 @@
|
|
|
67
67
|
</div>
|
|
68
68
|
|
|
69
69
|
<div class="flex justify-center">
|
|
70
|
-
<Tabs v-model="
|
|
70
|
+
<Tabs v-model="presetTab" :items="tabItems" @change="tabChanged"/>
|
|
71
71
|
</div>
|
|
72
72
|
</div>
|
|
73
73
|
|
|
74
74
|
<div class="flex-1 overflow-y-auto px-6 mb-6">
|
|
75
75
|
|
|
76
|
-
<div class="p-2 mt-6 flex flex-col" v-if="!
|
|
76
|
+
<div class="p-2 mt-6 flex flex-col" v-if="!presetTab || presetTab === 'column'">
|
|
77
77
|
<ListItem :items="presetColumns" @reorder="reorderColumns">
|
|
78
78
|
<template v-slot="{ item }">
|
|
79
79
|
<div class="flex flex-row items-center gap-2" :key="item">
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
</ListItem>
|
|
92
92
|
</div>
|
|
93
93
|
|
|
94
|
-
<div class="flex flex-col" v-else-if="
|
|
94
|
+
<div class="flex flex-col" v-else-if="presetTab === 'filter'">
|
|
95
95
|
<div v-if="filterableColumns.length > 0">
|
|
96
96
|
<ListPage1Filter v-if="preset.filters" v-for="filter in preset.filters"
|
|
97
97
|
:filter="filter" :column="config.columns[config.columns.findIndex((_) => _.key === filter.key)]"
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
</div>
|
|
111
111
|
</div>
|
|
112
112
|
|
|
113
|
-
<div class="flex flex-col" v-else-if="
|
|
113
|
+
<div class="flex flex-col" v-else-if="presetTab === 'sort'">
|
|
114
114
|
<div v-if="sortableColumns.length > 0" class="my-8">
|
|
115
115
|
<div v-for="sort in preset.sorts" class="py-4">
|
|
116
116
|
<div class="flex flex-row items-center gap-2">
|
|
@@ -148,26 +148,30 @@
|
|
|
148
148
|
</div>
|
|
149
149
|
</div>
|
|
150
150
|
|
|
151
|
-
<div class="flex flex-col gap-3 py-8" v-else-if="
|
|
151
|
+
<div class="flex flex-col gap-3 py-8" v-else-if="presetTab === 'summary'">
|
|
152
152
|
|
|
153
153
|
<div v-if="config.summaryOpenIdx === -1">
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
154
|
+
|
|
155
|
+
<div class="border-text-50 border-[1px] rounded-lg overflow-hidden divide-y divide-text-50">
|
|
156
|
+
<div v-for="(summary, idx) in preset.summaries">
|
|
157
|
+
<div class="flex flex-row items-center gap-3 px-2">
|
|
158
|
+
<div class="p-2">
|
|
159
|
+
<Checkbox :value="summary.enabled" @change="selectSummary(idx)" />
|
|
160
|
+
</div>
|
|
161
|
+
<div class="flex-1" @click="config.summaryOpenIdx = idx">
|
|
162
|
+
<label>{{ summary.title }}</label>
|
|
163
|
+
</div>
|
|
164
|
+
<button type="button" class="px-2" v-if="preset.summaries.length > 1" @click="removeSummary(summary)">
|
|
165
|
+
<svg width="16" height="16" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
|
|
166
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 10.25C10.4142 10.25 10.75 10.5858 10.75 11V16C10.75 16.4142 10.4142 16.75 10 16.75C9.58579 16.75 9.25 16.4142 9.25 16V11C9.25 10.5858 9.58579 10.25 10 10.25Z"/>
|
|
167
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 10.25C14.4142 10.25 14.75 10.5858 14.75 11V16C14.75 16.4142 14.4142 16.75 14 16.75C13.5858 16.75 13.25 16.4142 13.25 16V11C13.25 10.5858 13.5858 10.25 14 10.25Z"/>
|
|
168
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2.25C8.48122 2.25 7.25 3.48122 7.25 5V5.25H3C2.58579 5.25 2.25 5.58579 2.25 6C2.25 6.41421 2.58579 6.75 3 6.75H4.25V19C4.25 20.5188 5.48122 21.75 7 21.75H17C18.5188 21.75 19.75 20.5188 19.75 19V6.75H21C21.4142 6.75 21.75 6.41421 21.75 6C21.75 5.58579 21.4142 5.25 21 5.25H16.75V5C16.75 3.48122 15.5188 2.25 14 2.25H10ZM15.25 5.25V5C15.25 4.30964 14.6904 3.75 14 3.75H10C9.30964 3.75 8.75 4.30964 8.75 5V5.25H15.25ZM5.75 6.75V19C5.75 19.6904 6.30964 20.25 7 20.25H17C17.6904 20.25 18.25 19.6904 18.25 19V6.75H5.75Z"/>
|
|
169
|
+
</svg>
|
|
170
|
+
</button>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
171
175
|
<div class="p-2 text-center">
|
|
172
176
|
<button type="button" @click="addSummary">
|
|
173
177
|
<svg width="21" height="21" viewBox="0 0 24 24" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -215,187 +219,21 @@
|
|
|
215
219
|
</div>
|
|
216
220
|
</div>
|
|
217
221
|
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
<div class="p-3">
|
|
221
|
-
<label class="text-text-400 flex-1">{{ $t('Row') }}</label>
|
|
222
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
223
|
-
<Dropdown v-model="openedPresetSummary.table.rows[0].key" class="flex-1">
|
|
224
|
-
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
225
|
-
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
226
|
-
</Dropdown>
|
|
227
|
-
<Dropdown v-model="openedPresetSummary.table.rows[0].format" class="w-[100px]">
|
|
228
|
-
<option value="">{{ $t('Default') }}</option>
|
|
229
|
-
<option value="date">{{ $t('Date') }}</option>
|
|
230
|
-
<option value="month">{{ $t('Month') }}</option>
|
|
231
|
-
<option value="quarter">{{ $t('Quarterly') }}</option>
|
|
232
|
-
<option value="year">{{ $t('Year') }}</option>
|
|
233
|
-
</Dropdown>
|
|
234
|
-
</div>
|
|
235
|
-
<Checkbox class="mt-2" v-model="openedPresetSummary.table.showRowTotal">
|
|
236
|
-
Row Total
|
|
237
|
-
</Checkbox>
|
|
238
|
-
</div>
|
|
239
|
-
|
|
240
|
-
<div class="p-3">
|
|
241
|
-
<label class="text-text-400 flex-1">{{$t('Column') }}</label>
|
|
242
|
-
<div>
|
|
243
|
-
<Dropdown v-model="openedPresetSummary.table.columns[0].key" class="flex-1">
|
|
244
|
-
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
245
|
-
<option value="(none)">{{ $t('(None)') }}</option>
|
|
246
|
-
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
247
|
-
</Dropdown>
|
|
248
|
-
</div>
|
|
249
|
-
<Checkbox class="mt-2" v-model="openedPresetSummary.table.showColumnTotal">
|
|
250
|
-
Column Total
|
|
251
|
-
</Checkbox>
|
|
252
|
-
</div>
|
|
253
|
-
|
|
254
|
-
<div class="p-3">
|
|
255
|
-
<label class="text-text-400 flex-1">Values</label>
|
|
256
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
257
|
-
<Dropdown v-model="openedPresetSummary.table.values[0].aggregrate" class="flex-1">
|
|
258
|
-
<option value="" disabled selected>{{ $t('Select') }}</option>
|
|
259
|
-
<option value="count">{{ $t('Count') }}</option>
|
|
260
|
-
<option value="max">{{ $t('Max') }}</option>
|
|
261
|
-
<option value="min">{{ $t('Min') }}</option>
|
|
262
|
-
<option value="avg">{{ $t('Avg') }}</option>
|
|
263
|
-
</Dropdown>
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
|
|
267
|
-
</div>
|
|
268
|
-
|
|
269
|
-
<div v-else-if="openedPresetSummary.mode === 'bar'">
|
|
270
|
-
|
|
271
|
-
<div class="p-3">
|
|
272
|
-
<label class="text-text-400 flex-1">{{ $t('X-axis') }}</label>
|
|
273
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
274
|
-
<Dropdown v-model="openedPresetSummary.bar.rows[0].key" class="flex-1">
|
|
275
|
-
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
276
|
-
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
277
|
-
</Dropdown>
|
|
278
|
-
<Dropdown v-model="openedPresetSummary.bar.rows[0].format" class="w-[100px]">
|
|
279
|
-
<option value="">{{ $t('Default') }}</option>
|
|
280
|
-
<option value="date">{{ $t('Date') }}</option>
|
|
281
|
-
<option value="month">{{ $t('Month') }}</option>
|
|
282
|
-
<option value="quarter">{{ $t('Quarterly') }}</option>
|
|
283
|
-
<option value="year">{{ $t('Year') }}</option>
|
|
284
|
-
</Dropdown>
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
|
-
|
|
288
|
-
<div class="p-3">
|
|
289
|
-
<label class="text-text-400 flex-1">{{ $t('Column') }}</label>
|
|
290
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
291
|
-
<Dropdown v-model="openedPresetSummary.bar.columns[0].key" class="flex-1">
|
|
292
|
-
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
293
|
-
<option value="(none)">{{ $t('None') }}</option>
|
|
294
|
-
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
295
|
-
</Dropdown>
|
|
296
|
-
<Dropdown v-model="openedPresetSummary.bar.columns[0].format" class="w-[100px]">
|
|
297
|
-
<option value="">{{ $t('Default') }}</option>
|
|
298
|
-
<option value="date">{{ $t('Date') }}</option>
|
|
299
|
-
</Dropdown>
|
|
300
|
-
</div>
|
|
301
|
-
<Checkbox class="mt-2" v-model="openedPresetSummary.bar.stacked">
|
|
302
|
-
Stacked
|
|
303
|
-
</Checkbox>
|
|
304
|
-
</div>
|
|
305
|
-
|
|
306
|
-
<div class="p-3">
|
|
307
|
-
<label class="text-text-400 flex-1">Values</label>
|
|
308
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
309
|
-
<Dropdown v-model="openedPresetSummary.bar.values[0].aggregrate" class="flex-1">
|
|
310
|
-
<option value="" disabled selected>{{ $t('Select') }}</option>
|
|
311
|
-
<option value="count">{{ $t('Count') }}</option>
|
|
312
|
-
<option value="max">{{ $t('Max') }}</option>
|
|
313
|
-
<option value="min">{{ $t('Min') }}</option>
|
|
314
|
-
<option value="avg">{{ $t('Avg') }}</option>
|
|
315
|
-
</Dropdown>
|
|
316
|
-
</div>
|
|
317
|
-
</div>
|
|
318
|
-
|
|
319
|
-
</div>
|
|
320
|
-
|
|
321
|
-
<div v-else-if="openedPresetSummary.mode === 'line'">
|
|
222
|
+
<ListViewTableSummary v-if="openedPresetSummary.mode === 'table'"
|
|
223
|
+
:summary="openedPresetSummary" />
|
|
322
224
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
326
|
-
<Dropdown v-model="openedPresetSummary.line.rows[0].key" class="flex-1">
|
|
327
|
-
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
328
|
-
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
329
|
-
</Dropdown>
|
|
330
|
-
<Dropdown v-model="openedPresetSummary.line.rows[0].format" class="w-[100px]">
|
|
331
|
-
<option value="">{{ $t('Default') }}</option>
|
|
332
|
-
<option value="date">{{ $t('Date') }}</option>
|
|
333
|
-
<option value="month">{{ $t('Month') }}</option>
|
|
334
|
-
<option value="quarter">{{ $t('Quarterly') }}</option>
|
|
335
|
-
<option value="year">{{ $t('Year') }}</option>
|
|
336
|
-
</Dropdown>
|
|
337
|
-
</div>
|
|
338
|
-
</div>
|
|
225
|
+
<ListViewBarSummary v-else-if="openedPresetSummary.mode === 'bar'"
|
|
226
|
+
:summary="openedPresetSummary" :columns="filterableColumns" />
|
|
339
227
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
343
|
-
<Dropdown v-model="openedPresetSummary.line.columns[0].key" class="flex-1">
|
|
344
|
-
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
345
|
-
<option value="(none)">{{ $t('None') }}</option>
|
|
346
|
-
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
347
|
-
</Dropdown>
|
|
348
|
-
<Dropdown v-model="openedPresetSummary.line.columns[0].format" class="w-[100px]">
|
|
349
|
-
<option value="">{{ $t('Default') }}</option>
|
|
350
|
-
<option value="date">{{ $t('Date') }}</option>
|
|
351
|
-
</Dropdown>
|
|
352
|
-
</div>
|
|
353
|
-
</div>
|
|
354
|
-
|
|
355
|
-
<div class="p-3">
|
|
356
|
-
<label class="text-text-400 flex-1">Values</label>
|
|
357
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
358
|
-
<Dropdown v-model="openedPresetSummary.line.values[0].aggregrate" class="flex-1">
|
|
359
|
-
<option value="" disabled selected>{{ $t('Select') }}</option>
|
|
360
|
-
<option value="count">{{ $t('Count') }}</option>
|
|
361
|
-
<option value="max">{{ $t('Max') }}</option>
|
|
362
|
-
<option value="min">{{ $t('Min') }}</option>
|
|
363
|
-
<option value="avg">{{ $t('Avg') }}</option>
|
|
364
|
-
</Dropdown>
|
|
365
|
-
</div>
|
|
366
|
-
</div>
|
|
367
|
-
|
|
368
|
-
</div>
|
|
369
|
-
|
|
370
|
-
<div v-else-if="openedPresetSummary.mode === 'map'">
|
|
371
|
-
|
|
372
|
-
<div class="p-3 flex flex-row gap-3">
|
|
373
|
-
<div class="flex-1">
|
|
374
|
-
<label class="text-text-400 flex-1">Map Type</label>
|
|
375
|
-
<Dropdown class="mt-2" v-model="openedPresetSummary.map.mapType">
|
|
376
|
-
<option value="" disabled selected>(Default)</option>
|
|
377
|
-
<option value="heatmap">Heatmap</option>
|
|
378
|
-
</Dropdown>
|
|
379
|
-
</div>
|
|
380
|
-
<div>
|
|
381
|
-
<label class="text-text-400 flex-1">Radius</label>
|
|
382
|
-
<div class="flex flex-row mt-2 gap-2">
|
|
383
|
-
<Textbox v-model="openedPresetSummary.map.mapRadius" class="w-[60px]" />
|
|
384
|
-
</div>
|
|
385
|
-
</div>
|
|
386
|
-
</div>
|
|
228
|
+
<ListViewLineSummary v-else-if="openedPresetSummary.mode === 'line'"
|
|
229
|
+
:summary="openedPresetSummary" :columns="filterableColumns" />
|
|
387
230
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
<div>
|
|
391
|
-
<Dropdown v-model="openedPresetSummary.map.key" class="flex-1">
|
|
392
|
-
<option value="" disabled selected>{{ $t('Select Column') }}</option>
|
|
393
|
-
<option v-for="column in coordinateColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
394
|
-
</Dropdown>
|
|
395
|
-
</div>
|
|
396
|
-
</div>
|
|
231
|
+
<ListViewMapSummary v-else-if="openedPresetSummary.mode === 'map'"
|
|
232
|
+
:summary="openedPresetSummary" :columns="filterableColumns" />
|
|
397
233
|
|
|
398
|
-
|
|
234
|
+
<div class="p-3 mt-8">
|
|
235
|
+
<Button class="w-[80px]" variant="secondary" @click="config.summaryOpenIdx = -1">OK</Button>
|
|
236
|
+
</div>
|
|
399
237
|
|
|
400
238
|
</div>
|
|
401
239
|
|
|
@@ -523,6 +361,12 @@ export default{
|
|
|
523
361
|
this.configs.summaryOpenIdx = this.preset.summaries.length - 1
|
|
524
362
|
},
|
|
525
363
|
|
|
364
|
+
selectSummary(idx){
|
|
365
|
+
for(let i in this.preset.summaries){
|
|
366
|
+
this.preset.summaries[i].enabled = parseInt(i) === idx
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
526
370
|
},
|
|
527
371
|
|
|
528
372
|
|
|
@@ -621,24 +465,12 @@ export default{
|
|
|
621
465
|
presetSortSelector: null,
|
|
622
466
|
mediaPrefix: null,
|
|
623
467
|
detailMode: false,
|
|
468
|
+
presetTab: 'column'
|
|
624
469
|
}
|
|
625
470
|
},
|
|
626
471
|
|
|
627
472
|
mounted() {
|
|
628
473
|
this.mediaPrefix = this.$el.clientWidth <= 480 ? 'sm' : 'md'
|
|
629
|
-
},
|
|
630
|
-
|
|
631
|
-
watch: {
|
|
632
|
-
|
|
633
|
-
config: {
|
|
634
|
-
immediate: true,
|
|
635
|
-
handler(to){
|
|
636
|
-
if(!this.config.presetTab){
|
|
637
|
-
this.config.presetTab = 'column'
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
|
|
642
474
|
}
|
|
643
475
|
|
|
644
476
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.comp">
|
|
3
|
+
|
|
4
|
+
<div class="p-3">
|
|
5
|
+
<label class="text-text-400 flex-1">{{ $t('Row') }}</label>
|
|
6
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
7
|
+
<Dropdown v-model="summary.table.rows[0].key" class="flex-1">
|
|
8
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
9
|
+
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
10
|
+
</Dropdown>
|
|
11
|
+
<Dropdown v-model="summary.table.rows[0].format" class="w-[100px]">
|
|
12
|
+
<option value="">{{ $t('Default') }}</option>
|
|
13
|
+
<option value="date">{{ $t('Date') }}</option>
|
|
14
|
+
<option value="month">{{ $t('Month') }}</option>
|
|
15
|
+
<option value="quarter">{{ $t('Quarterly') }}</option>
|
|
16
|
+
<option value="year">{{ $t('Year') }}</option>
|
|
17
|
+
</Dropdown>
|
|
18
|
+
</div>
|
|
19
|
+
<Checkbox class="mt-2" v-model="summary.table.showRowTotal">
|
|
20
|
+
Row Total
|
|
21
|
+
</Checkbox>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="p-3">
|
|
25
|
+
<label class="text-text-400 flex-1">{{$t('Column') }}</label>
|
|
26
|
+
<div>
|
|
27
|
+
<Dropdown v-model="summary.table.columns[0].key" class="flex-1">
|
|
28
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
29
|
+
<option value="(none)">{{ $t('(None)') }}</option>
|
|
30
|
+
<option v-for="column in filterableColumns" :value="column.key">{{ column.label ?? column.key }}</option>
|
|
31
|
+
</Dropdown>
|
|
32
|
+
</div>
|
|
33
|
+
<Checkbox class="mt-2" v-model="summary.table.showColumnTotal">
|
|
34
|
+
Column Total
|
|
35
|
+
</Checkbox>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="p-3">
|
|
39
|
+
<label class="text-text-400 flex-1">Values</label>
|
|
40
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
41
|
+
<Dropdown v-model="summary.table.values[0].aggregrate" class="flex-1">
|
|
42
|
+
<option value="" disabled selected>{{ $t('Select') }}</option>
|
|
43
|
+
<option value="count">{{ $t('Count') }}</option>
|
|
44
|
+
<option value="max">{{ $t('Max') }}</option>
|
|
45
|
+
<option value="min">{{ $t('Min') }}</option>
|
|
46
|
+
<option value="avg">{{ $t('Avg') }}</option>
|
|
47
|
+
</Dropdown>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
</div>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<script>
|
|
55
|
+
|
|
56
|
+
export default{
|
|
57
|
+
|
|
58
|
+
props: {
|
|
59
|
+
|
|
60
|
+
summary: {
|
|
61
|
+
type: Object,
|
|
62
|
+
required: true
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<style module>
|
|
72
|
+
|
|
73
|
+
.comp{
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
</style>
|
package/src/index.js
CHANGED
|
@@ -254,6 +254,10 @@ export default{
|
|
|
254
254
|
app.component('ListItem', defineAsyncComponent(() => import("./components/ListItem.vue")))
|
|
255
255
|
app.component('ListView', defineAsyncComponent(() => import("./components/ListView.vue")))
|
|
256
256
|
app.component('ListViewSettings', defineAsyncComponent(() => import("./components/ListViewSettings.vue")))
|
|
257
|
+
app.component('ListViewBarSummary', defineAsyncComponent(() => import("./components/ListViewBarSummary.vue")))
|
|
258
|
+
app.component('ListViewTableSummary', defineAsyncComponent(() => import("./components/ListViewTableSummary.vue")))
|
|
259
|
+
app.component('ListViewLineSummary', defineAsyncComponent(() => import("./components/ListViewLineSummary.vue")))
|
|
260
|
+
app.component('ListViewMapSummary', defineAsyncComponent(() => import("./components/ListViewMapSummary.vue")))
|
|
257
261
|
app.component('Carousel', defineAsyncComponent(() => import("./components/Carousel.vue")))
|
|
258
262
|
app.component('ContextMenu', defineAsyncComponent(() => import("./components/ContextMenu.vue")))
|
|
259
263
|
app.component('Modal', defineAsyncComponent(() => import("./components/Modal.vue")))
|
package/src/utils/listview.js
CHANGED
|
@@ -90,36 +90,45 @@ let ListView = {
|
|
|
90
90
|
|
|
91
91
|
async load(params){
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
this.columns = params.columns
|
|
94
|
+
|
|
95
|
+
const { items, hasNext, count, derivedSql, where, replacements } = await this._load(params)
|
|
96
|
+
|
|
97
|
+
const summary = await this.loadSummary(params, {
|
|
98
|
+
derivedSql,
|
|
99
|
+
where,
|
|
100
|
+
replacements
|
|
101
|
+
})
|
|
94
102
|
|
|
95
103
|
if(this.name){
|
|
96
104
|
if(!this.socket.loadParams){
|
|
97
105
|
this.socket.loadParams = {}
|
|
98
106
|
}
|
|
99
|
-
|
|
100
107
|
this.socket.loadParams[this.name] = params
|
|
101
108
|
}
|
|
102
109
|
|
|
110
|
+
const extra = typeof this.postLoad === 'function' ? await this.postLoad() : undefined
|
|
111
|
+
|
|
103
112
|
return {
|
|
104
113
|
items,
|
|
105
114
|
hasNext,
|
|
106
|
-
count
|
|
115
|
+
count,
|
|
116
|
+
summary,
|
|
117
|
+
...(extra ?? {})
|
|
107
118
|
}
|
|
108
119
|
},
|
|
109
120
|
|
|
110
|
-
async loadSummary(params){
|
|
121
|
+
async loadSummary(params, loadResults){
|
|
111
122
|
|
|
112
123
|
if(!this.conn){
|
|
113
124
|
console.error('Unable to load summary, connection not found')
|
|
114
125
|
return
|
|
115
126
|
}
|
|
116
127
|
|
|
117
|
-
const
|
|
118
|
-
const summary = preset.summaries.filter((_) => _.enabled).pop()
|
|
119
|
-
|
|
128
|
+
const summary = params.preset.summaries.filter((_) => _.enabled).pop()
|
|
120
129
|
if(!summary) return
|
|
121
130
|
|
|
122
|
-
const { derivedSql, where, replacements } =
|
|
131
|
+
const { derivedSql, where, replacements } = loadResults
|
|
123
132
|
|
|
124
133
|
switch(summary.mode){
|
|
125
134
|
|
|
@@ -134,153 +143,14 @@ let ListView = {
|
|
|
134
143
|
|
|
135
144
|
case 'map':
|
|
136
145
|
if(this.loadMapSummary){
|
|
137
|
-
return await this.loadMapSummary(preset, {
|
|
146
|
+
return await this.loadMapSummary(params.preset, {
|
|
138
147
|
derivedSql,
|
|
139
148
|
where,
|
|
140
149
|
replacements
|
|
141
150
|
})
|
|
142
151
|
}
|
|
143
152
|
break
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
/*const attributes = []
|
|
152
|
-
const group = []
|
|
153
|
-
const summaryColumns = [
|
|
154
|
-
/!*{ key:"dfDate", label:"Date", width:"200px", visible:true },
|
|
155
|
-
{ key:"type-1", label:"Type 1", type:"number", visible:true },
|
|
156
|
-
{ key:"type-2", label:"Type 2", type:"number", visible:true },
|
|
157
|
-
{ key:"type-3", label:"Type 3", type:"number", visible:true },
|
|
158
|
-
{ key:"type-4", label:"Type 4", type:"number", visible:true },
|
|
159
|
-
{ key:"type-8", label:"Type 8", type:"number", visible:true },*!/
|
|
160
|
-
]
|
|
161
|
-
|
|
162
|
-
const row = rows[0]
|
|
163
|
-
const rowKey = row.key
|
|
164
|
-
const rowLabel = this.columns[row.key].label
|
|
165
|
-
switch(row.format){
|
|
166
|
-
case 'date':
|
|
167
|
-
attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
|
|
168
|
-
group.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d')`)
|
|
169
|
-
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"D MMM" })
|
|
170
|
-
break
|
|
171
|
-
|
|
172
|
-
case 'month':
|
|
173
|
-
attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m') as dfDate`)
|
|
174
|
-
group.push(`DATE_FORMAT(${rowKey}, '%Y-%m')`)
|
|
175
|
-
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"MMM YYYY" })
|
|
176
|
-
break
|
|
177
|
-
|
|
178
|
-
case 'quarter':
|
|
179
|
-
attributes.push(`CONCAT(YEAR(${rowKey}), ' Q', QUARTER(${rowKey})) as dfDate`)
|
|
180
|
-
group.push(`CONCAT(YEAR(${rowKey}), ' Q', QUARTER(${rowKey}))`)
|
|
181
|
-
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"MMM YYYY" })
|
|
182
|
-
break
|
|
183
|
-
|
|
184
|
-
case 'year':
|
|
185
|
-
attributes.push(`DATE_FORMAT(${rowKey}, '%Y') as dfDate`)
|
|
186
|
-
group.push(`DATE_FORMAT(${rowKey}, '%Y')`)
|
|
187
|
-
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"YYYY" })
|
|
188
|
-
break
|
|
189
|
-
|
|
190
|
-
default:
|
|
191
|
-
attributes.push(`${rowKey} as dfDate`)
|
|
192
|
-
group.push(`${rowKey}`)
|
|
193
|
-
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true })
|
|
194
|
-
break
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const value = values[0]
|
|
198
|
-
if(columnValues.length > 0){
|
|
199
|
-
columnValues.forEach((obj) => {
|
|
200
|
-
const columnValue = obj.cols
|
|
201
|
-
|
|
202
|
-
let columnLabel = (columnValue ?? '').toString()
|
|
203
|
-
const baseColumn = this.columns[colKey]
|
|
204
|
-
switch(baseColumn.type){
|
|
205
|
-
|
|
206
|
-
case 'enum':
|
|
207
|
-
const enumValue = baseColumn.typeParams.filter((_) => _.value === columnValue).pop()
|
|
208
|
-
if(enumValue) columnLabel = enumValue.text
|
|
209
|
-
break
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
switch(value.aggregrate){
|
|
213
|
-
case 'count':
|
|
214
|
-
const fn = columnValue === null ? `${colKey} is null` : `${colKey} = '${columnValue}'`
|
|
215
|
-
attributes.push(`SUM(IF(${fn}, 1, 0)) as \`${colKey}-${columnValue}\``)
|
|
216
|
-
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
217
|
-
break
|
|
218
|
-
|
|
219
|
-
case 'min':
|
|
220
|
-
attributes.push(`MIN(${colKey}) as \`${colKey}-${columnValue}\``)
|
|
221
|
-
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
222
|
-
break
|
|
223
|
-
|
|
224
|
-
case 'max':
|
|
225
|
-
attributes.push(`MAX(${colKey}) as \`${colKey}-${columnValue}\``)
|
|
226
|
-
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
227
|
-
break
|
|
228
|
-
|
|
229
|
-
case 'avg':
|
|
230
|
-
attributes.push(`AVG(${colKey}) as \`${colKey}-${columnValue}\``)
|
|
231
|
-
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
232
|
-
break
|
|
233
|
-
}
|
|
234
|
-
})
|
|
235
|
-
}
|
|
236
|
-
else{
|
|
237
|
-
switch(value.aggregrate) {
|
|
238
|
-
case 'count':
|
|
239
|
-
default:
|
|
240
|
-
attributes.push(`COUNT(*) as \`Count\``)
|
|
241
|
-
summaryColumns.push({ key:'Count', label:'Count', type:"number", visible:true, aggregrate:"count" })
|
|
242
|
-
}
|
|
243
153
|
}
|
|
244
|
-
|
|
245
|
-
//console.log(attributes)
|
|
246
|
-
|
|
247
|
-
let [ summaryItems ] = await this.conn.query(`
|
|
248
|
-
SELECT
|
|
249
|
-
${attributes.join(', ')}
|
|
250
|
-
FROM (
|
|
251
|
-
${derivedSql}
|
|
252
|
-
) as t1
|
|
253
|
-
GROUP BY ${group[0]}
|
|
254
|
-
ORDER BY dfDate ASC
|
|
255
|
-
`)
|
|
256
|
-
|
|
257
|
-
if(this.columns[rowKey] && this.columns[rowKey].type === 'enum'){
|
|
258
|
-
summaryItems = summaryItems.map((_) => {
|
|
259
|
-
|
|
260
|
-
const typeParam = this.columns[rowKey].typeParams.filter((__) => __.value === _.dfDate).pop()
|
|
261
|
-
if(typeParam){
|
|
262
|
-
_.dfDate = typeParam.text
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return _
|
|
266
|
-
})
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const summary = {
|
|
270
|
-
type: "table",
|
|
271
|
-
columns: summaryColumns,
|
|
272
|
-
items: summaryItems
|
|
273
|
-
/!*items: [
|
|
274
|
-
{ date:"21 Jan 23", "type-1":12, "type-2":11, "type-3":16, "type-4":2, "type-8":8 },
|
|
275
|
-
{ date:"22 Jan 23", "type-1":51, "type-2":87, "type-3":86, "type-4":3, "type-8":9 },
|
|
276
|
-
{ date:"23 Jan 23", "type-1":14, "type-2":66, "type-3":77, "type-4":4, "type-8":18 },
|
|
277
|
-
{ date:"24 Jan 23", "type-1":7, "type-2":3, "type-3":63, "type-4":5, "type-8":28 },
|
|
278
|
-
{ date:"25 Jan 23", "type-1":88, "type-2":55, "type-3":68, "type-4":6, "type-8":38 },
|
|
279
|
-
{ date:"26 Jan 23", "type-1":172, "type-2":13, "type-3":6, "type-4":7, "type-8":48 }
|
|
280
|
-
]*!/
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return summary*/
|
|
284
154
|
},
|
|
285
155
|
|
|
286
156
|
async subscribe(params, socket){
|
|
@@ -587,7 +457,7 @@ let ListView = {
|
|
|
587
457
|
|
|
588
458
|
async loadBarSummary(summary, derivedSql){
|
|
589
459
|
|
|
590
|
-
if(!summary.bar) return
|
|
460
|
+
if(!summary.bar && !this.columns) return
|
|
591
461
|
|
|
592
462
|
const { columns, rows, values } = summary.bar
|
|
593
463
|
|
|
@@ -626,7 +496,7 @@ let ListView = {
|
|
|
626
496
|
|
|
627
497
|
const row = rows[0]
|
|
628
498
|
const rowKey = row.key
|
|
629
|
-
const rowLabel = this.columns
|
|
499
|
+
const rowLabel = this.columns.find((_) => _.key === row.key).label
|
|
630
500
|
switch(row.format){
|
|
631
501
|
case 'date':
|
|
632
502
|
attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
|
|
@@ -665,7 +535,7 @@ let ListView = {
|
|
|
665
535
|
const columnValue = obj.cols
|
|
666
536
|
|
|
667
537
|
let columnLabel = (columnValue ?? '').toString()
|
|
668
|
-
const baseColumn = this.columns
|
|
538
|
+
const baseColumn = this.columns.find((_) => _.key === colKey)
|
|
669
539
|
switch(baseColumn.type){
|
|
670
540
|
|
|
671
541
|
case 'enum':
|
|
@@ -787,7 +657,7 @@ let ListView = {
|
|
|
787
657
|
|
|
788
658
|
const row = rows[0]
|
|
789
659
|
const rowKey = row.key
|
|
790
|
-
const rowLabel = this.columns
|
|
660
|
+
const rowLabel = this.columns.find((_) => _.key === row.key).label
|
|
791
661
|
switch(row.format){
|
|
792
662
|
case 'date':
|
|
793
663
|
attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
|
|
@@ -826,7 +696,7 @@ let ListView = {
|
|
|
826
696
|
const columnValue = obj.cols
|
|
827
697
|
|
|
828
698
|
let columnLabel = (columnValue ?? '').toString()
|
|
829
|
-
const baseColumn = this.columns
|
|
699
|
+
const baseColumn = this.columns.find((_) => _.key === colKey)
|
|
830
700
|
switch(baseColumn.type){
|
|
831
701
|
|
|
832
702
|
case 'enum':
|
|
@@ -948,7 +818,7 @@ let ListView = {
|
|
|
948
818
|
|
|
949
819
|
const row = rows[0]
|
|
950
820
|
const rowKey = row.key
|
|
951
|
-
const rowLabel = this.columns
|
|
821
|
+
const rowLabel = this.columns.find((_) => _.key === row.key).label
|
|
952
822
|
switch(row.format){
|
|
953
823
|
case 'date':
|
|
954
824
|
attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
|
|
@@ -987,7 +857,7 @@ let ListView = {
|
|
|
987
857
|
const columnValue = obj.cols
|
|
988
858
|
|
|
989
859
|
let columnLabel = (columnValue ?? '').toString()
|
|
990
|
-
const baseColumn = this.columns
|
|
860
|
+
const baseColumn = this.columns.find((_) => _.key === colKey)
|
|
991
861
|
switch(baseColumn.type){
|
|
992
862
|
|
|
993
863
|
case 'enum':
|