@mixd-id/web-scaffold 0.1.2301231365 → 0.1.2301231367
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 +2 -1
- package/src/components/Button.vue +14 -7
- package/src/components/Gmaps.vue +119 -0
- package/src/components/ListPage1.vue +36 -6
- package/src/components/ListPage1Filter.vue +2 -0
- package/src/index.js +1 -0
- package/src/utils/listpage1.js +231 -15
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.2301231367",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite serve",
|
|
7
7
|
"build": "vite build",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@faker-js/faker": "^7.3.0",
|
|
23
|
+
"@googlemaps/js-api-loader": "^1.15.1",
|
|
23
24
|
"@tailwindcss/line-clamp": "^0.4.0",
|
|
24
25
|
"@vueuse/core": "^9.0.2",
|
|
25
26
|
"adm-zip": "^0.5.10",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<button :class="computedClass" :disabled="isDisabled">
|
|
3
|
-
<
|
|
3
|
+
<div>
|
|
4
|
+
<slot></slot>
|
|
5
|
+
</div>
|
|
4
6
|
<div v-if="isLoading" :class="$style.loadingPane">
|
|
5
7
|
<svg :class="$style.spinner" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
6
8
|
<circle :class="$style.svgBg" cx="12" cy="12" r="10" stroke-width="4"></circle>
|
|
@@ -31,7 +33,10 @@ export default{
|
|
|
31
33
|
default: '' // 0|1|2|3, default: 2
|
|
32
34
|
},
|
|
33
35
|
|
|
34
|
-
state:
|
|
36
|
+
state: {
|
|
37
|
+
type: [ String, Number ],
|
|
38
|
+
default: 1
|
|
39
|
+
}, // -1:disabled, 1:normal, 2:loading, default: normal
|
|
35
40
|
|
|
36
41
|
},
|
|
37
42
|
|
|
@@ -50,11 +55,7 @@ export default{
|
|
|
50
55
|
},
|
|
51
56
|
|
|
52
57
|
isDisabled(){
|
|
53
|
-
return parseInt(this.computedState)
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
isNormal(){
|
|
57
|
-
return parseInt(this.computedState) !== 2
|
|
58
|
+
return parseInt(this.computedState) !== 1
|
|
58
59
|
},
|
|
59
60
|
|
|
60
61
|
isLoading(){
|
|
@@ -227,6 +228,12 @@ export default{
|
|
|
227
228
|
.loading:active{
|
|
228
229
|
@apply top-0 left-0;
|
|
229
230
|
}
|
|
231
|
+
.loading>*{
|
|
232
|
+
opacity: 0;
|
|
233
|
+
}
|
|
234
|
+
.loading .loadingPane{
|
|
235
|
+
opacity: 1;
|
|
236
|
+
}
|
|
230
237
|
|
|
231
238
|
.spacing-,
|
|
232
239
|
.spacing-2{
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.comp">
|
|
3
|
+
<div ref="map" class="flex-1"></div>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
|
|
9
|
+
import { Loader } from '@googlemaps/js-api-loader';
|
|
10
|
+
|
|
11
|
+
export default{
|
|
12
|
+
|
|
13
|
+
props: {
|
|
14
|
+
data: Array,
|
|
15
|
+
radius: Number
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
data(){
|
|
19
|
+
return {
|
|
20
|
+
map: null
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
mounted() {
|
|
25
|
+
this.load()
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
methods: {
|
|
29
|
+
|
|
30
|
+
load(){
|
|
31
|
+
|
|
32
|
+
if(!Array.isArray(this.data) || this.data.length < 1)
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
const loader = new Loader({
|
|
36
|
+
apiKey: "AIzaSyBmzMlmrR5St-njtN--64y_oLTa9FDLYfQ"
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
loader.load()
|
|
40
|
+
.then(async () => {
|
|
41
|
+
const { Map } = await google.maps.importLibrary([ "maps" ]);
|
|
42
|
+
await google.maps.importLibrary("visualization", ["HeatmapLayer"]);
|
|
43
|
+
|
|
44
|
+
var center = new google.maps.LatLng(-6.2088, 106.8456);
|
|
45
|
+
|
|
46
|
+
this.map = new Map(this.$refs.map, {
|
|
47
|
+
center: center,
|
|
48
|
+
zoom: 7,
|
|
49
|
+
mapTypeId: 'roadmap',
|
|
50
|
+
streetViewControl: false
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
var heatMapData = [
|
|
54
|
+
/*{location: new google.maps.LatLng(37.782, -122.447), weight: 0.5},
|
|
55
|
+
new google.maps.LatLng(37.782, -122.445),
|
|
56
|
+
{location: new google.maps.LatLng(37.782, -122.443), weight: 2},
|
|
57
|
+
{location: new google.maps.LatLng(37.782, -122.441), weight: 3},
|
|
58
|
+
{location: new google.maps.LatLng(37.782, -122.439), weight: 2},
|
|
59
|
+
new google.maps.LatLng(37.782, -122.437),
|
|
60
|
+
{location: new google.maps.LatLng(37.782, -122.435), weight: 0.5},
|
|
61
|
+
|
|
62
|
+
{location: new google.maps.LatLng(37.785, -122.447), weight: 3},
|
|
63
|
+
{location: new google.maps.LatLng(37.785, -122.445), weight: 2},
|
|
64
|
+
new google.maps.LatLng(37.785, -122.443),
|
|
65
|
+
{location: new google.maps.LatLng(37.785, -122.441), weight: 0.5},
|
|
66
|
+
new google.maps.LatLng(37.785, -122.439),
|
|
67
|
+
{location: new google.maps.LatLng(37.785, -122.437), weight: 2},
|
|
68
|
+
{location: new google.maps.LatLng(37.785, -122.435), weight: 3}*/
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
this.data.forEach((item) => {
|
|
72
|
+
heatMapData.push({
|
|
73
|
+
location: new google.maps.LatLng(item[0], item[1]),
|
|
74
|
+
weight: item[2]
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
var heatmap = new google.maps.visualization.HeatmapLayer({
|
|
79
|
+
data: heatMapData,
|
|
80
|
+
dissipating: true
|
|
81
|
+
});
|
|
82
|
+
heatmap.set('radius', this.radius ?? 12)
|
|
83
|
+
heatmap.setMap(this.map);
|
|
84
|
+
|
|
85
|
+
const gradient = [
|
|
86
|
+
"rgba(0, 255, 255, 0)",
|
|
87
|
+
"rgba(0, 255, 255, 1)",
|
|
88
|
+
"rgba(0, 191, 255, 1)",
|
|
89
|
+
"rgba(0, 127, 255, 1)",
|
|
90
|
+
"rgba(0, 63, 255, 1)",
|
|
91
|
+
"rgba(0, 0, 255, 1)",
|
|
92
|
+
"rgba(0, 0, 223, 1)",
|
|
93
|
+
"rgba(0, 0, 191, 1)",
|
|
94
|
+
"rgba(0, 0, 159, 1)",
|
|
95
|
+
"rgba(0, 0, 127, 1)",
|
|
96
|
+
"rgba(63, 0, 91, 1)",
|
|
97
|
+
"rgba(127, 0, 63, 1)",
|
|
98
|
+
"rgba(191, 0, 31, 1)",
|
|
99
|
+
"rgba(255, 0, 0, 1)",
|
|
100
|
+
];
|
|
101
|
+
heatmap.set("gradient", gradient);
|
|
102
|
+
|
|
103
|
+
console.log('Gmap, data count:', this.data.length)
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<style module>
|
|
114
|
+
|
|
115
|
+
.comp{
|
|
116
|
+
@apply flex-1 flex flex-col;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
</style>
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
<VirtualTable v-if="preset.summary.mode === 'table'" :items="summaryItems" :columns="summaryColumns" class="flex-1"></VirtualTable>
|
|
48
48
|
<Bar v-else-if="preset.summary.mode === 'bar'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
|
|
49
49
|
<Line v-else-if="preset.summary.mode === 'line'" :data="chartData" :options="chartOptions" class="flex-1 w-full p-2"/>
|
|
50
|
+
<Gmaps v-else-if="preset.summary.mode === 'map'" :data="summary.items" :radius="preset.summary.mapRadius" class="flex-1 w-full p-2" />
|
|
50
51
|
</div>
|
|
51
52
|
|
|
52
53
|
<div v-if="isLoading" :class="$style.loadingComp">
|
|
@@ -270,11 +271,12 @@
|
|
|
270
271
|
<option value="table">{{ $t('Table') }}</option>
|
|
271
272
|
<option value="bar">{{ $t('Bar Chart') }}</option>
|
|
272
273
|
<option value="line">{{ $t('Line Chart') }}</option>
|
|
274
|
+
<option value="map">{{ $t('Maps') }}</option>
|
|
273
275
|
</Dropdown>
|
|
274
276
|
</div>
|
|
275
277
|
</div>
|
|
276
278
|
|
|
277
|
-
<div class="p-3">
|
|
279
|
+
<div class="p-3" v-if="[ 'table', 'bar', 'line' ].includes(preset.summary.type)">
|
|
278
280
|
<label class="text-text-400 flex-1">{{ preset.summary.mode === 'table' ? $t('Row') : $t('X-axis') }}</label>
|
|
279
281
|
<div class="flex flex-row mt-2 gap-2">
|
|
280
282
|
<Dropdown v-model="preset.summary.rows[0].key" class="flex-1" @change="load">
|
|
@@ -295,18 +297,26 @@
|
|
|
295
297
|
</div>
|
|
296
298
|
|
|
297
299
|
<div class="p-3">
|
|
298
|
-
<label class="text-text-400 flex-1"
|
|
299
|
-
<
|
|
300
|
+
<label class="text-text-400 flex-1" v-if="preset.summary.mode === 'table'">{{$t('Column') }}</label>
|
|
301
|
+
<label class="text-text-400 flex-1" v-else-if="[ 'bar', 'line' ].includes(preset.summary.mode)">{{$t('Column') }}</label>
|
|
302
|
+
<label class="text-text-400 flex-1" v-else-if="[ 'map' ].includes(preset.summary.mode)">{{$t('Property') }}</label>
|
|
303
|
+
<div class="flex flex-row mt-2 gap-2" v-if="[ 'bar', 'line', 'table' ].includes(preset.summary.mode)">
|
|
300
304
|
<Dropdown v-model="preset.summary.columns[0].key" class="flex-1" @change="load">
|
|
301
305
|
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
302
306
|
<option value="(none)">{{ $t('None') }}</option>
|
|
303
307
|
<option v-for="column in filterableColumns" :value="column.key">{{ column.label }}</option>
|
|
304
308
|
</Dropdown>
|
|
305
|
-
<Dropdown v-model="preset.summary.columns[0].format" class="w-[100px]" @change="load">
|
|
309
|
+
<Dropdown v-if="preset.summary.mode !== 'map'" v-model="preset.summary.columns[0].format" class="w-[100px]" @change="load">
|
|
306
310
|
<option value="">{{ $t('Default') }}</option>
|
|
307
311
|
<option value="date">{{ $t('Date') }}</option>
|
|
308
312
|
</Dropdown>
|
|
309
313
|
</div>
|
|
314
|
+
<div v-else>
|
|
315
|
+
<Dropdown v-model="preset.summary.columns[0].key" class="flex-1" @change="load">
|
|
316
|
+
<option value="" disabled selected>{{ $t('Add Column') }}</option>
|
|
317
|
+
<option v-for="column in coordinateColumns" :value="column.key">{{ column.label }}</option>
|
|
318
|
+
</Dropdown>
|
|
319
|
+
</div>
|
|
310
320
|
<Checkbox class="mt-2" v-if="preset.summary.mode === 'table'" v-model="preset.summary.showColumnTotal">
|
|
311
321
|
Column Total
|
|
312
322
|
</Checkbox>
|
|
@@ -315,7 +325,7 @@
|
|
|
315
325
|
</Checkbox>
|
|
316
326
|
</div>
|
|
317
327
|
|
|
318
|
-
<div class="p-3">
|
|
328
|
+
<div class="p-3" v-if="preset.summary.mode !== 'map'">
|
|
319
329
|
<label class="text-text-400 flex-1">Values</label>
|
|
320
330
|
<div class="flex flex-row mt-2 gap-2">
|
|
321
331
|
<Dropdown v-model="preset.summary.values[0].aggregrate" class="flex-1" @change="load">
|
|
@@ -328,6 +338,13 @@
|
|
|
328
338
|
</div>
|
|
329
339
|
</div>
|
|
330
340
|
|
|
341
|
+
<div class="p-3" v-if="preset.summary.mode === 'map'">
|
|
342
|
+
<label class="text-text-400 flex-1">Radius</label>
|
|
343
|
+
<div class="flex flex-row mt-2 gap-2">
|
|
344
|
+
<Textbox v-model="preset.summary.mapRadius" class="w-[60px]" @blur="load" @keyup.enter="load" />
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
331
348
|
</div>
|
|
332
349
|
|
|
333
350
|
</div>
|
|
@@ -399,6 +416,10 @@ export default{
|
|
|
399
416
|
return Object.values(this.columns).filter((_) => _.filterable)
|
|
400
417
|
},
|
|
401
418
|
|
|
419
|
+
coordinateColumns(){
|
|
420
|
+
return Object.values(this.columns).filter((_) => _.type === 'coord')
|
|
421
|
+
},
|
|
422
|
+
|
|
402
423
|
sortableColumns(){
|
|
403
424
|
return Object.values(this.columns).filter((_) => _.sortable)
|
|
404
425
|
},
|
|
@@ -537,7 +558,7 @@ export default{
|
|
|
537
558
|
})
|
|
538
559
|
|
|
539
560
|
const dataset = {
|
|
540
|
-
label: column.
|
|
561
|
+
label: column.label,
|
|
541
562
|
data,
|
|
542
563
|
backgroundColor: this.chartOpt.backgroundColors[datasets.length % 9],
|
|
543
564
|
borderColor: this.chartOpt.borderColors[datasets.length % 9]
|
|
@@ -546,6 +567,8 @@ export default{
|
|
|
546
567
|
datasets.push(dataset)
|
|
547
568
|
})
|
|
548
569
|
|
|
570
|
+
console.log(this.columns)
|
|
571
|
+
console.log(this.summary.columns)
|
|
549
572
|
//console.log(labels)
|
|
550
573
|
//console.log(datasets)
|
|
551
574
|
|
|
@@ -755,6 +778,13 @@ export default{
|
|
|
755
778
|
},
|
|
756
779
|
|
|
757
780
|
load(){
|
|
781
|
+
|
|
782
|
+
if(this.preset.summary){
|
|
783
|
+
if(!this.preset.summary.enabled && this.preset.summary.hideDetails){
|
|
784
|
+
this.preset.summary.hideDetails = false
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
758
788
|
this.isLoading = true
|
|
759
789
|
this.socketEmit(`${this.computedDataSource}.load`, {
|
|
760
790
|
preset: this.preset
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
<option value="">Select</option>
|
|
29
29
|
<option value="today">Today</option>
|
|
30
30
|
<option value="thisWeek">This Week</option>
|
|
31
|
+
<option value="weekAgo">1 Week Ago</option>
|
|
31
32
|
<option value="thisMonth">This Month</option>
|
|
33
|
+
<option value="monthAgo">1 Month Ago</option>
|
|
32
34
|
<option value="lastMonth">Last Month</option>
|
|
33
35
|
<option value="thisMonth">This Year</option>
|
|
34
36
|
<option value="lastYear">Last Year</option>
|
package/src/index.js
CHANGED
|
@@ -219,6 +219,7 @@ export default{
|
|
|
219
219
|
app.component('DynamicTemplate', defineAsyncComponent(() => import("./components/DynamicTemplate.vue")))
|
|
220
220
|
app.component('ErrorText', defineAsyncComponent(() => import("./components/ErrorText.vue")))
|
|
221
221
|
app.component('Feed', defineAsyncComponent(() => import("./components/Feed.vue")))
|
|
222
|
+
app.component('Gmaps', defineAsyncComponent(() => import("./components/Gmaps.vue")))
|
|
222
223
|
app.component('HTMLEditor', defineAsyncComponent(() => import("./components/HTMLEditor.vue")))
|
|
223
224
|
app.component('Ahref', defineAsyncComponent(() => import("./components/Ahref.vue")))
|
|
224
225
|
app.component('Switch', defineAsyncComponent(() => import("./components/Switch.vue")))
|
package/src/utils/listpage1.js
CHANGED
|
@@ -79,6 +79,11 @@ let ListPage1 = {
|
|
|
79
79
|
[ 'id', 'desc' ],
|
|
80
80
|
]
|
|
81
81
|
|
|
82
|
+
let attributeIncludes
|
|
83
|
+
if(this.getAttributeIncludes){
|
|
84
|
+
attributeIncludes = this.getAttributeIncludes(preset)
|
|
85
|
+
}
|
|
86
|
+
|
|
82
87
|
let attributes = { id:1, updatedAt:1 }
|
|
83
88
|
if(preset.columns){
|
|
84
89
|
let modelAttributes = this.model.getAttributes();
|
|
@@ -86,6 +91,20 @@ let ListPage1 = {
|
|
|
86
91
|
if(column.visible && modelAttributes[column.key]){
|
|
87
92
|
attributes[column.key] = 1
|
|
88
93
|
}
|
|
94
|
+
else if(column.virtual){
|
|
95
|
+
switch(column.virtual.type){
|
|
96
|
+
|
|
97
|
+
case 'sql':
|
|
98
|
+
if(!Array.isArray(attributeIncludes)){
|
|
99
|
+
attributeIncludes = []
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
attributeIncludes.push([
|
|
103
|
+
Sequelize.literal(`(${column.virtual.value})`), column.key
|
|
104
|
+
])
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
}
|
|
89
108
|
})
|
|
90
109
|
}
|
|
91
110
|
attributes = Object.keys(attributes)
|
|
@@ -122,11 +141,7 @@ let ListPage1 = {
|
|
|
122
141
|
}
|
|
123
142
|
}
|
|
124
143
|
|
|
125
|
-
let
|
|
126
|
-
if(this.getAttributeIncludes){
|
|
127
|
-
attributeIncludes = this.getAttributeIncludes(preset)
|
|
128
|
-
}
|
|
129
|
-
|
|
144
|
+
let derivedSql
|
|
130
145
|
const { rows:items, count } = await this.model.findAndCountAll({
|
|
131
146
|
where,
|
|
132
147
|
attributes: {
|
|
@@ -136,7 +151,10 @@ let ListPage1 = {
|
|
|
136
151
|
order,
|
|
137
152
|
limit: itemsPerPage + 1,
|
|
138
153
|
replacements,
|
|
139
|
-
include: this.getModelIncludes ? this.getModelIncludes(preset) : undefined
|
|
154
|
+
include: this.getModelIncludes ? this.getModelIncludes(preset) : undefined,
|
|
155
|
+
logging: (sql, queryObject, p) => {
|
|
156
|
+
derivedSql = sql
|
|
157
|
+
}
|
|
140
158
|
})
|
|
141
159
|
|
|
142
160
|
const hasNext = items.length > itemsPerPage
|
|
@@ -146,10 +164,19 @@ let ListPage1 = {
|
|
|
146
164
|
|
|
147
165
|
let summary
|
|
148
166
|
if(!afterItem && preset.summary && preset.summary.enabled){
|
|
149
|
-
summary
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
167
|
+
if(preset.summary.mode === 'map' && this.loadMapSummary){
|
|
168
|
+
summary = await this.loadMapSummary(preset, {
|
|
169
|
+
where,
|
|
170
|
+
replacements
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
else{
|
|
174
|
+
derivedSql = derivedSql.replace('Executing (default): ', '').replace('LIMIT ' + (this.itemsPerPage + 1), '').slice(0, -1)
|
|
175
|
+
summary = await this.loadSummary(preset, {
|
|
176
|
+
where,
|
|
177
|
+
replacements
|
|
178
|
+
}, derivedSql)
|
|
179
|
+
}
|
|
153
180
|
}
|
|
154
181
|
|
|
155
182
|
if(this.socket){
|
|
@@ -164,7 +191,170 @@ let ListPage1 = {
|
|
|
164
191
|
}
|
|
165
192
|
},
|
|
166
193
|
|
|
167
|
-
async loadSummary(preset, modelParams){
|
|
194
|
+
async loadSummary(preset, modelParams, derivedSql){
|
|
195
|
+
|
|
196
|
+
if(!this.conn) return
|
|
197
|
+
|
|
198
|
+
const { mode, columns, rows, values } = preset.summary
|
|
199
|
+
|
|
200
|
+
if(!Array.isArray(columns) || columns.length !== 1) return
|
|
201
|
+
if(!Array.isArray(rows) || rows.length !== 1) return
|
|
202
|
+
if(!Array.isArray(values) || values.length !== 1) return
|
|
203
|
+
|
|
204
|
+
const col = columns[0]
|
|
205
|
+
let columnValues = []
|
|
206
|
+
let colKey
|
|
207
|
+
if(col.key !== '(none)'){
|
|
208
|
+
colKey = col.key
|
|
209
|
+
const [ _columnValues ] = await this.conn.query(`
|
|
210
|
+
SELECT
|
|
211
|
+
DISTINCT(${colKey}) AS cols
|
|
212
|
+
FROM (${derivedSql}) AS derived
|
|
213
|
+
ORDER BY cols ASC
|
|
214
|
+
`)
|
|
215
|
+
|
|
216
|
+
columnValues = _columnValues
|
|
217
|
+
}
|
|
218
|
+
if(columnValues.length > 100){
|
|
219
|
+
throw new Error('Too many columns')
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const attributes = []
|
|
223
|
+
const group = []
|
|
224
|
+
const summaryColumns = [
|
|
225
|
+
/*{ key:"dfDate", label:"Date", width:"200px", visible:true },
|
|
226
|
+
{ key:"type-1", label:"Type 1", type:"number", visible:true },
|
|
227
|
+
{ key:"type-2", label:"Type 2", type:"number", visible:true },
|
|
228
|
+
{ key:"type-3", label:"Type 3", type:"number", visible:true },
|
|
229
|
+
{ key:"type-4", label:"Type 4", type:"number", visible:true },
|
|
230
|
+
{ key:"type-8", label:"Type 8", type:"number", visible:true },*/
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
const row = rows[0]
|
|
234
|
+
const rowKey = row.key
|
|
235
|
+
const rowLabel = this.columns[row.key].label
|
|
236
|
+
switch(row.format){
|
|
237
|
+
case 'date':
|
|
238
|
+
attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d') as dfDate`)
|
|
239
|
+
group.push(`DATE_FORMAT(${rowKey}, '%Y-%m-%d')`)
|
|
240
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"D MMM" })
|
|
241
|
+
break
|
|
242
|
+
|
|
243
|
+
case 'month':
|
|
244
|
+
attributes.push(`DATE_FORMAT(${rowKey}, '%Y-%m') as dfDate`)
|
|
245
|
+
group.push(`DATE_FORMAT(${rowKey}, '%Y-%m')`)
|
|
246
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"MMM YYYY" })
|
|
247
|
+
break
|
|
248
|
+
|
|
249
|
+
case 'quarter':
|
|
250
|
+
attributes.push(`CONCAT(YEAR(${rowKey}), ' Q', QUARTER(${rowKey})) as dfDate`)
|
|
251
|
+
group.push(`CONCAT(YEAR(${rowKey}), ' Q', QUARTER(${rowKey}))`)
|
|
252
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"MMM YYYY" })
|
|
253
|
+
break
|
|
254
|
+
|
|
255
|
+
case 'year':
|
|
256
|
+
attributes.push(`DATE_FORMAT(${rowKey}, '%Y') as dfDate`)
|
|
257
|
+
group.push(`DATE_FORMAT(${rowKey}, '%Y')`)
|
|
258
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true, type:"date", format:"YYYY" })
|
|
259
|
+
break
|
|
260
|
+
|
|
261
|
+
default:
|
|
262
|
+
attributes.push(`${rowKey} as dfDate`)
|
|
263
|
+
group.push(`${rowKey}`)
|
|
264
|
+
summaryColumns.push({ key:"dfDate", label:rowLabel, width:"200px", visible:true })
|
|
265
|
+
break
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const value = values[0]
|
|
269
|
+
if(columnValues.length > 0){
|
|
270
|
+
columnValues.forEach((obj) => {
|
|
271
|
+
const columnValue = obj.cols
|
|
272
|
+
|
|
273
|
+
let columnLabel = (columnValue ?? '').toString()
|
|
274
|
+
const baseColumn = this.columns[colKey]
|
|
275
|
+
switch(baseColumn.type){
|
|
276
|
+
|
|
277
|
+
case 'enum':
|
|
278
|
+
const enumValue = baseColumn.typeParams.filter((_) => _.value === columnValue).pop()
|
|
279
|
+
if(enumValue) columnLabel = enumValue.text
|
|
280
|
+
break
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
switch(value.aggregrate){
|
|
284
|
+
case 'count':
|
|
285
|
+
const fn = columnValue === null ? `${colKey} is null` : `${colKey} = '${columnValue}'`
|
|
286
|
+
attributes.push(`SUM(IF(${fn}, 1, 0)) as \`${colKey}-${columnValue}\``)
|
|
287
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
288
|
+
break
|
|
289
|
+
|
|
290
|
+
case 'min':
|
|
291
|
+
attributes.push(`MIN(${colKey}) as \`${colKey}-${columnValue}\``)
|
|
292
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
293
|
+
break
|
|
294
|
+
|
|
295
|
+
case 'max':
|
|
296
|
+
attributes.push(`MAX(${colKey}) as \`${colKey}-${columnValue}\``)
|
|
297
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
298
|
+
break
|
|
299
|
+
|
|
300
|
+
case 'avg':
|
|
301
|
+
attributes.push(`AVG(${colKey}) as \`${colKey}-${columnValue}\``)
|
|
302
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
303
|
+
break
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
else{
|
|
308
|
+
switch(value.aggregrate) {
|
|
309
|
+
case 'count':
|
|
310
|
+
default:
|
|
311
|
+
attributes.push(`COUNT(*) as \`Count\``)
|
|
312
|
+
summaryColumns.push({ key:'Count', label:'Count', type:"number", visible:true, aggregrate:"count" })
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
//console.log(attributes)
|
|
317
|
+
|
|
318
|
+
let [ summaryItems ] = await this.conn.query(`
|
|
319
|
+
SELECT
|
|
320
|
+
${attributes.join(', ')}
|
|
321
|
+
FROM (
|
|
322
|
+
${derivedSql}
|
|
323
|
+
) as t1
|
|
324
|
+
GROUP BY ${group[0]}
|
|
325
|
+
ORDER BY dfDate ASC
|
|
326
|
+
`)
|
|
327
|
+
|
|
328
|
+
if(this.columns[rowKey] && this.columns[rowKey].type === 'enum'){
|
|
329
|
+
summaryItems = summaryItems.map((_) => {
|
|
330
|
+
|
|
331
|
+
const typeParam = this.columns[rowKey].typeParams.filter((__) => __.value === _.dfDate).pop()
|
|
332
|
+
if(typeParam){
|
|
333
|
+
_.dfDate = typeParam.text
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return _
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const summary = {
|
|
341
|
+
type: "table",
|
|
342
|
+
columns: summaryColumns,
|
|
343
|
+
items: summaryItems
|
|
344
|
+
/*items: [
|
|
345
|
+
{ date:"21 Jan 23", "type-1":12, "type-2":11, "type-3":16, "type-4":2, "type-8":8 },
|
|
346
|
+
{ date:"22 Jan 23", "type-1":51, "type-2":87, "type-3":86, "type-4":3, "type-8":9 },
|
|
347
|
+
{ date:"23 Jan 23", "type-1":14, "type-2":66, "type-3":77, "type-4":4, "type-8":18 },
|
|
348
|
+
{ date:"24 Jan 23", "type-1":7, "type-2":3, "type-3":63, "type-4":5, "type-8":28 },
|
|
349
|
+
{ date:"25 Jan 23", "type-1":88, "type-2":55, "type-3":68, "type-4":6, "type-8":38 },
|
|
350
|
+
{ date:"26 Jan 23", "type-1":172, "type-2":13, "type-3":6, "type-4":7, "type-8":48 }
|
|
351
|
+
]*/
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return summary
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
async loadSummary0(preset, modelParams){
|
|
168
358
|
|
|
169
359
|
const { columns, rows, values } = preset.summary
|
|
170
360
|
|
|
@@ -240,28 +430,38 @@ let ListPage1 = {
|
|
|
240
430
|
columnValues.forEach((obj) => {
|
|
241
431
|
const columnValue = obj.dataValues['cols']
|
|
242
432
|
|
|
433
|
+
let columnLabel = (columnValue ?? '').toString()
|
|
434
|
+
const baseColumn = this.columns[colKey]
|
|
435
|
+
switch(baseColumn.type){
|
|
436
|
+
|
|
437
|
+
case 'enum':
|
|
438
|
+
const enumValue = baseColumn.typeParams.filter((_) => _.value === columnValue).pop()
|
|
439
|
+
if(enumValue) columnLabel = enumValue.text
|
|
440
|
+
break
|
|
441
|
+
}
|
|
442
|
+
|
|
243
443
|
switch(value.aggregrate){
|
|
244
444
|
case 'count':
|
|
245
445
|
const fn = columnValue === null ?
|
|
246
446
|
Sequelize.literal(`${colKey} is null`) :
|
|
247
447
|
Sequelize.literal(`${colKey} = '${columnValue}'`)
|
|
248
448
|
attributes.push([Sequelize.fn("SUM", Sequelize.fn("IF", fn, 1, 0)), colKey + "-" + columnValue])
|
|
249
|
-
summaryColumns.push({ key:colKey + "-" + columnValue, label:
|
|
449
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
250
450
|
break
|
|
251
451
|
|
|
252
452
|
case 'min':
|
|
253
453
|
attributes.push([Sequelize.fn("MIN", colKey), colKey + "-" + columnValue])
|
|
254
|
-
summaryColumns.push({ key:colKey + "-" + columnValue, label:
|
|
454
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
255
455
|
break
|
|
256
456
|
|
|
257
457
|
case 'max':
|
|
258
458
|
attributes.push([Sequelize.fn("MAX", colKey), colKey + "-" + columnValue])
|
|
259
|
-
summaryColumns.push({ key:colKey + "-" + columnValue, label:
|
|
459
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
260
460
|
break
|
|
261
461
|
|
|
262
462
|
case 'avg':
|
|
263
463
|
attributes.push([Sequelize.fn("AVG", colKey), colKey + "-" + columnValue])
|
|
264
|
-
summaryColumns.push({ key:colKey + "-" + columnValue, label:
|
|
464
|
+
summaryColumns.push({ key:colKey + "-" + columnValue, label:columnLabel, visible:true, aggregrate:"count" })
|
|
265
465
|
break
|
|
266
466
|
}
|
|
267
467
|
})
|
|
@@ -380,6 +580,14 @@ let ListPage1 = {
|
|
|
380
580
|
}
|
|
381
581
|
break
|
|
382
582
|
|
|
583
|
+
case 'weekAgo':
|
|
584
|
+
whereValue = {
|
|
585
|
+
...whereValue,
|
|
586
|
+
[Op.gte]: dayjs().add(-1, 'week').format('YYYY-MM-DD 00:00:00'),
|
|
587
|
+
[Op.lt]: dayjs().format('YYYY-MM-DD 23:59:59'),
|
|
588
|
+
}
|
|
589
|
+
break
|
|
590
|
+
|
|
383
591
|
case 'thisMonth':
|
|
384
592
|
whereValue = {
|
|
385
593
|
...whereValue,
|
|
@@ -388,6 +596,14 @@ let ListPage1 = {
|
|
|
388
596
|
}
|
|
389
597
|
break
|
|
390
598
|
|
|
599
|
+
case 'monthAgo':
|
|
600
|
+
whereValue = {
|
|
601
|
+
...whereValue,
|
|
602
|
+
[Op.gte]: dayjs().add(-1, 'month').format('YYYY-MM-DD 00:00:00'),
|
|
603
|
+
[Op.lt]: dayjs().format('YYYY-MM-DD 23:59:59'),
|
|
604
|
+
}
|
|
605
|
+
break
|
|
606
|
+
|
|
391
607
|
case 'lastMonth':
|
|
392
608
|
whereValue = {
|
|
393
609
|
[Op.gte]: dayjs().add(-1, 'month').format('YYYY-MM-01 00:00:00'),
|