@mixd-id/web-scaffold 0.1.2301231350 → 0.1.2301231352
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/ContextMenu.vue +25 -14
- package/src/components/ListPage1.vue +175 -45
- package/src/components/ListPage1Filter.vue +61 -79
- package/src/components/VirtualTable.vue +4 -1
- package/src/index.js +47 -0
- package/src/utils/listpage1.js +79 -37
package/package.json
CHANGED
|
@@ -30,7 +30,12 @@ export default {
|
|
|
30
30
|
default: "auto" // bottom-left, bottom-right, top-left, top-right, bottom-center, top-center default: bottom-left
|
|
31
31
|
},
|
|
32
32
|
|
|
33
|
-
bodyClass: String
|
|
33
|
+
bodyClass: String,
|
|
34
|
+
|
|
35
|
+
dismiss: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: true
|
|
38
|
+
}
|
|
34
39
|
|
|
35
40
|
},
|
|
36
41
|
|
|
@@ -87,23 +92,14 @@ export default {
|
|
|
87
92
|
}, 77)
|
|
88
93
|
},
|
|
89
94
|
|
|
90
|
-
onDismiss(){
|
|
95
|
+
onDismiss(e){
|
|
91
96
|
if(typeof window === 'undefined') return
|
|
92
97
|
if(!this.$refs.contextMenu) return
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
window.removeEventListener('scroll', this.onDismiss)
|
|
97
|
-
if(this.$refs.contextMenu){
|
|
98
|
-
this.$refs.contextMenu.removeEventListener('transitionend', transitionEnd)
|
|
99
|
-
}
|
|
100
|
-
this.computedStyle = null
|
|
101
|
-
this.$emit('dismiss')
|
|
102
|
-
this.isOpen = false
|
|
103
|
-
}
|
|
99
|
+
if(e.target.closest('.' + this.$style.contextMenu) && !this.dismiss)
|
|
100
|
+
return
|
|
104
101
|
|
|
105
|
-
this
|
|
106
|
-
this.$refs.contextMenu.classList.remove(this.$style.active)
|
|
102
|
+
this.close()
|
|
107
103
|
},
|
|
108
104
|
|
|
109
105
|
open(caller, context){
|
|
@@ -186,6 +182,21 @@ export default {
|
|
|
186
182
|
console.warn('Invalid context menu caller', this.caller)
|
|
187
183
|
}
|
|
188
184
|
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
close(){
|
|
188
|
+
const transitionEnd = () => {
|
|
189
|
+
window.removeEventListener('click', this.onDismiss)
|
|
190
|
+
window.removeEventListener('scroll', this.onDismiss)
|
|
191
|
+
if(this.$refs.contextMenu){
|
|
192
|
+
this.$refs.contextMenu.removeEventListener('transitionend', transitionEnd)
|
|
193
|
+
}
|
|
194
|
+
this.computedStyle = null
|
|
195
|
+
this.isOpen = false
|
|
196
|
+
this.$emit('dismiss')
|
|
197
|
+
}
|
|
198
|
+
this.$refs.contextMenu.addEventListener('transitionend', transitionEnd)
|
|
199
|
+
this.$refs.contextMenu.classList.remove(this.$style.active)
|
|
189
200
|
}
|
|
190
201
|
|
|
191
202
|
}
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
<Suspense @resolve="patch">
|
|
3
3
|
<div :class="$style.comp">
|
|
4
4
|
|
|
5
|
-
<div class="flex-1 flex flex-col">
|
|
5
|
+
<div class="flex-1 flex flex-col p-6">
|
|
6
6
|
|
|
7
|
-
<div class="
|
|
7
|
+
<div class="pb-0 flex flex-row gap-3 items-center mb-6">
|
|
8
8
|
<slot v-if="$slots['lp-start']" name="lp-start" :preset="preset"/>
|
|
9
9
|
<div v-else class="mr-6 hidden md:block">
|
|
10
10
|
<h2>{{ title }}</h2>
|
|
11
11
|
</div>
|
|
12
12
|
<div class="flex-1"></div>
|
|
13
|
-
<div class="overflow-hidden
|
|
13
|
+
<div class="overflow-hidden hidden md:block">
|
|
14
14
|
<slot v-if="$slots['lp-search']" name="lp-search" :preset="preset"/>
|
|
15
|
-
<Textbox v-else :clearable="true" placeholder="
|
|
15
|
+
<Textbox v-else :clearable="true" :placeholder="$t('Search...')" class="w-[200px]" v-model="preset.search"
|
|
16
16
|
@clear="clearSearch" @submit="load">
|
|
17
17
|
<template #start>
|
|
18
18
|
<div class="px-2">
|
|
@@ -36,13 +36,9 @@
|
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
38
38
|
</Dropdown>
|
|
39
|
-
<button class="border-[1px] border-text-200 bg-base-50 hover:border-text-300 rounded-lg p-
|
|
40
|
-
@click="openPreset">
|
|
41
|
-
<svg width="
|
|
42
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 13.5C5.82843 13.5 6.5 12.8284 6.5 12C6.5 11.1716 5.82843 10.5 5 10.5C4.17157 10.5 3.5 11.1716 3.5 12C3.5 12.8284 4.17157 13.5 5 13.5ZM5 15C6.65685 15 8 13.6569 8 12C8 10.3431 6.65685 9 5 9C3.34315 9 2 10.3431 2 12C2 13.6569 3.34315 15 5 15Z" />
|
|
43
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 13.5C12.8284 13.5 13.5 12.8284 13.5 12C13.5 11.1716 12.8284 10.5 12 10.5C11.1716 10.5 10.5 11.1716 10.5 12C10.5 12.8284 11.1716 13.5 12 13.5ZM12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" />
|
|
44
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M19 13.5C19.8284 13.5 20.5 12.8284 20.5 12C20.5 11.1716 19.8284 10.5 19 10.5C18.1716 10.5 17.5 11.1716 17.5 12C17.5 12.8284 18.1716 13.5 19 13.5ZM19 15C20.6569 15 22 13.6569 22 12C22 10.3431 20.6569 9 19 9C17.3431 9 16 10.3431 16 12C16 13.6569 17.3431 15 19 15Z" />
|
|
45
|
-
</svg>
|
|
39
|
+
<button class="border-[1px] border-text-200 bg-base-50 hover:border-text-300 rounded-lg p-3"
|
|
40
|
+
@click="openPreset()">
|
|
41
|
+
<svg width="15" height="15" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="fill-text-300 hover:fill-primary"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M64 256V160H224v96H64zm0 64H224v96H64V320zm224 96V320H448v96H288zM448 256H288V160H448v96zM64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64z"/></svg>
|
|
46
42
|
</button>
|
|
47
43
|
</div>
|
|
48
44
|
</div>
|
|
@@ -56,29 +52,86 @@
|
|
|
56
52
|
</template>
|
|
57
53
|
</VirtualScroll>
|
|
58
54
|
|
|
59
|
-
<VirtualTable v-else class="flex-1
|
|
55
|
+
<VirtualTable v-else class="flex-1" :columns="presetColumns" :items="items"
|
|
60
56
|
:appearances="appearances" @scroll-end="loadNext" :pinned="pinned">
|
|
61
57
|
<template v-for="column in presetColumns" #[colOf(column.key)]="{}">
|
|
62
|
-
<div :class="getHeader(column)">
|
|
63
|
-
<div
|
|
58
|
+
<div :class="getHeader(column)" @click="openColumnOptions(column.key, $event.target.closest('.' + $style.header))">
|
|
59
|
+
<div>
|
|
64
60
|
{{ column.label }}
|
|
65
61
|
</div>
|
|
66
|
-
<div>
|
|
62
|
+
<div class="absolute top-0 right-0 p-2 bg-base-500" v-if="sortedColumns[column.key] === 'asc'">
|
|
63
|
+
<svg width="19" height="19" viewBox="0 0 24 24" class="fill-text-400" xmlns="http://www.w3.org/2000/svg">
|
|
64
|
+
<path d="M6.75 5C6.75 4.58579 6.41421 4.25 6 4.25C5.58579 4.25 5.25 4.58579 5.25 5V17.6893L3.53033 15.9697C3.23744 15.6768 2.76256 15.6768 2.46967 15.9697C2.17678 16.2626 2.17678 16.7374 2.46967 17.0303L4.76256 19.3232C5.44598 20.0066 6.55402 20.0066 7.23744 19.3232L9.53033 17.0303C9.82322 16.7374 9.82322 16.2626 9.53033 15.9697C9.23744 15.6768 8.76256 15.6768 8.46967 15.9697L6.75 17.6893V5Z"/>
|
|
65
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 17C12.25 17.4142 12.5858 17.75 13 17.75H21C21.4142 17.75 21.75 17.4142 21.75 17C21.75 16.5858 21.4142 16.25 21 16.25H13C12.5858 16.25 12.25 16.5858 12.25 17Z"/>
|
|
66
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 12C12.25 11.5858 12.5858 11.25 13 11.25H18C18.4142 11.25 18.75 11.5858 18.75 12C18.75 12.4142 18.4142 12.75 18 12.75H13C12.5858 12.75 12.25 12.4142 12.25 12Z"/>
|
|
67
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 7C12.25 6.58579 12.5858 6.25 13 6.25H15C15.4142 6.25 15.75 6.58579 15.75 7C15.75 7.41421 15.4142 7.75 15 7.75H13C12.5858 7.75 12.25 7.41421 12.25 7Z"/>
|
|
68
|
+
</svg>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="absolute top-0 right-0 p-2 bg-base-500" v-else-if="sortedColumns[column.key] === 'desc'">
|
|
71
|
+
<svg width="19" height="19" viewBox="0 0 24 24" class="fill-text-400" xmlns="http://www.w3.org/2000/svg">
|
|
72
|
+
<path d="M6.75 5C6.75 4.58579 6.41421 4.25 6 4.25C5.58579 4.25 5.25 4.58579 5.25 5V17.6893L3.53033 15.9697C3.23744 15.6768 2.76256 15.6768 2.46967 15.9697C2.17678 16.2626 2.17678 16.7374 2.46967 17.0303L4.76256 19.3232C5.44598 20.0066 6.55402 20.0066 7.23744 19.3232L9.53033 17.0303C9.82322 16.7374 9.82322 16.2626 9.53033 15.9697C9.23744 15.6768 8.76256 15.6768 8.46967 15.9697L6.75 17.6893V5Z"/>
|
|
73
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 7C12.25 6.58579 12.5858 6.25 13 6.25H21C21.4142 6.25 21.75 6.58579 21.75 7C21.75 7.41421 21.4142 7.75 21 7.75H13C12.5858 7.75 12.25 7.41421 12.25 7Z"/>
|
|
74
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 12C12.25 12.4142 12.5858 12.75 13 12.75H18C18.4142 12.75 18.75 12.4142 18.75 12C18.75 11.5858 18.4142 11.25 18 11.25H13C12.5858 11.25 12.25 11.5858 12.25 12Z"/>
|
|
75
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.25 17C12.25 17.4142 12.5858 17.75 13 17.75H15C15.4142 17.75 15.75 17.4142 15.75 17C15.75 16.5858 15.4142 16.25 15 16.25H13C12.5858 16.25 12.25 16.5858 12.25 17Z"/>
|
|
76
|
+
</svg>
|
|
67
77
|
</div>
|
|
68
78
|
</div>
|
|
69
79
|
</template>
|
|
70
|
-
<template v-for="(_, slot) in
|
|
80
|
+
<template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
|
|
81
|
+
<div :class="getHeader(slot.replace('col-', ''))" @click="openColumnOptions(slot.replace('col-', ''), $event.target.closest('.' + $style.header))">
|
|
82
|
+
<slot :name="slot" :item="item" :index="index"></slot>
|
|
83
|
+
</div>
|
|
84
|
+
</template>
|
|
85
|
+
<template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
|
|
71
86
|
<slot :name="slot" :item="item" :index="index"></slot>
|
|
72
87
|
</template>
|
|
73
88
|
</VirtualTable>
|
|
74
89
|
|
|
75
|
-
<ContextMenu
|
|
76
|
-
<div class="
|
|
77
|
-
<
|
|
78
|
-
|
|
90
|
+
<ContextMenu ref="columnMenu" :dismiss="false">
|
|
91
|
+
<div class="flex-1 flex flex-col w-[270px] p-3">
|
|
92
|
+
<div class="flex flex-col">
|
|
93
|
+
<div class="flex flex-row gap-2 items-center">
|
|
94
|
+
<div class="p-2 text-text-300 flex-1">Sort By</div>
|
|
95
|
+
<div class="text-primary cursor-pointer text-sm" @click="openPreset('sort');$refs.columnMenu.close()">Sort Options</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="p-2 cursor-pointer" :class="$style.hoverable" @click="setSortCurrent(1)">Sort Ascending</div>
|
|
98
|
+
<div class="p-2 cursor-pointer" :class="$style.hoverable" @click="setSortCurrent(2)">Sort Descending</div>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="h-[1px] bg-text-50 my-2"></div>
|
|
101
|
+
<div class="flex flex-col">
|
|
102
|
+
<div class="p-2 cursor-pointer" :class="$style.hoverable" @click="hide">Hide</div>
|
|
103
|
+
<div class="p-2 cursor-pointer" :class="$style.hoverable" @click="openPreset();$refs.columnMenu.close()">Column Options</div>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="h-[1px] bg-text-50 my-2"></div>
|
|
106
|
+
<div class="flex flex-col">
|
|
107
|
+
<div class="flex flex-row gap-2 items-center">
|
|
108
|
+
<div class="p-2 text-text-300 flex-1">Filters</div>
|
|
109
|
+
<div class="text-primary cursor-pointer text-sm" @click="openPreset('filter');$refs.columnMenu.close()">Filter Options</div>
|
|
110
|
+
</div>
|
|
111
|
+
<div v-if="currentFilters.length > 0">
|
|
112
|
+
<ListPage1Filter v-if="preset.filters" v-for="filter in currentFilters"
|
|
113
|
+
:filter="filter" :column="columns[filter.key]"
|
|
114
|
+
@remove="removeFilter(filter)" @change="load" />
|
|
115
|
+
</div>
|
|
116
|
+
<div v-else>
|
|
117
|
+
<div class="p-2 cursor-pointer" :class="$style.hoverable" @click.stop="addCurrentFilter">Add Filter</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
79
120
|
</div>
|
|
80
121
|
</ContextMenu>
|
|
81
122
|
|
|
123
|
+
<div v-if="count > 0 && !summaryTeleport" class="flex flex-row bg-base-500 divide-x divide-text-50 border-[1px] border-text-50">
|
|
124
|
+
<div class="p-2 px-3 text-text-400">
|
|
125
|
+
Count: {{ count }}
|
|
126
|
+
</div>
|
|
127
|
+
<div></div>
|
|
128
|
+
</div>
|
|
129
|
+
<Teleport v-else-if="count > 0" to=".footer-wrap">
|
|
130
|
+
<div>
|
|
131
|
+
Count: {{ count }}
|
|
132
|
+
</div>
|
|
133
|
+
</Teleport>
|
|
134
|
+
|
|
82
135
|
</div>
|
|
83
136
|
|
|
84
137
|
<div v-if="layout.presetOpen" class="w-[360px] border-l-[1px] border-text-50 bg-base-500 flex flex-col">
|
|
@@ -98,8 +151,8 @@
|
|
|
98
151
|
:caller="layout.presetContextMenu ? layout.presetContextMenu.caller : null"
|
|
99
152
|
@dismiss="layout.presetContextMenu = null" position="bottom-right">
|
|
100
153
|
<div class="min-w-[200px]">
|
|
101
|
-
<button class="w-full p-3 hover:bg-text-50" @click="copyPreset">Copy this Preset</button>
|
|
102
|
-
<button class="w-full p-3 text-red-
|
|
154
|
+
<button type="button" class="w-full p-3 hover:bg-text-50 text-left" @click="copyPreset">Copy this Preset</button>
|
|
155
|
+
<button type="button" class="w-full p-3 text-red-600 hover:text-red-500 text-left" @click="removePreset" v-if="presets.length > 1">Remove</button>
|
|
103
156
|
</div>
|
|
104
157
|
</ContextMenu>
|
|
105
158
|
<button @click="closePreset">
|
|
@@ -110,7 +163,7 @@
|
|
|
110
163
|
</div>
|
|
111
164
|
|
|
112
165
|
<div class="flex justify-center">
|
|
113
|
-
<Tabs v-model="layout.presetTab" :items="[{text:'
|
|
166
|
+
<Tabs v-model="layout.presetTab" :items="[{text:'Columns',value:'column'},{text:'Filters',value:'filter'},{text:'Sorts',value:'sort'}]" />
|
|
114
167
|
</div>
|
|
115
168
|
</div>
|
|
116
169
|
|
|
@@ -139,10 +192,9 @@
|
|
|
139
192
|
<ListPage1Filter v-if="preset.filters" v-for="filter in preset.filters"
|
|
140
193
|
:filter="filter" :column="columns[filter.key]"
|
|
141
194
|
@remove="removeFilter(filter)" @change="load"/>
|
|
142
|
-
|
|
143
195
|
<div class="py-8">
|
|
144
196
|
<Dropdown @change="addFilter" v-model="layout.presetFilterSelector">
|
|
145
|
-
<option value="" disabled selected>
|
|
197
|
+
<option value="" disabled selected>{{ $t('Add Filter')}}</option>
|
|
146
198
|
<option v-for="column in filterableColumns" :value="column.key">{{ column.label }}</option>
|
|
147
199
|
</Dropdown>
|
|
148
200
|
</div>
|
|
@@ -160,7 +212,7 @@
|
|
|
160
212
|
<div class="flex flex-row items-center gap-2">
|
|
161
213
|
<div class="flex-1">
|
|
162
214
|
<Dropdown v-model="sort.key" @change="load">
|
|
163
|
-
<option value="" disabled selected>
|
|
215
|
+
<option value="" disabled selected>{{ $t('Add Sort') }}</option>
|
|
164
216
|
<option v-for="column in sortableColumns" :value="column.key">{{ column.label }}</option>
|
|
165
217
|
</Dropdown>
|
|
166
218
|
</div>
|
|
@@ -177,12 +229,10 @@
|
|
|
177
229
|
</button>
|
|
178
230
|
</div>
|
|
179
231
|
</div>
|
|
180
|
-
|
|
181
232
|
<div v-if="preset.sorts && preset.sorts.length > 0" class="h-[1px] my-4 bg-text-100"></div>
|
|
182
|
-
|
|
183
233
|
<div class="py-4">
|
|
184
234
|
<Dropdown @change="addSort" v-model="layout.presetSortSelector">
|
|
185
|
-
<option value="" disabled selected>
|
|
235
|
+
<option value="" disabled selected>{{ $t('Add Sort') }}</option>
|
|
186
236
|
<option v-for="column in sortableColumns" :value="column.key">{{ column.label }}</option>
|
|
187
237
|
</Dropdown>
|
|
188
238
|
</div>
|
|
@@ -224,7 +274,9 @@ export default{
|
|
|
224
274
|
|
|
225
275
|
title: String,
|
|
226
276
|
|
|
227
|
-
pinned: Function
|
|
277
|
+
pinned: Function,
|
|
278
|
+
|
|
279
|
+
summaryTeleport: String
|
|
228
280
|
|
|
229
281
|
},
|
|
230
282
|
|
|
@@ -235,8 +287,9 @@ export default{
|
|
|
235
287
|
},
|
|
236
288
|
|
|
237
289
|
presetColumns(){
|
|
290
|
+
if(!this.preset.columns) return []
|
|
238
291
|
|
|
239
|
-
for(let i = 0 ; i < (this.preset.columns
|
|
292
|
+
for(let i = 0 ; i < (this.preset.columns).length ; i++){
|
|
240
293
|
const presetColumn = this.preset.columns[i]
|
|
241
294
|
const column = this.columns[presetColumn.key] ?? {}
|
|
242
295
|
for(let key in column){
|
|
@@ -246,7 +299,7 @@ export default{
|
|
|
246
299
|
}
|
|
247
300
|
}
|
|
248
301
|
|
|
249
|
-
return this.preset.columns
|
|
302
|
+
return this.preset.columns
|
|
250
303
|
},
|
|
251
304
|
|
|
252
305
|
appearances(){
|
|
@@ -261,8 +314,37 @@ export default{
|
|
|
261
314
|
return Object.values(this.columns).filter((_) => _.sortable)
|
|
262
315
|
},
|
|
263
316
|
|
|
317
|
+
sortedColumns(){
|
|
318
|
+
const c = {};
|
|
319
|
+
(this.preset.sorts ?? []).forEach((_) => c[_.key] = _.type);
|
|
320
|
+
return c
|
|
321
|
+
},
|
|
322
|
+
|
|
264
323
|
computedDataSource(){
|
|
265
324
|
return this.datasource ?? this.model
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
headerSlots(){
|
|
328
|
+
const slots = {}
|
|
329
|
+
for(let key in this.$slots){
|
|
330
|
+
if(key.startsWith('col-'))
|
|
331
|
+
slots[key] = this.$slots[key]
|
|
332
|
+
}
|
|
333
|
+
return slots
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
contentSlots(){
|
|
337
|
+
const slots = {}
|
|
338
|
+
for(let key in this.$slots){
|
|
339
|
+
if(!key.startsWith('col-'))
|
|
340
|
+
slots[key] = this.$slots[key]
|
|
341
|
+
}
|
|
342
|
+
return slots
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
currentFilters(){
|
|
346
|
+
if(!this.preset.filters) return []
|
|
347
|
+
return this.preset.filters.filter((_) => _.key === this.selectedColumn)
|
|
266
348
|
}
|
|
267
349
|
|
|
268
350
|
},
|
|
@@ -278,9 +360,11 @@ export default{
|
|
|
278
360
|
},
|
|
279
361
|
name: null,
|
|
280
362
|
columns: [],
|
|
363
|
+
selectedColumn: null,
|
|
281
364
|
presets: [],
|
|
282
365
|
presetIdx: -1,
|
|
283
366
|
items: [],
|
|
367
|
+
count: null,
|
|
284
368
|
hasNext: false,
|
|
285
369
|
modal: null
|
|
286
370
|
}
|
|
@@ -294,6 +378,9 @@ export default{
|
|
|
294
378
|
unmounted() {
|
|
295
379
|
window.removeEventListener('focus', this.onFocus)
|
|
296
380
|
this.socket.offAny(this.onHooks)
|
|
381
|
+
|
|
382
|
+
if(this.name)
|
|
383
|
+
this.socketEmit(this.name + '.unsubscribe', { name:this.name })
|
|
297
384
|
},
|
|
298
385
|
|
|
299
386
|
methods:{
|
|
@@ -303,15 +390,21 @@ export default{
|
|
|
303
390
|
this.socketEmit(`${this.computedDataSource}.patch`, {
|
|
304
391
|
...(urlQuery().reset ? { reset:1 } : {})
|
|
305
392
|
}, (res) => {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
this.
|
|
309
|
-
this.
|
|
393
|
+
const data = res.data ? res.data : res
|
|
394
|
+
//console.log('patch', data)
|
|
395
|
+
this.name = data.name
|
|
396
|
+
this.columns = data.columns
|
|
397
|
+
this.presets = data.presets
|
|
398
|
+
this.presetIdx = data.presetIdx
|
|
310
399
|
this.patchPresets()
|
|
311
400
|
this.$nextTick(() => {
|
|
312
|
-
this.items =
|
|
313
|
-
this.hasNext =
|
|
401
|
+
this.items = data.items
|
|
402
|
+
this.hasNext = data.hasNext
|
|
403
|
+
this.count = data.count
|
|
314
404
|
})
|
|
405
|
+
|
|
406
|
+
if(this.name)
|
|
407
|
+
this.socketEmit(this.name + '.subscribe', { name:this.name })
|
|
315
408
|
})
|
|
316
409
|
},
|
|
317
410
|
|
|
@@ -355,12 +448,12 @@ export default{
|
|
|
355
448
|
},
|
|
356
449
|
|
|
357
450
|
load(){
|
|
358
|
-
console.log('load')
|
|
359
451
|
this.socketEmit(`${this.computedDataSource}.load`, {
|
|
360
452
|
preset: this.preset
|
|
361
453
|
}, (res) => {
|
|
362
|
-
|
|
363
|
-
|
|
454
|
+
const data = res.data ? res.data : res
|
|
455
|
+
//console.log('load', data)
|
|
456
|
+
Object.assign(this.$data, data)
|
|
364
457
|
})
|
|
365
458
|
},
|
|
366
459
|
|
|
@@ -397,10 +490,12 @@ export default{
|
|
|
397
490
|
|
|
398
491
|
selectPreset(preset){
|
|
399
492
|
this.presetIdx = this.presets.findIndex((_) => _ === preset)
|
|
493
|
+
this.load()
|
|
400
494
|
},
|
|
401
495
|
|
|
402
|
-
openPreset(){
|
|
496
|
+
openPreset(presetTab = 'column'){
|
|
403
497
|
this.layout.presetOpen = true
|
|
498
|
+
this.layout.presetTab = presetTab
|
|
404
499
|
},
|
|
405
500
|
|
|
406
501
|
closePreset(){
|
|
@@ -486,6 +581,7 @@ export default{
|
|
|
486
581
|
}
|
|
487
582
|
|
|
488
583
|
this.preset.filters.push({
|
|
584
|
+
enabled: true,
|
|
489
585
|
key: column.key,
|
|
490
586
|
label: column.label,
|
|
491
587
|
type: column.type,
|
|
@@ -496,6 +592,10 @@ export default{
|
|
|
496
592
|
this.layout.presetFilterSelector = null
|
|
497
593
|
},
|
|
498
594
|
|
|
595
|
+
addCurrentFilter(){
|
|
596
|
+
this.addFilter(this.selectedColumn)
|
|
597
|
+
},
|
|
598
|
+
|
|
499
599
|
addSort(key){
|
|
500
600
|
|
|
501
601
|
const column = this.preset.columns.filter((_) => _.key === key).pop()
|
|
@@ -535,10 +635,33 @@ export default{
|
|
|
535
635
|
|
|
536
636
|
return [
|
|
537
637
|
this.$style.header,
|
|
538
|
-
(this.preset.filters ?? []).findIndex((_) => _.key === column.key) >= 0 ?
|
|
638
|
+
(this.preset.filters ?? []).findIndex((_) => _.key === column.key && _.enabled) >= 0 ?
|
|
539
639
|
this.$style.headerSelected : ''
|
|
540
640
|
]
|
|
541
641
|
.join(' ')
|
|
642
|
+
},
|
|
643
|
+
|
|
644
|
+
openColumnOptions(key, target){
|
|
645
|
+
this.selectedColumn = key
|
|
646
|
+
this.$refs.columnMenu.open(target)
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
setSortCurrent(sortType){
|
|
650
|
+
this.preset.sorts = [
|
|
651
|
+
{
|
|
652
|
+
key: this.selectedColumn,
|
|
653
|
+
label: this.columns[this.selectedColumn].label,
|
|
654
|
+
type: sortType === 2 ? 'desc' : 'asc'
|
|
655
|
+
}
|
|
656
|
+
]
|
|
657
|
+
this.load()
|
|
658
|
+
this.$refs.columnMenu.close()
|
|
659
|
+
},
|
|
660
|
+
|
|
661
|
+
hide(){
|
|
662
|
+
const idx = this.preset.columns.findIndex((_) => _.key === this.selectedColumn)
|
|
663
|
+
this.preset.columns[idx].visible = false
|
|
664
|
+
this.$refs.columnMenu.close()
|
|
542
665
|
}
|
|
543
666
|
|
|
544
667
|
},
|
|
@@ -566,11 +689,18 @@ export default{
|
|
|
566
689
|
}
|
|
567
690
|
|
|
568
691
|
.header{
|
|
569
|
-
@apply
|
|
692
|
+
@apply p-2 cursor-pointer border-b-[2px] border-transparent overflow-hidden;
|
|
693
|
+
}
|
|
694
|
+
.header>*:first-child{
|
|
695
|
+
@apply text-ellipsis whitespace-nowrap overflow-x-hidden;
|
|
570
696
|
}
|
|
571
697
|
|
|
572
698
|
.headerSelected{
|
|
573
|
-
@apply
|
|
699
|
+
@apply border-primary;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.hoverable{
|
|
703
|
+
@apply hover:bg-primary hover:text-white;
|
|
574
704
|
}
|
|
575
705
|
|
|
576
706
|
</style>
|
|
@@ -1,52 +1,49 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
<div class="flex flex-row items-center gap-2
|
|
2
|
+
<div :class="$style.comp">
|
|
3
|
+
<div class="flex flex-row items-center gap-2">
|
|
4
|
+
<Checkbox v-model="filter.enabled" @change="onFilterChange(item)"/>
|
|
4
5
|
<div class="flex-1">
|
|
5
|
-
<
|
|
6
|
+
<strong class="block text-ellipsis overflow-x-hidden whitespace-nowrap">{{ filter.label }}</strong>
|
|
6
7
|
</div>
|
|
7
|
-
<
|
|
8
|
+
<div class="w-[60px]">
|
|
9
|
+
<Dropdown v-model="filter.operand" @change="onFilterChange(item)">
|
|
10
|
+
<option value="">And</option>
|
|
11
|
+
<option value="or">Or</option>
|
|
12
|
+
</Dropdown>
|
|
13
|
+
</div>
|
|
14
|
+
<button @click.stop="removeFilter(filter)" class="text-primary">
|
|
8
15
|
<svg width="21" height="21" class="fill-text-100 hover:fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
9
16
|
<path class="secondary" fill-rule="evenodd" d="M15.78 14.36a1 1 0 0 1-1.42 1.42l-2.82-2.83-2.83 2.83a1 1 0 1 1-1.42-1.42l2.83-2.82L7.3 8.7a1 1 0 0 1 1.42-1.42l2.83 2.83 2.82-2.83a1 1 0 0 1 1.42 1.42l-2.83 2.83 2.83 2.82z"/>
|
|
10
17
|
</svg>
|
|
11
18
|
</button>
|
|
12
19
|
</div>
|
|
13
|
-
<div>
|
|
20
|
+
<div class="flex flex-col gap-3 py-3">
|
|
14
21
|
|
|
15
22
|
<div v-for="(item, idx) in filter.filters">
|
|
16
23
|
|
|
17
24
|
<div v-if="column && column.type === 'date'">
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
|
|
26
|
+
<div class="flex flex-row items-start gap-2">
|
|
27
|
+
<Dropdown v-model="item.operator" @change="onFilterChange(item)" class="flex-1 min-w-[100px]">
|
|
28
|
+
<option value="">Select</option>
|
|
29
|
+
<option value="today">Today</option>
|
|
30
|
+
<option value="thisWeek">This Week</option>
|
|
31
|
+
<option value="thisMonth">This Month</option>
|
|
32
|
+
<option value="lastMonth">Last Month</option>
|
|
33
|
+
<option value="thisMonth">This Year</option>
|
|
34
|
+
<option value="lastYear">Last Year</option>
|
|
35
|
+
<option value="between">Between</option>
|
|
36
|
+
</Dropdown>
|
|
37
|
+
<Datepicker v-if="item.operator === 'between'" v-model="item.value[0]" mode="popup" @change="onFilterChange(item)"/>
|
|
38
|
+
<Datepicker v-if="item.operator === 'between'" v-model="item.value[1]" mode="popup" @change="onFilterChange(item)"/>
|
|
39
|
+
<button v-if="filter.filters.length > 1" @click.stop="filter.filters.splice(filter.filters.indexOf(item), 1);onFilterChange(item)"
|
|
20
40
|
class="py-2">
|
|
21
41
|
<svg width="21" height="21" class="fill-text-100 hover:fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
22
42
|
<path class="secondary" fill-rule="evenodd" d="M15.78 14.36a1 1 0 0 1-1.42 1.42l-2.82-2.83-2.83 2.83a1 1 0 1 1-1.42-1.42l2.83-2.82L7.3 8.7a1 1 0 0 1 1.42-1.42l2.83 2.83 2.82-2.83a1 1 0 0 1 1.42 1.42l-2.83 2.83 2.83 2.82z"/>
|
|
23
43
|
</svg>
|
|
24
44
|
</button>
|
|
25
|
-
<div class="w-[60px]">
|
|
26
|
-
<Dropdown v-if="idx === 0" v-model="item.operand" @change="onFilterChange(item)">
|
|
27
|
-
<option value="">And</option>
|
|
28
|
-
<option value="or">Or</option>
|
|
29
|
-
</Dropdown>
|
|
30
|
-
</div>
|
|
31
|
-
<div class="flex-1 flex flex-col gap-2">
|
|
32
|
-
<Dropdown v-model="item.operator" @change="onFilterChange(item)">
|
|
33
|
-
<option value="">Select</option>
|
|
34
|
-
<option value="today">Today</option>
|
|
35
|
-
<option value="thisWeek">This Week</option>
|
|
36
|
-
<option value="thisMonth">This Month</option>
|
|
37
|
-
<option value="lastMonth">Last Month</option>
|
|
38
|
-
<option value="thisMonth">This Year</option>
|
|
39
|
-
<option value="lastYear">Last Year</option>
|
|
40
|
-
<option value="between">Between</option>
|
|
41
|
-
</Dropdown>
|
|
42
|
-
|
|
43
|
-
<Datepicker v-if="item.operator === 'between'" v-model="item.value[0]" mode="popup" @change="onFilterChange(item)"/>
|
|
44
|
-
|
|
45
|
-
<Datepicker v-if="item.operator === 'between'" v-model="item.value[1]" mode="popup" @change="onFilterChange(item)"/>
|
|
46
|
-
|
|
47
|
-
<button v-if="idx === filter.filters.length - 1" @click="addMore" class="text-primary">Add More</button>
|
|
48
|
-
</div>
|
|
49
45
|
</div>
|
|
46
|
+
|
|
50
47
|
</div>
|
|
51
48
|
|
|
52
49
|
<div v-else-if="column && column.type === 'enum'" class="flex flex-col">
|
|
@@ -57,70 +54,57 @@
|
|
|
57
54
|
</div>
|
|
58
55
|
|
|
59
56
|
<div v-else-if="column && [ 'currency', 'number' ].includes(column.type)">
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
|
|
58
|
+
<div class="flex flex-row items-start gap-2">
|
|
59
|
+
<Dropdown v-model="item.operator" @change="onFilterChange(item)">
|
|
60
|
+
<option value="">Select</option>
|
|
61
|
+
<option value="=">=</option>
|
|
62
|
+
<option value=">">></option>
|
|
63
|
+
<option value=">=">>=</option>
|
|
64
|
+
<option value="<"><</option>
|
|
65
|
+
<option value="<="><=</option>
|
|
66
|
+
</Dropdown>
|
|
67
|
+
<Textbox class="flex-1" v-model="item.value" @keyup.enter="onFilterChange(item)"/>
|
|
68
|
+
<button v-if="filter.filters.length > 1" @click.stop="filter.filters.splice(filter.filters.indexOf(item), 1);onFilterChange(item)"
|
|
62
69
|
class="py-2">
|
|
63
70
|
<svg width="21" height="21" class="fill-text-100 hover:fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
64
71
|
<path class="secondary" fill-rule="evenodd" d="M15.78 14.36a1 1 0 0 1-1.42 1.42l-2.82-2.83-2.83 2.83a1 1 0 1 1-1.42-1.42l2.83-2.82L7.3 8.7a1 1 0 0 1 1.42-1.42l2.83 2.83 2.82-2.83a1 1 0 0 1 1.42 1.42l-2.83 2.83 2.83 2.82z"/>
|
|
65
72
|
</svg>
|
|
66
73
|
</button>
|
|
67
|
-
<div class="w-[60px]">
|
|
68
|
-
<Dropdown v-if="idx === 0" v-model="item.operand" @change="onFilterChange(item)">
|
|
69
|
-
<option value="">And</option>
|
|
70
|
-
<option value="or">Or</option>
|
|
71
|
-
</Dropdown>
|
|
72
|
-
</div>
|
|
73
|
-
<div class="flex-1 flex flex-col gap-2">
|
|
74
|
-
<Dropdown v-model="item.operator" @change="onFilterChange(item)">
|
|
75
|
-
<option value="">Select</option>
|
|
76
|
-
<option value="=">=</option>
|
|
77
|
-
<option value=">">></option>
|
|
78
|
-
<option value=">=">>=</option>
|
|
79
|
-
<option value="<"><</option>
|
|
80
|
-
<option value="<="><=</option>
|
|
81
|
-
</Dropdown>
|
|
82
|
-
|
|
83
|
-
<Textbox v-model="item.value" />
|
|
84
|
-
|
|
85
|
-
<button v-if="idx === filter.filters.length - 1" @click="addMore" class="text-primary">Add More</button>
|
|
86
|
-
</div>
|
|
87
74
|
</div>
|
|
75
|
+
|
|
88
76
|
</div>
|
|
89
77
|
|
|
90
78
|
<div v-else>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
|
|
80
|
+
<div class="flex flex-row items-start gap-2">
|
|
81
|
+
<Dropdown v-model="item.operator" @change="onFilterChange(item)" class="flex-1 min-w-[100px]">
|
|
82
|
+
<option value="">{{ $t('Select') }}</option>
|
|
83
|
+
<option value="startsWith">{{ $t('Starts with') }}</option>
|
|
84
|
+
<option value="endsWith">{{ $t('Ends with') }}</option>
|
|
85
|
+
<option value="contains">{{ $t('Contains') }}</option>
|
|
86
|
+
<option value="=">{{ $t('Equal') }}</option>
|
|
87
|
+
<option value="empty">{{ $t('Empty')}}</option>
|
|
88
|
+
<option value="notEmpty">{{ $t('Not empty')}}</option>
|
|
89
|
+
<option value="regex">{{ $t('Regexp')}}</option>
|
|
90
|
+
</Dropdown>
|
|
91
|
+
<Textbox v-if="![ 'empty', 'notEmpty' ].includes(item.operator)" class="w-[150px]" v-model="item.value" @keyup.enter="onFilterChange(item)"/>
|
|
92
|
+
<button v-if="filter.filters.length > 1" @click.stop="filter.filters.splice(filter.filters.indexOf(item), 1);onFilterChange(item)"
|
|
93
|
+
class="py-2">
|
|
94
94
|
<svg width="21" height="21" class="fill-text-100 hover:fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
95
95
|
<path class="secondary" fill-rule="evenodd" d="M15.78 14.36a1 1 0 0 1-1.42 1.42l-2.82-2.83-2.83 2.83a1 1 0 1 1-1.42-1.42l2.83-2.82L7.3 8.7a1 1 0 0 1 1.42-1.42l2.83 2.83 2.82-2.83a1 1 0 0 1 1.42 1.42l-2.83 2.83 2.83 2.82z"/>
|
|
96
96
|
</svg>
|
|
97
97
|
</button>
|
|
98
|
-
<div class="w-[60px]">
|
|
99
|
-
<Dropdown v-if="idx === 0" v-model="item.operand" @change="onFilterChange(item)">
|
|
100
|
-
<option value="">And</option>
|
|
101
|
-
<option value="or">Or</option>
|
|
102
|
-
</Dropdown>
|
|
103
|
-
</div>
|
|
104
|
-
<div class="flex-1 flex flex-col gap-2">
|
|
105
|
-
<Dropdown v-model="item.operator" @change="onFilterChange(item)">
|
|
106
|
-
<option value="">Select</option>
|
|
107
|
-
<option value="startsWith">Dimulai</option>
|
|
108
|
-
<option value="endsWith">Diakhiri</option>
|
|
109
|
-
<option value="contains">Berisi</option>
|
|
110
|
-
<option value="=">=</option>
|
|
111
|
-
</Dropdown>
|
|
112
|
-
|
|
113
|
-
<Textbox v-model="item.value" @keyup.enter="onFilterChange(item)"/>
|
|
114
|
-
|
|
115
|
-
<button v-if="idx === filter.filters.length - 1" @click="addMore" class="text-primary">Add More</button>
|
|
116
|
-
</div>
|
|
117
98
|
</div>
|
|
118
99
|
|
|
119
|
-
|
|
120
100
|
</div>
|
|
121
101
|
|
|
122
102
|
</div>
|
|
123
103
|
|
|
104
|
+
<div class="text-center">
|
|
105
|
+
<button @click="addMore" class="text-primary">{{ $t('Add More') }}</button>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
124
108
|
</div>
|
|
125
109
|
|
|
126
110
|
</div>
|
|
@@ -145,14 +129,12 @@ export default{
|
|
|
145
129
|
case 'date':
|
|
146
130
|
case 'enum':
|
|
147
131
|
this.filter.filters.push({
|
|
148
|
-
operand: 'or',
|
|
149
132
|
value: []
|
|
150
133
|
})
|
|
151
134
|
break
|
|
152
135
|
|
|
153
136
|
default:
|
|
154
137
|
this.filter.filters.push({
|
|
155
|
-
operand: 'or'
|
|
156
138
|
})
|
|
157
139
|
break
|
|
158
140
|
}
|
|
@@ -180,7 +162,7 @@ export default{
|
|
|
180
162
|
<style module>
|
|
181
163
|
|
|
182
164
|
.comp{
|
|
183
|
-
|
|
165
|
+
@apply py-4;
|
|
184
166
|
}
|
|
185
167
|
|
|
186
168
|
</style>
|
|
@@ -262,7 +262,10 @@ export default{
|
|
|
262
262
|
const x2 = e.touches ? e.touches[0].clientX : e.clientX
|
|
263
263
|
const d = x2 - x1
|
|
264
264
|
x1 = x2
|
|
265
|
-
|
|
265
|
+
|
|
266
|
+
let width = parseInt(this.columns[idx].width ?? _DEFAULT_COLUMN_WIDTH) + d
|
|
267
|
+
if(width < 20) width = 20
|
|
268
|
+
this.columns[idx].width = width
|
|
266
269
|
}
|
|
267
270
|
|
|
268
271
|
const onMouseUp = (e) => {
|
package/src/index.js
CHANGED
|
@@ -129,6 +129,52 @@ const popPreloads = () => {
|
|
|
129
129
|
return link
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
const util = {
|
|
133
|
+
|
|
134
|
+
push: (arr, item, opt = { key:"id" }) => {
|
|
135
|
+
const index = arr.findIndex((_) => _[opt.key] === item[opt.key])
|
|
136
|
+
if(index >= 0){
|
|
137
|
+
Object.assign(arr[index], item)
|
|
138
|
+
}
|
|
139
|
+
else{
|
|
140
|
+
arr.push(item)
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
onEvent: (arr, updates, opt = {}) => {
|
|
145
|
+
|
|
146
|
+
const [ model, event, items ] = updates
|
|
147
|
+
|
|
148
|
+
if(opt.model && opt.model !== model)
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
items.forEach((item) => {
|
|
152
|
+
|
|
153
|
+
const index = arr.findIndex((_) => _.id === item.id)
|
|
154
|
+
|
|
155
|
+
switch(event){
|
|
156
|
+
|
|
157
|
+
case 'create':
|
|
158
|
+
case 'update':
|
|
159
|
+
if(index >= 0){
|
|
160
|
+
Object.assign(arr[index], item)
|
|
161
|
+
}
|
|
162
|
+
else{
|
|
163
|
+
arr.push(item)
|
|
164
|
+
}
|
|
165
|
+
break
|
|
166
|
+
|
|
167
|
+
case 'destroy':
|
|
168
|
+
if(index >= 0){
|
|
169
|
+
arr.splice(index, 1)
|
|
170
|
+
}
|
|
171
|
+
break
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
}
|
|
177
|
+
|
|
132
178
|
export default{
|
|
133
179
|
|
|
134
180
|
install: (app, options) => {
|
|
@@ -148,6 +194,7 @@ export default{
|
|
|
148
194
|
app.config.globalProperties.$download = download
|
|
149
195
|
app.config.globalProperties.$preload = preload
|
|
150
196
|
app.config.globalProperties.$popPreloads = popPreloads
|
|
197
|
+
app.config.globalProperties.$util = util
|
|
151
198
|
|
|
152
199
|
app.component('Alert', defineAsyncComponent(() => import("./components/Alert.vue")))
|
|
153
200
|
app.component('Button', defineAsyncComponent(() => import("./components/Button.vue")))
|
package/src/utils/listpage1.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { Op } = require('sequelize')
|
|
2
|
-
const
|
|
2
|
+
const Sequelize = require('sequelize')
|
|
3
3
|
const dayjs = require("dayjs");
|
|
4
|
+
const util = require("util");
|
|
4
5
|
|
|
5
6
|
let ListPage1 = {
|
|
6
7
|
|
|
@@ -9,7 +10,7 @@ let ListPage1 = {
|
|
|
9
10
|
model: '',
|
|
10
11
|
channel: '',
|
|
11
12
|
|
|
12
|
-
async patch(params){
|
|
13
|
+
async patch(params, socket){
|
|
13
14
|
|
|
14
15
|
const { reset } = params
|
|
15
16
|
|
|
@@ -80,22 +81,14 @@ let ListPage1 = {
|
|
|
80
81
|
]
|
|
81
82
|
|
|
82
83
|
let attributes = { id:1, updatedAt:1 }
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
case 'item':
|
|
89
|
-
case 'option':
|
|
90
|
-
// Ignored
|
|
91
|
-
break
|
|
92
|
-
|
|
93
|
-
default:
|
|
94
|
-
attributes[column.key] = 1
|
|
95
|
-
break
|
|
84
|
+
if(preset.columns){
|
|
85
|
+
let modelAttributes = this.model.getAttributes();
|
|
86
|
+
preset.columns.forEach((column) => {
|
|
87
|
+
if(column.visible && modelAttributes[column.key]){
|
|
88
|
+
attributes[column.key] = 1
|
|
96
89
|
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
90
|
+
})
|
|
91
|
+
}
|
|
99
92
|
attributes = Object.keys(attributes)
|
|
100
93
|
|
|
101
94
|
if(preset.filters){
|
|
@@ -129,12 +122,21 @@ let ListPage1 = {
|
|
|
129
122
|
}
|
|
130
123
|
}
|
|
131
124
|
|
|
132
|
-
|
|
125
|
+
let attributeIncludes
|
|
126
|
+
if(this.getAttributeIncludes){
|
|
127
|
+
attributeIncludes = this.getAttributeIncludes()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { rows:items, count } = await this.model.findAndCountAll({
|
|
133
131
|
where,
|
|
134
|
-
attributes
|
|
132
|
+
attributes: {
|
|
133
|
+
...attributes,
|
|
134
|
+
include: attributeIncludes
|
|
135
|
+
},
|
|
135
136
|
order,
|
|
136
137
|
limit: itemsPerPage + 1,
|
|
137
138
|
replacements,
|
|
139
|
+
include: this.getModelIncludes ? this.getModelIncludes() : undefined
|
|
138
140
|
})
|
|
139
141
|
|
|
140
142
|
const hasNext = items.length > itemsPerPage
|
|
@@ -148,7 +150,22 @@ let ListPage1 = {
|
|
|
148
150
|
|
|
149
151
|
return {
|
|
150
152
|
items,
|
|
151
|
-
hasNext
|
|
153
|
+
hasNext,
|
|
154
|
+
count
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
async subscribe(params, socket){
|
|
159
|
+
const { name } = params
|
|
160
|
+
if(name){
|
|
161
|
+
socket.join(this.name)
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
async unsubscribe(params, socket){
|
|
166
|
+
const { name } = params
|
|
167
|
+
if(name){
|
|
168
|
+
socket.leave(this.name)
|
|
152
169
|
}
|
|
153
170
|
},
|
|
154
171
|
|
|
@@ -163,17 +180,20 @@ let ListPage1 = {
|
|
|
163
180
|
|
|
164
181
|
preset.filters.forEach((filter) => {
|
|
165
182
|
|
|
183
|
+
if(!filter.enabled) return
|
|
184
|
+
|
|
166
185
|
const key = filter.key
|
|
167
186
|
const type = filter.type
|
|
187
|
+
const operand = filter.operand === 'or' ? 'or' : 'and'
|
|
168
188
|
const items = filter.filters ?? []
|
|
169
|
-
const operand = items[0] && items[0].operand && items[0].operand === 'or' && items.length > 1 ?
|
|
170
|
-
'or' : 'and'
|
|
171
189
|
|
|
172
190
|
let whereValue = []
|
|
173
191
|
switch(type){
|
|
174
192
|
|
|
175
193
|
case 'date':
|
|
176
194
|
items.forEach((item) => {
|
|
195
|
+
if(!('operator' in item) || (item.operator === 'between' && !('value' in item))) return
|
|
196
|
+
|
|
177
197
|
const operator = item.operator
|
|
178
198
|
const value = item.value
|
|
179
199
|
|
|
@@ -238,6 +258,8 @@ let ListPage1 = {
|
|
|
238
258
|
|
|
239
259
|
case 'enum':
|
|
240
260
|
items.forEach((item) => {
|
|
261
|
+
if(!('value' in item)) return
|
|
262
|
+
|
|
241
263
|
const value = item.value ?? []
|
|
242
264
|
whereValue = {
|
|
243
265
|
...whereValue,
|
|
@@ -249,6 +271,7 @@ let ListPage1 = {
|
|
|
249
271
|
case 'number':
|
|
250
272
|
case 'currency':
|
|
251
273
|
items.forEach((item) => {
|
|
274
|
+
if(!('operator' in item) || !('value' in item)) return
|
|
252
275
|
|
|
253
276
|
const operator = item.operator
|
|
254
277
|
const value = item.value
|
|
@@ -295,6 +318,9 @@ let ListPage1 = {
|
|
|
295
318
|
|
|
296
319
|
default:
|
|
297
320
|
items.forEach((item) => {
|
|
321
|
+
if(!('operator' in item) ||
|
|
322
|
+
([ 'startsWith', 'endsWith', 'contains', '=' ].includes(item.operator) && !'value' in item))
|
|
323
|
+
return
|
|
298
324
|
|
|
299
325
|
const operator = item.operator
|
|
300
326
|
const value = item.value
|
|
@@ -324,6 +350,33 @@ let ListPage1 = {
|
|
|
324
350
|
[Op.eq]: value
|
|
325
351
|
})
|
|
326
352
|
break
|
|
353
|
+
|
|
354
|
+
case 'empty':
|
|
355
|
+
whereValue.push({
|
|
356
|
+
[Op.or]: [
|
|
357
|
+
{ [Op.eq]: null },
|
|
358
|
+
{ [Op.eq]: '' },
|
|
359
|
+
]
|
|
360
|
+
})
|
|
361
|
+
break
|
|
362
|
+
|
|
363
|
+
case 'notEmpty':
|
|
364
|
+
whereValue.push({
|
|
365
|
+
[Op.and]: [
|
|
366
|
+
{ [Op.ne]: null },
|
|
367
|
+
{ [Op.ne]: '' },
|
|
368
|
+
]
|
|
369
|
+
})
|
|
370
|
+
break
|
|
371
|
+
|
|
372
|
+
case 'length':
|
|
373
|
+
break
|
|
374
|
+
|
|
375
|
+
case 'regex':
|
|
376
|
+
whereValue.push({
|
|
377
|
+
[Op.regexp]: value
|
|
378
|
+
})
|
|
379
|
+
break
|
|
327
380
|
}
|
|
328
381
|
})
|
|
329
382
|
break
|
|
@@ -352,6 +405,8 @@ let ListPage1 = {
|
|
|
352
405
|
})
|
|
353
406
|
}
|
|
354
407
|
|
|
408
|
+
//console.log(util.inspect(where, false, null, true /* enable colors */))
|
|
409
|
+
|
|
355
410
|
/*console.log(util.inspect(preset.filters, false, null, true /!* enable colors *!/))
|
|
356
411
|
console.log(where)
|
|
357
412
|
console.log(replacements)*/
|
|
@@ -416,28 +471,15 @@ let ListPage1 = {
|
|
|
416
471
|
return sortWhere
|
|
417
472
|
},
|
|
418
473
|
|
|
419
|
-
async onHooks(model, event, items,
|
|
474
|
+
async onHooks(model, event, items, socket){
|
|
420
475
|
|
|
421
476
|
switch(event){
|
|
422
|
-
|
|
423
477
|
case 'destroy':
|
|
424
478
|
case 'remove':
|
|
425
|
-
io.to(model).emit(model, event, items)
|
|
426
|
-
break
|
|
427
|
-
|
|
428
479
|
default:
|
|
429
|
-
|
|
430
|
-
where: {
|
|
431
|
-
id: {
|
|
432
|
-
[Op.in]: items.map((_) => _.id)
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
})
|
|
436
|
-
io.to(model).emit(`${model}${event}`, instances)
|
|
480
|
+
socket.emit(model, event, items)
|
|
437
481
|
break
|
|
438
|
-
|
|
439
482
|
}
|
|
440
|
-
|
|
441
483
|
}
|
|
442
484
|
|
|
443
485
|
}
|