@fy-/fws-vue 2.1.5 → 2.1.7
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/components/fws/CmsArticleBoxed.vue +23 -20
- package/components/fws/CmsArticleSingle.vue +74 -68
- package/components/fws/DataTable.vue +132 -125
- package/components/fws/FilterData.vue +99 -101
- package/components/fws/UserData.vue +33 -32
- package/components/fws/UserFlow.vue +163 -155
- package/components/fws/UserOAuth2.vue +73 -72
- package/components/fws/UserProfile.vue +98 -101
- package/components/fws/UserProfileStrict.vue +65 -64
- package/components/ssr/ClientOnly.ts +7 -7
- package/components/ui/DefaultBreadcrumb.vue +13 -13
- package/components/ui/DefaultConfirm.vue +35 -34
- package/components/ui/DefaultDateSelection.vue +19 -17
- package/components/ui/DefaultDropdown.vue +25 -25
- package/components/ui/DefaultDropdownLink.vue +15 -14
- package/components/ui/DefaultGallery.vue +179 -168
- package/components/ui/DefaultInput.vue +121 -126
- package/components/ui/DefaultLoader.vue +17 -17
- package/components/ui/DefaultModal.vue +35 -33
- package/components/ui/DefaultNotif.vue +51 -53
- package/components/ui/DefaultPaging.vue +92 -95
- package/components/ui/DefaultSidebar.vue +29 -25
- package/components/ui/DefaultTagInput.vue +121 -119
- package/components/ui/transitions/CollapseTransition.vue +1 -1
- package/components/ui/transitions/ExpandTransition.vue +1 -1
- package/components/ui/transitions/FadeTransition.vue +1 -1
- package/components/ui/transitions/ScaleTransition.vue +1 -1
- package/components/ui/transitions/SlideTransition.vue +3 -3
- package/composables/event-bus.ts +10 -8
- package/composables/rest.ts +59 -56
- package/composables/seo.ts +106 -95
- package/composables/ssr.ts +64 -62
- package/composables/templating.ts +57 -57
- package/composables/translations.ts +13 -13
- package/env.d.ts +6 -4
- package/index.ts +101 -98
- package/package.json +7 -7
- package/stores/serverRouter.ts +25 -25
- package/stores/user.ts +79 -72
- package/types.d.ts +65 -65
|
@@ -1,208 +1,211 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { onMounted, ref, onUnmounted, watch } from "vue";
|
|
3
2
|
import {
|
|
4
3
|
ArrowDownIcon,
|
|
5
|
-
ArrowUpIcon,
|
|
6
4
|
ArrowDownTrayIcon,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
5
|
+
ArrowUpIcon,
|
|
6
|
+
} from '@heroicons/vue/24/solid'
|
|
7
|
+
import { useStorage } from '@vueuse/core'
|
|
8
|
+
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
|
9
|
+
import { useRoute } from 'vue-router'
|
|
10
|
+
import { useEventBus } from '../../composables/event-bus'
|
|
11
|
+
import { useRest } from '../../composables/rest'
|
|
12
|
+
import DefaultInput from '../ui/DefaultInput.vue'
|
|
13
|
+
import DefaultPaging from '../ui/DefaultPaging.vue'
|
|
14
|
+
|
|
14
15
|
interface DefaultStringObject {
|
|
15
|
-
[key: string]: string
|
|
16
|
+
[key: string]: string
|
|
16
17
|
}
|
|
17
18
|
interface DefaultAnyObject {
|
|
18
|
-
[key: string]: any
|
|
19
|
+
[key: string]: any
|
|
19
20
|
}
|
|
20
21
|
interface DefaultBoolObject {
|
|
21
|
-
[key: string]: boolean
|
|
22
|
+
[key: string]: boolean
|
|
22
23
|
}
|
|
23
24
|
interface SortingField {
|
|
24
|
-
field: string
|
|
25
|
-
direction: string
|
|
25
|
+
field: string
|
|
26
|
+
direction: string
|
|
26
27
|
}
|
|
27
|
-
const eventBus = useEventBus()
|
|
28
|
-
const currentPage = ref<number>(1)
|
|
29
|
-
const route = useRoute()
|
|
30
|
-
const data = ref<any[]>([])
|
|
31
|
-
const paging = ref<any>(undefined)
|
|
28
|
+
const eventBus = useEventBus()
|
|
29
|
+
const currentPage = ref<number>(1)
|
|
30
|
+
const route = useRoute()
|
|
31
|
+
const data = ref<any[]>([])
|
|
32
|
+
const paging = ref<any>(undefined)
|
|
32
33
|
const perPageOptions = [
|
|
33
|
-
[
|
|
34
|
-
[
|
|
35
|
-
[
|
|
36
|
-
[
|
|
37
|
-
]
|
|
34
|
+
['10', '10'],
|
|
35
|
+
['25', '25'],
|
|
36
|
+
['50', '50'],
|
|
37
|
+
['100', '100'],
|
|
38
|
+
]
|
|
38
39
|
const props = withDefaults(
|
|
39
40
|
defineProps<{
|
|
40
|
-
id: string
|
|
41
|
-
headers: DefaultStringObject
|
|
42
|
-
sortables?: DefaultBoolObject
|
|
43
|
-
showHeaders?: boolean
|
|
44
|
-
exportableColumns?: string[]
|
|
45
|
-
csvFormatColumns?: Record<string, (value: any) => string
|
|
46
|
-
defaultPerPage?: number
|
|
47
|
-
filtersData: DefaultAnyObject
|
|
48
|
-
apiPath: string
|
|
49
|
-
defaultSort?: SortingField
|
|
50
|
-
restFunction?: Function | null
|
|
41
|
+
id: string
|
|
42
|
+
headers: DefaultStringObject
|
|
43
|
+
sortables?: DefaultBoolObject
|
|
44
|
+
showHeaders?: boolean
|
|
45
|
+
exportableColumns?: string[]
|
|
46
|
+
csvFormatColumns?: Record<string, (value: any) => string>
|
|
47
|
+
defaultPerPage?: number
|
|
48
|
+
filtersData: DefaultAnyObject
|
|
49
|
+
apiPath: string
|
|
50
|
+
defaultSort?: SortingField
|
|
51
|
+
restFunction?: Function | null
|
|
51
52
|
}>(),
|
|
52
53
|
{
|
|
53
54
|
showHeaders: true,
|
|
54
55
|
sortables: () => ({}),
|
|
55
56
|
exportableColumns: () => [],
|
|
56
57
|
csvFormatColumns: () => ({}),
|
|
57
|
-
exportableName:
|
|
58
|
+
exportableName: 'default',
|
|
58
59
|
defaultPerPage: 25,
|
|
59
|
-
defaultSort: () => ({ field:
|
|
60
|
+
defaultSort: () => ({ field: 'Created', direction: 'DESC' }),
|
|
60
61
|
restFunction: null,
|
|
61
62
|
},
|
|
62
|
-
)
|
|
63
|
-
const rest = useRest()
|
|
64
|
-
const restFunction = props.restFunction ?? rest
|
|
65
|
-
const perPage = useStorage<number>(`${props.id}PerPage`, props.defaultPerPage)
|
|
63
|
+
)
|
|
64
|
+
const rest = useRest()
|
|
65
|
+
const restFunction = props.restFunction ?? rest
|
|
66
|
+
const perPage = useStorage<number>(`${props.id}PerPage`, props.defaultPerPage)
|
|
66
67
|
const currentSort = useStorage<SortingField>(
|
|
67
68
|
`${props.id}CurrentSort`,
|
|
68
69
|
props.defaultSort,
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
eventBus.emit(
|
|
72
|
-
if (route.query.page) page = parseInt(route.query.page.toString())
|
|
73
|
-
const sort: any = {}
|
|
74
|
-
sort[currentSort.value.field] = currentSort.value.direction
|
|
70
|
+
)
|
|
71
|
+
async function getData(page: number = 1) {
|
|
72
|
+
eventBus.emit('main-loading', true)
|
|
73
|
+
if (route.query.page) page = Number.parseInt(route.query.page.toString())
|
|
74
|
+
const sort: any = {}
|
|
75
|
+
sort[currentSort.value.field] = currentSort.value.direction
|
|
75
76
|
const requestParams = {
|
|
76
77
|
...props.filtersData,
|
|
77
|
-
sort
|
|
78
|
+
sort,
|
|
78
79
|
results_per_page: perPage.value,
|
|
79
80
|
page_no: page,
|
|
80
|
-
}
|
|
81
|
-
const r = await restFunction(props.apiPath,
|
|
81
|
+
}
|
|
82
|
+
const r = await restFunction(props.apiPath, 'GET', requestParams, {
|
|
82
83
|
getBody: true,
|
|
83
|
-
})
|
|
84
|
-
currentPage.value = page
|
|
85
|
-
data.value = []
|
|
86
|
-
paging.value = undefined
|
|
87
|
-
if (r && r.result
|
|
88
|
-
data.value = r.data
|
|
89
|
-
paging.value = r.paging
|
|
90
|
-
eventBus.emit(`${props.id}NewData`, data.value)
|
|
84
|
+
})
|
|
85
|
+
currentPage.value = page
|
|
86
|
+
data.value = []
|
|
87
|
+
paging.value = undefined
|
|
88
|
+
if (r && r.result === 'success') {
|
|
89
|
+
data.value = r.data
|
|
90
|
+
paging.value = r.paging
|
|
91
|
+
eventBus.emit(`${props.id}NewData`, data.value)
|
|
91
92
|
}
|
|
92
|
-
eventBus.emit(
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (!props.sortables[key]) return
|
|
93
|
+
eventBus.emit('main-loading', false)
|
|
94
|
+
}
|
|
95
|
+
function sortData(key: string) {
|
|
96
|
+
if (!props.sortables[key]) return
|
|
96
97
|
const newSort: SortingField = {
|
|
97
98
|
field: currentSort.value.field,
|
|
98
99
|
direction: currentSort.value.direction,
|
|
99
|
-
}
|
|
100
|
-
if (key
|
|
101
|
-
if (newSort.direction
|
|
102
|
-
newSort.direction =
|
|
103
|
-
}
|
|
104
|
-
|
|
100
|
+
}
|
|
101
|
+
if (key === newSort.field) {
|
|
102
|
+
if (newSort.direction === 'desc') {
|
|
103
|
+
newSort.direction = 'asc'
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
newSort.direction = 'desc'
|
|
105
107
|
}
|
|
106
|
-
} else {
|
|
107
|
-
newSort.direction = "desc";
|
|
108
|
-
newSort.field = key;
|
|
109
108
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
else {
|
|
110
|
+
newSort.direction = 'desc'
|
|
111
|
+
newSort.field = key
|
|
112
|
+
}
|
|
113
|
+
currentSort.value = { ...newSort }
|
|
114
|
+
}
|
|
115
|
+
function exportToCsv() {
|
|
113
116
|
const header = props.exportableColumns
|
|
114
|
-
.map(
|
|
115
|
-
.join(
|
|
117
|
+
.map(column => props.headers[column] ?? column)
|
|
118
|
+
.join(',')
|
|
116
119
|
const rows = data.value
|
|
117
120
|
.map((row) => {
|
|
118
121
|
return props.exportableColumns
|
|
119
122
|
.map((column) => {
|
|
120
|
-
let cell = row[column]
|
|
123
|
+
let cell = row[column]
|
|
121
124
|
if (props.csvFormatColumns[column]) {
|
|
122
|
-
cell = props.csvFormatColumns[column](row)
|
|
125
|
+
cell = props.csvFormatColumns[column](row)
|
|
123
126
|
}
|
|
124
|
-
return `"${cell}"
|
|
127
|
+
return `"${cell}"`
|
|
125
128
|
})
|
|
126
|
-
.join(
|
|
129
|
+
.join(',')
|
|
127
130
|
})
|
|
128
|
-
.join(
|
|
131
|
+
.join('\n')
|
|
129
132
|
|
|
130
|
-
const csvContent = header
|
|
133
|
+
const csvContent = `${header}\n${rows}`
|
|
131
134
|
|
|
132
|
-
const blob = new Blob([csvContent], { type:
|
|
133
|
-
const link = document.createElement(
|
|
134
|
-
const url = URL.createObjectURL(blob)
|
|
135
|
-
link.setAttribute(
|
|
135
|
+
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' })
|
|
136
|
+
const link = document.createElement('a')
|
|
137
|
+
const url = URL.createObjectURL(blob)
|
|
138
|
+
link.setAttribute('href', url)
|
|
136
139
|
link.setAttribute(
|
|
137
|
-
|
|
140
|
+
'download',
|
|
138
141
|
`${props.id}_${new Date().toISOString().slice(0, 10)}_Page-${
|
|
139
142
|
currentPage.value
|
|
140
143
|
}_${perPage.value}-per-page_Order-by-${currentSort.value.field}-${
|
|
141
144
|
currentSort.value.direction
|
|
142
145
|
}.csv`,
|
|
143
|
-
)
|
|
144
|
-
link.style.visibility =
|
|
145
|
-
document.body.appendChild(link)
|
|
146
|
-
link.click()
|
|
147
|
-
document.body.removeChild(link)
|
|
148
|
-
}
|
|
146
|
+
)
|
|
147
|
+
link.style.visibility = 'hidden'
|
|
148
|
+
document.body.appendChild(link)
|
|
149
|
+
link.click()
|
|
150
|
+
document.body.removeChild(link)
|
|
151
|
+
}
|
|
149
152
|
watch(perPage, () => {
|
|
150
|
-
getData()
|
|
151
|
-
})
|
|
153
|
+
getData()
|
|
154
|
+
})
|
|
152
155
|
watch(currentSort, () => {
|
|
153
|
-
getData()
|
|
154
|
-
})
|
|
156
|
+
getData()
|
|
157
|
+
})
|
|
155
158
|
watch(
|
|
156
159
|
() => props.filtersData,
|
|
157
160
|
() => {
|
|
158
|
-
getData()
|
|
161
|
+
getData()
|
|
159
162
|
},
|
|
160
|
-
)
|
|
163
|
+
)
|
|
161
164
|
watch(
|
|
162
165
|
() => props.apiPath,
|
|
163
166
|
() => {
|
|
164
|
-
getData()
|
|
167
|
+
getData()
|
|
165
168
|
},
|
|
166
|
-
)
|
|
167
|
-
await getData()
|
|
169
|
+
)
|
|
170
|
+
await getData()
|
|
168
171
|
onMounted(() => {
|
|
169
|
-
eventBus.on(`${props.id}PagesGoToPage`, getData)
|
|
170
|
-
eventBus.on(`${props.id}Reload`, getData)
|
|
171
|
-
eventBus.on(`${props.id}Refresh`, getData)
|
|
172
|
-
})
|
|
172
|
+
eventBus.on(`${props.id}PagesGoToPage`, getData)
|
|
173
|
+
eventBus.on(`${props.id}Reload`, getData)
|
|
174
|
+
eventBus.on(`${props.id}Refresh`, getData)
|
|
175
|
+
})
|
|
173
176
|
onUnmounted(() => {
|
|
174
|
-
eventBus.off(`${props.id}PagesGoToPage`, getData)
|
|
175
|
-
eventBus.off(`${props.id}Reload`, getData)
|
|
176
|
-
eventBus.off(`${props.id}Refresh`, getData)
|
|
177
|
-
})
|
|
177
|
+
eventBus.off(`${props.id}PagesGoToPage`, getData)
|
|
178
|
+
eventBus.off(`${props.id}Reload`, getData)
|
|
179
|
+
eventBus.off(`${props.id}Refresh`, getData)
|
|
180
|
+
})
|
|
178
181
|
</script>
|
|
182
|
+
|
|
179
183
|
<template>
|
|
180
184
|
<div>
|
|
181
185
|
<div
|
|
182
186
|
class="flex gap-2 justify-between items-center border-b border-fv-primary-600 mb-2 pb-2"
|
|
183
187
|
>
|
|
184
|
-
<DefaultPaging
|
|
188
|
+
<DefaultPaging v-if="paging" :id="`${props.id}Pages`" :items="paging" />
|
|
185
189
|
<button
|
|
190
|
+
v-if="exportableColumns.length && data.length"
|
|
186
191
|
class="btn primary small"
|
|
187
192
|
@click="exportToCsv"
|
|
188
|
-
v-if="exportableColumns.length && data.length"
|
|
189
193
|
>
|
|
190
|
-
<ArrowDownTrayIcon class="w-4 h-4 mr-2"
|
|
191
|
-
>{{ $t("global_table_export") }}
|
|
194
|
+
<ArrowDownTrayIcon class="w-4 h-4 mr-2" />{{ $t("global_table_export") }}
|
|
192
195
|
</button>
|
|
193
196
|
<DefaultInput
|
|
197
|
+
:id="`${id}PerPage`"
|
|
194
198
|
v-model="perPage"
|
|
195
199
|
:options="perPageOptions"
|
|
196
200
|
:show-label="false"
|
|
197
|
-
:id="`${id}PerPage`"
|
|
198
201
|
type="select"
|
|
199
202
|
class="w-20"
|
|
200
203
|
/>
|
|
201
204
|
</div>
|
|
202
205
|
|
|
203
206
|
<div
|
|
204
|
-
class="relative overflow-x-auto border-fv-primary-600 sm:rounded-lg"
|
|
205
207
|
v-if="data.length"
|
|
208
|
+
class="relative overflow-x-auto border-fv-primary-600 sm:rounded-lg"
|
|
206
209
|
>
|
|
207
210
|
<table
|
|
208
211
|
class="w-full text-sm text-left text-fv-neutral-500 dark:text-fv-neutral-400"
|
|
@@ -215,6 +218,11 @@ onUnmounted(() => {
|
|
|
215
218
|
<th
|
|
216
219
|
v-for="(header, key) in headers"
|
|
217
220
|
:key="key"
|
|
221
|
+
scope="col"
|
|
222
|
+
class="px-6 py-3 whitespace-nowrap"
|
|
223
|
+
:class="{
|
|
224
|
+
'cursor-pointer': sortables[key],
|
|
225
|
+
}"
|
|
218
226
|
@click="
|
|
219
227
|
() => {
|
|
220
228
|
if (sortables[key]) {
|
|
@@ -222,16 +230,11 @@ onUnmounted(() => {
|
|
|
222
230
|
}
|
|
223
231
|
}
|
|
224
232
|
"
|
|
225
|
-
scope="col"
|
|
226
|
-
class="px-6 py-3 whitespace-nowrap"
|
|
227
|
-
:class="{
|
|
228
|
-
'cursor-pointer': sortables[key],
|
|
229
|
-
}"
|
|
230
233
|
>
|
|
231
234
|
{{ header }}
|
|
232
|
-
<template v-if="sortables[key] && currentSort.field
|
|
235
|
+
<template v-if="sortables[key] && currentSort.field === key">
|
|
233
236
|
<ArrowUpIcon
|
|
234
|
-
v-if="currentSort.direction
|
|
237
|
+
v-if="currentSort.direction === 'desc'"
|
|
235
238
|
class="inline w-3 h-3 align-top mt-0.5"
|
|
236
239
|
/>
|
|
237
240
|
<ArrowDownIcon v-else class="inline w-3 h-3 align-top mt-0.5" />
|
|
@@ -247,8 +250,12 @@ onUnmounted(() => {
|
|
|
247
250
|
>
|
|
248
251
|
<td v-for="(header, key) in headers" :key="key" class="px-6 py-4">
|
|
249
252
|
<slot :name="key" :value="row">
|
|
250
|
-
<template v-if="row[key]">
|
|
251
|
-
|
|
253
|
+
<template v-if="row[key]">
|
|
254
|
+
{{ row[key] }}
|
|
255
|
+
</template>
|
|
256
|
+
<template v-else>
|
|
257
|
+
{{ $t("global_table_empty_cell") }}
|
|
258
|
+
</template>
|
|
252
259
|
</slot>
|
|
253
260
|
</td>
|
|
254
261
|
</tr>
|
|
@@ -1,149 +1,147 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import type { FilterDataItems } from '../../types'
|
|
3
|
+
import useVuelidate from '@vuelidate/core'
|
|
4
|
+
import { onMounted, onUnmounted, reactive, ref } from 'vue'
|
|
5
|
+
import { useEventBus } from '../../composables/event-bus'
|
|
6
|
+
import { useTranslation } from '../../composables/translations'
|
|
7
|
+
import DefaultDateSelection from '../ui/DefaultDateSelection.vue'
|
|
8
|
+
import DefaultInput from '../ui/DefaultInput.vue'
|
|
9
|
+
|
|
10
|
+
const emit = defineEmits(['update:modelValue'])
|
|
11
|
+
const hidden = ref<boolean>(false)
|
|
12
|
+
const state = reactive<any>({ formData: {} })
|
|
13
|
+
const rules: any = { formData: {} }
|
|
14
|
+
const types = reactive<any>({})
|
|
15
|
+
const translate = useTranslation()
|
|
16
|
+
const fDynamicOptions = ref<any[]>([])
|
|
17
|
+
const eventBus = useEventBus()
|
|
11
18
|
|
|
12
|
-
const emit = defineEmits(["update:modelValue"]);
|
|
13
|
-
const hidden = ref<boolean>(false);
|
|
14
|
-
const state = reactive<any>({ formData: {} });
|
|
15
|
-
const rules: any = { formData: {} };
|
|
16
|
-
const types = reactive<any>({});
|
|
17
|
-
const translate = useTranslation();
|
|
18
|
-
const fDynamicOptions = ref<any[]>([]);
|
|
19
19
|
const props = withDefaults(
|
|
20
20
|
defineProps<{
|
|
21
|
-
data?: Array<Array<FilterDataItems
|
|
22
|
-
css: string
|
|
23
|
-
modelValue?: Record<string, unknown
|
|
21
|
+
data?: Array<Array<FilterDataItems>>
|
|
22
|
+
css: string
|
|
23
|
+
modelValue?: Record<string, unknown>
|
|
24
24
|
}>(),
|
|
25
25
|
{
|
|
26
26
|
showHeaders: true,
|
|
27
27
|
data: () => [],
|
|
28
28
|
},
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
undefinedValues: any[] = ["undefined"],
|
|
33
|
-
) => {
|
|
34
|
-
const output: any = {};
|
|
29
|
+
)
|
|
30
|
+
function removeUndefinedStrings(input: any, undefinedValues: any[] = ['undefined']) {
|
|
31
|
+
const output: any = {}
|
|
35
32
|
|
|
36
33
|
Object.keys(input).forEach((key) => {
|
|
37
34
|
if (!undefinedValues.includes(input[key]) && input[key] !== undefined) {
|
|
38
|
-
if (!input[key]
|
|
39
|
-
output[key] = input[key]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
if (!input[key].$between) {
|
|
36
|
+
output[key] = input[key]
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
input[key].$between[0]
|
|
40
|
+
= input[key].$between[0] === '' || input[key].$between[0] == null
|
|
43
41
|
? undefined
|
|
44
|
-
: input[key]
|
|
45
|
-
input[key]
|
|
46
|
-
input[key]
|
|
42
|
+
: input[key].$between[0]
|
|
43
|
+
input[key].$between[1]
|
|
44
|
+
= input[key].$between[1] === '' || input[key].$between[1] == null
|
|
47
45
|
? undefined
|
|
48
|
-
: input[key]
|
|
46
|
+
: input[key].$between[1]
|
|
49
47
|
if (
|
|
50
|
-
input[key]
|
|
51
|
-
input[key]
|
|
48
|
+
input[key].$between[0] !== undefined
|
|
49
|
+
|| input[key].$between[1] !== undefined
|
|
52
50
|
) {
|
|
53
|
-
output[key] = input[key]
|
|
51
|
+
output[key] = input[key]
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
|
-
})
|
|
55
|
+
})
|
|
58
56
|
|
|
59
|
-
return output
|
|
60
|
-
}
|
|
57
|
+
return output
|
|
58
|
+
}
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
function formatValues(obj: any) {
|
|
63
61
|
props.data.forEach((group) => {
|
|
64
62
|
group.forEach((f) => {
|
|
65
63
|
if (f.formats && f.formats[f.type]) {
|
|
66
|
-
obj[f.uid] = f.formats[f.type](obj[f.uid])
|
|
64
|
+
obj[f.uid] = f.formats[f.type](obj[f.uid])
|
|
67
65
|
}
|
|
68
66
|
if (f.formatRestValue) {
|
|
69
|
-
obj[f.uid] = f.formatRestValue(obj[f.uid])
|
|
67
|
+
obj[f.uid] = f.formatRestValue(obj[f.uid])
|
|
70
68
|
}
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
return removeUndefinedStrings(obj, [
|
|
74
|
-
}
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
return removeUndefinedStrings(obj, ['undefined', ''])
|
|
72
|
+
}
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
state.formData = {}
|
|
78
|
-
rules.formData = {}
|
|
74
|
+
function updateForms() {
|
|
75
|
+
state.formData = {}
|
|
76
|
+
rules.formData = {}
|
|
79
77
|
props.data.forEach((group) => {
|
|
80
78
|
group.forEach((f) => {
|
|
81
|
-
state.formData[f.uid]
|
|
82
|
-
typeof f.default ==
|
|
79
|
+
state.formData[f.uid]
|
|
80
|
+
= typeof f.default == 'object' && f.default
|
|
83
81
|
? JSON.parse(JSON.stringify(f.default))
|
|
84
|
-
: f.default
|
|
82
|
+
: f.default
|
|
85
83
|
|
|
86
|
-
types[f.uid] = f.type
|
|
84
|
+
types[f.uid] = f.type
|
|
87
85
|
|
|
88
86
|
if (f.options && f.options.length) {
|
|
89
87
|
f.options = f.options.map((status) => {
|
|
90
|
-
const [statusKey, statusValue] = status
|
|
91
|
-
const translatedValue = translate(statusValue)
|
|
92
|
-
return [statusKey, translatedValue]
|
|
93
|
-
})
|
|
88
|
+
const [statusKey, statusValue] = status
|
|
89
|
+
const translatedValue = translate(statusValue)
|
|
90
|
+
return [statusKey, translatedValue]
|
|
91
|
+
})
|
|
94
92
|
}
|
|
95
|
-
rules.formData[f.uid] = {}
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
emit(
|
|
99
|
-
}
|
|
100
|
-
updateForms()
|
|
101
|
-
const v$ = useVuelidate(rules, state)
|
|
93
|
+
rules.formData[f.uid] = {}
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
emit('update:modelValue', formatValues({ ...state.formData }))
|
|
97
|
+
}
|
|
98
|
+
updateForms()
|
|
99
|
+
const v$ = useVuelidate(rules, state)
|
|
102
100
|
|
|
103
|
-
|
|
101
|
+
function updateFormData(data: any) {
|
|
104
102
|
data.forEach((d: any) => {
|
|
105
103
|
if (d.uid) {
|
|
106
|
-
state.formData[d.uid] = d.value
|
|
104
|
+
state.formData[d.uid] = d.value
|
|
107
105
|
}
|
|
108
|
-
})
|
|
109
|
-
submitForm()
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const formData = formatValues({ ...state.formData })
|
|
113
|
-
emit(
|
|
114
|
-
eventBus.emit(
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
updateForms()
|
|
118
|
-
}
|
|
119
|
-
const eventBus = useEventBus();
|
|
106
|
+
})
|
|
107
|
+
submitForm()
|
|
108
|
+
}
|
|
109
|
+
function submitForm() {
|
|
110
|
+
const formData = formatValues({ ...state.formData })
|
|
111
|
+
emit('update:modelValue', formData)
|
|
112
|
+
eventBus.emit('forceUpdateFilters', true)
|
|
113
|
+
}
|
|
114
|
+
function resetForm() {
|
|
115
|
+
updateForms()
|
|
116
|
+
}
|
|
120
117
|
onMounted(() => {
|
|
121
|
-
eventBus.on(
|
|
122
|
-
eventBus.on(
|
|
123
|
-
})
|
|
118
|
+
eventBus.on('resetFilters', resetForm)
|
|
119
|
+
eventBus.on('updateFilters', updateFormData)
|
|
120
|
+
})
|
|
124
121
|
onUnmounted(() => {
|
|
125
|
-
eventBus.off(
|
|
126
|
-
eventBus.off(
|
|
127
|
-
})
|
|
122
|
+
eventBus.off('resetFilters', resetForm)
|
|
123
|
+
eventBus.off('updateFilters', updateFormData)
|
|
124
|
+
})
|
|
128
125
|
</script>
|
|
126
|
+
|
|
129
127
|
<template>
|
|
130
|
-
<form @submit.prevent="() => submitForm()"
|
|
128
|
+
<form v-if="!hidden" @submit.prevent="() => submitForm()">
|
|
131
129
|
<div :class="css">
|
|
132
130
|
<div v-for="(g, i) in data" :key="`index_${i}`" class="relative">
|
|
133
131
|
<template v-for="f in g" :key="f.uid">
|
|
134
132
|
<template v-if="!f.isHidden">
|
|
135
133
|
<DefaultInput
|
|
136
|
-
:type="f.type == 'autocomplete' ? 'text' : f.type"
|
|
137
|
-
:label="f.label"
|
|
138
|
-
:id="f.uid"
|
|
139
134
|
v-if="
|
|
140
135
|
['text', 'select', 'date', 'email', 'autocomplete'].includes(
|
|
141
136
|
f.type,
|
|
142
137
|
)
|
|
143
138
|
"
|
|
144
|
-
:
|
|
139
|
+
:id="f.uid"
|
|
145
140
|
v-model="state.formData[f.uid]"
|
|
146
|
-
:
|
|
141
|
+
:type="f.type === 'autocomplete' ? 'text' : f.type"
|
|
142
|
+
:label="f.label"
|
|
143
|
+
:options="f.options ? f.options : [[]]"
|
|
144
|
+
:error-vuelidate="v$.formData[f.uid].$errors"
|
|
147
145
|
class="mb-2"
|
|
148
146
|
@focus="
|
|
149
147
|
() => {
|
|
@@ -164,11 +162,11 @@ onUnmounted(() => {
|
|
|
164
162
|
}
|
|
165
163
|
}
|
|
166
164
|
"
|
|
167
|
-
@update:
|
|
168
|
-
(v) => {
|
|
165
|
+
@update:model-value="
|
|
166
|
+
(v:any) => {
|
|
169
167
|
if (f.autocomplete && v.length >= 2) {
|
|
170
168
|
fDynamicOptions = [];
|
|
171
|
-
f.autocomplete(v).then((r) => {
|
|
169
|
+
f.autocomplete(v).then((r:any) => {
|
|
172
170
|
fDynamicOptions = r;
|
|
173
171
|
});
|
|
174
172
|
}
|
|
@@ -176,7 +174,7 @@ onUnmounted(() => {
|
|
|
176
174
|
"
|
|
177
175
|
>
|
|
178
176
|
<div
|
|
179
|
-
v-if="f.type
|
|
177
|
+
v-if="f.type === 'autocomplete' && f.focused"
|
|
180
178
|
class="absolute flex flex-col gap-2 p-2 bottom-0 translate-y-full inset-x-0 bg-fv-neutral-200 dark:bg-fv-neutral-800 border border-fv-neutral-700 z-10"
|
|
181
179
|
>
|
|
182
180
|
<button
|
|
@@ -191,17 +189,17 @@ onUnmounted(() => {
|
|
|
191
189
|
}
|
|
192
190
|
"
|
|
193
191
|
>
|
|
194
|
-
{{ o[1] }} <small v-if="o[0]
|
|
192
|
+
{{ o[1] }} <small v-if="o[0] !== ''">({{ o[0] }})</small>
|
|
195
193
|
</button>
|
|
196
|
-
</div
|
|
197
|
-
>
|
|
194
|
+
</div>
|
|
195
|
+
</DefaultInput>
|
|
198
196
|
|
|
199
197
|
<DefaultDateSelection
|
|
198
|
+
v-if="f.type === 'range'"
|
|
200
199
|
:id="f.uid"
|
|
200
|
+
v-model="state.formData[f.uid]"
|
|
201
201
|
:label="f.label"
|
|
202
|
-
v-if="f.type === 'range'"
|
|
203
202
|
mode="interval"
|
|
204
|
-
v-model="state.formData[f.uid]"
|
|
205
203
|
class="mb-2"
|
|
206
204
|
/>
|
|
207
205
|
</template>
|
|
@@ -229,7 +227,7 @@ onUnmounted(() => {
|
|
|
229
227
|
</button>
|
|
230
228
|
</div>
|
|
231
229
|
</form>
|
|
232
|
-
<div class="flex justify-between mt-2 gap-x-2"
|
|
230
|
+
<div v-else class="flex justify-between mt-2 gap-x-2">
|
|
233
231
|
<button
|
|
234
232
|
type="button"
|
|
235
233
|
class="btn defaults primary !w-full flex-1 !text-center !items-center"
|