@mixd-id/web-scaffold 0.1.230406281 → 0.1.230406283
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/Button.vue +1 -0
- package/src/components/List.vue +182 -69
- package/src/components/Modal.vue +1 -1
- package/src/components/PresetSelectorFilterItem.vue +28 -0
- package/src/components/Tabs.vue +1 -1
- package/src/components/VirtualTable.vue +9 -5
- package/src/themes/default/index.js +22 -0
- package/src/utils/preset-selector.js +127 -16
- package/src/utils/preset-selector.mjs +7 -0
- package/src/utils/wss.mjs +11 -4
- package/src/widgets/PresetBar.vue +63 -41
package/package.json
CHANGED
|
@@ -208,6 +208,7 @@ export default{
|
|
|
208
208
|
|
|
209
209
|
.button-secondary{
|
|
210
210
|
@apply bg-primary-100 border-primary-100 text-primary-700 rounded-lg text-text;
|
|
211
|
+
border: solid 1px rgb(var(--primary-100));
|
|
211
212
|
}
|
|
212
213
|
.button-secondary:hover{
|
|
213
214
|
@apply bg-primary-200 border-primary-200;
|
package/src/components/List.vue
CHANGED
|
@@ -4,48 +4,82 @@
|
|
|
4
4
|
<div v-if="readyState === 1"
|
|
5
5
|
class="flex-1 flex flex-row">
|
|
6
6
|
|
|
7
|
-
<div v-if="computedPresetMode === 'sidebar' &&
|
|
7
|
+
<div v-if="computedPresetMode === 'sidebar' && sidebar.open"
|
|
8
8
|
class="relative flex flex-col border-r-[1px] border-text-50 panel-400"
|
|
9
|
-
:style="
|
|
9
|
+
:style="sidebarStyle">
|
|
10
10
|
|
|
11
11
|
<PresetBar :config="config"
|
|
12
12
|
class="flex-1"
|
|
13
13
|
@apply="load">
|
|
14
|
+
|
|
14
15
|
<template #toolbar>
|
|
15
|
-
<button type="button" class="
|
|
16
|
-
<svg width="
|
|
16
|
+
<button type="button" class="p-1" @click="togglePreset">
|
|
17
|
+
<svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"/></svg>
|
|
17
18
|
</button>
|
|
18
19
|
</template>
|
|
20
|
+
|
|
19
21
|
</PresetBar>
|
|
20
22
|
|
|
21
|
-
<div v-if="
|
|
23
|
+
<div v-if="sidebar.open"
|
|
22
24
|
:class="$style.resize1"
|
|
23
25
|
@mousedown="(e) => $util.dragResize(e, resize1)"></div>
|
|
24
26
|
</div>
|
|
25
27
|
|
|
26
|
-
<div class="flex-1 flex flex-col
|
|
28
|
+
<div class="flex-1 flex flex-col" :class="containerClass">
|
|
27
29
|
|
|
28
30
|
<slot name="head"
|
|
29
31
|
:preset="preset"
|
|
30
32
|
:presetSelector="$refs.presetSelector"
|
|
31
33
|
:compPrefix="compPrefix">
|
|
32
|
-
<div class="flex flex-col md:flex-row gap-3 md:items-start overflow-hidden leading-tight p-3 md:p-0 md:pl-3"
|
|
34
|
+
<div class="flex flex-col md:flex-row gap-3 md:items-start overflow-hidden leading-tight p-3 md:p-0 md:pl-3 panel-400 md:panel-none border-b-[1px] border-text-50 md:border-none"
|
|
33
35
|
:class="headerClass">
|
|
34
36
|
|
|
35
|
-
<div class="flex md:flex-1 flex-row gap-3">
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
<div class="flex md:flex-1 flex-row items-start gap-3">
|
|
38
|
+
|
|
39
|
+
<div class="flex flex-row gap-2 md:gap-3 items-center" ref="title">
|
|
40
|
+
<div class="flex flex-col whitespace-nowrap text-ellipsis overflow-hidden">
|
|
41
|
+
<div class="cursor-pointer group" @click="$refs.contextMenu.open($refs.title)">
|
|
42
|
+
<small class="text-text-400 text-xs">{{ title ?? 'Untitled' }}</small>
|
|
43
|
+
<div class="flex flex-row items-baseline gap-1">
|
|
44
|
+
<h5 class="whitespace-nowrap relative top-[-2px] text-ellipsis overflow-hidden group-hover:text-primary">
|
|
45
|
+
{{ preset.name ?? 'Preset Name' }}
|
|
46
|
+
</h5>
|
|
47
|
+
<svg width="13" height="13" class="fill-text group-hover:fill-primary relative top-[-1px]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M310.6 246.6l-127.1 128C176.4 380.9 168.2 384 160 384s-16.38-3.125-22.63-9.375l-127.1-128C.2244 237.5-2.516 223.7 2.438 211.8S19.07 192 32 192h255.1c12.94 0 24.62 7.781 29.58 19.75S319.8 237.5 310.6 246.6z"/></svg>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<ContextMenu ref="contextMenu" class="panel-400">
|
|
51
|
+
<div class="flex flex-col min-w-[300px] divide-y divide-text-50">
|
|
52
|
+
|
|
53
|
+
<div class="p-3 flex flex-col gap-1 bg-base-300">
|
|
54
|
+
<button v-for="(preset, idx) in config.presets"
|
|
55
|
+
class="p-2 px-5 text-left flex flex-row items-center rounded-md"
|
|
56
|
+
:class="config.presetIdx === idx ? 'bg-primary-200 text-white' : 'hover:bg-primary-100'"
|
|
57
|
+
type="button"
|
|
58
|
+
@click="selectPreset(idx)">
|
|
59
|
+
<label class="flex-1 pr-12">
|
|
60
|
+
{{ preset.name }}
|
|
61
|
+
</label>
|
|
62
|
+
<div class="p-1">
|
|
63
|
+
<div v-if="idx < 10"
|
|
64
|
+
class="border-[1px] border-text-300 px-2 text-mono text-xs text-text-400 rounded-lg">
|
|
65
|
+
Alt {{ idx + 1 }}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="px-3">
|
|
72
|
+
<button type="button" class="text-primary p-5 text-left w-full" @click="openPreset">Edit</button>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
</div>
|
|
76
|
+
</ContextMenu>
|
|
77
|
+
</div>
|
|
44
78
|
</div>
|
|
45
79
|
|
|
46
80
|
<div class="flex-1"></div>
|
|
47
81
|
|
|
48
|
-
<slot
|
|
82
|
+
<slot name="toolbar"></slot>
|
|
49
83
|
</div>
|
|
50
84
|
|
|
51
85
|
<div class="flex flex-row gap-2 gap-1" v-if="Boolean(toolbar)">
|
|
@@ -107,13 +141,13 @@
|
|
|
107
141
|
ref="grid"
|
|
108
142
|
:items="dataItems"
|
|
109
143
|
:column="computedGridColumn"
|
|
110
|
-
class="
|
|
144
|
+
:class="gridClass"
|
|
111
145
|
@scroll-end="loadNext"
|
|
112
146
|
:container-class="`${gridContainerClass}`"
|
|
113
147
|
:config="config">
|
|
114
148
|
<template #item="{ item }">
|
|
115
149
|
<slot name="gridItem" :item="item">
|
|
116
|
-
<div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden">
|
|
150
|
+
<div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
|
|
117
151
|
<div>
|
|
118
152
|
<Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
|
|
119
153
|
</div>
|
|
@@ -130,6 +164,7 @@
|
|
|
130
164
|
</div>
|
|
131
165
|
|
|
132
166
|
</div>
|
|
167
|
+
|
|
133
168
|
</div>
|
|
134
169
|
</template>
|
|
135
170
|
|
|
@@ -161,17 +196,26 @@ export default{
|
|
|
161
196
|
}
|
|
162
197
|
},
|
|
163
198
|
gridColumn: [ Number, String ],
|
|
164
|
-
|
|
199
|
+
gridClass: {
|
|
200
|
+
type: String,
|
|
201
|
+
default: "flex-1 md:p-3"
|
|
202
|
+
},
|
|
203
|
+
gridContainerClass: {
|
|
204
|
+
type: String,
|
|
205
|
+
default: "md:gap-5"
|
|
206
|
+
},
|
|
165
207
|
headerClass: String,
|
|
166
|
-
presetMode:
|
|
167
|
-
|
|
208
|
+
presetMode: {
|
|
209
|
+
type: String,
|
|
210
|
+
default: "sidebar"
|
|
211
|
+
},
|
|
212
|
+
containerClass: {
|
|
213
|
+
type: String,
|
|
214
|
+
default: "md:p-5 md:gap-3"
|
|
215
|
+
},
|
|
168
216
|
toolbar: {
|
|
169
217
|
type: [ String, Boolean ],
|
|
170
218
|
default: true
|
|
171
|
-
},
|
|
172
|
-
editHash: {
|
|
173
|
-
type: String,
|
|
174
|
-
default: '#719ed'
|
|
175
219
|
}
|
|
176
220
|
},
|
|
177
221
|
|
|
@@ -211,7 +255,8 @@ export default{
|
|
|
211
255
|
const columns = []
|
|
212
256
|
for(let key in data.items[0]){
|
|
213
257
|
if(key.startsWith('_')){
|
|
214
|
-
|
|
258
|
+
let [ k1, ...k2 ] = key.substring(1).split('-')
|
|
259
|
+
k2 = k2.join('-')
|
|
215
260
|
|
|
216
261
|
let label = k2.replace(/_/gi, ' ')
|
|
217
262
|
const column = tableColumns[k1]
|
|
@@ -240,6 +285,8 @@ export default{
|
|
|
240
285
|
this.preset.columns.push(...columns)
|
|
241
286
|
}
|
|
242
287
|
|
|
288
|
+
this.loadEnums()
|
|
289
|
+
|
|
243
290
|
Object.assign(this.data, data ?? {})
|
|
244
291
|
|
|
245
292
|
this.$emit('after-load')
|
|
@@ -300,12 +347,40 @@ export default{
|
|
|
300
347
|
}
|
|
301
348
|
},
|
|
302
349
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
350
|
+
loadEnums(){
|
|
351
|
+
|
|
352
|
+
for(let i in this.preset.columns){
|
|
353
|
+
const column = this.preset.columns[i]
|
|
306
354
|
|
|
307
|
-
|
|
308
|
-
|
|
355
|
+
if(column.enumSrc) {
|
|
356
|
+
const [ src, key, attr ] = column.enumSrc.split(':')
|
|
357
|
+
this.socket.send(src, {
|
|
358
|
+
columns: [
|
|
359
|
+
{ key:attr, visible:true }
|
|
360
|
+
],
|
|
361
|
+
filters: [
|
|
362
|
+
{
|
|
363
|
+
key:key, value: [ { operator:'in', value:[ 1 ] } ]
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
})
|
|
367
|
+
.then(res => {
|
|
368
|
+
this.preset.columns[i].typeParams = (res.items ?? []).map(_ => {
|
|
369
|
+
return {
|
|
370
|
+
text: _[attr] ?? _[key],
|
|
371
|
+
value: _[key]
|
|
372
|
+
}
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
.catch(err => this.log('Load enums error', err))
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
resize1(w){
|
|
382
|
+
if(this.config.sidebar.width + w >= 270 && this.config.sidebar.width + w <= 600){
|
|
383
|
+
this.config.sidebar.width += w
|
|
309
384
|
}
|
|
310
385
|
},
|
|
311
386
|
|
|
@@ -314,15 +389,7 @@ export default{
|
|
|
314
389
|
this.config.presetIdx = idx
|
|
315
390
|
this.load()
|
|
316
391
|
this.$refs.presetSelector.close()
|
|
317
|
-
|
|
318
|
-
},
|
|
319
|
-
|
|
320
|
-
setDefaultPresetView(){
|
|
321
|
-
if(window.innerWidth < 768){
|
|
322
|
-
this.preset.view = 'grid'
|
|
323
|
-
}
|
|
324
|
-
else if(!this.preset.view){
|
|
325
|
-
this.preset.view = window.innerWidth < 768 ? 'grid' : 'table'
|
|
392
|
+
this.$refs.contextMenu.close()
|
|
326
393
|
}
|
|
327
394
|
},
|
|
328
395
|
|
|
@@ -342,10 +409,7 @@ export default{
|
|
|
342
409
|
break
|
|
343
410
|
|
|
344
411
|
case 'sidebar':
|
|
345
|
-
this
|
|
346
|
-
...this.$route,
|
|
347
|
-
hash: this.$route.hash.replace(this.editHash, '') + this.editHash
|
|
348
|
-
})
|
|
412
|
+
this.sidebar.open = true
|
|
349
413
|
break
|
|
350
414
|
|
|
351
415
|
default:
|
|
@@ -355,11 +419,17 @@ export default{
|
|
|
355
419
|
}
|
|
356
420
|
},
|
|
357
421
|
|
|
358
|
-
|
|
359
|
-
this
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
422
|
+
togglePreset(){
|
|
423
|
+
switch(this.computedPresetMode){
|
|
424
|
+
|
|
425
|
+
case 'sidebar':
|
|
426
|
+
this.sidebar.open = !this.sidebar.open
|
|
427
|
+
break
|
|
428
|
+
|
|
429
|
+
case 'popup':
|
|
430
|
+
this.$refs.presetSelector.open()
|
|
431
|
+
break
|
|
432
|
+
}
|
|
363
433
|
},
|
|
364
434
|
|
|
365
435
|
onSignal(event, items){
|
|
@@ -391,6 +461,29 @@ export default{
|
|
|
391
461
|
resize(){
|
|
392
462
|
this.$refs.table ? this.$refs.table.resize() : null
|
|
393
463
|
this.$refs.grid ? this.$refs.grid.resize() : null
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
onKeyDown(e){
|
|
467
|
+
|
|
468
|
+
if(e.altKey){
|
|
469
|
+
|
|
470
|
+
switch(e.code){
|
|
471
|
+
|
|
472
|
+
case 'Backquote':
|
|
473
|
+
this.$refs.contextMenu.open(this.$refs.title)
|
|
474
|
+
break
|
|
475
|
+
|
|
476
|
+
default:
|
|
477
|
+
if(e.code.startsWith('Digit')){
|
|
478
|
+
const idx = parseInt(e.code.substring(5)) - 1
|
|
479
|
+
if(this.config.presets[idx]) {
|
|
480
|
+
this.selectPreset(idx)
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
break
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
}
|
|
394
487
|
}
|
|
395
488
|
|
|
396
489
|
},
|
|
@@ -406,7 +499,6 @@ export default{
|
|
|
406
499
|
|
|
407
500
|
this.loadPreset()
|
|
408
501
|
.then(() => {
|
|
409
|
-
this.setDefaultPresetView()
|
|
410
502
|
this.readyState = 1
|
|
411
503
|
this.load()
|
|
412
504
|
})
|
|
@@ -415,13 +507,13 @@ export default{
|
|
|
415
507
|
this.socket.send('user.subscribe', { name:this.subscribeKey })
|
|
416
508
|
this.socket.on(this.subscribeKey, this.onSignal)
|
|
417
509
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
510
|
+
|
|
511
|
+
window.addEventListener('keydown', this.onKeyDown)
|
|
421
512
|
},
|
|
422
513
|
|
|
423
514
|
unmounted() {
|
|
424
515
|
this.$removeResizeListener(this.$el)
|
|
516
|
+
window.removeEventListener('keydown', this.onKeyDown)
|
|
425
517
|
|
|
426
518
|
if(this.subscribeKey) {
|
|
427
519
|
this.socket.send('user.unsubscribe', {name: this.subscribeKey})
|
|
@@ -439,6 +531,23 @@ export default{
|
|
|
439
531
|
const columns = []
|
|
440
532
|
|
|
441
533
|
for(let row of this.preset.pivot.rows){
|
|
534
|
+
if(!tableColumns[row.key][0].defaultFormat)
|
|
535
|
+
tableColumns[row.key][0].defaultFormat = tableColumns[row.key][0].format
|
|
536
|
+
|
|
537
|
+
switch(row.aggregrate){
|
|
538
|
+
case 'year':
|
|
539
|
+
tableColumns[row.key][0].format = 'YYYY'
|
|
540
|
+
break
|
|
541
|
+
|
|
542
|
+
case 'month':
|
|
543
|
+
tableColumns[row.key][0].format = 'MMM YYYY'
|
|
544
|
+
break
|
|
545
|
+
|
|
546
|
+
case 'date':
|
|
547
|
+
tableColumns[row.key][0].format = 'D MMM YYYY'
|
|
548
|
+
break
|
|
549
|
+
}
|
|
550
|
+
|
|
442
551
|
columns.push(tableColumns[row.key][0])
|
|
443
552
|
}
|
|
444
553
|
|
|
@@ -453,7 +562,15 @@ export default{
|
|
|
453
562
|
|
|
454
563
|
return columns
|
|
455
564
|
}
|
|
456
|
-
|
|
565
|
+
|
|
566
|
+
for(let idx in this.preset.columns){
|
|
567
|
+
if(this.preset.columns[idx].defaultFormat){
|
|
568
|
+
this.preset.columns[idx].format = this.preset.columns[idx].defaultFormat
|
|
569
|
+
delete this.preset.columns[idx].defaultFormat
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return this.preset.columns
|
|
457
574
|
},
|
|
458
575
|
|
|
459
576
|
contentSlots(){
|
|
@@ -503,13 +620,19 @@ export default{
|
|
|
503
620
|
(this.view ?? this.preset.view)
|
|
504
621
|
},
|
|
505
622
|
|
|
506
|
-
|
|
507
|
-
|
|
623
|
+
sidebar(){
|
|
624
|
+
if(!this.config.sidebar || !('open' in this.config.sidebar))
|
|
625
|
+
this.config.sidebar = {
|
|
626
|
+
open: false,
|
|
627
|
+
width: 270
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return this.config.sidebar
|
|
508
631
|
},
|
|
509
632
|
|
|
510
|
-
|
|
633
|
+
sidebarStyle(){
|
|
511
634
|
return {
|
|
512
|
-
width:
|
|
635
|
+
width: !this.sidebar.open ? 0 : this.sidebar.width + 'px'
|
|
513
636
|
}
|
|
514
637
|
},
|
|
515
638
|
|
|
@@ -535,12 +658,6 @@ export default{
|
|
|
535
658
|
}
|
|
536
659
|
},
|
|
537
660
|
|
|
538
|
-
preset(to){
|
|
539
|
-
if(!to.view){
|
|
540
|
-
this.setDefaultPresetView()
|
|
541
|
-
}
|
|
542
|
-
},
|
|
543
|
-
|
|
544
661
|
},
|
|
545
662
|
|
|
546
663
|
}
|
|
@@ -553,8 +670,4 @@ export default{
|
|
|
553
670
|
@apply w-[3px] cursor-ew-resize absolute top-0 right-0 bottom-0;
|
|
554
671
|
}
|
|
555
672
|
|
|
556
|
-
.headerSelected{
|
|
557
|
-
@apply border-primary;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
673
|
</style>
|
package/src/components/Modal.vue
CHANGED
|
@@ -57,6 +57,19 @@
|
|
|
57
57
|
<Radio :name="value.key" :value="false" v-model="value.value" @change="apply">False</Radio>
|
|
58
58
|
</div>
|
|
59
59
|
|
|
60
|
+
<div v-else-if="type === 'enum'" class="flex flex-row gap-2">
|
|
61
|
+
|
|
62
|
+
<div class="flex flex-col gap-2">
|
|
63
|
+
<Checkbox v-for="param in typeParams"
|
|
64
|
+
v-model="cValue.value"
|
|
65
|
+
:value="param.value"
|
|
66
|
+
@change="apply">
|
|
67
|
+
{{ param.text }}
|
|
68
|
+
</Checkbox>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
</div>
|
|
72
|
+
|
|
60
73
|
<div v-else-if="type === 'component'">
|
|
61
74
|
<component :is="column.component"
|
|
62
75
|
:value="value"
|
|
@@ -99,7 +112,11 @@ export default{
|
|
|
99
112
|
type: Object,
|
|
100
113
|
default: {}
|
|
101
114
|
},
|
|
115
|
+
|
|
102
116
|
type: String,
|
|
117
|
+
|
|
118
|
+
typeParams: Array,
|
|
119
|
+
|
|
103
120
|
value: Object,
|
|
104
121
|
|
|
105
122
|
},
|
|
@@ -109,6 +126,17 @@ export default{
|
|
|
109
126
|
valueRequired(){
|
|
110
127
|
return (!(['yesterday', 'today', 'thisWeek', 'thisMonth', 'thisYear'].includes(this.value.operator) ||
|
|
111
128
|
this.type === 'boolean'));
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
cValue(){
|
|
132
|
+
if(this.type === 'enum' && (!this.value.operator || !Array.isArray(this.value.value))){
|
|
133
|
+
Object.assign(this.value, {
|
|
134
|
+
operator: 'in',
|
|
135
|
+
value: []
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return this.value
|
|
112
140
|
}
|
|
113
141
|
|
|
114
142
|
},
|
package/src/components/Tabs.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="computedClass">
|
|
3
3
|
<button v-for="(item, index) in filteredItems" :class="tabClass(item)"
|
|
4
|
-
class="
|
|
4
|
+
class="whitespace-nowrap"
|
|
5
5
|
@click="onClick(item)" type="button" >
|
|
6
6
|
<slot name="default" :="{ item, index }">
|
|
7
7
|
<label class="px-1">
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
<td v-for="column in visibleColumns"
|
|
38
38
|
:class="tdClass(item, column)">
|
|
39
39
|
<slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
|
|
40
|
-
<
|
|
40
|
+
<slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
|
|
41
|
+
<div v-else :class="columnClass(column)" v-html="formatColumn(item, column)"></div>
|
|
41
42
|
</td>
|
|
42
43
|
<td :class="$style.spacer"></td>
|
|
43
44
|
</tr>
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
<tr>
|
|
64
65
|
<td v-for="column in columns" :style="thStyle(column)">
|
|
65
66
|
<slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="items[0]"></slot>
|
|
67
|
+
<slot v-else-if="$slots.default" name="default" :column="column" :item="items[0]"></slot>
|
|
66
68
|
<div v-else :class="columnClass(column)" v-html="formatColumn(items[0] ?? {}, column)"></div>
|
|
67
69
|
</td>
|
|
68
70
|
<td :class="$style.spacer"></td>
|
|
@@ -89,6 +91,8 @@ export default{
|
|
|
89
91
|
props:{
|
|
90
92
|
columns: Array,
|
|
91
93
|
|
|
94
|
+
itemClass: String,
|
|
95
|
+
|
|
92
96
|
items: Array,
|
|
93
97
|
|
|
94
98
|
pinned: Function,
|
|
@@ -218,9 +222,9 @@ export default{
|
|
|
218
222
|
},
|
|
219
223
|
|
|
220
224
|
tdClass(item, column){
|
|
221
|
-
if(Array.isArray(item._highlight) && item._highlight.includes(column.key))
|
|
222
|
-
return this.$style.highlight
|
|
223
|
-
|
|
225
|
+
if(Array.isArray(item._highlight) && item._highlight.includes(column.key))
|
|
226
|
+
return this.$style.highlight + (this.itemClass ? ' ' + this.itemClass : '')
|
|
227
|
+
return this.itemClass ?? ''
|
|
224
228
|
},
|
|
225
229
|
|
|
226
230
|
select(item, index){
|
|
@@ -393,7 +397,7 @@ export default{
|
|
|
393
397
|
break
|
|
394
398
|
|
|
395
399
|
case 'enum':
|
|
396
|
-
const enumObj = column.typeParams.filter((_) => _.value === value).pop()
|
|
400
|
+
const enumObj = (column.typeParams ?? []).filter((_) => _.value === value).pop()
|
|
397
401
|
text = enumObj ? enumObj.text : value
|
|
398
402
|
break
|
|
399
403
|
}
|
|
@@ -60,6 +60,7 @@ const plugin = Plugin(function({ addBase, addUtilities, config, theme }) {
|
|
|
60
60
|
'--facebook-100': '205, 215, 231',
|
|
61
61
|
'--facebook-500': '8, 102, 255',
|
|
62
62
|
|
|
63
|
+
'--panel-none': 'transparent',
|
|
63
64
|
'--panel-300': 'rgb(235, 237, 240)',
|
|
64
65
|
'--panel-400': 'rgb(245, 247, 250)',
|
|
65
66
|
'--panel-500': 'rgb(255, 255, 255)',
|
|
@@ -234,6 +235,27 @@ const plugin = Plugin(function({ addBase, addUtilities, config, theme }) {
|
|
|
234
235
|
transform: 'scale(.9)'
|
|
235
236
|
},
|
|
236
237
|
|
|
238
|
+
'.openltr-move, .openltr-enter-active, .openltr-leave-active': {
|
|
239
|
+
transition: 'all 200ms cubic-bezier(0.25, 1, 0.5, 1)'
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
'.openltr-enter-from, .openltr-leave-to': {
|
|
243
|
+
opacity: 0,
|
|
244
|
+
transform: 'translateX(30px)'
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
'.openltr-leave-active': {
|
|
248
|
+
position: 'absolute',
|
|
249
|
+
right: 0,
|
|
250
|
+
width: '100%'
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
'.panel-none': {
|
|
254
|
+
'--webkit-backdrop-filter': 'var(--panel-none-backdrop-filter)',
|
|
255
|
+
'backdrop-filter': 'var(--panel-none-backdrop-filter)',
|
|
256
|
+
'background-color': 'var(--panel-none)',
|
|
257
|
+
},
|
|
258
|
+
|
|
237
259
|
'.panel-300': {
|
|
238
260
|
'--webkit-backdrop-filter': 'var(--panel-300-backdrop-filter)',
|
|
239
261
|
'backdrop-filter': 'var(--panel-300-backdrop-filter)',
|
|
@@ -452,7 +452,7 @@ const afterItemToSequelizeWhere = async (order, afterItem) => {
|
|
|
452
452
|
}
|
|
453
453
|
|
|
454
454
|
const searchToSequelizeWhere = async (search, params) => {
|
|
455
|
-
if(!search){
|
|
455
|
+
if(!search && (!Array.isArray(params) || params.length < 1)){
|
|
456
456
|
return {
|
|
457
457
|
where: {},
|
|
458
458
|
replacements: []
|
|
@@ -657,7 +657,6 @@ const chartToSequelizeWhere = async (chart, opt) => {
|
|
|
657
657
|
const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
658
658
|
|
|
659
659
|
const { model, sorts = [] } = opt
|
|
660
|
-
//console.log(sorts)
|
|
661
660
|
|
|
662
661
|
const attributes = []
|
|
663
662
|
const group = []
|
|
@@ -665,7 +664,6 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
|
665
664
|
const sortExists = sorts.length > 0
|
|
666
665
|
|
|
667
666
|
const groupedSorts = groupBy(sorts, 'key')
|
|
668
|
-
//console.log(util.inspect(pivot, false, null, true /* enable colors */))
|
|
669
667
|
|
|
670
668
|
for(let row of pivot.rows){
|
|
671
669
|
|
|
@@ -674,15 +672,15 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
|
674
672
|
switch(row.aggregrate){
|
|
675
673
|
|
|
676
674
|
case 'date':
|
|
677
|
-
attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%
|
|
678
|
-
group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%
|
|
679
|
-
if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%
|
|
675
|
+
attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), row.key ])
|
|
676
|
+
group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), row.key ])
|
|
677
|
+
if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m-%d'), 'asc' ])
|
|
680
678
|
break
|
|
681
679
|
|
|
682
680
|
case 'month':
|
|
683
|
-
attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%
|
|
684
|
-
group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%
|
|
685
|
-
if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%
|
|
681
|
+
attributes.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), row.key ])
|
|
682
|
+
group.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), row.key ])
|
|
683
|
+
if(!sortExists) order.push([ fn('DATE_FORMAT', literal(`${model.name}.${field}`), '%Y-%m'), 'asc' ])
|
|
686
684
|
break
|
|
687
685
|
|
|
688
686
|
case 'year':
|
|
@@ -701,6 +699,7 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
|
701
699
|
|
|
702
700
|
const sortKeyToOrders = {} // Helper to determine group ordering
|
|
703
701
|
for(let value of pivot.values){
|
|
702
|
+
|
|
704
703
|
switch(value.aggregrate){
|
|
705
704
|
|
|
706
705
|
case 'sum':
|
|
@@ -742,21 +741,19 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
|
742
741
|
}
|
|
743
742
|
|
|
744
743
|
const field = model.rawAttributes[value.key].field
|
|
745
|
-
//console.log(field)
|
|
746
744
|
|
|
747
745
|
groups.forEach(group => {
|
|
748
746
|
if(group[value.key] === null){
|
|
749
|
-
attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} is null THEN 1 ELSE 0 END)`), `_${value.key}-null` ])
|
|
747
|
+
attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} is null THEN 1 ELSE 0 END)`), `_${value.key}-(null)` ])
|
|
750
748
|
}
|
|
751
749
|
else if(group[value.key] === ''){
|
|
752
|
-
attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = '' THEN 1 ELSE 0 END)`), `_${value.key}-empty` ])
|
|
750
|
+
attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = '' THEN 1 ELSE 0 END)`), `_${value.key}-(empty)` ])
|
|
753
751
|
}
|
|
754
752
|
else{
|
|
755
753
|
const groupValue = (group[value.key] ?? '').toString().replace(/\s/g, '_')
|
|
756
754
|
attributes.push([ literal(`SUM(CASE WHEN ${model.name}.${field} = '${group[value.key]}' THEN 1 ELSE 0 END)`), `_${value.key}-${groupValue}` ])
|
|
757
755
|
}
|
|
758
756
|
})
|
|
759
|
-
//console.log(attributes)
|
|
760
757
|
|
|
761
758
|
break
|
|
762
759
|
}
|
|
@@ -773,9 +770,6 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
|
773
770
|
}
|
|
774
771
|
}
|
|
775
772
|
|
|
776
|
-
//console.log(util.inspect(attributes, false, null, true /* enable colors */))
|
|
777
|
-
//console.log(util.inspect(group, false, null, true /* enable colors */))
|
|
778
|
-
|
|
779
773
|
return {
|
|
780
774
|
attributes,
|
|
781
775
|
group,
|
|
@@ -887,6 +881,121 @@ const filtersToSequelizeInclude = async(filters, opt, includes) => {
|
|
|
887
881
|
return includes
|
|
888
882
|
}
|
|
889
883
|
|
|
884
|
+
const syntaxSearchToSequelizeOpt = async(search, opt) => {
|
|
885
|
+
|
|
886
|
+
let where = {}
|
|
887
|
+
let replacements = []
|
|
888
|
+
|
|
889
|
+
const { config } = opt
|
|
890
|
+
|
|
891
|
+
let syntaxKey = search[1]
|
|
892
|
+
let syntaxVal = search[2].trim()
|
|
893
|
+
|
|
894
|
+
return {
|
|
895
|
+
where,
|
|
896
|
+
replacements
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const presetToSequelizeList = async(preset, {
|
|
901
|
+
conn, model, order:initialOrder, config, where:initialWhere, replacements: initialReplacements
|
|
902
|
+
}) => {
|
|
903
|
+
|
|
904
|
+
if(!initialOrder){
|
|
905
|
+
initialOrder = [
|
|
906
|
+
[ 'updatedAt', 'desc' ],
|
|
907
|
+
[ 'id', 'desc' ],
|
|
908
|
+
]
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
const { itemsPerPage = 20, filters = [], sorts, pivot, afterItem, search, id } = preset;
|
|
912
|
+
|
|
913
|
+
let where = {}
|
|
914
|
+
const replacements = []
|
|
915
|
+
const { order } = await sortsToSequelizeWhere(sorts, {
|
|
916
|
+
order: initialOrder
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
if(Array.isArray(id)){
|
|
920
|
+
where = {
|
|
921
|
+
...where,
|
|
922
|
+
id: {
|
|
923
|
+
[Op.in]: id
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
const { where:filterWhere, replacements:filterReplacements } = await filtersToSequelizeWhere(filters, config)
|
|
929
|
+
|
|
930
|
+
let searchWhere = {}, searchReplacements = []
|
|
931
|
+
if(search){
|
|
932
|
+
const syntaxSearch = (search ?? '').match(/^(\w+)\:(.*)/)
|
|
933
|
+
if(syntaxSearch !== null){
|
|
934
|
+
const syntaxSearchOpt = syntaxSearchToSequelizeOpt(syntaxSearch, { config })
|
|
935
|
+
searchWhere = syntaxSearchOpt.where
|
|
936
|
+
searchReplacements = syntaxSearchOpt.replacements
|
|
937
|
+
}
|
|
938
|
+
else{
|
|
939
|
+
const match = (config.columns ?? []).filter(_ => (_.search && _.key)).map(_ => _.key)
|
|
940
|
+
if(match !== null){
|
|
941
|
+
const searchOpt = await searchToSequelizeWhere(search, {
|
|
942
|
+
match
|
|
943
|
+
})
|
|
944
|
+
searchWhere = searchOpt.where
|
|
945
|
+
searchReplacements = searchOpt.replacements
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
if(pivot && pivot.enabled){
|
|
951
|
+
const { attributes, group, order } = await pivotToSequelizeWhere(pivot, {
|
|
952
|
+
model,
|
|
953
|
+
conn
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
return {
|
|
957
|
+
attributes,
|
|
958
|
+
where: {
|
|
959
|
+
[Op.and]: [
|
|
960
|
+
initialWhere ?? {},
|
|
961
|
+
where,
|
|
962
|
+
filterWhere,
|
|
963
|
+
searchWhere
|
|
964
|
+
]
|
|
965
|
+
},
|
|
966
|
+
replacements: [
|
|
967
|
+
...filterReplacements,
|
|
968
|
+
...searchReplacements
|
|
969
|
+
],
|
|
970
|
+
group,
|
|
971
|
+
order
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
else{
|
|
975
|
+
const { where:afterItemWhere } = await afterItemToSequelizeWhere(order, afterItem)
|
|
976
|
+
|
|
977
|
+
return {
|
|
978
|
+
where: {
|
|
979
|
+
[Op.and]: [
|
|
980
|
+
where,
|
|
981
|
+
afterItemWhere,
|
|
982
|
+
filterWhere,
|
|
983
|
+
searchWhere,
|
|
984
|
+
]
|
|
985
|
+
},
|
|
986
|
+
limit: itemsPerPage,
|
|
987
|
+
order,
|
|
988
|
+
replacements: [
|
|
989
|
+
...(initialReplacements ?? []),
|
|
990
|
+
...replacements,
|
|
991
|
+
...filterReplacements,
|
|
992
|
+
...searchReplacements
|
|
993
|
+
]
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
|
|
890
999
|
module.exports = {
|
|
891
1000
|
filtersToSequelizeWhere,
|
|
892
1001
|
afterItemToSequelizeWhere,
|
|
@@ -895,4 +1004,6 @@ module.exports = {
|
|
|
895
1004
|
pivotToSequelizeWhere,
|
|
896
1005
|
sortsToSequelizeWhere,
|
|
897
1006
|
filtersToSequelizeInclude,
|
|
1007
|
+
|
|
1008
|
+
presetToSequelizeList
|
|
898
1009
|
}
|
|
@@ -42,6 +42,13 @@ const setupConfig = (config) => {
|
|
|
42
42
|
if(config.presets[idx].columns[0] && typeof config.presets[idx].columns[0] === 'string'){
|
|
43
43
|
config.presets[idx].columns = pickColumns(config.columns ?? [], config.presets[idx].columns)
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
if(!config.presets[idx].view)
|
|
47
|
+
config.presets[idx].view = 'table'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if(!config.presetIdx && config.presets.length > 0){
|
|
51
|
+
config.presetIdx = 0
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
|
package/src/utils/wss.mjs
CHANGED
|
@@ -58,7 +58,10 @@ class WSS extends EventEmitter2{
|
|
|
58
58
|
url: '',
|
|
59
59
|
}, opt ?? {})
|
|
60
60
|
|
|
61
|
-
this.connect()
|
|
61
|
+
this.connect()
|
|
62
|
+
.catch(e => {
|
|
63
|
+
this.emit('error', e, [])
|
|
64
|
+
})
|
|
62
65
|
|
|
63
66
|
if(typeof window !== 'undefined'){
|
|
64
67
|
window.addEventListener('visibilitychange', () => {
|
|
@@ -119,7 +122,7 @@ class WSS extends EventEmitter2{
|
|
|
119
122
|
this._pendingSend = []
|
|
120
123
|
}
|
|
121
124
|
else if(_requestId){
|
|
122
|
-
if(this._callbacks[_requestId]){
|
|
125
|
+
if(this._callbacks[_requestId] && this._instance.readyState === 1){
|
|
123
126
|
const { cb, err, path, params, t1 } = this._callbacks[_requestId]
|
|
124
127
|
status === 200 ? cb(data) : err(data)
|
|
125
128
|
delete this._callbacks[_requestId]
|
|
@@ -167,6 +170,10 @@ class WSS extends EventEmitter2{
|
|
|
167
170
|
};
|
|
168
171
|
}
|
|
169
172
|
|
|
173
|
+
async close(){
|
|
174
|
+
this._instance.close()
|
|
175
|
+
}
|
|
176
|
+
|
|
170
177
|
async reconnect(opt){
|
|
171
178
|
if(this._instance){
|
|
172
179
|
this._instance.close()
|
|
@@ -178,7 +185,7 @@ class WSS extends EventEmitter2{
|
|
|
178
185
|
}
|
|
179
186
|
|
|
180
187
|
sendSync(path, params, cb, err, override){
|
|
181
|
-
if(this._instance.readyState > 1){
|
|
188
|
+
if(!this._instance || this._instance.readyState > 1){
|
|
182
189
|
return
|
|
183
190
|
}
|
|
184
191
|
else if(!this._instance.isAuth){
|
|
@@ -212,7 +219,7 @@ class WSS extends EventEmitter2{
|
|
|
212
219
|
}
|
|
213
220
|
|
|
214
221
|
setTimeout(() => {
|
|
215
|
-
if(this._callbacks[_requestId]){
|
|
222
|
+
if(this._callbacks[_requestId] && this._instance.readyState === 1){
|
|
216
223
|
err({ message: 'Timeout' })
|
|
217
224
|
delete this._callbacks[_requestId]
|
|
218
225
|
}
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
<TransitionGroup name="openltr" tag="div" class="flex-1 flex">
|
|
5
5
|
|
|
6
|
-
<div v-if="
|
|
6
|
+
<div v-if="presetBar.view === 1" key="panel1" class="flex-1 flex flex-col">
|
|
7
7
|
|
|
8
|
-
<div class="flex flex-row items-center gap-2 p-
|
|
8
|
+
<div class="flex flex-row items-center gap-2 p-3">
|
|
9
9
|
<slot name="start"></slot>
|
|
10
10
|
<div class="flex-1">
|
|
11
11
|
<h5 class="px-3">{{ config.name ?? 'Config' }}</h5>
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
class="px-3 p-2 text-left hover:bg-primary-100 flex flex-row gap-2 items-center">
|
|
23
23
|
<Radio v-model="config.presetIdx" :value="idx" @change="apply()" />
|
|
24
24
|
<button type="button" class="flex-1 py-1 text-left overflow-hidden text-ellipsis whitespace-nowrap"
|
|
25
|
-
@click="
|
|
25
|
+
@click="select(idx)">
|
|
26
26
|
{{ _preset.name }}
|
|
27
27
|
</button>
|
|
28
28
|
<button v-if="config.presetIdx === idx"
|
|
@@ -73,14 +73,18 @@
|
|
|
73
73
|
<div v-else key="panel2" class="flex-1 flex flex-col">
|
|
74
74
|
|
|
75
75
|
<div class="flex flex-row gap-2 p-3">
|
|
76
|
-
<button type="button" @click="deselect">
|
|
77
|
-
<svg width="
|
|
76
|
+
<button type="button" @click="deselect" class="p-1">
|
|
77
|
+
<svg width="16" height="16" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M80 208h-64C7.125 208 0 215.1 0 224v64c0 8.875 7.125 16 16 16h64C88.88 304 96 296.9 96 288V224C96 215.1 88.88 208 80 208zM80 368h-64C7.125 368 0 375.1 0 384v64c0 8.875 7.125 16 16 16h64C88.88 464 96 456.9 96 448v-64C96 375.1 88.88 368 80 368zM80 48h-64C7.125 48 0 55.13 0 64v64c0 8.875 7.125 16 16 16h64C88.88 144 96 136.9 96 128V64C96 55.13 88.88 48 80 48zM488 232H183.1C170.7 232 160 242.7 160 256s10.75 24 23.1 24H488C501.3 280 512 269.3 512 256S501.3 232 488 232zM488 72H183.1C170.7 72 160 82.75 160 95.1S170.7 120 183.1 120H488c13.25 0 24-10.75 24-23.1S501.3 72 488 72zM488 392H183.1C170.7 392 160 402.7 160 416s10.75 24 23.1 24H488c13.25 0 24-10.75 24-24S501.3 392 488 392z"/></svg>
|
|
78
78
|
</button>
|
|
79
|
-
<Textbox v-model="preset.name"
|
|
79
|
+
<Textbox v-model="preset.name"
|
|
80
|
+
class="flex-1 rounded-none border-none bg-transparent font-bold"
|
|
81
|
+
item-class="p-1"
|
|
82
|
+
variant="minimal"
|
|
83
|
+
maxlength="100" />
|
|
80
84
|
<slot name="toolbar" :edit="true"></slot>
|
|
81
85
|
</div>
|
|
82
86
|
|
|
83
|
-
<div class="flex items-center justify-center border-b-[1px] border-text-100">
|
|
87
|
+
<div class="flex items-center justify-center border-b-[1px] border-text-100 px-3">
|
|
84
88
|
<Tabs :items="tabItems[config.type ?? 'list']" v-model="config.presetBarTabIndex" />
|
|
85
89
|
</div>
|
|
86
90
|
|
|
@@ -88,7 +92,7 @@
|
|
|
88
92
|
|
|
89
93
|
<div v-if="config.presetBarTabIndex === 1" class="flex-1 flex flex-col p-5">
|
|
90
94
|
<ListItem :items="presetColumns"
|
|
91
|
-
body-class="divide-y divide-text-50"
|
|
95
|
+
body-class="divide-y divide-text-50 rounded-lg border-[1px] border-text-50 bg-base-300"
|
|
92
96
|
@reorder="(from, to) => { presetColumns.splice(to, 0, presetColumns.splice(from, 1)[0]); }">
|
|
93
97
|
<template v-slot="{ item }">
|
|
94
98
|
<div v-if="!item.key.startsWith('_') || (item.key.startsWith('_') && (preset.pivot && preset.pivot.enabled))" class="flex flex-row items-center gap-3 px-3 p-2 hover:bg-primary-100">
|
|
@@ -136,6 +140,7 @@
|
|
|
136
140
|
<PresetSelectorFilterItem
|
|
137
141
|
class="flex-1"
|
|
138
142
|
:type="typeOf(column(filter.key).type)"
|
|
143
|
+
:typeParams="typeParamsOf(column(filter.key))"
|
|
139
144
|
:value="filterVal"
|
|
140
145
|
@change="apply()" />
|
|
141
146
|
|
|
@@ -169,11 +174,12 @@
|
|
|
169
174
|
|
|
170
175
|
<div v-else-if="config.presetBarTabIndex === 3" class="flex-1 flex flex-col p-5">
|
|
171
176
|
|
|
172
|
-
<ListItem
|
|
177
|
+
<ListItem v-if="Array.isArray(preset.sorts) && preset.sorts.length > 0"
|
|
178
|
+
:items="preset.sorts"
|
|
173
179
|
@reorder="(from, to) => { preset.sorts.splice(to, 0, preset.sorts.splice(from, 1)[0]); apply() }"
|
|
174
|
-
container-class="flex flex-col
|
|
180
|
+
container-class="flex flex-col rounded-lg border-[1px] border-text-50 bg-base-300 divide-y divide-text-50">
|
|
175
181
|
<template v-slot="{ item, index }">
|
|
176
|
-
<div class="flex flex-row items-center gap-3">
|
|
182
|
+
<div class="flex flex-row items-center gap-3 p-1">
|
|
177
183
|
<div data-reorder>
|
|
178
184
|
<svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
|
|
179
185
|
</div>
|
|
@@ -208,13 +214,12 @@
|
|
|
208
214
|
</template>
|
|
209
215
|
</ListItem>
|
|
210
216
|
|
|
211
|
-
<
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
</div>
|
|
217
|
+
<button type="button"
|
|
218
|
+
class="text-primary flex flex-row items-center justify-center p-3"
|
|
219
|
+
@click="addSort({ type:'asc' })">
|
|
220
|
+
<svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
|
|
221
|
+
Add Sort
|
|
222
|
+
</button>
|
|
218
223
|
|
|
219
224
|
</div>
|
|
220
225
|
|
|
@@ -455,19 +460,20 @@
|
|
|
455
460
|
</div>
|
|
456
461
|
<ListItem :items="presetPivot.rows"
|
|
457
462
|
@reorder="(from, to) => { presetPivot.rows.splice(to, 0, presetPivot.rows.splice(from, 1)[0]); apply() }"
|
|
458
|
-
class="mt-2 h-[
|
|
463
|
+
class="mt-2 h-[25vh] overflow-y-auto border-[1px] border-text-200 bg-base-400 rounded-lg p-0.5"
|
|
464
|
+
container-class="divide-y divide-text-50">
|
|
459
465
|
<template v-slot="{ item, index }">
|
|
460
466
|
<div class="flex flex-row items-center gap-3 p-2 bg-base-500 hover:bg-primary-100 first:rounded-t-md last:rounded-b-md">
|
|
461
467
|
<div data-reorder v-if="(presetPivot.rows ?? []).length > 1">
|
|
462
468
|
<svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
|
|
463
469
|
</div>
|
|
464
470
|
<div class="flex-1 flex flex-row gap-3">
|
|
465
|
-
<strong class="flex-1">
|
|
471
|
+
<strong class="flex-1 cursor-pointer" @click="$refs.columnSelector.open(obj => Object.assign(item, obj))">
|
|
466
472
|
{{ item.label ? item.label : item.key }}
|
|
467
473
|
</strong>
|
|
468
474
|
<select v-if="item.type === 'date'"
|
|
469
475
|
v-model="item.aggregrate"
|
|
470
|
-
class="appearance-none bg-
|
|
476
|
+
class="px-1 appearance-none bg-base-300 rounded-md border-[1px] border-text-50 outline-none"
|
|
471
477
|
@change="apply()">
|
|
472
478
|
<option value="date">Date</option>
|
|
473
479
|
<option value="month">Month</option>
|
|
@@ -491,7 +497,7 @@
|
|
|
491
497
|
</div>
|
|
492
498
|
<ListItem :items="presetPivot.values"
|
|
493
499
|
@reorder="(from, to) => { presetPivot.values.splice(to, 0, presetPivot.values.splice(from, 1)[0]); }"
|
|
494
|
-
class="mt-2 h-[
|
|
500
|
+
class="mt-2 h-[25vh] overflow-y-auto border-[1px] border-text-200 bg-base-400 rounded-lg p-0.5">
|
|
495
501
|
<template v-slot="{ item, index }">
|
|
496
502
|
<div class="flex flex-row items-center gap-3 p-2 bg-base-500 hover:bg-primary-100 first:rounded-t-md last:rounded-b-md">
|
|
497
503
|
<div data-reorder v-if="(presetPivot.values ?? []).length > 1">
|
|
@@ -502,7 +508,9 @@
|
|
|
502
508
|
{{ item.label ? item.label : item.key }}
|
|
503
509
|
</strong>
|
|
504
510
|
<select v-model="item.aggregrate"
|
|
505
|
-
class="appearance-none bg-
|
|
511
|
+
class="px-1 appearance-none bg-base-300 rounded-md border-[1px] border-text-50 outline-none"
|
|
512
|
+
@change="apply()">
|
|
513
|
+
<option value="">Default</option>
|
|
506
514
|
<option value="count">Count</option>
|
|
507
515
|
<option v-if="[ 'number' ].includes(item.type)" value="sum">Sum</option>
|
|
508
516
|
<option v-if="[ 'number' ].includes(item.type)" value="avg">Average</option>
|
|
@@ -525,12 +533,18 @@
|
|
|
525
533
|
<div v-else-if="config.presetBarTabIndex === 9" class="flex-1 p-6 flex flex-col gap-6">
|
|
526
534
|
|
|
527
535
|
<div>
|
|
528
|
-
<
|
|
536
|
+
<div class="flex flex-row">
|
|
537
|
+
<strong class="flex-1">Preset</strong>
|
|
538
|
+
<button type="button" class="text-primary" @click="log(preset)">console.log</button>
|
|
539
|
+
</div>
|
|
529
540
|
<ObjectTree :value="preset" />
|
|
530
541
|
</div>
|
|
531
542
|
|
|
532
543
|
<div>
|
|
533
|
-
<
|
|
544
|
+
<div class="flex flex-row">
|
|
545
|
+
<strong class="flex-1">Config</strong>
|
|
546
|
+
<button type="button" class="text-primary" @click="log(config)">console.log</button>
|
|
547
|
+
</div>
|
|
534
548
|
<ObjectTree :value="config" />
|
|
535
549
|
</div>
|
|
536
550
|
|
|
@@ -542,6 +556,7 @@
|
|
|
542
556
|
:columns="config.columns" />
|
|
543
557
|
|
|
544
558
|
</div>
|
|
559
|
+
|
|
545
560
|
</TransitionGroup>
|
|
546
561
|
|
|
547
562
|
</div>
|
|
@@ -561,12 +576,7 @@ export default{
|
|
|
561
576
|
|
|
562
577
|
props: {
|
|
563
578
|
|
|
564
|
-
config: Object
|
|
565
|
-
|
|
566
|
-
editHash: {
|
|
567
|
-
type: String,
|
|
568
|
-
default: '#e1a5f'
|
|
569
|
-
}
|
|
579
|
+
config: Object
|
|
570
580
|
|
|
571
581
|
},
|
|
572
582
|
|
|
@@ -652,18 +662,13 @@ export default{
|
|
|
652
662
|
},
|
|
653
663
|
|
|
654
664
|
select(idx){
|
|
655
|
-
this.config.
|
|
656
|
-
this
|
|
657
|
-
|
|
658
|
-
hash: (this.$route.hash.replace(this.editHash, '')) + this.editHash
|
|
659
|
-
})
|
|
665
|
+
this.config.presetIdx = idx
|
|
666
|
+
this.presetBar.view = 2
|
|
667
|
+
this.apply()
|
|
660
668
|
},
|
|
661
669
|
|
|
662
670
|
deselect(){
|
|
663
|
-
this
|
|
664
|
-
...this.$route,
|
|
665
|
-
hash: this.$route.hash.replace(this.editHash, '')
|
|
666
|
-
})
|
|
671
|
+
this.config.presetBar.view = 1
|
|
667
672
|
},
|
|
668
673
|
|
|
669
674
|
filterColumnAdded(column){
|
|
@@ -677,10 +682,18 @@ export default{
|
|
|
677
682
|
return 'date'
|
|
678
683
|
else if([ 'number' ].includes(type))
|
|
679
684
|
return 'number'
|
|
685
|
+
else if([ 'enum' ].includes(type))
|
|
686
|
+
return 'enum'
|
|
680
687
|
else
|
|
681
688
|
return 'text'
|
|
682
689
|
},
|
|
683
690
|
|
|
691
|
+
typeParamsOf(column){
|
|
692
|
+
if([ 'enum' ].includes(column.type))
|
|
693
|
+
return column.typeParams
|
|
694
|
+
return []
|
|
695
|
+
},
|
|
696
|
+
|
|
684
697
|
},
|
|
685
698
|
|
|
686
699
|
computed: {
|
|
@@ -698,7 +711,7 @@ export default{
|
|
|
698
711
|
},
|
|
699
712
|
|
|
700
713
|
preset(){
|
|
701
|
-
return this.config.presets[this.config.
|
|
714
|
+
return this.config.presets[this.config.presetIdx]
|
|
702
715
|
},
|
|
703
716
|
|
|
704
717
|
presetChart(){
|
|
@@ -742,6 +755,15 @@ export default{
|
|
|
742
755
|
return this.chartOpt.yAxis
|
|
743
756
|
},
|
|
744
757
|
|
|
758
|
+
presetBar(){
|
|
759
|
+
if(!this.config.presetBar || !this.config.presetBar.view)
|
|
760
|
+
this.config.presetBar = {
|
|
761
|
+
view: 2
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return this.config.presetBar
|
|
765
|
+
}
|
|
766
|
+
|
|
745
767
|
},
|
|
746
768
|
|
|
747
769
|
data(){
|