@mixd-id/web-scaffold 0.1.230406394 → 0.1.230406395
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 +191 -74
- 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.230406395",
|
|
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,41 +2,57 @@
|
|
|
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
|
|
|
@@ -50,38 +66,76 @@
|
|
|
50
66
|
</td>
|
|
51
67
|
<td :class="$style.spacer"></td>
|
|
52
68
|
</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
|
-
|
|
69
|
+
</tbody>
|
|
70
|
+
</table>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div ref="xcont" class="flex-1 overflow-x-auto overflow-y-hidden">
|
|
75
|
+
<div v-if="state === 3" ref="spinner" class="flex-1 flex items-center justify-center p-8">
|
|
76
|
+
<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>
|
|
77
|
+
</div>
|
|
78
|
+
<div ref="scroller" v-else-if="visibleColumns.length > 0" :class="$style.scroller" :style="scrollerStyle">
|
|
79
|
+
<div :class="$style.spacer" ref="spacer" :style="spacerStyle">
|
|
80
|
+
|
|
81
|
+
<table :class="$style.table" class="flex-1 overflow-x-auto">
|
|
82
|
+
<thead>
|
|
83
|
+
<tr>
|
|
84
|
+
<th v-for="column in unfreezedColumns" :style="thStyle(column)"></th>
|
|
85
|
+
<th :class="$style.spacer"></th>
|
|
86
|
+
</tr>
|
|
87
|
+
</thead>
|
|
88
|
+
<tbody ref="tbody">
|
|
89
|
+
<tr v-for="(item, index) in visibleItems" :key="item"
|
|
90
|
+
@click="select(item, index)"
|
|
91
|
+
:class="trClass(item, index)">
|
|
92
|
+
<td v-for="(column, columnIndex) in unfreezedColumns"
|
|
93
|
+
:class="tdClass(item, column)"
|
|
94
|
+
@click="$emit('item-click', item, column)">
|
|
95
|
+
|
|
96
|
+
<div v-if="columnIndex === 0 && item._type === 'totalRow'" :class="$style.total">Total</div>
|
|
97
|
+
<div v-else-if="item._type === 'totalRow' && !column.key.startsWith('_')"></div>
|
|
98
|
+
|
|
99
|
+
<slot v-else-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
|
|
100
|
+
<slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
|
|
101
|
+
<div v-else :class="columnClass(column, item)" v-html="formatColumn(item, column)"></div>
|
|
102
|
+
|
|
103
|
+
</td>
|
|
104
|
+
<td :class="$style.spacer"></td>
|
|
105
|
+
</tr>
|
|
106
|
+
</tbody>
|
|
107
|
+
</table>
|
|
108
|
+
<div v-if="state === 2" ref="spinner" class="h-[44px] relative">
|
|
109
|
+
<div class="absolute top-0 left-0 w-screen h-[44px] flex items-center justify-center">
|
|
110
|
+
<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>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<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">
|
|
116
|
+
<h5 class="text-text-300">No active column</h5>
|
|
117
|
+
</div>
|
|
118
|
+
<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">
|
|
119
|
+
<h5 class="text-text-300">No data available</h5>
|
|
120
|
+
</div>
|
|
121
|
+
<slot name="end"></slot>
|
|
122
|
+
<div :class="$style.calc" v-if="visibleColumns.length > 0 && cItems && cItems.length > 0" ref="calc">
|
|
123
|
+
<table :class="$style.table">
|
|
124
|
+
<tbody>
|
|
125
|
+
<tr>
|
|
126
|
+
<td v-for="column in cColumns" :style="thStyle(column)" :class="thClass(column)">
|
|
127
|
+
<slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="cItems[0]"></slot>
|
|
128
|
+
<slot v-else-if="$slots.default" name="default" :column="column" :item="cItems[0]"></slot>
|
|
129
|
+
<div v-else :class="columnClass(column)" v-html="formatColumn(cItems[0] ?? {}, column)"></div>
|
|
130
|
+
</td>
|
|
131
|
+
<td :class="$style.spacer"></td>
|
|
132
|
+
</tr>
|
|
133
|
+
</tbody>
|
|
134
|
+
</table>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
</div>
|
|
85
139
|
</div>
|
|
86
140
|
</div>
|
|
87
141
|
</template>
|
|
@@ -101,10 +155,15 @@ export default{
|
|
|
101
155
|
emits: [ 'scroll-end', 'item-click' ],
|
|
102
156
|
|
|
103
157
|
props:{
|
|
158
|
+
|
|
104
159
|
columns: Array,
|
|
105
160
|
|
|
106
161
|
enumCache: Object,
|
|
107
162
|
|
|
163
|
+
freezeLeft: Number,
|
|
164
|
+
|
|
165
|
+
freezeRight: Number,
|
|
166
|
+
|
|
108
167
|
itemClass: String,
|
|
109
168
|
|
|
110
169
|
uid: String,
|
|
@@ -130,6 +189,7 @@ export default{
|
|
|
130
189
|
type: String,
|
|
131
190
|
|
|
132
191
|
value: Object,
|
|
192
|
+
|
|
133
193
|
},
|
|
134
194
|
|
|
135
195
|
data(){
|
|
@@ -152,6 +212,11 @@ export default{
|
|
|
152
212
|
return this.datasourceColumns ?? this.columns
|
|
153
213
|
},
|
|
154
214
|
|
|
215
|
+
xContStyle(){
|
|
216
|
+
return {
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
|
|
155
220
|
cItems(){
|
|
156
221
|
return this.value?.items ?? this.items
|
|
157
222
|
},
|
|
@@ -197,7 +262,7 @@ export default{
|
|
|
197
262
|
return {}
|
|
198
263
|
|
|
199
264
|
const height = (this.cItems.length * this.itemHeight) + (this.state === 2 ? 48 : 0)
|
|
200
|
-
const width = this.
|
|
265
|
+
const width = this.unfreezedColumns.reduce((r, item) => r + parseInt(item.width ?? _DEFAULT_COLUMN_WIDTH), 0)
|
|
201
266
|
|
|
202
267
|
return {
|
|
203
268
|
height: height + 'px',
|
|
@@ -205,18 +270,57 @@ export default{
|
|
|
205
270
|
}
|
|
206
271
|
},
|
|
207
272
|
|
|
273
|
+
freezeLeftScrollerStyle(){
|
|
274
|
+
if(!this.cItems || this.cItems.length < 1)
|
|
275
|
+
return {}
|
|
276
|
+
|
|
277
|
+
const height = (this.cItems.length * this.itemHeight) + (this.state === 2 ? 48 : 0)
|
|
278
|
+
const width = this.freezeLeftColumns.reduce((r, item) => r + parseInt(item.width ?? _DEFAULT_COLUMN_WIDTH), 0)
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
height: height + 'px',
|
|
282
|
+
width: width + 'px'
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
|
|
208
286
|
tableHeadStyle(){
|
|
209
287
|
return {
|
|
210
288
|
transform: "translate3d(" + (this.scrollLeft * -1) + "px, 0, 0)"
|
|
211
289
|
}
|
|
212
290
|
},
|
|
213
291
|
|
|
292
|
+
freezeLeftColumns(){
|
|
293
|
+
const columns = []
|
|
294
|
+
for(let i = 0 ; i < (this.freezeLeft ?? 0) && i < this.visibleColumns.length ; i++){
|
|
295
|
+
const column = this.visibleColumns[i]
|
|
296
|
+
if(!('visible' in column) || column.visible){
|
|
297
|
+
columns.push(column)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return columns
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
unfreezedColumns(){
|
|
304
|
+
const columns = []
|
|
305
|
+
for(let i = this.freezeLeft ?? 0 ; i < this.visibleColumns.length ; i++){
|
|
306
|
+
const column = this.visibleColumns[i]
|
|
307
|
+
if(!('visible' in column) || column.visible){
|
|
308
|
+
columns.push(column)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return columns
|
|
312
|
+
},
|
|
313
|
+
|
|
214
314
|
visibleColumns(){
|
|
215
|
-
|
|
216
|
-
|
|
315
|
+
const columns = []
|
|
316
|
+
for(let i = 0 ; i < this.cColumns.length ; i++){
|
|
317
|
+
const column = this.cColumns[i]
|
|
217
318
|
|
|
218
|
-
|
|
219
|
-
|
|
319
|
+
if(!('visible' in column) || column.visible){
|
|
320
|
+
columns.push(column)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return columns
|
|
220
324
|
},
|
|
221
325
|
|
|
222
326
|
},
|
|
@@ -229,6 +333,14 @@ export default{
|
|
|
229
333
|
this.passiveScrollSupported() ? { passive: true } : false
|
|
230
334
|
)
|
|
231
335
|
|
|
336
|
+
this.$refs.xcont.addEventListener(
|
|
337
|
+
"scroll",
|
|
338
|
+
this.handleScrollX,
|
|
339
|
+
this.passiveScrollSupported() ? { passive: true } : false
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
this.$refs.xcont.style.minHeight = `${this.$refs.cont.clientHeight}px`
|
|
343
|
+
|
|
232
344
|
this.resize()
|
|
233
345
|
|
|
234
346
|
const observer = new MutationObserver((mutationsList) => {
|
|
@@ -302,11 +414,18 @@ export default{
|
|
|
302
414
|
|
|
303
415
|
},
|
|
304
416
|
|
|
417
|
+
handleScrollX: throttle(function(){
|
|
418
|
+
if(!this.$refs.scroller || !this.$refs.xcont) return
|
|
419
|
+
|
|
420
|
+
this.scrollLeft = this.$refs.xcont.scrollLeft
|
|
421
|
+
|
|
422
|
+
this.scrolled = this.scrollTop > 0
|
|
423
|
+
}, 16),
|
|
424
|
+
|
|
305
425
|
handleScroll: throttle(function(){
|
|
306
426
|
if(!this.$refs.scroller || !this.$refs.cont) return
|
|
307
427
|
|
|
308
428
|
this.scrollTop = this.$refs.cont.scrollTop
|
|
309
|
-
this.scrollLeft = this.$refs.cont.scrollLeft
|
|
310
429
|
|
|
311
430
|
if(this.scrollTop > this.$refs.scroller.offsetHeight - this.$refs.cont.clientHeight - this.itemHeight){
|
|
312
431
|
if(!this.isOnEndScroll){
|
|
@@ -625,7 +744,7 @@ export default{
|
|
|
625
744
|
id: (this.items ?? [])[this.visibleStartIndex]?.id,
|
|
626
745
|
index: this.visibleStartIndex
|
|
627
746
|
}
|
|
628
|
-
}
|
|
747
|
+
}
|
|
629
748
|
|
|
630
749
|
}
|
|
631
750
|
|
|
@@ -641,11 +760,12 @@ export default{
|
|
|
641
760
|
}
|
|
642
761
|
|
|
643
762
|
.comp>*:last-child{
|
|
644
|
-
@apply flex-1
|
|
763
|
+
@apply flex-1 relative max-h-[100vh];
|
|
645
764
|
}
|
|
646
765
|
|
|
647
766
|
.header{
|
|
648
767
|
@apply border-b-[1px] border-text-50;
|
|
768
|
+
@apply flex flex-row;
|
|
649
769
|
}
|
|
650
770
|
|
|
651
771
|
.headerCol{
|
|
@@ -660,6 +780,7 @@ export default{
|
|
|
660
780
|
}
|
|
661
781
|
|
|
662
782
|
.spacer{
|
|
783
|
+
@apply w-full flex flex-row;
|
|
663
784
|
will-change: auto;
|
|
664
785
|
position: relative;
|
|
665
786
|
height: 0;
|
|
@@ -711,10 +832,6 @@ export default{
|
|
|
711
832
|
@apply border-text-50;
|
|
712
833
|
}
|
|
713
834
|
|
|
714
|
-
.spacer{
|
|
715
|
-
@apply w-full
|
|
716
|
-
}
|
|
717
|
-
|
|
718
835
|
.align-left{ @apply text-left; }
|
|
719
836
|
.align-center{ @apply text-center; }
|
|
720
837
|
.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.5, ++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>
|