@mixd-id/web-scaffold 0.1.230406394 → 0.1.230406396
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 +5 -5
- package/src/components/Datepicker.vue +26 -7
- package/src/components/HTMLEditor.vue +11 -6
- package/src/components/List.vue +10 -0
- package/src/components/VirtualTable.vue +201 -76
- package/src/themes/default/index.js +6 -0
- package/src/utils/preset-selector.js +10 -0
- package/src/utils/wss.mjs +9 -4
- package/src/widgets/PresetBarPivot.vue +2 -0
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.230406396",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite serve",
|
|
7
7
|
"build": "vite build",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@tailwindcss/line-clamp": "^0.4.0",
|
|
53
53
|
"@vueuse/core": "^9.0.2",
|
|
54
54
|
"adm-zip": "^0.5.10",
|
|
55
|
-
"axios": "
|
|
55
|
+
"axios": "1.8.2",
|
|
56
56
|
"bcrypt": "^5.1.1",
|
|
57
57
|
"chart.js": "^4.2.1",
|
|
58
58
|
"compression": "^1.7.4",
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
"md5": "^2.3.0",
|
|
70
70
|
"mitt": "^3.0.1",
|
|
71
71
|
"nprogress": "^0.2.0",
|
|
72
|
-
"pinia": "^2.
|
|
73
|
-
"prismjs": "
|
|
72
|
+
"pinia": "^2.0.2",
|
|
73
|
+
"prismjs": "1.30.0",
|
|
74
74
|
"redis": "^4.6.13",
|
|
75
75
|
"sequelize": "^6.37.5",
|
|
76
|
-
"serve-static": "
|
|
76
|
+
"serve-static": "2.1.0",
|
|
77
77
|
"tailwindcss": "^3.2.4",
|
|
78
78
|
"vue": "^3.2.25",
|
|
79
79
|
"vue-chartjs": "^5.2.0",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<div v-else :class="compClass">
|
|
19
19
|
|
|
20
20
|
<div v-if="mode === 'popup'" ref="popup"
|
|
21
|
-
@click="!readonly ? contextMenu = { caller:$refs.popup, value:this.
|
|
21
|
+
@click="!readonly ? contextMenu = { caller:$refs.popup, value:this.mModelValue } : null"
|
|
22
22
|
class="flex-1">
|
|
23
23
|
<input class="flex-1" type="text" readonly :value="DMMMYYYY"/>
|
|
24
24
|
<div :class="$style.arrow" v-if="!readonly">
|
|
@@ -70,11 +70,14 @@
|
|
|
70
70
|
<div class="grid grid-cols-7 gap-2 mt-2">
|
|
71
71
|
<div v-for="i in 7">{{ getDayOfWeekLabel(i) }}</div>
|
|
72
72
|
<button type="button" :class="buttonStyle(d.value)"
|
|
73
|
-
:disabled="allowedDates && !allowedDates.includes(d.value)"
|
|
73
|
+
:disabled="(allowedDates && !allowedDates.includes(d.value)) || (onlyTodayAndFuture && isValidTodayAndFuture(d.value))"
|
|
74
74
|
v-for="d in contextMenuDates" @click="setValue(d.value)">
|
|
75
75
|
{{ d.date }}
|
|
76
76
|
</button>
|
|
77
77
|
</div>
|
|
78
|
+
<div class="mt-6">
|
|
79
|
+
<button type="button" class="text-primary" @click="this.$emit('update:modelValue', 'today')">Today</button>
|
|
80
|
+
</div>
|
|
78
81
|
</div>
|
|
79
82
|
</ContextMenu>
|
|
80
83
|
|
|
@@ -103,9 +106,9 @@ export default{
|
|
|
103
106
|
|
|
104
107
|
setup(props, { emit }){
|
|
105
108
|
|
|
106
|
-
const DD = ref(dayjs(props.
|
|
107
|
-
const MM = ref(dayjs(props.
|
|
108
|
-
const YYYY = ref(dayjs(props.
|
|
109
|
+
const DD = ref(dayjs(props.mModelValue ?? props.defaultValue).format('DD'))
|
|
110
|
+
const MM = ref(dayjs(props.mModelValue ?? props.defaultValue).format('MM'))
|
|
111
|
+
const YYYY = ref(dayjs(props.mModelValue ?? props.defaultValue).format('YYYY'))
|
|
109
112
|
|
|
110
113
|
watch([ YYYY, MM, DD ], (to) => {
|
|
111
114
|
emit('update:modelValue', to.join('-'))
|
|
@@ -143,18 +146,30 @@ export default{
|
|
|
143
146
|
|
|
144
147
|
allowedDates: Array,
|
|
145
148
|
|
|
149
|
+
onlyTodayAndFuture: Boolean
|
|
150
|
+
|
|
146
151
|
},
|
|
147
152
|
|
|
148
153
|
computed:{
|
|
149
154
|
|
|
155
|
+
mModelValue(){
|
|
156
|
+
if(this.modelValue === 'today')
|
|
157
|
+
return dayjs().format('YYYY-MM-DD')
|
|
158
|
+
return this.modelValue
|
|
159
|
+
},
|
|
160
|
+
|
|
150
161
|
DMMMYYYY(){
|
|
151
|
-
return this.
|
|
162
|
+
return this.mModelValue ? dayjs(this.mModelValue).format('D MMM YYYY') : ''
|
|
152
163
|
},
|
|
153
164
|
|
|
154
165
|
YYYYMMM(){
|
|
155
166
|
return dayjs(this.contextMenu.value).format('YYYY-MM')
|
|
156
167
|
},
|
|
157
168
|
|
|
169
|
+
today(){
|
|
170
|
+
return dayjs().format('YYYY-MM-DD')
|
|
171
|
+
},
|
|
172
|
+
|
|
158
173
|
compClass(){
|
|
159
174
|
return [
|
|
160
175
|
this.$style.datepicker,
|
|
@@ -235,7 +250,7 @@ export default{
|
|
|
235
250
|
buttonStyle(val){
|
|
236
251
|
return [
|
|
237
252
|
this.$style.button,
|
|
238
|
-
this.
|
|
253
|
+
this.mModelValue === val ? this.$style.selected : '',
|
|
239
254
|
dayjs(val).format('YYYY-MM') !== this.YYYYMMM ? this.$style.otherMonth : ''
|
|
240
255
|
]
|
|
241
256
|
.join(' ')
|
|
@@ -255,6 +270,10 @@ export default{
|
|
|
255
270
|
|
|
256
271
|
},
|
|
257
272
|
|
|
273
|
+
isValidTodayAndFuture(val){
|
|
274
|
+
return val.localeCompare(this.today) < 0
|
|
275
|
+
},
|
|
276
|
+
|
|
258
277
|
setValue(d){
|
|
259
278
|
this.$emit('update:modelValue', d)
|
|
260
279
|
this.$emit('change', d)
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<button class="p-3" type="button" ref="listBtn" @click="$refs.listContext.open($refs.listBtn)">
|
|
22
22
|
<svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M64 144c26.5 0 48-21.5 48-48s-21.5-48-48-48S16 69.5 16 96s21.5 48 48 48zM192 64c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zM64 464c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm48-208c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48z"/></svg>
|
|
23
23
|
</button>
|
|
24
|
-
<button v-if="
|
|
24
|
+
<button v-if="canUpload" class="p-3" type="button" @click="createImage">
|
|
25
25
|
<svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M152 120c-26.51 0-48 21.49-48 48s21.49 48 48 48s48-21.49 48-48S178.5 120 152 120zM447.1 32h-384C28.65 32-.0091 60.65-.0091 96v320c0 35.35 28.65 64 63.1 64h384c35.35 0 64-28.65 64-64V96C511.1 60.65 483.3 32 447.1 32zM463.1 409.3l-136.8-185.9C323.8 218.8 318.1 216 312 216c-6.113 0-11.82 2.768-15.21 7.379l-106.6 144.1l-37.09-46.1c-3.441-4.279-8.934-6.809-14.77-6.809c-5.842 0-11.33 2.529-14.78 6.809l-75.52 93.81c0-.0293 0 .0293 0 0L47.99 96c0-8.822 7.178-16 16-16h384c8.822 0 16 7.178 16 16V409.3z"/></svg>
|
|
26
26
|
</button>
|
|
27
27
|
<button class="p-3" type="button" @click="createLink">
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
</div>
|
|
36
36
|
|
|
37
37
|
<article ref="article" contenteditable="true" v-html="html" @paste="onPaste" spellcheck="false"
|
|
38
|
-
@input="onInput" @click="onClick" :class="
|
|
38
|
+
@input="onInput" @click="onClick" :class="itemClass"
|
|
39
39
|
@blur="onBlur">
|
|
40
40
|
</article>
|
|
41
41
|
|
|
@@ -137,10 +137,10 @@
|
|
|
137
137
|
</template>
|
|
138
138
|
<div class="flex-1 flex flex-col gap-5 p-5">
|
|
139
139
|
<div>
|
|
140
|
-
<Image class="min-h-[100px] bg-
|
|
140
|
+
<Image class="min-h-[100px] bg-text-50 cursor-pointer rounded-xl" ref="image" :src="newImage.src" editable="true"
|
|
141
141
|
@click="$refs.image.edit()" @change="onImageChanged">
|
|
142
142
|
<template #empty="{ instance }">
|
|
143
|
-
<div class="absolute right-0 top-0 bottom-0 left-0 flex items-center justify-center">
|
|
143
|
+
<div class="absolute text-primary underline right-0 top-0 bottom-0 left-0 flex items-center justify-center">
|
|
144
144
|
Click to Add Image...
|
|
145
145
|
</div>
|
|
146
146
|
</template>
|
|
@@ -259,12 +259,17 @@ export default{
|
|
|
259
259
|
props:{
|
|
260
260
|
modelValue: String,
|
|
261
261
|
uploadImage: String,
|
|
262
|
+
uploadImageFn: String,
|
|
262
263
|
containerClass: String,
|
|
263
264
|
itemClass: String,
|
|
264
265
|
},
|
|
265
266
|
|
|
266
267
|
computed: {
|
|
267
268
|
|
|
269
|
+
canUpload(){
|
|
270
|
+
return `${this.uploadImage ?? ''}`.length > 0 || typeof this.uploadImageFn === 'function'
|
|
271
|
+
},
|
|
272
|
+
|
|
268
273
|
linkCanSave(){
|
|
269
274
|
return this.newLink.href && this.newLink.text
|
|
270
275
|
},
|
|
@@ -310,8 +315,8 @@ export default{
|
|
|
310
315
|
}
|
|
311
316
|
else{
|
|
312
317
|
this.newImage = {
|
|
313
|
-
width: "",
|
|
314
|
-
height: "",
|
|
318
|
+
width: "120px",
|
|
319
|
+
height: "120px",
|
|
315
320
|
element: null
|
|
316
321
|
}
|
|
317
322
|
}
|
package/src/components/List.vue
CHANGED
|
@@ -799,6 +799,12 @@ export default{
|
|
|
799
799
|
}
|
|
800
800
|
},
|
|
801
801
|
|
|
802
|
+
onConnect(reconnect){
|
|
803
|
+
if(reconnect){
|
|
804
|
+
this.load()
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
|
|
802
808
|
onKeyDown(e){
|
|
803
809
|
|
|
804
810
|
if(e.altKey){
|
|
@@ -850,6 +856,8 @@ export default{
|
|
|
850
856
|
pop: this.loadQueued,
|
|
851
857
|
delay: this.updateInterval
|
|
852
858
|
})
|
|
859
|
+
|
|
860
|
+
this.socket.on('connect', this.onConnect)
|
|
853
861
|
},
|
|
854
862
|
|
|
855
863
|
unmounted() {
|
|
@@ -860,6 +868,8 @@ export default{
|
|
|
860
868
|
this.socket.send('user.unsubscribe', {name: this.subscribeKey})
|
|
861
869
|
this.socket.off(this.subscribeKey, this.onSignal)
|
|
862
870
|
}
|
|
871
|
+
|
|
872
|
+
this.socket.off('connect', this.onConnect)
|
|
863
873
|
},
|
|
864
874
|
|
|
865
875
|
computed: {
|
|
@@ -2,86 +2,148 @@
|
|
|
2
2
|
<div :class="$style.comp">
|
|
3
3
|
|
|
4
4
|
<div :class="$style.header" v-if="visibleColumns.length > 0">
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
<table :class="$style.table">
|
|
6
|
+
<thead>
|
|
7
|
+
<tr>
|
|
8
|
+
<th v-for="column in freezeLeftColumns" :style="thStyle(column)" :class="thClass(column)"
|
|
9
|
+
v-tooltip="column.tooltip">
|
|
10
|
+
<slot v-if="$slots['col-' + column.key]" :name="'col-' + column.key" :column="column"></slot>
|
|
11
|
+
<div v-else :class="headerColumnClass(column)">
|
|
12
|
+
<span>{{ column.label2 ? column.label2 : (column.label ?? column.key) }}</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div :class="$style.separator" @mousedown="startResize($event, column)"></div>
|
|
15
|
+
</th>
|
|
16
|
+
<th :class="$style.spacer"></th>
|
|
17
|
+
</tr>
|
|
18
|
+
</thead>
|
|
19
|
+
</table>
|
|
20
|
+
<div class="overflow-hidden">
|
|
21
|
+
<table :class="$style.table" ref="tableHead" :style="tableHeadStyle">
|
|
22
|
+
<thead>
|
|
23
|
+
<tr>
|
|
24
|
+
<th v-for="column in unfreezedColumns" :style="thStyle(column)" :class="thClass(column)"
|
|
25
|
+
v-tooltip="column.tooltip">
|
|
26
|
+
<slot v-if="$slots['col-' + column.key]" :name="'col-' + column.key" :column="column"></slot>
|
|
27
|
+
<div v-else :class="headerColumnClass(column)">
|
|
28
|
+
<span>{{ column.label2 ? column.label2 : (column.label ?? column.key) }}</span>
|
|
29
|
+
</div>
|
|
30
|
+
<div :class="$style.separator" @mousedown="startResize($event, column)"></div>
|
|
31
|
+
</th>
|
|
32
|
+
<th :class="$style.spacer"></th>
|
|
33
|
+
</tr>
|
|
34
|
+
</thead>
|
|
35
|
+
</table>
|
|
36
|
+
</div>
|
|
20
37
|
</div>
|
|
21
38
|
|
|
22
|
-
<div ref="cont">
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<tbody ref="tbody">
|
|
39
|
+
<div ref="cont" class="flex-1 overflow-y-auto">
|
|
40
|
+
<div class="flex-1 flex flex-row">
|
|
41
|
+
|
|
42
|
+
<div :style="freezeLeftScrollerStyle">
|
|
43
|
+
<div :class="$style.spacer" :style="spacerStyle">
|
|
44
|
+
<table :class="$style.table" class="flex-1 overflow-x-auto">
|
|
45
|
+
<thead>
|
|
46
|
+
<tr>
|
|
47
|
+
<th v-for="column in freezeLeftColumns" :style="thStyle(column)"></th>
|
|
48
|
+
<th :class="$style.spacer"></th>
|
|
49
|
+
</tr>
|
|
50
|
+
</thead>
|
|
51
|
+
<tbody ref="tbody">
|
|
36
52
|
<tr v-for="(item, index) in visibleItems" :key="item"
|
|
37
53
|
@click="select(item, index)"
|
|
38
54
|
:class="trClass(item, index)">
|
|
39
|
-
<td v-for="(column, columnIndex) in
|
|
55
|
+
<td v-for="(column, columnIndex) in freezeLeftColumns"
|
|
40
56
|
:class="tdClass(item, column)"
|
|
41
57
|
@click="$emit('item-click', item, column)">
|
|
42
58
|
|
|
43
59
|
<div v-if="columnIndex === 0 && item._type === 'totalRow'" :class="$style.total">Total</div>
|
|
44
60
|
<div v-else-if="item._type === 'totalRow' && !column.key.startsWith('_')"></div>
|
|
45
61
|
|
|
46
|
-
<slot v-else-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"
|
|
47
|
-
|
|
62
|
+
<slot v-else-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index">
|
|
63
|
+
<div :class="columnClass(column, item)"> </div>
|
|
64
|
+
</slot>
|
|
65
|
+
<slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index">
|
|
66
|
+
<div :class="columnClass(column, item)"> </div>
|
|
67
|
+
</slot>
|
|
48
68
|
<div v-else :class="columnClass(column, item)" v-html="formatColumn(item, column)"></div>
|
|
49
69
|
|
|
50
70
|
</td>
|
|
51
71
|
<td :class="$style.spacer"></td>
|
|
52
72
|
</tr>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
73
|
+
</tbody>
|
|
74
|
+
</table>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div ref="xcont" class="flex-1 overflow-x-auto overflow-y-hidden">
|
|
79
|
+
<div v-if="state === 3" ref="spinner" class="flex-1 flex items-center justify-center p-8">
|
|
80
|
+
<svg class="animate-spin aspect-square w-[32px] h-[32px] text-primary" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
|
|
81
|
+
</div>
|
|
82
|
+
<div ref="scroller" v-else-if="visibleColumns.length > 0" :class="$style.scroller" :style="scrollerStyle">
|
|
83
|
+
<div :class="$style.spacer" ref="spacer" :style="spacerStyle">
|
|
84
|
+
|
|
85
|
+
<table :class="$style.table" class="flex-1 overflow-x-auto">
|
|
86
|
+
<thead>
|
|
87
|
+
<tr>
|
|
88
|
+
<th v-for="column in unfreezedColumns" :style="thStyle(column)"></th>
|
|
89
|
+
<th :class="$style.spacer"></th>
|
|
90
|
+
</tr>
|
|
91
|
+
</thead>
|
|
92
|
+
<tbody ref="tbody">
|
|
93
|
+
<tr v-for="(item, index) in visibleItems" :key="item"
|
|
94
|
+
@click="select(item, index)"
|
|
95
|
+
:class="trClass(item, index)">
|
|
96
|
+
<td v-for="(column, columnIndex) in unfreezedColumns"
|
|
97
|
+
:class="tdClass(item, column)"
|
|
98
|
+
@click="$emit('item-click', item, column)">
|
|
99
|
+
|
|
100
|
+
<div v-if="columnIndex === 0 && item._type === 'totalRow'" :class="$style.total">Total</div>
|
|
101
|
+
<div v-else-if="item._type === 'totalRow' && !column.key.startsWith('_')"></div>
|
|
102
|
+
|
|
103
|
+
<slot v-else-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index">
|
|
104
|
+
<div :class="columnClass(column, item)"> </div>
|
|
105
|
+
</slot>
|
|
106
|
+
<slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index">
|
|
107
|
+
<div :class="columnClass(column, item)"> </div>
|
|
108
|
+
</slot>
|
|
109
|
+
<div v-else :class="columnClass(column, item)" v-html="formatColumn(item, column)"></div>
|
|
110
|
+
|
|
111
|
+
</td>
|
|
112
|
+
<td :class="$style.spacer"></td>
|
|
113
|
+
</tr>
|
|
114
|
+
</tbody>
|
|
115
|
+
</table>
|
|
116
|
+
<div v-if="state === 2" ref="spinner" class="h-[44px] relative">
|
|
117
|
+
<div class="absolute top-0 left-0 w-screen h-[44px] flex items-center justify-center">
|
|
118
|
+
<svg class="animate-spin aspect-square w-[16px] h-[16px] text-primary" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
<div v-else-if="visibleColumns.length <= 0 && Array.isArray(columns)" class="text-center p-3 flex-1 min-h-[100%] flex items-center justify-center">
|
|
124
|
+
<h5 class="text-text-300">No active column</h5>
|
|
125
|
+
</div>
|
|
126
|
+
<div v-else-if="Array.isArray(cItems) && cItems.length <= 0" class="text-center p-3 flex-1 min-h-[100%] flex items-center justify-center">
|
|
127
|
+
<h5 class="text-text-300">No data available</h5>
|
|
128
|
+
</div>
|
|
129
|
+
<slot name="end"></slot>
|
|
130
|
+
<div :class="$style.calc" v-if="visibleColumns.length > 0 && cItems && cItems.length > 0" ref="calc">
|
|
131
|
+
<table :class="$style.table">
|
|
132
|
+
<tbody>
|
|
133
|
+
<tr>
|
|
134
|
+
<td v-for="column in cColumns" :style="thStyle(column)" :class="thClass(column)">
|
|
135
|
+
<slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="cItems[0]"></slot>
|
|
136
|
+
<slot v-else-if="$slots.default" name="default" :column="column" :item="cItems[0]"></slot>
|
|
137
|
+
<div v-else :class="columnClass(column)" v-html="formatColumn(cItems[0] ?? {}, column)"></div>
|
|
138
|
+
</td>
|
|
139
|
+
<td :class="$style.spacer"></td>
|
|
140
|
+
</tr>
|
|
141
|
+
</tbody>
|
|
142
|
+
</table>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
</div>
|
|
85
147
|
</div>
|
|
86
148
|
</div>
|
|
87
149
|
</template>
|
|
@@ -101,10 +163,15 @@ export default{
|
|
|
101
163
|
emits: [ 'scroll-end', 'item-click' ],
|
|
102
164
|
|
|
103
165
|
props:{
|
|
166
|
+
|
|
104
167
|
columns: Array,
|
|
105
168
|
|
|
106
169
|
enumCache: Object,
|
|
107
170
|
|
|
171
|
+
freezeLeft: Number,
|
|
172
|
+
|
|
173
|
+
freezeRight: Number,
|
|
174
|
+
|
|
108
175
|
itemClass: String,
|
|
109
176
|
|
|
110
177
|
uid: String,
|
|
@@ -130,6 +197,7 @@ export default{
|
|
|
130
197
|
type: String,
|
|
131
198
|
|
|
132
199
|
value: Object,
|
|
200
|
+
|
|
133
201
|
},
|
|
134
202
|
|
|
135
203
|
data(){
|
|
@@ -152,6 +220,11 @@ export default{
|
|
|
152
220
|
return this.datasourceColumns ?? this.columns
|
|
153
221
|
},
|
|
154
222
|
|
|
223
|
+
xContStyle(){
|
|
224
|
+
return {
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
|
|
155
228
|
cItems(){
|
|
156
229
|
return this.value?.items ?? this.items
|
|
157
230
|
},
|
|
@@ -197,7 +270,7 @@ export default{
|
|
|
197
270
|
return {}
|
|
198
271
|
|
|
199
272
|
const height = (this.cItems.length * this.itemHeight) + (this.state === 2 ? 48 : 0)
|
|
200
|
-
const width = this.
|
|
273
|
+
const width = this.unfreezedColumns.reduce((r, item) => r + parseInt(item.width ?? _DEFAULT_COLUMN_WIDTH), 0)
|
|
201
274
|
|
|
202
275
|
return {
|
|
203
276
|
height: height + 'px',
|
|
@@ -205,18 +278,57 @@ export default{
|
|
|
205
278
|
}
|
|
206
279
|
},
|
|
207
280
|
|
|
281
|
+
freezeLeftScrollerStyle(){
|
|
282
|
+
if(!this.cItems || this.cItems.length < 1)
|
|
283
|
+
return {}
|
|
284
|
+
|
|
285
|
+
const height = (this.cItems.length * this.itemHeight) + (this.state === 2 ? 48 : 0)
|
|
286
|
+
const width = this.freezeLeftColumns.reduce((r, item) => r + parseInt(item.width ?? _DEFAULT_COLUMN_WIDTH), 0)
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
height: height + 'px',
|
|
290
|
+
width: width + 'px'
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
|
|
208
294
|
tableHeadStyle(){
|
|
209
295
|
return {
|
|
210
296
|
transform: "translate3d(" + (this.scrollLeft * -1) + "px, 0, 0)"
|
|
211
297
|
}
|
|
212
298
|
},
|
|
213
299
|
|
|
300
|
+
freezeLeftColumns(){
|
|
301
|
+
const columns = []
|
|
302
|
+
for(let i = 0 ; i < (this.freezeLeft ?? 0) && i < this.visibleColumns.length ; i++){
|
|
303
|
+
const column = this.visibleColumns[i]
|
|
304
|
+
if(!('visible' in column) || column.visible){
|
|
305
|
+
columns.push(column)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return columns
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
unfreezedColumns(){
|
|
312
|
+
const columns = []
|
|
313
|
+
for(let i = this.freezeLeft ?? 0 ; i < this.visibleColumns.length ; i++){
|
|
314
|
+
const column = this.visibleColumns[i]
|
|
315
|
+
if(!('visible' in column) || column.visible){
|
|
316
|
+
columns.push(column)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return columns
|
|
320
|
+
},
|
|
321
|
+
|
|
214
322
|
visibleColumns(){
|
|
215
|
-
|
|
216
|
-
|
|
323
|
+
const columns = []
|
|
324
|
+
for(let i = 0 ; i < this.cColumns.length ; i++){
|
|
325
|
+
const column = this.cColumns[i]
|
|
217
326
|
|
|
218
|
-
|
|
219
|
-
|
|
327
|
+
if(!('visible' in column) || column.visible){
|
|
328
|
+
columns.push(column)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return columns
|
|
220
332
|
},
|
|
221
333
|
|
|
222
334
|
},
|
|
@@ -229,6 +341,14 @@ export default{
|
|
|
229
341
|
this.passiveScrollSupported() ? { passive: true } : false
|
|
230
342
|
)
|
|
231
343
|
|
|
344
|
+
this.$refs.xcont.addEventListener(
|
|
345
|
+
"scroll",
|
|
346
|
+
this.handleScrollX,
|
|
347
|
+
this.passiveScrollSupported() ? { passive: true } : false
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
this.$refs.xcont.style.minHeight = `${this.$refs.cont.clientHeight}px`
|
|
351
|
+
|
|
232
352
|
this.resize()
|
|
233
353
|
|
|
234
354
|
const observer = new MutationObserver((mutationsList) => {
|
|
@@ -302,11 +422,18 @@ export default{
|
|
|
302
422
|
|
|
303
423
|
},
|
|
304
424
|
|
|
425
|
+
handleScrollX: throttle(function(){
|
|
426
|
+
if(!this.$refs.scroller || !this.$refs.xcont) return
|
|
427
|
+
|
|
428
|
+
this.scrollLeft = this.$refs.xcont.scrollLeft
|
|
429
|
+
|
|
430
|
+
this.scrolled = this.scrollTop > 0
|
|
431
|
+
}, 16),
|
|
432
|
+
|
|
305
433
|
handleScroll: throttle(function(){
|
|
306
434
|
if(!this.$refs.scroller || !this.$refs.cont) return
|
|
307
435
|
|
|
308
436
|
this.scrollTop = this.$refs.cont.scrollTop
|
|
309
|
-
this.scrollLeft = this.$refs.cont.scrollLeft
|
|
310
437
|
|
|
311
438
|
if(this.scrollTop > this.$refs.scroller.offsetHeight - this.$refs.cont.clientHeight - this.itemHeight){
|
|
312
439
|
if(!this.isOnEndScroll){
|
|
@@ -625,7 +752,7 @@ export default{
|
|
|
625
752
|
id: (this.items ?? [])[this.visibleStartIndex]?.id,
|
|
626
753
|
index: this.visibleStartIndex
|
|
627
754
|
}
|
|
628
|
-
}
|
|
755
|
+
}
|
|
629
756
|
|
|
630
757
|
}
|
|
631
758
|
|
|
@@ -641,11 +768,12 @@ export default{
|
|
|
641
768
|
}
|
|
642
769
|
|
|
643
770
|
.comp>*:last-child{
|
|
644
|
-
@apply flex-1
|
|
771
|
+
@apply flex-1 relative max-h-[100vh];
|
|
645
772
|
}
|
|
646
773
|
|
|
647
774
|
.header{
|
|
648
775
|
@apply border-b-[1px] border-text-50;
|
|
776
|
+
@apply flex flex-row;
|
|
649
777
|
}
|
|
650
778
|
|
|
651
779
|
.headerCol{
|
|
@@ -660,6 +788,7 @@ export default{
|
|
|
660
788
|
}
|
|
661
789
|
|
|
662
790
|
.spacer{
|
|
791
|
+
@apply w-full flex flex-row;
|
|
663
792
|
will-change: auto;
|
|
664
793
|
position: relative;
|
|
665
794
|
height: 0;
|
|
@@ -711,10 +840,6 @@ export default{
|
|
|
711
840
|
@apply border-text-50;
|
|
712
841
|
}
|
|
713
842
|
|
|
714
|
-
.spacer{
|
|
715
|
-
@apply w-full
|
|
716
|
-
}
|
|
717
|
-
|
|
718
843
|
.align-left{ @apply text-left; }
|
|
719
844
|
.align-center{ @apply text-center; }
|
|
720
845
|
.align-right{ @apply text-right; }
|
|
@@ -276,6 +276,12 @@ const plugin = Plugin(function({ addBase, addUtilities, config, theme }) {
|
|
|
276
276
|
'background-color': 'var(--panel-500)',
|
|
277
277
|
},
|
|
278
278
|
|
|
279
|
+
'.text-ellipsis-nowrap': {
|
|
280
|
+
'white-space': 'nowrap',
|
|
281
|
+
'overflow': 'hidden',
|
|
282
|
+
'text-overflow': 'ellipsis',
|
|
283
|
+
},
|
|
284
|
+
|
|
279
285
|
})
|
|
280
286
|
|
|
281
287
|
}, {
|
|
@@ -1085,6 +1085,14 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
|
1085
1085
|
sortKeyToOrders[`_${value.key}-${value.aggregrate}`] = literal(`COUNT(DISTINCT ${model.name}.${valueKey})`)
|
|
1086
1086
|
break
|
|
1087
1087
|
|
|
1088
|
+
case 'first':
|
|
1089
|
+
attributes.push([ literal(`(SELECT ${valueKey} FROM ${model.tableName} WHERE id = MIN(${model.name}.id))`), `_${value.key}-${value.aggregrate}` ])
|
|
1090
|
+
break
|
|
1091
|
+
|
|
1092
|
+
case 'last':
|
|
1093
|
+
attributes.push([ literal(`(SELECT ${valueKey} FROM ${model.tableName} WHERE id = MAX(${model.name}.id))`), `_${value.key}-${value.aggregrate}` ])
|
|
1094
|
+
break
|
|
1095
|
+
|
|
1088
1096
|
case 'count':
|
|
1089
1097
|
attributes.push([ fn('COUNT', literal(`${model.name}.${valueKey}`)), `_${value.key}-${value.aggregrate}` ])
|
|
1090
1098
|
sortKeyToOrders[`_${value.key}-${value.aggregrate}`] = fn('COUNT', literal(`${model.name}.${valueKey}`))
|
|
@@ -1254,6 +1262,8 @@ const filtersToSequelizeInclude = async(filters, opt, includes) => {
|
|
|
1254
1262
|
include.where = { [Op.and]: [] }
|
|
1255
1263
|
}
|
|
1256
1264
|
include.where[Op.and].push({ [currentKey]: whereObj[key] })
|
|
1265
|
+
|
|
1266
|
+
include.required = true
|
|
1257
1267
|
}
|
|
1258
1268
|
|
|
1259
1269
|
//console.log('includes', util.inspect(includes, false, null, true /* enable colors */))
|
package/src/utils/wss.mjs
CHANGED
|
@@ -46,6 +46,7 @@ class WSS extends EventEmitter2{
|
|
|
46
46
|
_callbacks = {}
|
|
47
47
|
_pendingSend = []
|
|
48
48
|
_pinging = false
|
|
49
|
+
_retryCount = 1
|
|
49
50
|
|
|
50
51
|
constructor(opt) {
|
|
51
52
|
super();
|
|
@@ -143,15 +144,15 @@ class WSS extends EventEmitter2{
|
|
|
143
144
|
|
|
144
145
|
};
|
|
145
146
|
|
|
146
|
-
this._instance.
|
|
147
|
-
|
|
147
|
+
this._instance.onopen = () => {
|
|
148
|
+
this._retryCount = 0
|
|
149
|
+
}
|
|
148
150
|
|
|
151
|
+
this._instance.onerror = (e) => {
|
|
149
152
|
this.emit('error', e, [])
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
this._instance.onclose = (e) => {
|
|
153
|
-
console.log('onclose', e)
|
|
154
|
-
|
|
155
156
|
switch(e.code){
|
|
156
157
|
|
|
157
158
|
case 1002:
|
|
@@ -163,9 +164,13 @@ class WSS extends EventEmitter2{
|
|
|
163
164
|
|
|
164
165
|
default:
|
|
165
166
|
this.emit('disconnect', null, [])
|
|
167
|
+
|
|
168
|
+
const retryAfter = (num => num > 60000 ? 60000 : num)(import.meta.env.DEV ? 1000 : Math.pow(1.2, ++this._retryCount) * 1000)
|
|
169
|
+
window.setTimeout(() => this.connect(true), retryAfter)
|
|
166
170
|
break
|
|
167
171
|
}
|
|
168
172
|
};
|
|
173
|
+
|
|
169
174
|
}
|
|
170
175
|
|
|
171
176
|
async close(){
|
|
@@ -76,6 +76,8 @@
|
|
|
76
76
|
<option value="">Default</option>
|
|
77
77
|
<option value="count">Count</option>
|
|
78
78
|
<option value="countDistinct">Distinct Count</option>
|
|
79
|
+
<option value="first">First</option>
|
|
80
|
+
<option value="last">Last</option>
|
|
79
81
|
<option v-if="[ 'number', 'currency' ].includes(item.type)" value="sum">Sum</option>
|
|
80
82
|
<option v-if="[ 'number', 'currency' ].includes(item.type)" value="avg">Average</option>
|
|
81
83
|
<option v-if="[ 'number', 'currency' ].includes(item.type)" value="min">Min</option>
|