@mixd-id/web-scaffold 0.1.2301231352 → 0.1.2301231354
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mixd-id/web-scaffold",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2301231354",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite serve",
|
|
7
7
|
"build": "vite build",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@vueuse/core": "^9.0.2",
|
|
25
25
|
"adm-zip": "^0.5.10",
|
|
26
26
|
"axios": "^1.3.4",
|
|
27
|
+
"chart.js": "^4.2.1",
|
|
27
28
|
"compression": "^1.7.4",
|
|
28
29
|
"cookie-parser": "^1.4.6",
|
|
29
30
|
"cors": "^2.8.5",
|
|
@@ -41,13 +42,13 @@
|
|
|
41
42
|
"serve-static": "^1.15.0",
|
|
42
43
|
"tailwindcss": "^3.2.4",
|
|
43
44
|
"vue": "^3.2.25",
|
|
45
|
+
"vue-chartjs": "^5.2.0",
|
|
44
46
|
"vue-router": "^4.0.14"
|
|
45
47
|
},
|
|
46
48
|
"devDependencies": {
|
|
47
49
|
"@vitejs/plugin-vue": "^2.2.0",
|
|
48
50
|
"autoprefixer": "^10.4.4",
|
|
49
51
|
"postcss": "^8.4.12",
|
|
50
|
-
"sequelize": "^6.29.0",
|
|
51
52
|
"vite": "^2.8.0"
|
|
52
53
|
},
|
|
53
54
|
"description": "This scaffold based on express vitejs vuejs",
|
|
@@ -56,7 +56,8 @@ export default {
|
|
|
56
56
|
return {
|
|
57
57
|
computedStyle: null,
|
|
58
58
|
isOpen: false,
|
|
59
|
-
context: null
|
|
59
|
+
context: null,
|
|
60
|
+
closing: false
|
|
60
61
|
}
|
|
61
62
|
},
|
|
62
63
|
|
|
@@ -80,16 +81,17 @@ export default {
|
|
|
80
81
|
if(typeof window === 'undefined') return
|
|
81
82
|
|
|
82
83
|
const transitionEnd = () => {
|
|
83
|
-
this.$emit('open')
|
|
84
84
|
window.addEventListener('click', this.onDismiss)
|
|
85
85
|
window.addEventListener('scroll', this.onDismiss)
|
|
86
86
|
this.$refs.contextMenu.removeEventListener('transitionend', transitionEnd)
|
|
87
|
+
this.$emit('open')
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
this.$refs.contextMenu.removeEventListener('transitionend', transitionEnd)
|
|
89
91
|
this.$refs.contextMenu.addEventListener('transitionend', transitionEnd)
|
|
90
|
-
|
|
92
|
+
this.$nextTick(() => {
|
|
91
93
|
this.$refs.contextMenu.classList.add(this.$style.active)
|
|
92
|
-
}
|
|
94
|
+
})
|
|
93
95
|
},
|
|
94
96
|
|
|
95
97
|
onDismiss(e){
|
|
@@ -104,6 +106,14 @@ export default {
|
|
|
104
106
|
|
|
105
107
|
open(caller, context){
|
|
106
108
|
|
|
109
|
+
if(this.isOpen){
|
|
110
|
+
return this.close(() => {
|
|
111
|
+
window.setTimeout(() => {
|
|
112
|
+
this.open(caller, context)
|
|
113
|
+
}, 30)
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
107
117
|
if(caller){
|
|
108
118
|
this.isOpen = true
|
|
109
119
|
}
|
|
@@ -181,10 +191,9 @@ export default {
|
|
|
181
191
|
else{
|
|
182
192
|
console.warn('Invalid context menu caller', this.caller)
|
|
183
193
|
}
|
|
184
|
-
|
|
185
194
|
},
|
|
186
195
|
|
|
187
|
-
close(){
|
|
196
|
+
close(fn){
|
|
188
197
|
const transitionEnd = () => {
|
|
189
198
|
window.removeEventListener('click', this.onDismiss)
|
|
190
199
|
window.removeEventListener('scroll', this.onDismiss)
|
|
@@ -194,7 +203,9 @@ export default {
|
|
|
194
203
|
this.computedStyle = null
|
|
195
204
|
this.isOpen = false
|
|
196
205
|
this.$emit('dismiss')
|
|
206
|
+
if(fn) fn()
|
|
197
207
|
}
|
|
208
|
+
this.$refs.contextMenu.removeEventListener('transitionend', transitionEnd)
|
|
198
209
|
this.$refs.contextMenu.addEventListener('transitionend', transitionEnd)
|
|
199
210
|
this.$refs.contextMenu.classList.remove(this.$style.active)
|
|
200
211
|
}
|
|
@@ -43,16 +43,13 @@
|
|
|
43
43
|
</div>
|
|
44
44
|
</div>
|
|
45
45
|
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
</div>
|
|
52
|
-
</template>
|
|
53
|
-
</VirtualScroll>
|
|
46
|
+
<div class="flex-1 mb-4 flex flex-col bg-base-500 border-text-50 border-[1px]" v-if="summary">
|
|
47
|
+
<VirtualTable v-if="preset.summary.mode === 'table'" :items="summaryItems" :columns="summaryColumns" class="flex-1"></VirtualTable>
|
|
48
|
+
<Bar v-else-if="preset.summary.mode === 'bar'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
|
|
49
|
+
<Line v-else-if="preset.summary.mode === 'line'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
|
|
50
|
+
</div>
|
|
54
51
|
|
|
55
|
-
<VirtualTable v-
|
|
52
|
+
<VirtualTable v-if="!hideDetails" class="flex-1" :columns="presetColumns" :items="items"
|
|
56
53
|
:appearances="appearances" @scroll-end="loadNext" :pinned="pinned">
|
|
57
54
|
<template v-for="column in presetColumns" #[colOf(column.key)]="{}">
|
|
58
55
|
<div :class="getHeader(column)" @click="openColumnOptions(column.key, $event.target.closest('.' + $style.header))">
|
|
@@ -163,7 +160,7 @@
|
|
|
163
160
|
</div>
|
|
164
161
|
|
|
165
162
|
<div class="flex justify-center">
|
|
166
|
-
<Tabs v-model="layout.presetTab" :items="
|
|
163
|
+
<Tabs v-model="layout.presetTab" :items="tabItems" />
|
|
167
164
|
</div>
|
|
168
165
|
</div>
|
|
169
166
|
|
|
@@ -187,7 +184,7 @@
|
|
|
187
184
|
</ListItem>
|
|
188
185
|
</div>
|
|
189
186
|
|
|
190
|
-
<div class="flex flex-col" v-if="layout.presetTab === 'filter'">
|
|
187
|
+
<div class="flex flex-col" v-else-if="layout.presetTab === 'filter'">
|
|
191
188
|
<div v-if="filterableColumns.length > 0">
|
|
192
189
|
<ListPage1Filter v-if="preset.filters" v-for="filter in preset.filters"
|
|
193
190
|
:filter="filter" :column="columns[filter.key]"
|
|
@@ -206,7 +203,7 @@
|
|
|
206
203
|
</div>
|
|
207
204
|
</div>
|
|
208
205
|
|
|
209
|
-
<div class="flex flex-col" v-if="layout.presetTab === 'sort'">
|
|
206
|
+
<div class="flex flex-col" v-else-if="layout.presetTab === 'sort'">
|
|
210
207
|
<div v-if="sortableColumns.length > 0" class="my-8">
|
|
211
208
|
<div v-for="sort in preset.sorts" class="py-4">
|
|
212
209
|
<div class="flex flex-row items-center gap-2">
|
|
@@ -244,9 +241,85 @@
|
|
|
244
241
|
</div>
|
|
245
242
|
</div>
|
|
246
243
|
|
|
247
|
-
|
|
244
|
+
<div class="flex flex-col gap-3 py-8" v-else-if="layout.presetTab === 'summary'">
|
|
245
|
+
|
|
246
|
+
<div class="flex flex-row items-center p-3">
|
|
247
|
+
<label class="text-text-400 flex-1">Enabled</label>
|
|
248
|
+
<Checkbox v-model="preset.summary.enabled" @change="load"/>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div class="flex flex-row items-center p-3">
|
|
252
|
+
<label class="text-text-400 flex-1">Hide Details</label>
|
|
253
|
+
<Checkbox v-model="preset.summary.hideDetails" @change="load"/>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<div class="p-3">
|
|
257
|
+
<label class="text-text-400 flex-1">Type</label>
|
|
258
|
+
<div class="mt-2">
|
|
259
|
+
<Dropdown v-model="preset.summary.mode" class="flex-1" @change="load">
|
|
260
|
+
<option value="table">{{ $t('Table') }}</option>
|
|
261
|
+
<option value="bar">{{ $t('Bar Chart') }}</option>
|
|
262
|
+
<option value="line">{{ $t('Line Chart') }}</option>
|
|
263
|
+
</Dropdown>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div class="p-3">
|
|
268
|
+
<label class="text-text-400 flex-1">{{ preset.summary.mode === 'table' ? $t('Row') : $t('X-axis') }}</label>
|
|
269
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
270
|
+
<Dropdown v-model="preset.summary.rows[0].key" class="flex-1" @change="load">
|
|
271
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
272
|
+
<option v-for="column in filterableColumns" :value="column.key">{{ column.label }}</option>
|
|
273
|
+
</Dropdown>
|
|
274
|
+
<Dropdown v-model="preset.summary.rows[0].format" class="w-[100px]" @change="load">
|
|
275
|
+
<option value="">{{ $t('Default') }}</option>
|
|
276
|
+
<option value="date">{{ $t('Date') }}</option>
|
|
277
|
+
<option value="month">{{ $t('Month') }}</option>
|
|
278
|
+
<option value="year">{{ $t('Year') }}</option>
|
|
279
|
+
</Dropdown>
|
|
280
|
+
</div>
|
|
281
|
+
<Checkbox class="mt-2" v-if="preset.summary.mode === 'table'" v-model="preset.summary.showRowTotal">
|
|
282
|
+
Row Total
|
|
283
|
+
</Checkbox>
|
|
284
|
+
</div>
|
|
248
285
|
|
|
249
|
-
|
|
286
|
+
<div class="p-3">
|
|
287
|
+
<label class="text-text-400 flex-1">{{ preset.summary.mode === 'table' ? $t('Column') : $t('Dimension') }}</label>
|
|
288
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
289
|
+
<Dropdown v-model="preset.summary.columns[0].key" class="flex-1" @change="load">
|
|
290
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
291
|
+
<option value="(none)">{{ $t('None') }}</option>
|
|
292
|
+
<option v-for="column in filterableColumns" :value="column.key">{{ column.label }}</option>
|
|
293
|
+
</Dropdown>
|
|
294
|
+
<Dropdown v-model="preset.summary.columns[0].format" class="w-[100px]" @change="load">
|
|
295
|
+
<option value="">{{ $t('Default') }}</option>
|
|
296
|
+
<option value="date">{{ $t('Date') }}</option>
|
|
297
|
+
</Dropdown>
|
|
298
|
+
</div>
|
|
299
|
+
<Checkbox class="mt-2" v-if="preset.summary.mode === 'table'" v-model="preset.summary.showColumnTotal">
|
|
300
|
+
Column Total
|
|
301
|
+
</Checkbox>
|
|
302
|
+
<Checkbox class="mt-2" v-else-if="preset.summary.mode === 'bar'" v-model="preset.summary.stacked">
|
|
303
|
+
Stacked
|
|
304
|
+
</Checkbox>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<div class="p-3">
|
|
308
|
+
<label class="text-text-400 flex-1">Values</label>
|
|
309
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
310
|
+
<Dropdown v-model="preset.summary.values[0].aggregrate" class="flex-1" @change="load">
|
|
311
|
+
<option value="" disabled selected>{{ $t('Select') }}</option>
|
|
312
|
+
<option value="count">{{ $t('Count') }}</option>
|
|
313
|
+
<option value="max">{{ $t('Max') }}</option>
|
|
314
|
+
<option value="min">{{ $t('Min') }}</option>
|
|
315
|
+
<option value="avg">{{ $t('Avg') }}</option>
|
|
316
|
+
</Dropdown>
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
</div>
|
|
250
323
|
|
|
251
324
|
</div>
|
|
252
325
|
|
|
@@ -261,9 +334,14 @@
|
|
|
261
334
|
|
|
262
335
|
import throttle from "lodash/throttle";
|
|
263
336
|
import {urlQuery} from "../utils/helpers.mjs";
|
|
337
|
+
import { Bar, Line } from 'vue-chartjs'
|
|
338
|
+
import Chart from 'chart.js/auto'
|
|
339
|
+
import dayjs from "dayjs";
|
|
264
340
|
|
|
265
341
|
export default{
|
|
266
342
|
|
|
343
|
+
components: { Bar, Line },
|
|
344
|
+
|
|
267
345
|
inject: [ 'socket', 'confirm', 'alert', 'socketEmit', 'toast' ],
|
|
268
346
|
|
|
269
347
|
props:{
|
|
@@ -345,12 +423,169 @@ export default{
|
|
|
345
423
|
currentFilters(){
|
|
346
424
|
if(!this.preset.filters) return []
|
|
347
425
|
return this.preset.filters.filter((_) => _.key === this.selectedColumn)
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
chartOptions(){
|
|
429
|
+
|
|
430
|
+
var style = getComputedStyle(document.body)
|
|
431
|
+
var gridColor = style.getPropertyValue('--text-50')
|
|
432
|
+
var gridColor2 = style.getPropertyValue('--text-200')
|
|
433
|
+
|
|
434
|
+
const baseColumn = this.summary.columns[0]
|
|
435
|
+
const baseColumnType = baseColumn.type
|
|
436
|
+
|
|
437
|
+
let highGrids = []
|
|
438
|
+
if(baseColumnType === 'date'){
|
|
439
|
+
this.summary.items.forEach((item) => {
|
|
440
|
+
if(dayjs(item.dfDate).day() === 1){
|
|
441
|
+
highGrids.push(dayjs(item.dfDate).format('D MMM'))
|
|
442
|
+
}
|
|
443
|
+
})
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
responsive: true,
|
|
448
|
+
maintainAspectRatio: false,
|
|
449
|
+
plugins: {
|
|
450
|
+
legend: true
|
|
451
|
+
},
|
|
452
|
+
scales: {
|
|
453
|
+
x: {
|
|
454
|
+
grid: {
|
|
455
|
+
color: function(context){
|
|
456
|
+
if(baseColumnType === 'date' && context.tick && highGrids.includes(context.tick.label)){
|
|
457
|
+
return `rgb(${gridColor2})`
|
|
458
|
+
}
|
|
459
|
+
return `rgb(${gridColor})`
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
stacked: this.preset.summary.stacked
|
|
463
|
+
},
|
|
464
|
+
y: {
|
|
465
|
+
grid: {
|
|
466
|
+
color: function(context){
|
|
467
|
+
return `rgb(${gridColor})`
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
|
|
475
|
+
chartData(){
|
|
476
|
+
|
|
477
|
+
//console.log(this.summary)
|
|
478
|
+
|
|
479
|
+
const column = this.summary.columns.filter((_) => _.key === 'dfDate').pop()
|
|
480
|
+
|
|
481
|
+
const labels = []
|
|
482
|
+
this.summary.items.forEach((item) => {
|
|
483
|
+
|
|
484
|
+
let label = item.dfDate
|
|
485
|
+
switch(column.type){
|
|
486
|
+
|
|
487
|
+
case 'date':
|
|
488
|
+
const dateFormat = column.format ?? 'D MMM YY HH:mm:ss'
|
|
489
|
+
const djs = dayjs(item.dfDate)
|
|
490
|
+
label = djs.isValid() ? djs.format(dateFormat) : item.dfDate
|
|
491
|
+
break
|
|
492
|
+
}
|
|
493
|
+
labels.push(label)
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
const datasets = []
|
|
497
|
+
this.summary.columns.forEach((column) => {
|
|
498
|
+
if(column.key === 'dfDate') return
|
|
499
|
+
|
|
500
|
+
const data = []
|
|
501
|
+
this.summary.items.forEach((item) => {
|
|
502
|
+
data.push(parseInt(item[column.key]))
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
const dataset = {
|
|
506
|
+
label: column.key,
|
|
507
|
+
data,
|
|
508
|
+
backgroundColor: this.chartOpt.backgroundColors[datasets.length % 9],
|
|
509
|
+
borderColor: this.chartOpt.borderColors[datasets.length % 9]
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
datasets.push(dataset)
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
//console.log(labels)
|
|
516
|
+
//console.log(datasets)
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
labels,
|
|
520
|
+
datasets
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/*return {
|
|
524
|
+
labels: [ 'January', 'February', 'March' ],
|
|
525
|
+
datasets: [ { data: [40, 20, 12] } ]
|
|
526
|
+
}*/
|
|
527
|
+
},
|
|
528
|
+
|
|
529
|
+
hideDetails(){
|
|
530
|
+
if(this.preset && this.preset.summary && this.preset.summary.hideDetails)
|
|
531
|
+
return this.preset.summary.hideDetails
|
|
532
|
+
return false;
|
|
533
|
+
},
|
|
534
|
+
|
|
535
|
+
summaryColumns(){
|
|
536
|
+
|
|
537
|
+
const columns = [ ...this.summary.columns ]
|
|
538
|
+
|
|
539
|
+
if(this.preset.summary.showColumnTotal){
|
|
540
|
+
columns.push({ key:"_total", label:"Total", visible:true, type:"number" })
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return columns
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
summaryItems(){
|
|
547
|
+
|
|
548
|
+
const items = [ ...this.summary.items ]
|
|
549
|
+
|
|
550
|
+
//console.log('#1', this.preset.summary.showColumnTotal, this.preset.summary.showRowTotal, items.length)
|
|
551
|
+
|
|
552
|
+
if(this.preset.summary.showColumnTotal){
|
|
553
|
+
items.forEach((item) => {
|
|
554
|
+
let total = 0
|
|
555
|
+
for(let key in item){
|
|
556
|
+
if([ 'dfDate', '_total' ].includes(key)) continue
|
|
557
|
+
total += parseInt(item[key])
|
|
558
|
+
}
|
|
559
|
+
item._total = total
|
|
560
|
+
})
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if(this.preset.summary.showRowTotal){
|
|
564
|
+
const totalItem = { dfDate:"Total" }
|
|
565
|
+
this.summary.items.forEach((item) => {
|
|
566
|
+
|
|
567
|
+
for(let key in item){
|
|
568
|
+
if([ 'dfDate' ].includes(key)) continue
|
|
569
|
+
|
|
570
|
+
if(!(key in totalItem))
|
|
571
|
+
totalItem[key] = 0
|
|
572
|
+
totalItem[key] += parseInt(item[key])
|
|
573
|
+
}
|
|
574
|
+
})
|
|
575
|
+
items.push(totalItem)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return items
|
|
348
579
|
}
|
|
349
580
|
|
|
350
581
|
},
|
|
351
582
|
|
|
352
583
|
data(){
|
|
353
584
|
return {
|
|
585
|
+
chartOpt: {
|
|
586
|
+
backgroundColors: [ '#5D9CEC', '#4FC1E9', '#48CFAD', '#A0D468', '#FFCE54', '#FC6E51', '#ED5565', '#AC92EC', '#EC87C0' ],
|
|
587
|
+
borderColors: [ '#4A89DC', '#3BAFDA', '#37BC9B', '#8CC152', '#F6BB42', '#E9573F', '#DA4453', '#967ADC', '#D770AD' ],
|
|
588
|
+
},
|
|
354
589
|
layout: {
|
|
355
590
|
presetOpen: false,
|
|
356
591
|
presetTab: 'column',
|
|
@@ -358,12 +593,19 @@ export default{
|
|
|
358
593
|
presetFilterSelector: null,
|
|
359
594
|
presetSortSelector: null,
|
|
360
595
|
},
|
|
596
|
+
tabItems: [
|
|
597
|
+
{ text:'Columns', value:'column' },
|
|
598
|
+
{ text:'Filters', value:'filter' },
|
|
599
|
+
{ text:'Sorts', value:'sort' },
|
|
600
|
+
{ text:'Summary', value:'summary' },
|
|
601
|
+
],
|
|
361
602
|
name: null,
|
|
362
603
|
columns: [],
|
|
363
604
|
selectedColumn: null,
|
|
364
605
|
presets: [],
|
|
365
606
|
presetIdx: -1,
|
|
366
607
|
items: [],
|
|
608
|
+
summary: null,
|
|
367
609
|
count: null,
|
|
368
610
|
hasNext: false,
|
|
369
611
|
modal: null
|
|
@@ -399,6 +641,7 @@ export default{
|
|
|
399
641
|
this.patchPresets()
|
|
400
642
|
this.$nextTick(() => {
|
|
401
643
|
this.items = data.items
|
|
644
|
+
this.summary = data.summary
|
|
402
645
|
this.hasNext = data.hasNext
|
|
403
646
|
this.count = data.count
|
|
404
647
|
})
|
|
@@ -452,8 +695,10 @@ export default{
|
|
|
452
695
|
preset: this.preset
|
|
453
696
|
}, (res) => {
|
|
454
697
|
const data = res.data ? res.data : res
|
|
455
|
-
|
|
456
|
-
|
|
698
|
+
this.items = data.items
|
|
699
|
+
this.summary = data.summary
|
|
700
|
+
this.hasNext = data.hasNext
|
|
701
|
+
this.count = data.count
|
|
457
702
|
})
|
|
458
703
|
},
|
|
459
704
|
|
|
@@ -331,12 +331,13 @@ export default{
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
let text = value
|
|
334
|
+
let dateFormat, val
|
|
334
335
|
switch(column.type){
|
|
335
336
|
|
|
336
337
|
case 'date':
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
text = val.isValid() ? val.format(dateFormat) :
|
|
338
|
+
dateFormat = column.format ?? 'D MMM YY HH:mm:ss'
|
|
339
|
+
val = dayjs(value)
|
|
340
|
+
text = val.isValid() ? val.format(dateFormat) : value
|
|
340
341
|
break
|
|
341
342
|
|
|
342
343
|
case 'currency':
|
package/src/utils/listpage1.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { Op } = require('sequelize')
|
|
2
|
-
const Sequelize = require('sequelize')
|
|
1
|
+
const { Op } = module.parent.require('sequelize')
|
|
2
|
+
const Sequelize = module.parent.require('sequelize')
|
|
3
3
|
const dayjs = require("dayjs");
|
|
4
4
|
const util = require("util");
|
|
5
5
|
|
|
@@ -107,11 +107,12 @@ let ListPage1 = {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
if(preset.search){
|
|
110
|
+
const tableName = this.model.tableName
|
|
110
111
|
where = {
|
|
111
112
|
...where,
|
|
112
|
-
[Op.and]: Sequelize.literal(
|
|
113
|
+
[Op.and]: Sequelize.literal(`MATCH(\`${tableName}\`.tag) AGAINST (? IN BOOLEAN MODE)`)
|
|
113
114
|
}
|
|
114
|
-
replacements.push(
|
|
115
|
+
replacements.push(preset.search)
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
if(afterItem){
|
|
@@ -124,7 +125,7 @@ let ListPage1 = {
|
|
|
124
125
|
|
|
125
126
|
let attributeIncludes
|
|
126
127
|
if(this.getAttributeIncludes){
|
|
127
|
-
attributeIncludes = this.getAttributeIncludes()
|
|
128
|
+
attributeIncludes = this.getAttributeIncludes(preset)
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
const { rows:items, count } = await this.model.findAndCountAll({
|
|
@@ -136,7 +137,7 @@ let ListPage1 = {
|
|
|
136
137
|
order,
|
|
137
138
|
limit: itemsPerPage + 1,
|
|
138
139
|
replacements,
|
|
139
|
-
include: this.getModelIncludes ? this.getModelIncludes() : undefined
|
|
140
|
+
include: this.getModelIncludes ? this.getModelIncludes(preset) : undefined
|
|
140
141
|
})
|
|
141
142
|
|
|
142
143
|
const hasNext = items.length > itemsPerPage
|
|
@@ -144,6 +145,14 @@ let ListPage1 = {
|
|
|
144
145
|
items.splice(items.length - 1, 1)
|
|
145
146
|
}
|
|
146
147
|
|
|
148
|
+
let summary
|
|
149
|
+
if(!afterItem && preset.summary && preset.summary.enabled){
|
|
150
|
+
summary = await this.loadSummary(preset, {
|
|
151
|
+
where,
|
|
152
|
+
replacements
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
147
156
|
if(this.socket){
|
|
148
157
|
this.socket.joinWithArgs(this.channel, { preset })
|
|
149
158
|
}
|
|
@@ -151,8 +160,157 @@ let ListPage1 = {
|
|
|
151
160
|
return {
|
|
152
161
|
items,
|
|
153
162
|
hasNext,
|
|
154
|
-
count
|
|
163
|
+
count,
|
|
164
|
+
summary
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
async loadSummary(preset, modelParams){
|
|
169
|
+
|
|
170
|
+
const { columns, rows, values } = preset.summary
|
|
171
|
+
|
|
172
|
+
if(!Array.isArray(columns) || columns.length !== 1) return
|
|
173
|
+
if(!Array.isArray(rows) || rows.length !== 1) return
|
|
174
|
+
if(!Array.isArray(values) || values.length !== 1) return
|
|
175
|
+
|
|
176
|
+
const col = columns[0]
|
|
177
|
+
let columnValues = []
|
|
178
|
+
let colKey
|
|
179
|
+
if(col.key !== '(none)'){
|
|
180
|
+
colKey = this.model.rawAttributes[col.key].field
|
|
181
|
+
columnValues = await this.model.findAll({
|
|
182
|
+
attributes: [
|
|
183
|
+
[ Sequelize.fn("DISTINCT", Sequelize.col(colKey)), 'cols' ]
|
|
184
|
+
],
|
|
185
|
+
order: [
|
|
186
|
+
[ colKey, 'asc' ]
|
|
187
|
+
],
|
|
188
|
+
...modelParams
|
|
189
|
+
})
|
|
190
|
+
//console.log(columnValues)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const attributes = []
|
|
194
|
+
const group = []
|
|
195
|
+
const summaryColumns = [
|
|
196
|
+
/*{ key:"dfDate", label:"Date", width:"200px", visible:true },
|
|
197
|
+
{ key:"type-1", label:"Type 1", type:"number", visible:true },
|
|
198
|
+
{ key:"type-2", label:"Type 2", type:"number", visible:true },
|
|
199
|
+
{ key:"type-3", label:"Type 3", type:"number", visible:true },
|
|
200
|
+
{ key:"type-4", label:"Type 4", type:"number", visible:true },
|
|
201
|
+
{ key:"type-8", label:"Type 8", type:"number", visible:true },*/
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
const row = rows[0]
|
|
205
|
+
const rowKey = this.model.rawAttributes[row.key].field
|
|
206
|
+
const rowLabel = this.columns[row.key].label
|
|
207
|
+
switch(row.format){
|
|
208
|
+
case 'date':
|
|
209
|
+
attributes.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y-%m-%d'), "dfDate"])
|
|
210
|
+
group.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y-%m-%d'), "dfDate"])
|
|
211
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"D MMM" })
|
|
212
|
+
break
|
|
213
|
+
|
|
214
|
+
case 'month':
|
|
215
|
+
attributes.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y-%m'), "dfDate"])
|
|
216
|
+
group.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y-%m'), "dfDate"])
|
|
217
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"MMM YYYY" })
|
|
218
|
+
break
|
|
219
|
+
|
|
220
|
+
case 'year':
|
|
221
|
+
attributes.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y'), "dfDate"])
|
|
222
|
+
group.push([Sequelize.fn("DATE_FORMAT", Sequelize.col(rowKey), '%Y'), "dfDate"])
|
|
223
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"YYYY" })
|
|
224
|
+
break
|
|
225
|
+
|
|
226
|
+
default:
|
|
227
|
+
attributes.push([Sequelize.col(rowKey), "dfDate"])
|
|
228
|
+
group.push([Sequelize.col(rowKey), "dfDate"])
|
|
229
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true })
|
|
230
|
+
break
|
|
155
231
|
}
|
|
232
|
+
|
|
233
|
+
const value = values[0]
|
|
234
|
+
if(columnValues.length > 0){
|
|
235
|
+
columnValues.forEach((obj) => {
|
|
236
|
+
const columnValue = obj.dataValues['cols']
|
|
237
|
+
|
|
238
|
+
switch(value.aggregrate){
|
|
239
|
+
case 'count':
|
|
240
|
+
attributes.push([Sequelize.fn("SUM", Sequelize.fn("IF", Sequelize.literal(`${colKey} = '${columnValue}'`), 1, 0)), colKey + "-" + columnValue])
|
|
241
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:(columnValue ?? '').toString(), visible:true, aggregrate:"count" })
|
|
242
|
+
break
|
|
243
|
+
|
|
244
|
+
case 'min':
|
|
245
|
+
attributes.push([Sequelize.fn("MIN", colKey), colKey + "-" + columnValue])
|
|
246
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:(columnValue ?? '').toString(), visible:true, aggregrate:"count" })
|
|
247
|
+
break
|
|
248
|
+
|
|
249
|
+
case 'max':
|
|
250
|
+
attributes.push([Sequelize.fn("MAX", colKey), colKey + "-" + columnValue])
|
|
251
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:(columnValue ?? '').toString(), visible:true, aggregrate:"count" })
|
|
252
|
+
break
|
|
253
|
+
|
|
254
|
+
case 'avg':
|
|
255
|
+
attributes.push([Sequelize.fn("AVG", colKey), colKey + "-" + columnValue])
|
|
256
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:(columnValue ?? '').toString(), visible:true, aggregrate:"count" })
|
|
257
|
+
break
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
else{
|
|
262
|
+
switch(value.aggregrate) {
|
|
263
|
+
case 'count':
|
|
264
|
+
default:
|
|
265
|
+
attributes.push([Sequelize.fn('count', '*'), 'Count'])
|
|
266
|
+
summaryColumns.push({ key:'Count', label:'Count', type:"number", visible:true, aggregrate:"count" })
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
//console.log(attributes)
|
|
271
|
+
|
|
272
|
+
const summaryItems = await this.model.findAll({
|
|
273
|
+
attributes,
|
|
274
|
+
group,
|
|
275
|
+
order: [
|
|
276
|
+
[ 'dfDate', 'asc' ]
|
|
277
|
+
],
|
|
278
|
+
...modelParams
|
|
279
|
+
})
|
|
280
|
+
/*const summaryItems = await this.model.findAll({
|
|
281
|
+
attributes: [
|
|
282
|
+
[Sequelize.fn("DATE_FORMAT", Sequelize.col("created_at"), '%Y-%m-%d'), "dfDate"],
|
|
283
|
+
[Sequelize.fn("SUM", Sequelize.fn("IF", Sequelize.literal("type = 1"), 1, 0)), "type-1"],
|
|
284
|
+
[Sequelize.literal("SUM(IF(type = 2, 1, 0))"), "type-2"],
|
|
285
|
+
[Sequelize.literal("SUM(IF(type = 3, 1, 0))"), "type-3"],
|
|
286
|
+
[Sequelize.literal("SUM(IF(type = 4, 1, 0))"), "type-4"],
|
|
287
|
+
[Sequelize.literal("SUM(IF(type = 8, 1, 0))"), "type-8"],
|
|
288
|
+
],
|
|
289
|
+
group: [
|
|
290
|
+
[Sequelize.fn("DATE_FORMAT", Sequelize.col("created_at"), '%Y-%m-%d')]
|
|
291
|
+
],
|
|
292
|
+
order: [
|
|
293
|
+
[ 'dfDate', 'asc' ]
|
|
294
|
+
],
|
|
295
|
+
...modelParams
|
|
296
|
+
})*/
|
|
297
|
+
//console.log(summaryItems)
|
|
298
|
+
|
|
299
|
+
const summary = {
|
|
300
|
+
type: "table",
|
|
301
|
+
columns: summaryColumns,
|
|
302
|
+
items: summaryItems
|
|
303
|
+
/*items: [
|
|
304
|
+
{ date:"21 Jan 23", "type-1":12, "type-2":11, "type-3":16, "type-4":2, "type-8":8 },
|
|
305
|
+
{ date:"22 Jan 23", "type-1":51, "type-2":87, "type-3":86, "type-4":3, "type-8":9 },
|
|
306
|
+
{ date:"23 Jan 23", "type-1":14, "type-2":66, "type-3":77, "type-4":4, "type-8":18 },
|
|
307
|
+
{ date:"24 Jan 23", "type-1":7, "type-2":3, "type-3":63, "type-4":5, "type-8":28 },
|
|
308
|
+
{ date:"25 Jan 23", "type-1":88, "type-2":55, "type-3":68, "type-4":6, "type-8":38 },
|
|
309
|
+
{ date:"26 Jan 23", "type-1":172, "type-2":13, "type-3":6, "type-4":7, "type-8":48 }
|
|
310
|
+
]*/
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return summary
|
|
156
314
|
},
|
|
157
315
|
|
|
158
316
|
async subscribe(params, socket){
|