@d-mok/quasar-app-extension-quasar-axe 3.1.46 → 3.1.48
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/templates/src/utils/dialog/advanced.ts +66 -60
- package/src/templates/src/utils/dialog/basic.ts +1 -1
- package/src/templates/src/utils/dialog/custom/dialogCheckboxText.vue +97 -0
- package/src/templates/src/utils/dialog/custom/dialogForm.vue +56 -14
- package/src/templates/src/utils/dialog/custom/dialogRadioText.vue +77 -0
- package/src/templates/src/utils/dialog/custom/dialogTable.vue +3 -0
- package/src/templates/src/utils/dialog/custom/handson.vue +3 -2
- package/src/templates/src/utils/dialog/custom_base.ts +16 -0
- package/src/templates/src/utils/dialog/form.ts +75 -0
- package/src/templates/src/utils/dialog/index.ts +2 -1
package/package.json
CHANGED
|
@@ -1,33 +1,28 @@
|
|
|
1
|
-
import { Dialog, QDialogOptions } from 'quasar'
|
|
2
1
|
import dialogTextarea from './custom/dialogTextarea.vue'
|
|
3
2
|
import dialogTable from './custom/dialogTable.vue'
|
|
4
|
-
import dialogForm from './custom/dialogForm.vue'
|
|
5
3
|
import dialogBtn from './custom/dialogBtn.vue'
|
|
6
4
|
import dialogFile from './custom/dialogFile.vue'
|
|
5
|
+
import dialogRadioText from './custom/dialogRadioText.vue'
|
|
6
|
+
import dialogCheckboxText from './custom/dialogCheckboxText.vue'
|
|
7
7
|
import { throwError } from './basic'
|
|
8
|
+
import type { Component } from 'vue'
|
|
9
|
+
import { base } from './custom_base'
|
|
8
10
|
|
|
9
11
|
type Predicate<T> = (_: T) => boolean
|
|
10
12
|
type Mapper<T, S> = (_: T) => S
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
...props,
|
|
19
|
-
},
|
|
20
|
-
})
|
|
21
|
-
.onOk((data: any) => resolve(data))
|
|
22
|
-
.onCancel(() => reject('dialog cancelled by user.'))
|
|
23
|
-
)
|
|
24
|
-
}
|
|
14
|
+
type VueComponent = Component & (new (...args: any) => any)
|
|
15
|
+
type PropType<C extends VueComponent> = Omit<
|
|
16
|
+
InstanceType<C>['$props'],
|
|
17
|
+
'onOk' | 'onHide' | `onVnode${string}` | `ref_${string}`
|
|
18
|
+
>
|
|
19
|
+
type OkType<C extends VueComponent> = Parameters<InstanceType<C>['$emit']>[1]
|
|
25
20
|
|
|
26
|
-
export async function custom(
|
|
27
|
-
component:
|
|
28
|
-
props
|
|
29
|
-
) {
|
|
30
|
-
return await base(component, props)
|
|
21
|
+
export async function custom<C extends VueComponent>(
|
|
22
|
+
component: C,
|
|
23
|
+
props?: PropType<C>
|
|
24
|
+
): Promise<OkType<C>> {
|
|
25
|
+
return await base(component, props ?? {})
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
/**
|
|
@@ -75,7 +70,7 @@ export async function askFn(
|
|
|
75
70
|
funcs: Record<string, () => any>
|
|
76
71
|
): Promise<string> {
|
|
77
72
|
let key = await askBtn(title, message, Object.keys(funcs))
|
|
78
|
-
await funcs[key]()
|
|
73
|
+
await funcs[key]?.()
|
|
79
74
|
return key
|
|
80
75
|
}
|
|
81
76
|
|
|
@@ -89,7 +84,8 @@ export async function askTable<T extends object>(
|
|
|
89
84
|
message: string = '',
|
|
90
85
|
prefills: T[][],
|
|
91
86
|
validators: [(_: T) => boolean, string][] = [[$ => true, '']],
|
|
92
|
-
arrayValidators: [(_: T[]) => boolean, string][] = [[$ => true, '']]
|
|
87
|
+
arrayValidators: [(_: T[]) => boolean, string][] = [[$ => true, '']],
|
|
88
|
+
headersMap: Record<string, string> = {}
|
|
93
89
|
): Promise<T[]> {
|
|
94
90
|
if (prefills.flat().length === 0)
|
|
95
91
|
throwError('Error', 'All prefills are empty array.')
|
|
@@ -100,6 +96,7 @@ export async function askTable<T extends object>(
|
|
|
100
96
|
sample: [...prefills].reverse().find($ => $.length > 0)![0],
|
|
101
97
|
validators,
|
|
102
98
|
arrayValidators,
|
|
99
|
+
headersMap,
|
|
103
100
|
cancel: true,
|
|
104
101
|
allowAddRow: true,
|
|
105
102
|
})
|
|
@@ -113,7 +110,8 @@ export async function editTable<T extends object>(
|
|
|
113
110
|
message: string = '',
|
|
114
111
|
prefill: T[],
|
|
115
112
|
validators: [(_: T) => boolean, string][] = [[$ => true, '']],
|
|
116
|
-
arrayValidators: [(_: T[]) => boolean, string][] = [[$ => true, '']]
|
|
113
|
+
arrayValidators: [(_: T[]) => boolean, string][] = [[$ => true, '']],
|
|
114
|
+
headersMap: Record<string, string> = {}
|
|
117
115
|
): Promise<T[]> {
|
|
118
116
|
if (prefill.length === 0) throwError('Error', 'prefill is empty array.')
|
|
119
117
|
return await base(dialogTable, {
|
|
@@ -123,6 +121,7 @@ export async function editTable<T extends object>(
|
|
|
123
121
|
sample: prefill[0],
|
|
124
122
|
validators,
|
|
125
123
|
arrayValidators,
|
|
124
|
+
headersMap,
|
|
126
125
|
cancel: true,
|
|
127
126
|
allowAddRow: false,
|
|
128
127
|
})
|
|
@@ -148,43 +147,6 @@ export async function askArray(
|
|
|
148
147
|
return result.pluck('value').unmatch([''])
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
type FormPrefill = Record<
|
|
152
|
-
string,
|
|
153
|
-
| string
|
|
154
|
-
| number
|
|
155
|
-
| boolean
|
|
156
|
-
| { type: 'select'; value: string; options: string[] }
|
|
157
|
-
| { type: 'autocomplete'; value: string; options: string[] }
|
|
158
|
-
>
|
|
159
|
-
|
|
160
|
-
type FormFlat<T extends FormPrefill> = {
|
|
161
|
-
[P in keyof T]: T[P] extends object ? T[P]['value'] : T[P]
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Ask for an objects.
|
|
166
|
-
*/
|
|
167
|
-
export async function askForm<T extends FormPrefill>(
|
|
168
|
-
title: string,
|
|
169
|
-
message: string,
|
|
170
|
-
prefill: T,
|
|
171
|
-
validators: [(_: FormFlat<T>) => boolean, string][] = [[$ => true, '']]
|
|
172
|
-
): Promise<FormFlat<T>> {
|
|
173
|
-
const standardPrefill = Object.mapValues(prefill, $ => {
|
|
174
|
-
if (typeof $ === 'string') return { type: 'string', value: $ }
|
|
175
|
-
if (typeof $ === 'number') return { type: 'number', value: $ }
|
|
176
|
-
if (typeof $ === 'boolean') return { type: 'boolean', value: $ }
|
|
177
|
-
return $
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
return await base(dialogForm, {
|
|
181
|
-
title,
|
|
182
|
-
message,
|
|
183
|
-
prefill: standardPrefill,
|
|
184
|
-
validators,
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
|
|
188
150
|
/**
|
|
189
151
|
* Show a dialog with text area display.
|
|
190
152
|
*/
|
|
@@ -269,3 +231,47 @@ export async function askFiles(
|
|
|
269
231
|
cancel: true,
|
|
270
232
|
})
|
|
271
233
|
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Ask to select one text by a radio button.
|
|
237
|
+
*/
|
|
238
|
+
export async function askRadioText(
|
|
239
|
+
title: string,
|
|
240
|
+
message: string = '',
|
|
241
|
+
items: string[],
|
|
242
|
+
prefill: string = items[0]!,
|
|
243
|
+
newLabel: string = '',
|
|
244
|
+
isValid: Predicate<string> = $ => true
|
|
245
|
+
): Promise<string> {
|
|
246
|
+
return await base(dialogRadioText, {
|
|
247
|
+
title,
|
|
248
|
+
message,
|
|
249
|
+
items,
|
|
250
|
+
prefill,
|
|
251
|
+
newLabel,
|
|
252
|
+
isValid,
|
|
253
|
+
cancel: true,
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Ask to select multiple text by checkboxs.
|
|
259
|
+
*/
|
|
260
|
+
export async function askCheckboxText(
|
|
261
|
+
title: string,
|
|
262
|
+
message: string = '',
|
|
263
|
+
items: string[],
|
|
264
|
+
prefill: string[] = [],
|
|
265
|
+
newLabel: string = '',
|
|
266
|
+
isValid: Predicate<string[]> = $ => true
|
|
267
|
+
): Promise<string[]> {
|
|
268
|
+
return await base(dialogCheckboxText, {
|
|
269
|
+
title,
|
|
270
|
+
message,
|
|
271
|
+
items,
|
|
272
|
+
prefill,
|
|
273
|
+
newLabel,
|
|
274
|
+
isValid,
|
|
275
|
+
cancel: true,
|
|
276
|
+
})
|
|
277
|
+
}
|
|
@@ -143,7 +143,7 @@ export async function askRadio<T>(
|
|
|
143
143
|
title: string,
|
|
144
144
|
message: string = '',
|
|
145
145
|
items: T[],
|
|
146
|
-
prefill: T = items[0]
|
|
146
|
+
prefill: T = items[0]!,
|
|
147
147
|
label?: (string & keyof T) | Mapper<T, string>
|
|
148
148
|
): Promise<T> {
|
|
149
149
|
let labelFunc = ($: T) => Object.print($, label)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<q-dialog
|
|
3
|
+
ref="dialogRef"
|
|
4
|
+
@hide="onDialogHide"
|
|
5
|
+
>
|
|
6
|
+
<q-card style="width: 400px">
|
|
7
|
+
<q-card-section>
|
|
8
|
+
<p class="text-h6">{{ title }}</p>
|
|
9
|
+
<p v-html="message" />
|
|
10
|
+
<div style="max-height: 340px; overflow-y: auto">
|
|
11
|
+
<div v-for="item in items">
|
|
12
|
+
<q-checkbox
|
|
13
|
+
v-model="select"
|
|
14
|
+
:val="item"
|
|
15
|
+
:label="item"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
<div v-for="item in newItems">
|
|
19
|
+
<div class="row q-pr-md">
|
|
20
|
+
<q-checkbox
|
|
21
|
+
class="col-auto"
|
|
22
|
+
v-model="item.checked"
|
|
23
|
+
:val="item.val"
|
|
24
|
+
/>
|
|
25
|
+
<q-input
|
|
26
|
+
class="col"
|
|
27
|
+
v-model="item.val"
|
|
28
|
+
:label="newLabel"
|
|
29
|
+
dense
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</q-card-section>
|
|
35
|
+
|
|
36
|
+
<q-card-actions align="right">
|
|
37
|
+
<q-btn
|
|
38
|
+
label="Cancel"
|
|
39
|
+
flat
|
|
40
|
+
@click="onDialogCancel"
|
|
41
|
+
/>
|
|
42
|
+
<q-btn
|
|
43
|
+
label="OK"
|
|
44
|
+
flat
|
|
45
|
+
@click="onDialogOK(final)"
|
|
46
|
+
:disable="
|
|
47
|
+
![...items, ...newItems.pluck('val')].includesEvery(
|
|
48
|
+
final
|
|
49
|
+
) || !isValid(final)
|
|
50
|
+
"
|
|
51
|
+
/>
|
|
52
|
+
</q-card-actions>
|
|
53
|
+
</q-card>
|
|
54
|
+
</q-dialog>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script lang="ts" setup>
|
|
58
|
+
import { useDialogPluginComponent } from 'quasar'
|
|
59
|
+
import { computed, ref, watch } from 'vue'
|
|
60
|
+
|
|
61
|
+
defineEmits([...useDialogPluginComponent.emits])
|
|
62
|
+
|
|
63
|
+
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
64
|
+
useDialogPluginComponent()
|
|
65
|
+
|
|
66
|
+
const props = defineProps<{
|
|
67
|
+
title: string
|
|
68
|
+
message: string
|
|
69
|
+
items: string[]
|
|
70
|
+
prefill: string[]
|
|
71
|
+
newLabel: string
|
|
72
|
+
isValid: (_: string[]) => boolean
|
|
73
|
+
}>()
|
|
74
|
+
|
|
75
|
+
let select = ref([...props.prefill.match(props.items)])
|
|
76
|
+
|
|
77
|
+
let newItems = ref<{ val: string; checked: boolean }[]>([
|
|
78
|
+
{ val: '', checked: false },
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
let final = computed(() => {
|
|
82
|
+
return [
|
|
83
|
+
...select.value.sortedBy(props.items),
|
|
84
|
+
...newItems.value.match('checked').pluck('val'),
|
|
85
|
+
]
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
watch(
|
|
89
|
+
newItems,
|
|
90
|
+
() => {
|
|
91
|
+
if (newItems.value.at(-1)?.val !== '') {
|
|
92
|
+
newItems.value.push({ val: '', checked: false })
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{ deep: true }
|
|
96
|
+
)
|
|
97
|
+
</script>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
ref="dialogRef"
|
|
4
4
|
@hide="onDialogHide"
|
|
5
5
|
>
|
|
6
|
-
<q-card style="
|
|
6
|
+
<q-card style="width: 400px; max-width: 600px">
|
|
7
7
|
<q-card-section class="q-pb-xs">
|
|
8
8
|
<p class="text-h6">{{ title }}</p>
|
|
9
9
|
<p v-html="message" />
|
|
@@ -14,30 +14,50 @@
|
|
|
14
14
|
<q-input
|
|
15
15
|
v-if="spec.type === 'string'"
|
|
16
16
|
v-model="spec.value"
|
|
17
|
-
:label="field"
|
|
17
|
+
:label="spec.label ?? field"
|
|
18
|
+
:hint="spec.hint"
|
|
18
19
|
/>
|
|
19
20
|
<q-input
|
|
20
21
|
v-if="spec.type === 'number'"
|
|
21
22
|
v-model.number="spec.value"
|
|
22
|
-
:label="field"
|
|
23
|
+
:label="spec.label ?? field"
|
|
23
24
|
type="number"
|
|
25
|
+
:hint="spec.hint"
|
|
24
26
|
/>
|
|
25
27
|
<q-checkbox
|
|
26
28
|
v-if="spec.type === 'boolean'"
|
|
27
29
|
v-model="spec.value"
|
|
28
|
-
:label="field"
|
|
30
|
+
:label="spec.label ?? field"
|
|
31
|
+
/>
|
|
32
|
+
<q-input
|
|
33
|
+
v-if="spec.type === 'date'"
|
|
34
|
+
v-model="spec.value"
|
|
35
|
+
:label="spec.label ?? field"
|
|
36
|
+
type="date"
|
|
37
|
+
:hint="spec.hint"
|
|
38
|
+
/>
|
|
39
|
+
<q-input
|
|
40
|
+
v-if="spec.type === 'time'"
|
|
41
|
+
v-model="spec.value"
|
|
42
|
+
:label="spec.label ?? field"
|
|
43
|
+
type="time"
|
|
44
|
+
:hint="spec.hint"
|
|
29
45
|
/>
|
|
30
46
|
<q-select
|
|
31
47
|
v-if="spec.type === 'select'"
|
|
32
48
|
v-model="spec.value"
|
|
33
49
|
:options="spec.options"
|
|
34
|
-
:label="field"
|
|
50
|
+
:label="spec.label ?? field"
|
|
51
|
+
:hint="spec.hint"
|
|
35
52
|
/>
|
|
36
53
|
<qx-input-autocomplete
|
|
37
54
|
v-if="spec.type === 'autocomplete'"
|
|
38
55
|
v-model="spec.value"
|
|
39
56
|
:autocomplete="spec.options"
|
|
40
|
-
:label="field"
|
|
57
|
+
:label="spec.label ?? field"
|
|
58
|
+
:hint="spec.hint"
|
|
59
|
+
:no-filter="spec.noFilter"
|
|
60
|
+
:immediate="spec.immediate"
|
|
41
61
|
/>
|
|
42
62
|
</div>
|
|
43
63
|
<div
|
|
@@ -68,7 +88,7 @@
|
|
|
68
88
|
|
|
69
89
|
<script lang="ts" setup>
|
|
70
90
|
import { useDialogPluginComponent } from 'quasar'
|
|
71
|
-
import { computed, reactive, ref } from 'vue'
|
|
91
|
+
import { computed, reactive, ref, watch } from 'vue'
|
|
72
92
|
|
|
73
93
|
defineEmits([...useDialogPluginComponent.emits])
|
|
74
94
|
|
|
@@ -77,20 +97,37 @@ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
|
77
97
|
|
|
78
98
|
type row = Record<string, string | number | boolean>
|
|
79
99
|
|
|
80
|
-
type prefill = Record<
|
|
100
|
+
export type prefill = Record<
|
|
81
101
|
string,
|
|
82
|
-
| { type: 'string'; value: string }
|
|
83
|
-
| { type: 'number'; value: number }
|
|
84
|
-
| { type: 'boolean'; value: boolean }
|
|
85
|
-
| { type: '
|
|
86
|
-
| { type: '
|
|
102
|
+
| { type: 'string'; value: string; label?: string; hint?: string }
|
|
103
|
+
| { type: 'number'; value: number; label?: string; hint?: string }
|
|
104
|
+
| { type: 'boolean'; value: boolean; label?: string }
|
|
105
|
+
| { type: 'date'; value: string; label?: string; hint?: string }
|
|
106
|
+
| { type: 'time'; value: string; label?: string; hint?: string }
|
|
107
|
+
| {
|
|
108
|
+
type: 'select'
|
|
109
|
+
value: string
|
|
110
|
+
options: string[]
|
|
111
|
+
label?: string
|
|
112
|
+
hint?: string
|
|
113
|
+
}
|
|
114
|
+
| {
|
|
115
|
+
type: 'autocomplete'
|
|
116
|
+
value: string
|
|
117
|
+
options: string[]
|
|
118
|
+
label?: string
|
|
119
|
+
hint?: string
|
|
120
|
+
noFilter?: boolean
|
|
121
|
+
immediate?: boolean
|
|
122
|
+
}
|
|
87
123
|
>
|
|
88
124
|
|
|
89
|
-
const { title, message, prefill, validators } = defineProps<{
|
|
125
|
+
const { title, message, prefill, validators, onChange } = defineProps<{
|
|
90
126
|
title: string
|
|
91
127
|
message: string
|
|
92
128
|
prefill: prefill
|
|
93
129
|
validators: [(_: row) => boolean, string][]
|
|
130
|
+
onChange: (current: prefill, old: prefill) => void
|
|
94
131
|
}>()
|
|
95
132
|
|
|
96
133
|
const content = reactive(JSON.clone(prefill))
|
|
@@ -103,4 +140,9 @@ function err(): false | string {
|
|
|
103
140
|
}
|
|
104
141
|
return false
|
|
105
142
|
}
|
|
143
|
+
|
|
144
|
+
watch(
|
|
145
|
+
() => JSON.stringify(content),
|
|
146
|
+
(newbie, oldie) => onChange(content, JSON.parse(oldie))
|
|
147
|
+
)
|
|
106
148
|
</script>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<q-dialog
|
|
3
|
+
ref="dialogRef"
|
|
4
|
+
@hide="onDialogHide"
|
|
5
|
+
>
|
|
6
|
+
<q-card style="width: 400px">
|
|
7
|
+
<q-card-section>
|
|
8
|
+
<p class="text-h6">{{ title }}</p>
|
|
9
|
+
<p v-html="message" />
|
|
10
|
+
<div style="max-height: 340px; overflow-y: auto">
|
|
11
|
+
<div v-for="item in items">
|
|
12
|
+
<q-radio
|
|
13
|
+
v-model="select"
|
|
14
|
+
:val="item"
|
|
15
|
+
:label="item"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="row q-pr-md">
|
|
19
|
+
<q-radio
|
|
20
|
+
class="col-auto"
|
|
21
|
+
v-model="select"
|
|
22
|
+
:val="add"
|
|
23
|
+
/>
|
|
24
|
+
<q-input
|
|
25
|
+
class="col"
|
|
26
|
+
v-model="add"
|
|
27
|
+
:label="newLabel"
|
|
28
|
+
dense
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</q-card-section>
|
|
33
|
+
|
|
34
|
+
<q-card-actions align="right">
|
|
35
|
+
<q-btn
|
|
36
|
+
label="Cancel"
|
|
37
|
+
flat
|
|
38
|
+
@click="onDialogCancel"
|
|
39
|
+
/>
|
|
40
|
+
<q-btn
|
|
41
|
+
label="OK"
|
|
42
|
+
flat
|
|
43
|
+
@click="onDialogOK(select)"
|
|
44
|
+
:disable="
|
|
45
|
+
![...items, add].includes(select) || !isValid(select)
|
|
46
|
+
"
|
|
47
|
+
/>
|
|
48
|
+
</q-card-actions>
|
|
49
|
+
</q-card>
|
|
50
|
+
</q-dialog>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<script lang="ts" setup>
|
|
54
|
+
import { useDialogPluginComponent } from 'quasar'
|
|
55
|
+
import { ref, watch } from 'vue'
|
|
56
|
+
|
|
57
|
+
defineEmits([...useDialogPluginComponent.emits])
|
|
58
|
+
|
|
59
|
+
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
60
|
+
useDialogPluginComponent()
|
|
61
|
+
|
|
62
|
+
const props = defineProps<{
|
|
63
|
+
title: string
|
|
64
|
+
message: string
|
|
65
|
+
items: string[]
|
|
66
|
+
prefill: string
|
|
67
|
+
newLabel: string
|
|
68
|
+
isValid: (_: string) => boolean
|
|
69
|
+
}>()
|
|
70
|
+
|
|
71
|
+
let select = ref(props.prefill)
|
|
72
|
+
let add = ref('')
|
|
73
|
+
|
|
74
|
+
watch(add, () => {
|
|
75
|
+
select.value = add.value
|
|
76
|
+
})
|
|
77
|
+
</script>
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
v-model="dummy"
|
|
26
26
|
:sample="sample"
|
|
27
27
|
:allowAddRow="allowAddRow"
|
|
28
|
+
:headersMap="headersMap"
|
|
28
29
|
v-if="exist"
|
|
29
30
|
></handson>
|
|
30
31
|
</q-card>
|
|
@@ -83,6 +84,7 @@ const {
|
|
|
83
84
|
arrayValidators,
|
|
84
85
|
cancel,
|
|
85
86
|
allowAddRow,
|
|
87
|
+
headersMap,
|
|
86
88
|
} = defineProps<{
|
|
87
89
|
title: string
|
|
88
90
|
message: string
|
|
@@ -92,6 +94,7 @@ const {
|
|
|
92
94
|
arrayValidators: [(_: row[]) => boolean, string][]
|
|
93
95
|
cancel: boolean
|
|
94
96
|
allowAddRow: boolean
|
|
97
|
+
headersMap: Record<string, string>
|
|
95
98
|
}>()
|
|
96
99
|
|
|
97
100
|
// delay render
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
width="100%"
|
|
8
8
|
stretchH="all"
|
|
9
9
|
:data="content"
|
|
10
|
-
:colHeaders="Object.keys(sample)"
|
|
10
|
+
:colHeaders="Object.keys(sample).map($ => headersMap[$] ?? $)"
|
|
11
11
|
:columns="columns()"
|
|
12
12
|
:rowHeaders="true"
|
|
13
13
|
:afterChange="shake"
|
|
@@ -40,9 +40,10 @@ const content = defineModel<Record<string, string | number | boolean>[]>({
|
|
|
40
40
|
required: true,
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
const { sample, allowAddRow } = defineProps<{
|
|
43
|
+
const { sample, allowAddRow, headersMap } = defineProps<{
|
|
44
44
|
sample: Record<string, string | number | boolean>
|
|
45
45
|
allowAddRow: boolean
|
|
46
|
+
headersMap: Record<string, string>
|
|
46
47
|
}>()
|
|
47
48
|
|
|
48
49
|
function columns() {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Dialog, QDialogOptions } from 'quasar'
|
|
2
|
+
import type { Component } from 'vue'
|
|
3
|
+
|
|
4
|
+
export async function base(component: Component, props: any) {
|
|
5
|
+
return new Promise<any>((resolve, reject) =>
|
|
6
|
+
Dialog.create({
|
|
7
|
+
component,
|
|
8
|
+
componentProps: {
|
|
9
|
+
persistent: true,
|
|
10
|
+
...props,
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
.onOk((data: any) => resolve(data))
|
|
14
|
+
.onCancel(() => reject('dialog cancelled by user.'))
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import dialogForm from './custom/dialogForm.vue'
|
|
2
|
+
import { base } from './custom_base'
|
|
3
|
+
|
|
4
|
+
type FormPrefill = Record<
|
|
5
|
+
string,
|
|
6
|
+
| string
|
|
7
|
+
| number
|
|
8
|
+
| boolean
|
|
9
|
+
| { type: 'string'; value: string; label?: string; hint?: string }
|
|
10
|
+
| { type: 'number'; value: number; label?: string; hint?: string }
|
|
11
|
+
| { type: 'boolean'; value: boolean; label?: string }
|
|
12
|
+
| { type: 'date'; value: string; label?: string; hint?: string }
|
|
13
|
+
| { type: 'time'; value: string; label?: string; hint?: string }
|
|
14
|
+
| {
|
|
15
|
+
type: 'select'
|
|
16
|
+
value: string
|
|
17
|
+
options: string[]
|
|
18
|
+
label?: string
|
|
19
|
+
hint?: string
|
|
20
|
+
}
|
|
21
|
+
| {
|
|
22
|
+
type: 'autocomplete'
|
|
23
|
+
value: string
|
|
24
|
+
options: string[]
|
|
25
|
+
label?: string
|
|
26
|
+
hint?: string
|
|
27
|
+
noFilter?: boolean
|
|
28
|
+
immediate?: boolean
|
|
29
|
+
}
|
|
30
|
+
>
|
|
31
|
+
|
|
32
|
+
type FormPrefillStandard<T extends FormPrefill> = {
|
|
33
|
+
[P in keyof T]: T[P] extends object
|
|
34
|
+
? T[P]
|
|
35
|
+
: T[P] extends string
|
|
36
|
+
? { type: 'string'; value: string; label?: string; hint?: string }
|
|
37
|
+
: T[P] extends number
|
|
38
|
+
? { type: 'number'; value: number; label?: string; hint?: string }
|
|
39
|
+
: T[P] extends boolean
|
|
40
|
+
? { type: 'boolean'; value: boolean; label?: string }
|
|
41
|
+
: never
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type FormFlat<T extends FormPrefill> = {
|
|
45
|
+
[P in keyof T]: T[P] extends object ? T[P]['value'] : T[P]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Ask for an objects.
|
|
50
|
+
*/
|
|
51
|
+
export async function askForm<T extends FormPrefill>(
|
|
52
|
+
title: string,
|
|
53
|
+
message: string,
|
|
54
|
+
prefill: T,
|
|
55
|
+
validators: [(_: FormFlat<T>) => boolean, string][] = [[$ => true, '']],
|
|
56
|
+
onChange: (
|
|
57
|
+
current: FormPrefillStandard<T>,
|
|
58
|
+
old: FormPrefillStandard<T>
|
|
59
|
+
) => void = (c, o) => {}
|
|
60
|
+
): Promise<FormFlat<T>> {
|
|
61
|
+
const standardPrefill = Object.mapValues(prefill, $ => {
|
|
62
|
+
if (typeof $ === 'string') return { type: 'string', value: $ }
|
|
63
|
+
if (typeof $ === 'number') return { type: 'number', value: $ }
|
|
64
|
+
if (typeof $ === 'boolean') return { type: 'boolean', value: $ }
|
|
65
|
+
return $
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
return await base(dialogForm, {
|
|
69
|
+
title,
|
|
70
|
+
message,
|
|
71
|
+
prefill: standardPrefill,
|
|
72
|
+
validators,
|
|
73
|
+
onChange,
|
|
74
|
+
})
|
|
75
|
+
}
|