@mixd-id/web-scaffold 0.1.230406393 → 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 +15 -8
- package/src/components/List.vue +14 -2
- package/src/components/Modal.vue +4 -1
- package/src/components/VirtualTable.vue +191 -74
- package/src/themes/default/index.js +6 -0
- package/src/utils/helpers.js +7 -3
- package/src/utils/preset-selector.js +10 -0
- package/src/utils/preset-selector.mjs +2 -1
- 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)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="$style.comp">
|
|
3
3
|
|
|
4
|
-
<div class="overflow-x-auto bg-text-50">
|
|
4
|
+
<div class="overflow-x-auto bg-text-50" :class="containerClass">
|
|
5
5
|
<div class="flex flex-row">
|
|
6
6
|
<button class="p-3" type="button" @click="format('bold')">
|
|
7
7
|
<svg width="14" height="14" class="fill-text-400 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M0 64C0 46.3 14.3 32 32 32H80 96 224c70.7 0 128 57.3 128 128c0 31.3-11.3 60.1-30 82.3c37.1 22.4 62 63.1 62 109.7c0 70.7-57.3 128-128 128H96 80 32c-17.7 0-32-14.3-32-32s14.3-32 32-32H48V256 96H32C14.3 96 0 81.7 0 64zM224 224c35.3 0 64-28.7 64-64s-28.7-64-64-64H112V224H224zM112 288V416H256c35.3 0 64-28.7 64-64s-28.7-64-64-64H224 112z"/></svg>
|
|
@@ -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 class="p-3" type="button" @click="createImage">
|
|
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"
|
|
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>
|
|
@@ -258,11 +258,18 @@ export default{
|
|
|
258
258
|
|
|
259
259
|
props:{
|
|
260
260
|
modelValue: String,
|
|
261
|
-
uploadImage: String
|
|
261
|
+
uploadImage: String,
|
|
262
|
+
uploadImageFn: String,
|
|
263
|
+
containerClass: String,
|
|
264
|
+
itemClass: String,
|
|
262
265
|
},
|
|
263
266
|
|
|
264
267
|
computed: {
|
|
265
268
|
|
|
269
|
+
canUpload(){
|
|
270
|
+
return `${this.uploadImage ?? ''}`.length > 0 || typeof this.uploadImageFn === 'function'
|
|
271
|
+
},
|
|
272
|
+
|
|
266
273
|
linkCanSave(){
|
|
267
274
|
return this.newLink.href && this.newLink.text
|
|
268
275
|
},
|
|
@@ -308,8 +315,8 @@ export default{
|
|
|
308
315
|
}
|
|
309
316
|
else{
|
|
310
317
|
this.newImage = {
|
|
311
|
-
width: "",
|
|
312
|
-
height: "",
|
|
318
|
+
width: "120px",
|
|
319
|
+
height: "120px",
|
|
313
320
|
element: null
|
|
314
321
|
}
|
|
315
322
|
}
|
package/src/components/List.vue
CHANGED
|
@@ -546,9 +546,11 @@ export default{
|
|
|
546
546
|
selectPreset(idx){
|
|
547
547
|
if(this.config.presets[idx]){
|
|
548
548
|
this.configParams.presetIdx = this.config.presets[idx].uid
|
|
549
|
+
console.log('idx', this.config.presets)
|
|
550
|
+
console.log('idx', idx, this.configParams.presetIdx)
|
|
549
551
|
this.load()
|
|
550
|
-
this.$refs.presetSelector
|
|
551
|
-
this.$refs.contextMenu
|
|
552
|
+
this.$refs.presetSelector?.close()
|
|
553
|
+
this.$refs.contextMenu?.close()
|
|
552
554
|
}
|
|
553
555
|
},
|
|
554
556
|
|
|
@@ -797,6 +799,12 @@ export default{
|
|
|
797
799
|
}
|
|
798
800
|
},
|
|
799
801
|
|
|
802
|
+
onConnect(reconnect){
|
|
803
|
+
if(reconnect){
|
|
804
|
+
this.load()
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
|
|
800
808
|
onKeyDown(e){
|
|
801
809
|
|
|
802
810
|
if(e.altKey){
|
|
@@ -848,6 +856,8 @@ export default{
|
|
|
848
856
|
pop: this.loadQueued,
|
|
849
857
|
delay: this.updateInterval
|
|
850
858
|
})
|
|
859
|
+
|
|
860
|
+
this.socket.on('connect', this.onConnect)
|
|
851
861
|
},
|
|
852
862
|
|
|
853
863
|
unmounted() {
|
|
@@ -858,6 +868,8 @@ export default{
|
|
|
858
868
|
this.socket.send('user.unsubscribe', {name: this.subscribeKey})
|
|
859
869
|
this.socket.off(this.subscribeKey, this.onSignal)
|
|
860
870
|
}
|
|
871
|
+
|
|
872
|
+
this.socket.off('connect', this.onConnect)
|
|
861
873
|
},
|
|
862
874
|
|
|
863
875
|
computed: {
|
package/src/components/Modal.vue
CHANGED
|
@@ -141,11 +141,14 @@ export default{
|
|
|
141
141
|
close(){
|
|
142
142
|
if(this.hash){
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
let hash = this.$route.hash
|
|
145
145
|
.split('#')
|
|
146
146
|
.filter(_ => _.length > 0 && !_.startsWith(this.hash.substring(1)))
|
|
147
147
|
.join('#')
|
|
148
148
|
|
|
149
|
+
if(hash.substring(0, 1) !== '#')
|
|
150
|
+
hash = '#' + hash
|
|
151
|
+
|
|
149
152
|
this.$router.replace({
|
|
150
153
|
...this.$route,
|
|
151
154
|
hash
|
|
@@ -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
|
}, {
|
package/src/utils/helpers.js
CHANGED
|
@@ -536,16 +536,20 @@ function capitalize(str) {
|
|
|
536
536
|
}
|
|
537
537
|
|
|
538
538
|
const removeUnderscoredKey = (obj) => {
|
|
539
|
-
if(
|
|
539
|
+
if (Array.isArray(obj)) {
|
|
540
|
+
return obj.map(removeUnderscoredKey);
|
|
541
|
+
}
|
|
542
|
+
else if (obj !== null && typeof obj === 'object') {
|
|
540
543
|
for(let key in obj){
|
|
541
544
|
if(key.startsWith('_')){
|
|
542
545
|
delete obj[key]
|
|
543
546
|
}
|
|
544
|
-
else if(Array.isArray(obj[key])){
|
|
545
|
-
obj[key]
|
|
547
|
+
else if(Array.isArray(obj[key]) || (obj[key] !== null && typeof obj[key] === 'object')){
|
|
548
|
+
removeUnderscoredKey(obj[key])
|
|
546
549
|
}
|
|
547
550
|
}
|
|
548
551
|
}
|
|
552
|
+
return obj;
|
|
549
553
|
}
|
|
550
554
|
|
|
551
555
|
const hashMake = async (text) => {
|
|
@@ -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 */))
|
|
@@ -202,8 +202,9 @@ const generateTotalColumns = (preset, items) => {
|
|
|
202
202
|
items.push(totalItem)
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
let presetUidSalt = 0
|
|
205
206
|
const getPresetUid = function(extra = ''){
|
|
206
|
-
return md5('preset-' + new Date().getTime() + extra)
|
|
207
|
+
return md5('preset-' + new Date().getTime() + extra + (++presetUidSalt))
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
export {
|
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>
|