@mixd-id/web-scaffold 0.1.230406284 → 0.1.230406285
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/Checkbox.vue +2 -2
- package/src/components/List.vue +440 -187
- package/src/components/PresetSelectorFilterItem.vue +22 -12
- package/src/components/VirtualTable.vue +18 -5
- package/src/utils/preset-selector.js +18 -0
- package/src/widgets/PresetBar.vue +51 -34
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="$style.comp">
|
|
3
3
|
<input :id="id" type="checkbox" :checked="checked" @change="onChange" :disabled="isDisabled"/>
|
|
4
|
-
<label :for="id" :class="labelClass">
|
|
4
|
+
<label :for="id" :class="labelClass" class="whitespace-nowrap text-ellipsis overflow-hidden">
|
|
5
5
|
<div :class="$style.indicator" v-if="Boolean(showIcon)">
|
|
6
6
|
<Transition name="checkbox">
|
|
7
7
|
<div v-if="checked">
|
|
@@ -127,7 +127,7 @@ export default {
|
|
|
127
127
|
<style module>
|
|
128
128
|
|
|
129
129
|
.comp{
|
|
130
|
-
@apply flex
|
|
130
|
+
@apply flex flex-row;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
.comp label{
|
package/src/components/List.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="flex flex-col">
|
|
3
3
|
|
|
4
|
-
<div v-if="readyState
|
|
4
|
+
<div v-if="readyState >= 1"
|
|
5
5
|
class="flex-1 flex flex-row">
|
|
6
6
|
|
|
7
7
|
<div v-if="computedPresetMode === 'sidebar' && sidebar.open"
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<PresetBar :config="config"
|
|
12
12
|
class="flex-1"
|
|
13
|
+
:enumCache="enumCache"
|
|
13
14
|
@apply="load">
|
|
14
15
|
|
|
15
16
|
<template #toolbar>
|
|
@@ -25,142 +26,210 @@
|
|
|
25
26
|
@mousedown="(e) => $util.dragResize(e, resize1)"></div>
|
|
26
27
|
</div>
|
|
27
28
|
|
|
28
|
-
<div class="flex-1 flex flex-col"
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<div class="flex flex-
|
|
41
|
-
<div class="
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
<div class="flex-1 flex flex-col">
|
|
30
|
+
|
|
31
|
+
<div class="flex-1 flex flex-col" :class="containerClass">
|
|
32
|
+
<slot name="head"
|
|
33
|
+
:preset="preset"
|
|
34
|
+
:presetSelector="$refs.presetSelector"
|
|
35
|
+
:compPrefix="compPrefix">
|
|
36
|
+
<div class="flex flex-col md:flex-row gap-3 md:items-start overflow-hidden leading-tight p-3 md:p-0 md:pl-3 panel-400 md:panel-none border-b-[1px] border-text-50 md:border-none"
|
|
37
|
+
:class="headerClass">
|
|
38
|
+
|
|
39
|
+
<div class="flex md:flex-1 flex-row items-start gap-3">
|
|
40
|
+
|
|
41
|
+
<div class="flex flex-row gap-2 md:gap-3 items-center" ref="title">
|
|
42
|
+
<div class="flex flex-col whitespace-nowrap text-ellipsis overflow-hidden">
|
|
43
|
+
<div class="cursor-pointer group" @click="$refs.contextMenu.open($refs.title)">
|
|
44
|
+
<small class="text-text-400 text-xs">{{ title ?? 'Untitled' }}</small>
|
|
45
|
+
<div class="flex flex-row items-baseline gap-1">
|
|
46
|
+
<h5 class="whitespace-nowrap relative top-[-2px] text-ellipsis overflow-hidden group-hover:text-primary">
|
|
47
|
+
{{ preset.name ?? 'Preset Name' }}
|
|
48
|
+
</h5>
|
|
49
|
+
<svg width="13" height="13" class="fill-text group-hover:fill-primary relative top-[-1px]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M310.6 246.6l-127.1 128C176.4 380.9 168.2 384 160 384s-16.38-3.125-22.63-9.375l-127.1-128C.2244 237.5-2.516 223.7 2.438 211.8S19.07 192 32 192h255.1c12.94 0 24.62 7.781 29.58 19.75S319.8 237.5 310.6 246.6z"/></svg>
|
|
50
|
+
</div>
|
|
48
51
|
</div>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
<ContextMenu ref="contextMenu" class="panel-400">
|
|
53
|
+
<div class="flex flex-col min-w-[300px] divide-y divide-text-50">
|
|
54
|
+
|
|
55
|
+
<div class="p-3 flex flex-col gap-1 bg-base-300">
|
|
56
|
+
<button v-for="(preset, idx) in config.presets"
|
|
57
|
+
class="p-2 px-5 text-left flex flex-row items-center rounded-md"
|
|
58
|
+
:class="config.presetIdx === idx ? 'bg-primary-200 text-white' : 'hover:bg-primary-100'"
|
|
59
|
+
type="button"
|
|
60
|
+
@click="selectPreset(idx)">
|
|
61
|
+
<label class="flex-1 pr-12">
|
|
62
|
+
{{ preset.name }}
|
|
63
|
+
</label>
|
|
64
|
+
<div class="p-1">
|
|
65
|
+
<div v-if="idx < 10"
|
|
66
|
+
class="border-[1px] border-text-300 px-2 text-mono text-xs text-text-400 rounded-lg">
|
|
67
|
+
Alt {{ idx + 1 }}
|
|
68
|
+
</div>
|
|
66
69
|
</div>
|
|
67
|
-
</
|
|
68
|
-
</
|
|
69
|
-
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="px-3">
|
|
74
|
+
<button type="button" class="text-primary p-5 text-left w-full" @click="openPreset">Edit</button>
|
|
75
|
+
</div>
|
|
70
76
|
|
|
71
|
-
<div class="px-3">
|
|
72
|
-
<button type="button" class="text-primary p-5 text-left w-full" @click="openPreset">Edit</button>
|
|
73
77
|
</div>
|
|
78
|
+
</ContextMenu>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="flex-1"></div>
|
|
83
|
+
|
|
84
|
+
<slot name="toolbar"></slot>
|
|
85
|
+
</div>
|
|
74
86
|
|
|
87
|
+
<div class="flex flex-row gap-2 gap-1" v-if="Boolean(toolbar)">
|
|
88
|
+
<Textbox class="flex-1 md:w-[240px]" placeholder="Search..." clearable="1"
|
|
89
|
+
v-model="preset.search"
|
|
90
|
+
@clear="delete preset.search; load()"
|
|
91
|
+
@keyup.enter="load">
|
|
92
|
+
<template #start>
|
|
93
|
+
<div class="p-2 pr-0">
|
|
94
|
+
<svg width="16" height="16" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M504.1 471l-134-134C399.1 301.5 415.1 256.8 415.1 208c0-114.9-93.13-208-208-208S-.0002 93.13-.0002 208S93.12 416 207.1 416c48.79 0 93.55-16.91 129-45.04l134 134C475.7 509.7 481.9 512 488 512s12.28-2.344 16.97-7.031C514.3 495.6 514.3 480.4 504.1 471zM48 208c0-88.22 71.78-160 160-160s160 71.78 160 160s-71.78 160-160 160S48 296.2 48 208z"/></svg>
|
|
75
95
|
</div>
|
|
76
|
-
</
|
|
96
|
+
</template>
|
|
97
|
+
</Textbox>
|
|
98
|
+
|
|
99
|
+
<div v-if="$slots.gridItem"
|
|
100
|
+
class="hidden md:grid grid-cols-2 gap-[1px] border-[1px] border-text-50 rounded-lg">
|
|
101
|
+
<Radio v-model="preset.view"
|
|
102
|
+
name="view"
|
|
103
|
+
value="table"
|
|
104
|
+
:custom="true"
|
|
105
|
+
class="p-2 px-3 rounded-lg rounded-r-none"
|
|
106
|
+
:class="preset.view === 'table' ? 'bg-primary-500' : 'bg-base-500'">
|
|
107
|
+
<svg width="14" height="14" :class="preset.view === 'table' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M448 32H64.05C28.7 32 .0492 60.65 .0492 96v320c0 35.35 28.65 64 64 64h383.1c35.35 0 64-28.65 64-64V96C512 60.65 483.4 32 448 32zM224 416H64v-96h160V416zM224 256H64V160h160V256zM448 416h-160v-96h160V416zM448 256h-160V160h160V256z"/></svg>
|
|
108
|
+
</Radio>
|
|
109
|
+
<Radio v-model="preset.view"
|
|
110
|
+
name="view"
|
|
111
|
+
value="grid"
|
|
112
|
+
:custom="true"
|
|
113
|
+
class="p-2 px-3 bg-base-500 rounded-lg rounded-l-none"
|
|
114
|
+
:class="preset.view === 'grid' ? 'bg-primary-500' : 'bg-base-500'">
|
|
115
|
+
<svg width="14" height="14" :class="preset.view === 'grid' ? 'fill-white' : 'fill-text'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M144 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C176 302.3 161.7 288 144 288zM368 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C400 302.3 385.7 288 368 288zM592 288h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32v-96C624 302.3 609.7 288 592 288zM144 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C176 78.33 161.7 64 144 64zM368 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C400 78.33 385.7 64 368 64zM592 64h-96c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h96c17.67 0 32-14.33 32-32V96C624 78.33 609.7 64 592 64z"/></svg>
|
|
116
|
+
</Radio>
|
|
77
117
|
</div>
|
|
78
118
|
</div>
|
|
79
119
|
|
|
80
|
-
<div class="flex-1"></div>
|
|
81
|
-
|
|
82
|
-
<slot name="toolbar"></slot>
|
|
83
120
|
</div>
|
|
121
|
+
</slot>
|
|
122
|
+
|
|
123
|
+
<VirtualTable v-if="presetView === 'table'"
|
|
124
|
+
ref="table"
|
|
125
|
+
:columns="columns"
|
|
126
|
+
class="flex-1 rounded-lg panel-400"
|
|
127
|
+
:items="data.items"
|
|
128
|
+
:enumCache="enumCache"
|
|
129
|
+
@scroll-end="loadNext"
|
|
130
|
+
@item-click="onTableItemClick">
|
|
131
|
+
|
|
132
|
+
<template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
|
|
133
|
+
<div :class="getHeader(slot.replace('col-', ''))">
|
|
134
|
+
<slot :name="slot" :item="item" :index="index"></slot>
|
|
135
|
+
</div>
|
|
136
|
+
</template>
|
|
84
137
|
|
|
85
|
-
<
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
138
|
+
<template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
|
|
139
|
+
<slot :name="slot" :item="item" :index="index"></slot>
|
|
140
|
+
</template>
|
|
141
|
+
|
|
142
|
+
</VirtualTable>
|
|
143
|
+
|
|
144
|
+
<VirtualGrid v-else-if="presetView === 'grid'"
|
|
145
|
+
ref="grid"
|
|
146
|
+
:items="dataItems"
|
|
147
|
+
:column="computedGridColumn"
|
|
148
|
+
:class="gridClass"
|
|
149
|
+
@scroll-end="loadNext"
|
|
150
|
+
:container-class="`${gridContainerClass}`"
|
|
151
|
+
:config="config">
|
|
152
|
+
<template #item="{ item }">
|
|
153
|
+
<slot name="gridItem" :item="item" :enumCache="enumCache">
|
|
154
|
+
<div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
|
|
155
|
+
<div>
|
|
156
|
+
<Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
|
|
93
157
|
</div>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
158
|
+
<div class="flex-1 flex flex-col leading-tight">
|
|
159
|
+
<small>{{ item.code }}</small>
|
|
160
|
+
<strong>{{ item.name }}</strong>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</slot>
|
|
164
|
+
</template>
|
|
165
|
+
</VirtualGrid>
|
|
166
|
+
|
|
167
|
+
<PresetSelector ref="presetSelector" :config="config" @select="load" />
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div v-if="extBar.open && pivotEnabled"
|
|
171
|
+
:style="extStyle" class="border-t-[1px] border-text-50 flex flex-col relative md:p-5">
|
|
172
|
+
|
|
173
|
+
<div :class="$style.resize2"
|
|
174
|
+
@mousedown="(e) => $util.dragResize(e, resize2)">
|
|
175
|
+
<button type="button" @click.prevent="extBar.open = false"
|
|
176
|
+
:class="$style.extClose">
|
|
177
|
+
<svg width="14"
|
|
178
|
+
height="14"
|
|
179
|
+
class="fill-text-300 cursor-pointer"
|
|
180
|
+
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432.6 209.3l-191.1 183.1C235.1 397.8 229.1 400 224 400s-11.97-2.219-16.59-6.688L15.41 209.3C5.814 200.2 5.502 184.1 14.69 175.4c9.125-9.625 24.38-9.938 33.91-.7187L224 342.8l175.4-168c9.5-9.219 24.78-8.906 33.91 .7187C442.5 184.1 442.2 200.2 432.6 209.3z"/></svg>
|
|
181
|
+
</button>
|
|
182
|
+
</div>
|
|
117
183
|
|
|
184
|
+
<div v-if="readyState === 3" class="flex-1 flex items-center justify-center">
|
|
185
|
+
<svg class="animate-spin" width="36" height="36" 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>
|
|
118
186
|
</div>
|
|
119
|
-
</slot>
|
|
120
187
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
188
|
+
<VirtualTable v-else-if="presetView === 'table'"
|
|
189
|
+
ref="table"
|
|
190
|
+
:columns="preset.columns"
|
|
191
|
+
class="flex-1 rounded-lg panel-400"
|
|
192
|
+
:items="data.extItems"
|
|
193
|
+
:enumCache="enumCache"
|
|
194
|
+
@scroll-end="loadExtNext">
|
|
195
|
+
|
|
196
|
+
<template v-for="(_, slot) in headerSlots" #[slot]="{ item, index }">
|
|
197
|
+
<div :class="getHeader(slot.replace('col-', ''))">
|
|
198
|
+
<slot :name="slot" :item="item" :index="index"></slot>
|
|
199
|
+
</div>
|
|
200
|
+
</template>
|
|
127
201
|
|
|
128
|
-
|
|
129
|
-
<div :class="getHeader(slot.replace('col-', ''))">
|
|
202
|
+
<template v-for="(_, slot) in contentSlots" #[slot]="{ item, index }">
|
|
130
203
|
<slot :name="slot" :item="item" :index="index"></slot>
|
|
131
|
-
</
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
</div>
|
|
154
|
-
<div class="flex-1 flex flex-col leading-tight">
|
|
155
|
-
<small>{{ item.code }}</small>
|
|
156
|
-
<strong>{{ item.name }}</strong>
|
|
204
|
+
</template>
|
|
205
|
+
|
|
206
|
+
</VirtualTable>
|
|
207
|
+
|
|
208
|
+
<VirtualGrid v-else-if="presetView === 'grid'"
|
|
209
|
+
ref="grid"
|
|
210
|
+
:items="data.extItems"
|
|
211
|
+
:column="computedGridColumn"
|
|
212
|
+
:class="gridClass"
|
|
213
|
+
@scroll-end="loadExtNext"
|
|
214
|
+
:container-class="`${gridContainerClass}`"
|
|
215
|
+
:config="config">
|
|
216
|
+
<template #item="{ item }">
|
|
217
|
+
<slot name="gridItem" :item="item">
|
|
218
|
+
<div class="flex flex-row panel-400 rounded-lg overflow-hidden md:rounded-lg overflow-hidden p-3 gap-3">
|
|
219
|
+
<div>
|
|
220
|
+
<Image :src="item.imageUrl" class="bg-text-50 w-[64px] h-[64px] rounded-lg" />
|
|
221
|
+
</div>
|
|
222
|
+
<div class="flex-1 flex flex-col leading-tight">
|
|
223
|
+
<small>{{ item.code }}</small>
|
|
224
|
+
<strong>{{ item.name }}</strong>
|
|
225
|
+
</div>
|
|
157
226
|
</div>
|
|
158
|
-
</
|
|
159
|
-
</
|
|
160
|
-
</
|
|
161
|
-
|
|
227
|
+
</slot>
|
|
228
|
+
</template>
|
|
229
|
+
</VirtualGrid>
|
|
230
|
+
|
|
231
|
+
</div>
|
|
162
232
|
|
|
163
|
-
<PresetSelector ref="presetSelector" :config="config" @select="load" />
|
|
164
233
|
</div>
|
|
165
234
|
|
|
166
235
|
</div>
|
|
@@ -180,6 +249,10 @@ import PresetBar from "../widgets/PresetBar.vue";
|
|
|
180
249
|
|
|
181
250
|
export default{
|
|
182
251
|
|
|
252
|
+
emits: [ 'after-load', 'open-preset', 'signal', 'pivot-item-click' ],
|
|
253
|
+
|
|
254
|
+
inject: [ 'socket', 'toast' ],
|
|
255
|
+
|
|
183
256
|
props: {
|
|
184
257
|
config: {
|
|
185
258
|
type: Object,
|
|
@@ -231,9 +304,103 @@ export default{
|
|
|
231
304
|
.join(' ')
|
|
232
305
|
},
|
|
233
306
|
|
|
307
|
+
generatePivotColumns(items){
|
|
308
|
+
if(!this.preset.pivot || !this.preset.pivot.enabled) return
|
|
309
|
+
if(!Array.isArray(items) && items.length < 1) return
|
|
310
|
+
|
|
311
|
+
const tableColumns = (this.preset.columns ?? this.config.columns)
|
|
312
|
+
.reduce((cur, obj) => {
|
|
313
|
+
cur[obj.key] = obj
|
|
314
|
+
return cur
|
|
315
|
+
}, {})
|
|
316
|
+
|
|
317
|
+
const currentPivotColumns = [ ...(this.preset.pivot.columns ?? []) ]
|
|
318
|
+
.reduce((cur, obj) => {
|
|
319
|
+
cur[obj.key] = obj
|
|
320
|
+
return cur
|
|
321
|
+
}, {})
|
|
322
|
+
|
|
323
|
+
const pivotColumns = []
|
|
324
|
+
|
|
325
|
+
for(let row of this.preset.pivot.rows){
|
|
326
|
+
|
|
327
|
+
let format
|
|
328
|
+
switch(row.aggregrate){
|
|
329
|
+
case 'year':
|
|
330
|
+
format = 'YYYY'
|
|
331
|
+
break
|
|
332
|
+
|
|
333
|
+
case 'month':
|
|
334
|
+
format = 'MMM YYYY'
|
|
335
|
+
break
|
|
336
|
+
|
|
337
|
+
case 'date':
|
|
338
|
+
format = 'D MMM YYYY'
|
|
339
|
+
break
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if(currentPivotColumns[row.key]){
|
|
343
|
+
Object.assign(currentPivotColumns[row.key], {
|
|
344
|
+
format,
|
|
345
|
+
visible: true
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
pivotColumns.push(currentPivotColumns[row.key])
|
|
349
|
+
}
|
|
350
|
+
else{
|
|
351
|
+
pivotColumns.push({
|
|
352
|
+
...tableColumns[row.key],
|
|
353
|
+
format,
|
|
354
|
+
visible: true
|
|
355
|
+
})
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for(let key in items[0]){
|
|
360
|
+
if(key.startsWith('_')){
|
|
361
|
+
let [ k1, ...k2 ] = key.substring(1).split('-')
|
|
362
|
+
k2 = k2.join('-')
|
|
363
|
+
|
|
364
|
+
let label = k2.replace(/_/gi, ' ')
|
|
365
|
+
const column = tableColumns[k1]
|
|
366
|
+
if(column){
|
|
367
|
+
switch(column.type){
|
|
368
|
+
case 'enum':
|
|
369
|
+
label = (column.typeParams ?? []).find(_ => `${_.value}` === `${k2}`)?.text ?? label
|
|
370
|
+
break
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if(currentPivotColumns[key]){
|
|
375
|
+
Object.assign(currentPivotColumns[key], {
|
|
376
|
+
visible: true
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
pivotColumns.push(currentPivotColumns[key])
|
|
380
|
+
}
|
|
381
|
+
else{
|
|
382
|
+
pivotColumns.push({
|
|
383
|
+
key,
|
|
384
|
+
label,
|
|
385
|
+
label2: (((tableColumns[k1] ?? {}).label ?? '') + ': ' + label).trim(),
|
|
386
|
+
type: 'number',
|
|
387
|
+
})
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
(this.preset.pivot.columns ?? []).filter(_ => !pivotColumns.find(i => i.key === _.key))
|
|
393
|
+
.forEach(_ => {
|
|
394
|
+
_.visible = false
|
|
395
|
+
pivotColumns.push(_)
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
this.preset.pivot.columns = pivotColumns
|
|
399
|
+
|
|
400
|
+
},
|
|
401
|
+
|
|
234
402
|
load(){
|
|
235
403
|
if(this.src){
|
|
236
|
-
|
|
237
404
|
if(!this.preset.columns){
|
|
238
405
|
this.preset.columns = JSON.parse(JSON.stringify(this.config.columns))
|
|
239
406
|
}
|
|
@@ -244,54 +411,15 @@ export default{
|
|
|
244
411
|
})
|
|
245
412
|
.then(data => {
|
|
246
413
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const tableColumns = (this.preset.columns ?? this.config.columns).reduce((cur, obj) => {
|
|
251
|
-
cur[obj.key] = obj
|
|
252
|
-
return cur
|
|
253
|
-
}, {})
|
|
254
|
-
|
|
255
|
-
const columns = []
|
|
256
|
-
for(let key in data.items[0]){
|
|
257
|
-
if(key.startsWith('_')){
|
|
258
|
-
let [ k1, ...k2 ] = key.substring(1).split('-')
|
|
259
|
-
k2 = k2.join('-')
|
|
260
|
-
|
|
261
|
-
let label = k2.replace(/_/gi, ' ')
|
|
262
|
-
const column = tableColumns[k1]
|
|
263
|
-
if(column){
|
|
264
|
-
switch(column.type){
|
|
265
|
-
case 'enum':
|
|
266
|
-
label = (column.typeParams ?? []).find(_ => `${_.value}` === `${k2}`)?.text ?? label
|
|
267
|
-
break
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if(!tableColumns[key]){
|
|
272
|
-
columns.push({
|
|
273
|
-
key,
|
|
274
|
-
label,
|
|
275
|
-
label2: (((tableColumns[k1] ?? {}).label ?? '') + ': ' + label).trim(),
|
|
276
|
-
type: 'number',
|
|
277
|
-
})
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if(!Array.isArray(this.preset.columns)){
|
|
283
|
-
this.preset.columns = JSON.parse(JSON.stringify(this.config.columns))
|
|
284
|
-
}
|
|
285
|
-
this.preset.columns.push(...columns)
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
this.loadEnums()
|
|
289
|
-
|
|
414
|
+
this.generatePivotColumns(data.items)
|
|
415
|
+
this.loadEnums(data.items)
|
|
290
416
|
Object.assign(this.data, data ?? {})
|
|
291
|
-
|
|
292
417
|
this.$emit('after-load')
|
|
418
|
+
|
|
419
|
+
})
|
|
420
|
+
.catch(err => {
|
|
421
|
+
this.toast(err)
|
|
293
422
|
})
|
|
294
|
-
.catch(err => this.toast(err))
|
|
295
423
|
}
|
|
296
424
|
return new Promise(resolve => resolve())
|
|
297
425
|
},
|
|
@@ -312,6 +440,7 @@ export default{
|
|
|
312
440
|
//console.log('next id', data.items[0].id)
|
|
313
441
|
|
|
314
442
|
this.data.items.push(...data.items)
|
|
443
|
+
this.loadEnums(data.items)
|
|
315
444
|
})
|
|
316
445
|
.catch(err => this.toast(err))
|
|
317
446
|
}
|
|
@@ -347,37 +476,94 @@ export default{
|
|
|
347
476
|
}
|
|
348
477
|
},
|
|
349
478
|
|
|
350
|
-
loadEnums(){
|
|
479
|
+
loadEnums(items){
|
|
351
480
|
|
|
352
481
|
for(let i in this.preset.columns){
|
|
353
482
|
const column = this.preset.columns[i]
|
|
354
483
|
|
|
355
484
|
if(column.enumSrc) {
|
|
356
|
-
|
|
357
|
-
this.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
{
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
485
|
+
|
|
486
|
+
if(!this.enumCache[column.key])
|
|
487
|
+
this.enumCache[column.key] = {}
|
|
488
|
+
|
|
489
|
+
for(let item of items){
|
|
490
|
+
if(item[column.key] && !this.enumCache[column.key][item[column.key]]){
|
|
491
|
+
this.enumCache[column.key][item[column.key]] = { value:item[column.key] }
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const reqItems = Object.values(this.enumCache[column.key]).filter(_ => !_.text).map(_ => _.value)
|
|
496
|
+
|
|
497
|
+
if(reqItems.length > 0){
|
|
498
|
+
|
|
499
|
+
const [ src, key, attr ] = column.enumSrc.split(':')
|
|
500
|
+
this.socket.send(src, {
|
|
501
|
+
columns: [
|
|
502
|
+
{ key:attr, visible:true }
|
|
503
|
+
],
|
|
504
|
+
itemsPerPage: 1000,
|
|
505
|
+
filters: [
|
|
506
|
+
{
|
|
507
|
+
key:key, value: [ { operator:'in', value:reqItems } ]
|
|
372
508
|
}
|
|
373
|
-
|
|
509
|
+
]
|
|
374
510
|
})
|
|
375
|
-
|
|
511
|
+
.then(res => {
|
|
512
|
+
(res.items ?? []).forEach(_ => {
|
|
513
|
+
this.enumCache[column.key][_[key]] = {
|
|
514
|
+
text: _[attr] ?? _[key],
|
|
515
|
+
value: _[key],
|
|
516
|
+
}
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
for(let key in this.enumCache[column.key]){
|
|
520
|
+
if(!this.enumCache[column.key][key].text)
|
|
521
|
+
this.enumCache[column.key][key].text = key
|
|
522
|
+
}
|
|
523
|
+
})
|
|
524
|
+
.catch(err => this.log('Load enums error', err))
|
|
525
|
+
}
|
|
376
526
|
}
|
|
377
527
|
}
|
|
378
528
|
|
|
379
529
|
},
|
|
380
530
|
|
|
531
|
+
loadExt(){
|
|
532
|
+
if(!this.preset.filters || !this.extBar.open) return
|
|
533
|
+
|
|
534
|
+
this.readyState = 3
|
|
535
|
+
return this.socket.send(this.src, {
|
|
536
|
+
...this.preset,
|
|
537
|
+
filters: [
|
|
538
|
+
...(this.preset.filters ?? []),
|
|
539
|
+
...(this.extBar.filters ?? [])
|
|
540
|
+
],
|
|
541
|
+
pivot: null
|
|
542
|
+
})
|
|
543
|
+
.then((res) => {
|
|
544
|
+
this.data.extItems = res.items
|
|
545
|
+
})
|
|
546
|
+
.finally(_ => this.readyState = 1)
|
|
547
|
+
},
|
|
548
|
+
|
|
549
|
+
loadExtNext(){
|
|
550
|
+
if(!this.preset.filters) return
|
|
551
|
+
|
|
552
|
+
return this.socket.send(this.src, {
|
|
553
|
+
...this.preset,
|
|
554
|
+
filters: [
|
|
555
|
+
...(this.preset.filters ?? []),
|
|
556
|
+
...(this.extBar.filters ?? [])
|
|
557
|
+
],
|
|
558
|
+
pivot: null,
|
|
559
|
+
afterItem: this.data.extItems[this.data.extItems.length - 1],
|
|
560
|
+
})
|
|
561
|
+
.then(data => {
|
|
562
|
+
this.data.extItems.push(...data.items)
|
|
563
|
+
})
|
|
564
|
+
.catch(err => this.toast(err))
|
|
565
|
+
},
|
|
566
|
+
|
|
381
567
|
resize1(w){
|
|
382
568
|
if(this.config.sidebar.width + w >= 270 && this.config.sidebar.width + w <= 600){
|
|
383
569
|
this.config.sidebar.width += w
|
|
@@ -458,11 +644,49 @@ export default{
|
|
|
458
644
|
this.$emit('signal', event, items)
|
|
459
645
|
},
|
|
460
646
|
|
|
647
|
+
onTableItemClick(item, column){
|
|
648
|
+
if((this.preset.pivot ?? {}).enabled && (column.key ?? '').startsWith('_')){
|
|
649
|
+
|
|
650
|
+
const filters = []
|
|
651
|
+
|
|
652
|
+
for(let idx in this.preset.pivot.rows){
|
|
653
|
+
filters.push({
|
|
654
|
+
key: this.preset.pivot.rows[idx].key,
|
|
655
|
+
value: [{
|
|
656
|
+
operator: '=',
|
|
657
|
+
value: item[this.preset.pivot.rows[idx].key]
|
|
658
|
+
}]
|
|
659
|
+
})
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const [ kk, vv ] = column.key.split('-')
|
|
663
|
+
if(![ 'count' ].includes(vv)){
|
|
664
|
+
filters.push({
|
|
665
|
+
key: kk.substring(1),
|
|
666
|
+
value: [{
|
|
667
|
+
operator: '=',
|
|
668
|
+
value: vv
|
|
669
|
+
}]
|
|
670
|
+
})
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
this.extBar.open = true
|
|
674
|
+
this.extBar.filters = filters
|
|
675
|
+
this.loadExt()
|
|
676
|
+
}
|
|
677
|
+
},
|
|
678
|
+
|
|
461
679
|
resize(){
|
|
462
680
|
this.$refs.table ? this.$refs.table.resize() : null
|
|
463
681
|
this.$refs.grid ? this.$refs.grid.resize() : null
|
|
464
682
|
},
|
|
465
683
|
|
|
684
|
+
resize2(_, h){
|
|
685
|
+
if(this.extBar.height - h >= 100 && this.extBar.height - h <= 600){
|
|
686
|
+
this.extBar.height -= h
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
|
|
466
690
|
onKeyDown(e){
|
|
467
691
|
|
|
468
692
|
if(e.altKey){
|
|
@@ -490,10 +714,6 @@ export default{
|
|
|
490
714
|
|
|
491
715
|
components: {PresetBar, PresetSelector, VirtualGrid, VirtualTable},
|
|
492
716
|
|
|
493
|
-
emits: [ 'after-load', 'open-preset', 'signal' ],
|
|
494
|
-
|
|
495
|
-
inject: [ 'socket', 'toast' ],
|
|
496
|
-
|
|
497
717
|
mounted(){
|
|
498
718
|
this.$addResizeListener(this.$el, () => this.compPrefix = this.$util.calculateMediaPrefix(this.$el.clientWidth))
|
|
499
719
|
|
|
@@ -501,6 +721,7 @@ export default{
|
|
|
501
721
|
.then(() => {
|
|
502
722
|
this.readyState = 1
|
|
503
723
|
this.load()
|
|
724
|
+
this.loadExt()
|
|
504
725
|
})
|
|
505
726
|
|
|
506
727
|
if(this.subscribeKey){
|
|
@@ -525,6 +746,8 @@ export default{
|
|
|
525
746
|
|
|
526
747
|
columns(){
|
|
527
748
|
if((this.preset.pivot ?? {}).enabled){
|
|
749
|
+
return this.preset.pivot.columns ?? []
|
|
750
|
+
|
|
528
751
|
|
|
529
752
|
const tableColumns = groupBy(this.preset.columns ?? this.config.columns, 'key')
|
|
530
753
|
|
|
@@ -636,6 +859,26 @@ export default{
|
|
|
636
859
|
}
|
|
637
860
|
},
|
|
638
861
|
|
|
862
|
+
extBar(){
|
|
863
|
+
if(!this.config.extbar)
|
|
864
|
+
this.config.extbar = {
|
|
865
|
+
open: false,
|
|
866
|
+
height: Math.round(window.innerHeight / 2.2)
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
return this.config.extbar
|
|
870
|
+
},
|
|
871
|
+
|
|
872
|
+
extStyle(){
|
|
873
|
+
return {
|
|
874
|
+
height: this.extBar.height + "px"
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
|
|
878
|
+
pivotEnabled(){
|
|
879
|
+
return ((this.preset ?? {}).pivot ?? {}).enabled
|
|
880
|
+
}
|
|
881
|
+
|
|
639
882
|
},
|
|
640
883
|
|
|
641
884
|
data(){
|
|
@@ -646,6 +889,8 @@ export default{
|
|
|
646
889
|
},
|
|
647
890
|
observer: null,
|
|
648
891
|
compPrefix: '',
|
|
892
|
+
enumCache: {},
|
|
893
|
+
extItems: null,
|
|
649
894
|
}
|
|
650
895
|
},
|
|
651
896
|
|
|
@@ -670,4 +915,12 @@ export default{
|
|
|
670
915
|
@apply w-[3px] cursor-ew-resize absolute top-0 right-0 bottom-0;
|
|
671
916
|
}
|
|
672
917
|
|
|
918
|
+
.resize2{
|
|
919
|
+
@apply h-[3px] cursor-n-resize absolute top-0 left-0 right-0 flex items-center justify-center;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.extClose{
|
|
923
|
+
@apply w-[21px] h-[21px] rounded-full bg-base-300 flex items-center justify-center hover:bg-red-600;
|
|
924
|
+
}
|
|
925
|
+
|
|
673
926
|
</style>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<div v-if="type === 'date'" class="flex-1 flex flex-row gap-2">
|
|
6
6
|
<div class="flex-1 flex flex-row gap-2">
|
|
7
7
|
<Dropdown v-model="value.operator"
|
|
8
|
-
class="w-[100px]"
|
|
8
|
+
:class="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear' ].includes(value.operator) ? 'w-[100px]' : 'w-full'"
|
|
9
9
|
@change="apply">
|
|
10
10
|
<option value="=">=</option>
|
|
11
11
|
<option value=">">></option>
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
<option value="today">Today</option>
|
|
18
18
|
<option value="thisWeek">This Week</option>
|
|
19
19
|
<option value="thisMonth">This Month</option>
|
|
20
|
+
<option value="lastMonth">Last Month</option>
|
|
20
21
|
<option value="thisYear">This Year</option>
|
|
21
22
|
</Dropdown>
|
|
22
|
-
<div v-if="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'thisYear' ].includes(value.operator)"
|
|
23
|
+
<div v-if="![ 'yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear' ].includes(value.operator)"
|
|
23
24
|
class="flex-1 flex flex-row gap-2">
|
|
24
25
|
<Datepicker class="flex-1"
|
|
25
26
|
mode="popup"
|
|
@@ -57,16 +58,16 @@
|
|
|
57
58
|
<Radio :name="value.key" :value="false" v-model="value.value" @change="apply">False</Radio>
|
|
58
59
|
</div>
|
|
59
60
|
|
|
60
|
-
<div v-else-if="type === 'enum'
|
|
61
|
+
<div v-else-if="type === 'enum' && Array.isArray(cTypeParams) && cTypeParams.length > 0"
|
|
62
|
+
class="flex flex-col gap-1">
|
|
61
63
|
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
</div>
|
|
64
|
+
<Checkbox v-for="param in cTypeParams"
|
|
65
|
+
v-model="cValue.value"
|
|
66
|
+
class="flex-1"
|
|
67
|
+
:value="param.value"
|
|
68
|
+
@change="apply">
|
|
69
|
+
{{ param.text }}
|
|
70
|
+
</Checkbox>
|
|
70
71
|
|
|
71
72
|
</div>
|
|
72
73
|
|
|
@@ -119,12 +120,21 @@ export default{
|
|
|
119
120
|
|
|
120
121
|
value: Object,
|
|
121
122
|
|
|
123
|
+
enumCache: Object,
|
|
124
|
+
|
|
122
125
|
},
|
|
123
126
|
|
|
124
127
|
computed:{
|
|
125
128
|
|
|
129
|
+
cTypeParams(){
|
|
130
|
+
if(this.enumCache && this.column && this.enumCache[this.column.key]){
|
|
131
|
+
return Object.values(this.enumCache[this.column.key])
|
|
132
|
+
}
|
|
133
|
+
return this.typeParams
|
|
134
|
+
},
|
|
135
|
+
|
|
126
136
|
valueRequired(){
|
|
127
|
-
return (!(['yesterday', 'today', 'thisWeek', 'thisMonth', 'thisYear'].includes(this.value.operator) ||
|
|
137
|
+
return (!(['yesterday', 'today', 'thisWeek', 'thisMonth', 'lastMonth', 'thisYear'].includes(this.value.operator) ||
|
|
128
138
|
this.type === 'boolean'));
|
|
129
139
|
},
|
|
130
140
|
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
@click="select(item, index)"
|
|
36
36
|
:class="trClass(item, index)">
|
|
37
37
|
<td v-for="column in visibleColumns"
|
|
38
|
-
:class="tdClass(item, column)"
|
|
38
|
+
:class="tdClass(item, column)"
|
|
39
|
+
@click="$emit('item-click', item, column)">
|
|
39
40
|
<slot v-if="$slots[column.key]" :name="column.key" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
|
|
40
41
|
<slot v-else-if="$slots.default" name="default" :column="column" :item="item" :index="visibleStartIndex + index"></slot>
|
|
41
42
|
<div v-else :class="columnClass(column)" v-html="formatColumn(item, column)"></div>
|
|
@@ -86,11 +87,13 @@ const _DEFAULT_COLUMN_WIDTH = '100px'
|
|
|
86
87
|
|
|
87
88
|
export default{
|
|
88
89
|
|
|
89
|
-
emits: [ 'scroll-end' ],
|
|
90
|
+
emits: [ 'scroll-end', 'item-click' ],
|
|
90
91
|
|
|
91
92
|
props:{
|
|
92
93
|
columns: Array,
|
|
93
94
|
|
|
95
|
+
enumCache: Object,
|
|
96
|
+
|
|
94
97
|
itemClass: String,
|
|
95
98
|
|
|
96
99
|
items: Array,
|
|
@@ -105,7 +108,7 @@ export default{
|
|
|
105
108
|
appearances: {
|
|
106
109
|
type: Object,
|
|
107
110
|
default: {}
|
|
108
|
-
}
|
|
111
|
+
},
|
|
109
112
|
},
|
|
110
113
|
|
|
111
114
|
data(){
|
|
@@ -397,8 +400,18 @@ export default{
|
|
|
397
400
|
break
|
|
398
401
|
|
|
399
402
|
case 'enum':
|
|
400
|
-
|
|
401
|
-
|
|
403
|
+
let enumText
|
|
404
|
+
|
|
405
|
+
if(Array.isArray(column.typeParams)){
|
|
406
|
+
const typeParam = (column.typeParams ?? []).filter((_) => _.value === value).pop()
|
|
407
|
+
enumText = typeParam && typeParam.text ? typeParam.text : enumText
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if(this.enumCache && this.enumCache[column.key] && this.enumCache[column.key][value]){
|
|
411
|
+
enumText = this.enumCache[column.key][value].text ?? enumText
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
text = enumText ? enumText : text
|
|
402
415
|
break
|
|
403
416
|
}
|
|
404
417
|
|
|
@@ -99,6 +99,22 @@ const getValue = (filter, opt) => {
|
|
|
99
99
|
}
|
|
100
100
|
break
|
|
101
101
|
|
|
102
|
+
case 'lastMonth':
|
|
103
|
+
withoutKey ?
|
|
104
|
+
whereObj = {
|
|
105
|
+
[Op.between]: [
|
|
106
|
+
dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD 00:00:00'),
|
|
107
|
+
dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD 23:59:59'),
|
|
108
|
+
]
|
|
109
|
+
} :
|
|
110
|
+
whereObj[key] = {
|
|
111
|
+
[Op.between]: [
|
|
112
|
+
dayjs().subtract(1, 'month').startOf('month').format('YYYY-MM-DD 00:00:00'),
|
|
113
|
+
dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD 23:59:59'),
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
break
|
|
117
|
+
|
|
102
118
|
case 'thisYear':
|
|
103
119
|
withoutKey ?
|
|
104
120
|
whereObj = {
|
|
@@ -770,6 +786,8 @@ const pivotToSequelizeWhere = async (pivot, opt) => {
|
|
|
770
786
|
}
|
|
771
787
|
}
|
|
772
788
|
|
|
789
|
+
attributes.push([ literal(`1`), 'isPivot' ])
|
|
790
|
+
|
|
773
791
|
return {
|
|
774
792
|
attributes,
|
|
775
793
|
group,
|
|
@@ -116,11 +116,16 @@
|
|
|
116
116
|
|
|
117
117
|
<div v-for="(filter, idx) in preset.filters">
|
|
118
118
|
|
|
119
|
-
<div class="flex flex-row items-start
|
|
120
|
-
<Checkbox v-model="filter.enabled" :default="true" />
|
|
119
|
+
<div class="flex flex-row items-start">
|
|
120
|
+
<Checkbox v-model="filter.enabled" :default="true" @change="apply" />
|
|
121
121
|
<div class="flex-1 flex flex-col gap-3">
|
|
122
|
-
<div class="flex-1 flex flex-row gap-
|
|
123
|
-
<strong class="flex-1"
|
|
122
|
+
<div class="flex-1 flex flex-row gap-2">
|
|
123
|
+
<strong class="flex-1 cursor-pointer" @click="filter._collapsed = !filter._collapsed">
|
|
124
|
+
<svg class="inline fill-text transition-transform relative top-[-1px]"
|
|
125
|
+
:class="filter._collapsed === true ? `` : `rotate-90`"
|
|
126
|
+
width="11" height="11" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M118.6 105.4l128 127.1C252.9 239.6 256 247.8 256 255.1s-3.125 16.38-9.375 22.63l-128 127.1c-9.156 9.156-22.91 11.9-34.88 6.943S64 396.9 64 383.1V128c0-12.94 7.781-24.62 19.75-29.58S109.5 96.23 118.6 105.4z"/></svg>
|
|
127
|
+
{{ column(filter.key).label ?? filter.key }}
|
|
128
|
+
</strong>
|
|
124
129
|
<select v-if="filter.value.length > 1"
|
|
125
130
|
v-model="filter.modifier"
|
|
126
131
|
class="appearance-none bg-text-50 min-w-[60px] text-text-400 px-2 outline-none"
|
|
@@ -128,34 +133,40 @@
|
|
|
128
133
|
<option value="and">And</option>
|
|
129
134
|
<option value="or">Or</option>
|
|
130
135
|
</select>
|
|
131
|
-
<button type="button" @click="filter.value.push({})">
|
|
136
|
+
<button v-if="typeOf(column(filter.key).type) !== 'enum' && filter._collapsed !== true" type="button" @click="filter.value.push({})">
|
|
132
137
|
<svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
|
|
133
138
|
</button>
|
|
134
139
|
<button type="button" class="p-1" @click="preset.filters.splice(idx, 1); apply()">
|
|
135
140
|
<svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
|
|
136
141
|
</button>
|
|
137
142
|
</div>
|
|
138
|
-
<div v-if="
|
|
139
|
-
<div class="flex flex-
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
143
|
+
<div v-if="filter._collapsed !== true">
|
|
144
|
+
<div v-if="Array.isArray(filter.value)" class="flex flex-col gap-2">
|
|
145
|
+
<div class="flex flex-row" v-for="(filterVal, filterIdx) in filter.value">
|
|
146
|
+
<PresetSelectorFilterItem
|
|
147
|
+
class="flex-1"
|
|
148
|
+
:column="column(filter.key)"
|
|
149
|
+
:type="typeOf(column(filter.key).type)"
|
|
150
|
+
:typeParams="typeParamsOf(column(filter.key))"
|
|
151
|
+
:value="filterVal"
|
|
152
|
+
:enumCache="enumCache"
|
|
153
|
+
@change="apply()" />
|
|
154
|
+
|
|
155
|
+
<button v-if="filter.value.length > 1"
|
|
156
|
+
type="button"
|
|
157
|
+
class="p-1"
|
|
158
|
+
@click="filter.value.splice(filterIdx, 1); apply()">
|
|
159
|
+
<svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"/></svg>
|
|
160
|
+
</button>
|
|
161
|
+
</div>
|
|
153
162
|
</div>
|
|
163
|
+
<PresetSelectorFilterItem v-else
|
|
164
|
+
:column="column(filter.key)"
|
|
165
|
+
:type="typeOf(column(filter.key).type)"
|
|
166
|
+
:value="filter"
|
|
167
|
+
:enumCache="enumCache"
|
|
168
|
+
@change="apply()" />
|
|
154
169
|
</div>
|
|
155
|
-
<PresetSelectorFilterItem v-else
|
|
156
|
-
:type="typeOf(column(filter.key).type)"
|
|
157
|
-
:value="filter"
|
|
158
|
-
@change="apply()" />
|
|
159
170
|
</div>
|
|
160
171
|
</div>
|
|
161
172
|
|
|
@@ -453,7 +464,7 @@
|
|
|
453
464
|
|
|
454
465
|
<div>
|
|
455
466
|
<div class="flex flex-row items-center gap-2">
|
|
456
|
-
<label>
|
|
467
|
+
<label>Groups</label>
|
|
457
468
|
<button type="button" @click="$refs.columnSelector.open(obj => addPivot('rows', obj))">
|
|
458
469
|
<svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
|
|
459
470
|
</button>
|
|
@@ -468,7 +479,8 @@
|
|
|
468
479
|
<svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
|
|
469
480
|
</div>
|
|
470
481
|
<div class="flex-1 flex flex-row gap-3">
|
|
471
|
-
<strong class="flex-1 cursor-pointer
|
|
482
|
+
<strong class="flex-1 cursor-pointer text-ellipsis overflow-hidden whitespace-nowrap"
|
|
483
|
+
@click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() });">
|
|
472
484
|
{{ item.label ? item.label : item.key }}
|
|
473
485
|
</strong>
|
|
474
486
|
<select v-if="item.type === 'date'"
|
|
@@ -490,7 +502,7 @@
|
|
|
490
502
|
|
|
491
503
|
<div>
|
|
492
504
|
<div class="flex flex-row items-center gap-2">
|
|
493
|
-
<label>
|
|
505
|
+
<label>Aggregrates</label>
|
|
494
506
|
<button type="button" @click="$refs.columnSelector.open(obj => addPivot('values', obj))">
|
|
495
507
|
<svg width="16" height="16" class="fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
|
|
496
508
|
</button>
|
|
@@ -504,7 +516,8 @@
|
|
|
504
516
|
<svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
|
|
505
517
|
</div>
|
|
506
518
|
<div class="flex-1 flex flex-row gap-3">
|
|
507
|
-
<strong class="flex-1"
|
|
519
|
+
<strong class="flex-1 cursor-pointer text-ellipsis overflow-hidden whitespace-nowrap"
|
|
520
|
+
@click="$refs.columnSelector.open(obj => { Object.assign(item, obj); apply() });">
|
|
508
521
|
{{ item.label ? item.label : item.key }}
|
|
509
522
|
</strong>
|
|
510
523
|
<select v-model="item.aggregrate"
|
|
@@ -518,7 +531,7 @@
|
|
|
518
531
|
<option v-if="[ 'number' ].includes(item.type)" value="max">Max</option>
|
|
519
532
|
</select>
|
|
520
533
|
</div>
|
|
521
|
-
<button type="button" @click="presetPivot.values.splice(index, 1)">
|
|
534
|
+
<button type="button" @click="presetPivot.values.splice(index, 1); apply()">
|
|
522
535
|
<svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
|
|
523
536
|
</button>
|
|
524
537
|
</div>
|
|
@@ -576,7 +589,9 @@ export default{
|
|
|
576
589
|
|
|
577
590
|
props: {
|
|
578
591
|
|
|
579
|
-
config: Object
|
|
592
|
+
config: Object,
|
|
593
|
+
|
|
594
|
+
enumCache: Object
|
|
580
595
|
|
|
581
596
|
},
|
|
582
597
|
|
|
@@ -611,11 +626,12 @@ export default{
|
|
|
611
626
|
|
|
612
627
|
if(this.preset){
|
|
613
628
|
if(this.config.presetBarTabIndex === 2){
|
|
614
|
-
|
|
629
|
+
return true
|
|
630
|
+
/*if(Array.isArray(this.preset.filters)){
|
|
615
631
|
if(this.preset.filters.filter(_ => !_.key).length > 0){
|
|
616
632
|
return false
|
|
617
633
|
}
|
|
618
|
-
}
|
|
634
|
+
}*/
|
|
619
635
|
}
|
|
620
636
|
else if(this.config.presetBarTabIndex === 3){
|
|
621
637
|
if(Array.isArray(this.preset.sorts)){
|
|
@@ -643,7 +659,7 @@ export default{
|
|
|
643
659
|
},
|
|
644
660
|
|
|
645
661
|
column(key){
|
|
646
|
-
return this.
|
|
662
|
+
return this.preset.columns.filter(_ => _.key === key).pop() ?? {}
|
|
647
663
|
},
|
|
648
664
|
|
|
649
665
|
duplicate(idx){
|
|
@@ -689,8 +705,9 @@ export default{
|
|
|
689
705
|
},
|
|
690
706
|
|
|
691
707
|
typeParamsOf(column){
|
|
692
|
-
if([ 'enum' ].includes(column.type))
|
|
708
|
+
if([ 'enum' ].includes(column.type)){
|
|
693
709
|
return column.typeParams
|
|
710
|
+
}
|
|
694
711
|
return []
|
|
695
712
|
},
|
|
696
713
|
|