@ramathibodi/nuxt-commons 0.1.38 → 0.1.39
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/dist/module.json +1 -1
- package/dist/runtime/components/document/Form.vue +43 -0
- package/dist/runtime/components/document/TemplateBuilder.vue +198 -62
- package/dist/runtime/components/form/Pad.vue +4 -2
- package/dist/runtime/components/master/Autocomplete.vue +43 -130
- package/dist/runtime/components/master/label.vue +20 -19
- package/dist/runtime/components/model/Autocomplete.vue +23 -17
- package/dist/runtime/components/model/label.vue +8 -5
- package/dist/runtime/composables/document/template.d.ts +23 -1
- package/dist/runtime/composables/document/template.js +70 -54
- package/dist/runtime/composables/document/templateFormHidden.d.ts +2 -0
- package/dist/runtime/composables/document/templateFormHidden.js +10 -0
- package/dist/runtime/composables/document/templateFormTable.d.ts +2 -0
- package/dist/runtime/composables/document/templateFormTable.js +9 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import {ref, withDefaults,watch,computed} from 'vue'
|
|
3
|
+
import {graphqlOperation} from '#imports'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
templateCode: string
|
|
7
|
+
parentTemplates?: string|string[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
11
|
+
parentTemplates: (): string[] => [],
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const template = ref<any>()
|
|
15
|
+
const templateScript = ref<string>()
|
|
16
|
+
|
|
17
|
+
const parentTemplates = computed(()=>{
|
|
18
|
+
if (typeof props.parentTemplates === 'string') {
|
|
19
|
+
if (props.parentTemplates && props.parentTemplates!="") return props.parentTemplates.split("|")
|
|
20
|
+
else return []
|
|
21
|
+
} else {
|
|
22
|
+
return props.parentTemplates
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const parentTemplatesNext = computed(()=>{
|
|
27
|
+
return [...parentTemplates.value,props.templateCode]
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
watch(()=>props.templateCode,()=>{
|
|
31
|
+
if (!parentTemplates.value.includes(props.templateCode) && props.templateCode) {
|
|
32
|
+
if (graphqlOperation["documentTemplateByTemplateCode"]) {
|
|
33
|
+
graphqlOperation["documentTemplateByTemplateCode"].call(["*"],{templateCode: props.templateCode}).then((result: any)=>{
|
|
34
|
+
template.value = result.editTemplate
|
|
35
|
+
templateScript.value = result.templateScript
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},{immediate: true})
|
|
40
|
+
</script>
|
|
41
|
+
<template>
|
|
42
|
+
<form-pad :template="template" :template-script="templateScript" :parent-templates="parentTemplatesNext"></form-pad>
|
|
43
|
+
</template>
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
import {computed, ref, watch} from 'vue'
|
|
3
3
|
import * as prettier from 'prettier'
|
|
4
4
|
import prettierPluginHtml from 'prettier/plugins/html'
|
|
5
|
-
import {useDocumentTemplate, validationRulesRegex} from '../../composables/document/template'
|
|
5
|
+
import {useDocumentTemplate, validationRulesRegex,optionStringToChoiceObject} from '../../composables/document/template'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
12
|
+
title: "Document Template"
|
|
13
|
+
})
|
|
6
14
|
|
|
7
15
|
const emit = defineEmits(['update:modelValue'])
|
|
8
16
|
const modelValue = defineModel<string>()
|
|
@@ -49,6 +57,33 @@ const headers = ref([
|
|
|
49
57
|
},
|
|
50
58
|
])
|
|
51
59
|
|
|
60
|
+
const formTableHeaders = ref([
|
|
61
|
+
{
|
|
62
|
+
title: 'Title',
|
|
63
|
+
key: 'title',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
title: 'Key',
|
|
67
|
+
key: 'key',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
title: 'Width',
|
|
71
|
+
key: 'width',
|
|
72
|
+
},
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
const choiceHeaders = ref([
|
|
76
|
+
{
|
|
77
|
+
title: 'Label',
|
|
78
|
+
key: 'label',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
title: 'Value',
|
|
82
|
+
key: 'value',
|
|
83
|
+
},
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
|
|
52
87
|
function isValidJsonArrayOfObjects(str: string | undefined) {
|
|
53
88
|
try {
|
|
54
89
|
const parsed = JSON.parse(str as string)
|
|
@@ -72,17 +107,48 @@ async function convertToAdvanceMode() {
|
|
|
72
107
|
modelValue.value = await prettier.format(useDocumentTemplate(templateItems.value).replaceAll('>', '>\n'), { parser: 'html', plugins: [prettierPluginHtml] })
|
|
73
108
|
}
|
|
74
109
|
}
|
|
110
|
+
const inputTypeChoice = ref([
|
|
111
|
+
{ label: 'Text Field', value: 'VTextField' },
|
|
112
|
+
{ label: 'Text Area', value: 'VTextarea' },
|
|
113
|
+
{ label: 'Text Date Picker', value: 'FormDate' },
|
|
114
|
+
{ label: 'Text Time Picker', value: 'FormTime' },
|
|
115
|
+
{ label: 'Text Date & Time Picker', value: 'FormDateTime' },
|
|
116
|
+
{ label: 'Select Dropdown', value: 'VSelect' },
|
|
117
|
+
{ label: 'Select Combobox', value: 'VCombobox' },
|
|
118
|
+
{ label: 'Autocomplete', value: 'VAutocomplete' },
|
|
119
|
+
{ label: 'Autocomplete from Master', value: 'MasterAutocomplete' },
|
|
120
|
+
{ label: 'Radio Buttons', value: 'VRadio' },
|
|
121
|
+
{ label: 'Radio Buttons Inline', value: 'VRadioInline' },
|
|
122
|
+
{ label: 'Checkbox', value: 'VCheckbox' },
|
|
123
|
+
{ label: 'Switch', value: 'VSwitch' },
|
|
124
|
+
{ label: 'File Upload', value: 'FormFile' },
|
|
125
|
+
{ label: 'Signature Pad', value: 'FormSignPad' },
|
|
126
|
+
{ label: 'Table', value: 'FormTable' },
|
|
127
|
+
{ label: '[Decoration] Header', value: 'Header' },
|
|
128
|
+
{ label: '[Decoration] Separator', value: 'Separator' },
|
|
129
|
+
{ label: '[Advanced] Hidden Field', value: 'FormHidden' },
|
|
130
|
+
{ label: '[Advanced] Inherit Form', value: 'DocumentForm' },
|
|
131
|
+
{ label: '[Advanced] Custom Code', value: 'CustomCode' },
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
const requireOption = ref(['VSelect', 'VAutocomplete', 'VCombobox', 'VRadio', 'VRadioInline', 'MasterAutocomplete','FormTable','DocumentForm'])
|
|
135
|
+
const notRequireVariable = ref(['Header', 'Separator', 'CustomCode','DocumentForm'])
|
|
136
|
+
const notRequireLabel = ref(['Separator', 'CustomCode', 'FormFile', 'FormHidden','DocumentForm'])
|
|
137
|
+
const notRequireWidth = ref(['Separator', 'FormHidden','DocumentForm'])
|
|
138
|
+
const notRequireOptions = ref(['Separator','CustomCode', 'FormFile'])
|
|
139
|
+
const notRequireRules = ref(['Separator','Header','CustomCode','FormHidden','DocumentForm'])
|
|
140
|
+
const notRequireInputAttributes = ref(['CustomCode','Header', 'FormHidden','DocumentForm'])
|
|
141
|
+
const notRequireColumnAttributes = ref(['Separator', 'FormHidden','DocumentForm'])
|
|
142
|
+
const notRequireAdvancedSetting = ref(['Separator','CustomCode', 'FormHidden'])
|
|
75
143
|
|
|
76
|
-
const
|
|
77
|
-
const requireOption = ref(['VSelect', 'VAutocomplete', 'VCombobox', 'VRadio', 'VRadioInline', 'MasterAutocomplete'])
|
|
78
|
-
const notRequireVariable = ref(['Header', 'Separator', 'CustomCode'])
|
|
79
|
-
const notRequireLabel = ref(['Separator', 'CustomCode', 'FormFile', 'FormSignPad', 'FormTable', 'FormHidden'])
|
|
80
|
-
const notRequireOptions = ref(['CustomCode', 'FormSignPad', 'FormFile', 'FormTable'])
|
|
81
|
-
const notRequireRules = ref(['CustomCode', 'FormSignPad', 'FormFile', 'FormTable' ,'FormHidden'])
|
|
82
|
-
const notRequireInputAttributes = ref(['CustomCode', 'FormHidden'])
|
|
83
|
-
const notRequireColumnAttributes = ref(['Separator', 'FormHidden'])
|
|
144
|
+
const hasSpecificOption = ref(['FormHidden','FormTable'])
|
|
84
145
|
|
|
85
|
-
const choiceOption = ref(['VSelect', '
|
|
146
|
+
const choiceOption = ref(['VRadio', 'VRadioInline','VSelect', 'VAutocomplete', 'VCombobox'])
|
|
147
|
+
const inputOptionsLabel = ref<Record<string,string>>({
|
|
148
|
+
'MasterAutocomplete' : "Group Key",
|
|
149
|
+
'Header' : "Class",
|
|
150
|
+
'DocumentForm': "Template Code",
|
|
151
|
+
})
|
|
86
152
|
|
|
87
153
|
const ruleOptions = (inputType: string) => (value: any) => {
|
|
88
154
|
if (choiceOption.value.includes(inputType) && !(/^[^'",]+(,[^'",]+)*$/.test(value))) return 'Invalid options format'
|
|
@@ -96,7 +162,7 @@ const ruleOptions = (inputType: string) => (value: any) => {
|
|
|
96
162
|
v-if="!isAdvanceMode"
|
|
97
163
|
v-model="templateItems"
|
|
98
164
|
:headers="headers"
|
|
99
|
-
title="
|
|
165
|
+
:title="props.title"
|
|
100
166
|
>
|
|
101
167
|
<template #toolbarItems>
|
|
102
168
|
<VBtn
|
|
@@ -118,19 +184,20 @@ const ruleOptions = (inputType: string) => (value: any) => {
|
|
|
118
184
|
v-model="data.inputType"
|
|
119
185
|
label="Input Type"
|
|
120
186
|
:items="inputTypeChoice"
|
|
187
|
+
item-title="label"
|
|
188
|
+
item-value="value"
|
|
189
|
+
:return-object="false"
|
|
121
190
|
:rules="[rules.require()]"
|
|
122
191
|
/>
|
|
123
192
|
</v-col>
|
|
124
|
-
<v-col
|
|
125
|
-
v-if="data.inputType!='Separator'"
|
|
126
|
-
cols="1"
|
|
127
|
-
>
|
|
193
|
+
<v-col cols="1" v-if="!notRequireWidth.includes(data.inputType)">
|
|
128
194
|
<v-text-field
|
|
129
195
|
v-model="data.width"
|
|
130
196
|
label="Width"
|
|
131
197
|
:rules="[rules.require()]"
|
|
132
198
|
type="number"
|
|
133
199
|
/>
|
|
200
|
+
<form-hidden v-model="data.inputOptions" :item-value="data.inputType" :hook="()=>undefined"></form-hidden>
|
|
134
201
|
</v-col>
|
|
135
202
|
<v-col cols="4" v-if="!notRequireLabel.includes(data.inputType)">
|
|
136
203
|
<v-text-field
|
|
@@ -150,32 +217,67 @@ const ruleOptions = (inputType: string) => (value: any) => {
|
|
|
150
217
|
v-if="!notRequireOptions.includes(data.inputType)"
|
|
151
218
|
cols="12"
|
|
152
219
|
>
|
|
220
|
+
<form-pad v-model="data.inputOptions" v-if="data.inputType=='FormTable'">
|
|
221
|
+
<template #default="{data: optionData}">
|
|
222
|
+
<v-row dense>
|
|
223
|
+
<v-col cols="12">
|
|
224
|
+
<form-table v-model="optionData.headers" :headers="formTableHeaders" title="Headers">
|
|
225
|
+
<template #form="{data: headerData,rules}">
|
|
226
|
+
<v-container fluid>
|
|
227
|
+
<v-row dense>
|
|
228
|
+
<v-col cols="6"><v-text-field v-model="headerData.title" label="Title"></v-text-field></v-col>
|
|
229
|
+
<v-col cols="3"><v-text-field v-model="headerData.key" label="Key" :rules="[rules.require()]"></v-text-field></v-col>
|
|
230
|
+
<v-col cols="3"><v-text-field v-model="headerData.width" label="Width"></v-text-field></v-col>
|
|
231
|
+
</v-row>
|
|
232
|
+
</v-container>
|
|
233
|
+
</template>
|
|
234
|
+
</form-table>
|
|
235
|
+
</v-col>
|
|
236
|
+
<v-col cols="12">
|
|
237
|
+
<document-template-builder v-model="optionData.formTemplate" title="Form Template"></document-template-builder>
|
|
238
|
+
</v-col>
|
|
239
|
+
</v-row>
|
|
240
|
+
</template>
|
|
241
|
+
</form-pad>
|
|
242
|
+
<form-pad v-model="data.inputOptions" v-if="data.inputType=='FormHidden'">
|
|
243
|
+
<template #default="{data: optionData}">
|
|
244
|
+
<v-row dense>
|
|
245
|
+
<v-col cols="12">
|
|
246
|
+
<v-text-field
|
|
247
|
+
v-model="optionData.hook"
|
|
248
|
+
label="Hook"
|
|
249
|
+
:rules="[rules.require()]"
|
|
250
|
+
/>
|
|
251
|
+
</v-col>
|
|
252
|
+
<v-col cols="12">
|
|
253
|
+
<v-text-field
|
|
254
|
+
v-model="optionData.itemValue"
|
|
255
|
+
label="Item Value"
|
|
256
|
+
/>
|
|
257
|
+
</v-col>
|
|
258
|
+
</v-row>
|
|
259
|
+
</template>
|
|
260
|
+
</form-pad>
|
|
261
|
+
<form-table v-model="data.inputOptions" :headers="choiceHeaders" title="Choices" v-if="choiceOption.includes(data.inputType) && (!data.inputOptions || typeof data.inputOptions === 'object')">
|
|
262
|
+
<template #form="{data: optionData,rules}">
|
|
263
|
+
<v-container fluid>
|
|
264
|
+
<v-row dense>
|
|
265
|
+
<v-col cols="6"><v-text-field v-model="optionData.label" label="Label"></v-text-field></v-col>
|
|
266
|
+
<v-col cols="6"><v-text-field v-model="optionData.value" label="Value" :rules="[rules.require()]"></v-text-field></v-col>
|
|
267
|
+
</v-row>
|
|
268
|
+
</v-container>
|
|
269
|
+
</template>
|
|
270
|
+
</form-table>
|
|
271
|
+
<form-hidden v-model="data.inputOptions" :itemValue="data.inputOptions" :hook="(option: any)=>optionStringToChoiceObject(option)" v-if="choiceOption.includes(data.inputType) && data.inputOptions && typeof data.inputOptions === 'string'"></form-hidden>
|
|
153
272
|
<v-textarea
|
|
154
273
|
v-model="data.inputOptions"
|
|
155
|
-
label="Input Options"
|
|
274
|
+
:label="inputOptionsLabel[data.inputType] || 'Input Options'"
|
|
156
275
|
auto-grow
|
|
157
276
|
:rules="[rules.requireIf(requireOption.includes(data.inputType)), ruleOptions(data.inputType)]"
|
|
277
|
+
v-if="!hasSpecificOption.includes(data.inputType) && !choiceOption.includes(data.inputType)"
|
|
158
278
|
/>
|
|
159
279
|
</v-col>
|
|
160
|
-
|
|
161
|
-
<v-text-field
|
|
162
|
-
v-model="data.validationRules"
|
|
163
|
-
label="Validation Rules"
|
|
164
|
-
:rules="[rules.regex(validationRulesRegex)]"
|
|
165
|
-
/>
|
|
166
|
-
</v-col>
|
|
167
|
-
<v-col v-if="data.inputType!='CustomCode'">
|
|
168
|
-
<v-text-field
|
|
169
|
-
v-model="data.inputAttributes"
|
|
170
|
-
label="Input Attributes"
|
|
171
|
-
/>
|
|
172
|
-
</v-col>
|
|
173
|
-
<v-col v-if="data.inputType!='Separator'">
|
|
174
|
-
<v-text-field
|
|
175
|
-
v-model="data.columnAttributes"
|
|
176
|
-
label="Column Attributes"
|
|
177
|
-
/>
|
|
178
|
-
</v-col>
|
|
280
|
+
|
|
179
281
|
<v-col
|
|
180
282
|
v-if="data.inputType=='CustomCode'"
|
|
181
283
|
cols="12"
|
|
@@ -186,25 +288,67 @@ const ruleOptions = (inputType: string) => (value: any) => {
|
|
|
186
288
|
:rules="[rules.require()]"
|
|
187
289
|
/>
|
|
188
290
|
</v-col>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
v-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
291
|
+
</v-row>
|
|
292
|
+
<v-row dense>
|
|
293
|
+
<v-col>
|
|
294
|
+
<v-expansion-panels>
|
|
295
|
+
<v-expansion-panel v-if="!notRequireRules.includes(data.inputType)">
|
|
296
|
+
<v-expansion-panel-title collapse-icon="mdi mdi-minus" expand-icon="mdi mdi-plus" class="font-weight-bold">Validations</v-expansion-panel-title>
|
|
297
|
+
<v-expansion-panel-text>
|
|
298
|
+
<v-container fluid>
|
|
299
|
+
<v-row dense>
|
|
300
|
+
<v-col cols="12">
|
|
301
|
+
<v-text-field
|
|
302
|
+
v-model="data.validationRules"
|
|
303
|
+
label="Validation Rules"
|
|
304
|
+
:rules="[rules.regex(validationRulesRegex)]"
|
|
305
|
+
/>
|
|
306
|
+
<b>Available rules :</b> require, requireIf(condition), requireTrue, requireTrueIf(condition), numeric, range(min,max), integer, unique, length(length), lengthGreater(length), lengthLess(length), telephone, email, regex(regex), idcard, DateFuture, DatetimeFuture, DateHappen, DatetimeHappen, DateAfter(date), DateBefore(Date), DateEqual(date)
|
|
307
|
+
</v-col>
|
|
308
|
+
</v-row>
|
|
309
|
+
</v-container>
|
|
310
|
+
</v-expansion-panel-text>
|
|
311
|
+
</v-expansion-panel>
|
|
312
|
+
<v-expansion-panel v-if="!notRequireAdvancedSetting.includes(data.inputType)">
|
|
313
|
+
<v-expansion-panel-title collapse-icon="mdi mdi-minus" expand-icon="mdi mdi-plus" class="font-weight-bold">Advanced settings</v-expansion-panel-title>
|
|
314
|
+
<v-expansion-panel-text>
|
|
315
|
+
<v-container fluid>
|
|
316
|
+
<v-row dense>
|
|
317
|
+
<v-col cols="12" md="6" v-if="!notRequireInputAttributes.includes(data.inputType)">
|
|
318
|
+
<v-text-field
|
|
319
|
+
v-model="data.inputAttributes"
|
|
320
|
+
label="Input Attributes"
|
|
321
|
+
/>
|
|
322
|
+
</v-col>
|
|
323
|
+
<v-col cols="12" md="6" v-if="!notRequireColumnAttributes.includes(data.inputType)">
|
|
324
|
+
<v-text-field
|
|
325
|
+
v-model="data.columnAttributes"
|
|
326
|
+
label="Column Attributes"
|
|
327
|
+
/>
|
|
328
|
+
</v-col>
|
|
329
|
+
<v-col cols="12" md="6">
|
|
330
|
+
<v-text-field
|
|
331
|
+
v-model="data.conditionalDisplay"
|
|
332
|
+
label="Conditional Display"
|
|
333
|
+
/>
|
|
334
|
+
</v-col>
|
|
335
|
+
<v-col cols="12" md="6">
|
|
336
|
+
<v-text-field
|
|
337
|
+
v-model="data.computedValue"
|
|
338
|
+
label="Computed Value"
|
|
339
|
+
/>
|
|
340
|
+
</v-col>
|
|
341
|
+
<v-col cols="12" md="6">
|
|
342
|
+
<v-text-field
|
|
343
|
+
v-model="data.retrievedValue"
|
|
344
|
+
label="Retrieved Value"
|
|
345
|
+
/>
|
|
346
|
+
</v-col>
|
|
347
|
+
</v-row>
|
|
348
|
+
</v-container>
|
|
349
|
+
</v-expansion-panel-text>
|
|
350
|
+
</v-expansion-panel>
|
|
351
|
+
</v-expansion-panels>
|
|
208
352
|
</v-col>
|
|
209
353
|
</v-row>
|
|
210
354
|
</v-container>
|
|
@@ -223,14 +367,6 @@ const ruleOptions = (inputType: string) => (value: any) => {
|
|
|
223
367
|
<template v-if="props.item.inputAttributes">
|
|
224
368
|
<b>Input Attributes :</b> {{ props.item.inputAttributes }}<br>
|
|
225
369
|
</template>
|
|
226
|
-
<template v-if="props.item.inputType=='FormTable'">
|
|
227
|
-
<b>Form Table :</b><br>
|
|
228
|
-
<span style="white-space: pre-line">{{ props.item.inputFormTable }}</span>
|
|
229
|
-
</template>
|
|
230
|
-
<template v-if="props.item.inputType=='FormHidden'">
|
|
231
|
-
<b>Form Hidden :</b><br>
|
|
232
|
-
<span style="white-space: pre-line">{{ props.item.inputFormHidden }}</span>
|
|
233
|
-
</template>
|
|
234
370
|
</template>
|
|
235
371
|
</FormTable>
|
|
236
372
|
<FormCodeEditor
|
|
@@ -30,6 +30,7 @@ interface Props {
|
|
|
30
30
|
readonly?: boolean
|
|
31
31
|
isolated?: boolean
|
|
32
32
|
decoration?: object
|
|
33
|
+
parentTemplates?: string|string[]
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -37,6 +38,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
37
38
|
readonly: false,
|
|
38
39
|
isolated: false,
|
|
39
40
|
decoration: () => { return {} },
|
|
41
|
+
parentTemplates: (): string[] => [],
|
|
40
42
|
})
|
|
41
43
|
|
|
42
44
|
const emit = defineEmits(['update:modelValue'])
|
|
@@ -89,7 +91,7 @@ const formComponent = shallowRef()
|
|
|
89
91
|
function buildFormComponent() {
|
|
90
92
|
if (!trimmedTemplate.value) return
|
|
91
93
|
const originalConsoleError = console.warn
|
|
92
|
-
console.warn = (error) => { throw new Error(error) } // eslint-disable-line
|
|
94
|
+
console.warn = (error: any) => { throw new Error(error) } // eslint-disable-line
|
|
93
95
|
try {
|
|
94
96
|
const componentTemplate = '<form-pad ref="formPadTemplate" v-model="formComponentData" :disabled="disabled" :readonly="readonly" :decoration="decoration" :isolated="isolated"><template v-slot="{ data,isDisabled,isReadonly,rules,formProvided,decoration }">' + trimmedTemplate.value + '</template></form-pad>'
|
|
95
97
|
compile(componentTemplate)
|
|
@@ -159,7 +161,7 @@ onMounted(() => {
|
|
|
159
161
|
})
|
|
160
162
|
|
|
161
163
|
watchDebounced(() => props.template, (newValue) => {
|
|
162
|
-
trimmedTemplate.value = useDocumentTemplate(newValue).trim() || ''
|
|
164
|
+
trimmedTemplate.value = useDocumentTemplate(newValue,props.parentTemplates).trim() || ''
|
|
163
165
|
buildFormComponent()
|
|
164
166
|
}, { debounce: 500, maxWait: 5000, deep: true, immediate: true })
|
|
165
167
|
watchDebounced(() => props.templateScript, buildFormComponent, { debounce: 500, maxWait: 5000 })
|
|
@@ -1,113 +1,56 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import {VAutocomplete} from 'vuetify/components/VAutocomplete'
|
|
3
|
-
import {concat
|
|
4
|
-
import {computed
|
|
5
|
-
import {watchDebounced} from '@vueuse/core'
|
|
6
|
-
import {useFuzzy} from '../../composables/utils/fuzzy'
|
|
7
|
-
import {useGraphQl} from '../../composables/graphql'
|
|
3
|
+
import {concat} from 'lodash-es'
|
|
4
|
+
import {computed} from 'vue'
|
|
8
5
|
|
|
9
6
|
interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {
|
|
10
|
-
|
|
7
|
+
sortBy?: 'itemCode' | 'itemValue'
|
|
8
|
+
showCode?: boolean
|
|
9
|
+
|
|
11
10
|
groupKey: string
|
|
11
|
+
itemCodes?: string[]
|
|
12
12
|
lang?: 'TH' | 'EN'
|
|
13
|
-
fields?: string
|
|
13
|
+
fields?: Array<string | object>
|
|
14
14
|
noDataText?: string
|
|
15
15
|
filterText?: string
|
|
16
16
|
waitForFilter?: boolean
|
|
17
17
|
waitForFilterText?: string
|
|
18
|
-
modelValue?: string
|
|
19
|
-
sortBy?: 'itemCode' | 'itemValue'
|
|
20
|
-
showCode?: boolean
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
const props = withDefaults(defineProps<Props>(), {
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
sortBy: 'itemValue',
|
|
22
|
+
showCode: false,
|
|
23
|
+
|
|
26
24
|
lang: 'TH',
|
|
25
|
+
noDataText: 'ไม่พบข้อมูล',
|
|
27
26
|
waitForFilter: false,
|
|
28
|
-
sortBy: 'itemValue',
|
|
29
|
-
showCode: false
|
|
30
27
|
})
|
|
31
28
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
const masterItems = ref<Array<any>>([])
|
|
35
|
-
const items = ref<Array<any>>([])
|
|
36
|
-
const selectedItem = ref<any>()
|
|
37
|
-
|
|
38
|
-
const searchData = ref<string>('')
|
|
39
|
-
|
|
40
|
-
const isLoading = ref(false)
|
|
41
|
-
const isErrorLoading = ref(false)
|
|
42
|
-
|
|
43
|
-
function query(groupKey: string, filterText: string | undefined) {
|
|
29
|
+
const computedModelName = computed(()=>{
|
|
44
30
|
let operation = 'masterItemByGroupKey'
|
|
31
|
+
if (props.filterText) operation = 'masterItemByGroupKeyAndFilterText'
|
|
32
|
+
if (props.itemCodes && props.itemCodes.length>0) operation = 'masterItemByGroupKeyAndItemCodeIn'
|
|
33
|
+
if (props.waitForFilter && !props.filterText) operation = ""
|
|
34
|
+
return operation
|
|
35
|
+
})
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
const computedModelBy = computed(()=>{
|
|
38
|
+
let modelBy : Record<string,any> = {groupKey: props.groupKey}
|
|
39
|
+
if (props.filterText) {
|
|
40
|
+
modelBy["filterText"] = props.filterText
|
|
50
41
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (props.fields) fields = concat(fields, props.fields)
|
|
54
|
-
|
|
55
|
-
isLoading.value = true
|
|
56
|
-
|
|
57
|
-
useGraphQl().queryPromise(operation, fields, variables).then((result: any) => {
|
|
58
|
-
masterItems.value = result
|
|
59
|
-
items.value = result
|
|
60
|
-
isErrorLoading.value = false
|
|
61
|
-
}).catch((_error) => {
|
|
62
|
-
isErrorLoading.value = true
|
|
63
|
-
}).finally(() => {
|
|
64
|
-
isLoading.value = false
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
watchEffect(()=>{
|
|
69
|
-
if (props.waitForFilter && !props.filterText) {
|
|
70
|
-
masterItems.value = []
|
|
71
|
-
items.value = []
|
|
72
|
-
selectedItem.value = undefined
|
|
73
|
-
} else {
|
|
74
|
-
query(props.groupKey, props.filterText)
|
|
42
|
+
if (props.itemCodes && props.itemCodes.length>0) {
|
|
43
|
+
modelBy["itemCodes"] = props.itemCodes
|
|
75
44
|
}
|
|
45
|
+
return modelBy
|
|
76
46
|
})
|
|
77
47
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}, { immediate: true })
|
|
81
|
-
|
|
82
|
-
watch(selectedItem, (newValue) => {
|
|
83
|
-
emit('update:modelValue', newValue)
|
|
48
|
+
const computedFields = computed(()=>{
|
|
49
|
+
return concat(['itemCode', 'itemValue', 'itemValueAlternative'],props.fields)
|
|
84
50
|
})
|
|
85
51
|
|
|
86
|
-
async function fuzzySearch() {
|
|
87
|
-
if (props.fuzzy) {
|
|
88
|
-
if (isEmpty(searchData.value)) {
|
|
89
|
-
items.value = masterItems.value
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
let fields: any[] = ['itemCode', 'itemValue', 'itemValueAlternative']
|
|
93
|
-
if (props.fields) fields = concat(fields, props.fields)
|
|
94
|
-
|
|
95
|
-
const results: any = useFuzzy(searchData.value, masterItems.value, fields)
|
|
96
|
-
items.value = []
|
|
97
|
-
if (results.value.length) {
|
|
98
|
-
for (let i = 0; results.value.length > i; i++) {
|
|
99
|
-
if (results.value[i].item) items.value.push(results.value[i].item)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
watchDebounced(searchData, fuzzySearch, { debounce: 1000, maxWait: 5000 })
|
|
107
|
-
|
|
108
52
|
const itemTitleField = computed(() => {
|
|
109
|
-
|
|
110
|
-
else return 'itemValueAlternative'
|
|
53
|
+
return (props.lang == 'TH') ? 'itemValue' : 'itemValueAlternative'
|
|
111
54
|
})
|
|
112
55
|
|
|
113
56
|
const computedNoDataText = computed(() => {
|
|
@@ -116,64 +59,34 @@ const computedNoDataText = computed(() => {
|
|
|
116
59
|
})
|
|
117
60
|
|
|
118
61
|
const computedSortBy = computed(()=>{
|
|
119
|
-
|
|
120
|
-
if (sortByField == 'itemValue') {
|
|
62
|
+
if (props.sortBy == 'itemValue') {
|
|
121
63
|
if (props.showCode) return ['itemCode']
|
|
122
64
|
else return [itemTitleField.value,'itemValue','itemCode']
|
|
123
65
|
} else {
|
|
124
|
-
return [
|
|
125
|
-
}
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
const computedItems = computed(()=>{
|
|
129
|
-
if (props.fuzzy && !isEmpty(searchData.value)) {
|
|
130
|
-
return items.value
|
|
131
|
-
} else {
|
|
132
|
-
return sortBy(items.value, computedSortBy.value)
|
|
66
|
+
return [props.sortBy]
|
|
133
67
|
}
|
|
134
68
|
})
|
|
135
69
|
</script>
|
|
136
70
|
|
|
137
71
|
<template>
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
:loading="isLoading"
|
|
72
|
+
<model-autocomplete
|
|
73
|
+
:model-name="computedModelName"
|
|
74
|
+
:model-by="computedModelBy"
|
|
75
|
+
:fields="computedFields"
|
|
76
|
+
:sort-by="computedSortBy"
|
|
77
|
+
:item-title="itemTitleField"
|
|
78
|
+
item-value="itemCode"
|
|
79
|
+
:no-data-text="computedNoDataText"
|
|
80
|
+
:show-code="props.showCode"
|
|
148
81
|
>
|
|
149
|
-
<!-- @ts-ignore -->
|
|
150
82
|
<template
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
#[name]="slotData"
|
|
154
|
-
>
|
|
155
|
-
<slot
|
|
156
|
-
:name="name"
|
|
157
|
-
v-bind="((slotData || {}) as object)"
|
|
158
|
-
:operation="operation"
|
|
159
|
-
/>
|
|
160
|
-
</template>
|
|
161
|
-
<template
|
|
162
|
-
v-if="!$slots.item"
|
|
163
|
-
#item="{ props, item }"
|
|
83
|
+
v-if="!$slots.item"
|
|
84
|
+
#item="{ props, item }"
|
|
164
85
|
>
|
|
165
86
|
<v-list-item
|
|
166
|
-
|
|
167
|
-
|
|
87
|
+
v-bind="props"
|
|
88
|
+
:title="(showCode ? item.raw.itemCode+'-' : '')+(item.title || item.raw.itemValue || item.raw.itemCode)"
|
|
168
89
|
/>
|
|
169
90
|
</template>
|
|
170
|
-
|
|
171
|
-
v-if="isErrorLoading"
|
|
172
|
-
#append
|
|
173
|
-
>
|
|
174
|
-
<v-icon color="error">
|
|
175
|
-
mdi mdi-alert
|
|
176
|
-
</v-icon>
|
|
177
|
-
</template>
|
|
178
|
-
</v-autocomplete>
|
|
91
|
+
</model-autocomplete>
|
|
179
92
|
</template>
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computedAsync } from '@vueuse/core'
|
|
3
|
-
import { useGraphQl } from '../../composables/graphql'
|
|
4
|
-
|
|
5
2
|
interface Props {
|
|
6
3
|
groupKey?: string | null
|
|
7
4
|
itemCode?: string | null
|
|
8
5
|
locale?: string
|
|
6
|
+
|
|
9
7
|
notFoundText?: string
|
|
10
8
|
placeholder?: string
|
|
11
9
|
}
|
|
@@ -14,24 +12,27 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
14
12
|
locale: 'TH',
|
|
15
13
|
})
|
|
16
14
|
|
|
17
|
-
const
|
|
18
|
-
if (props.groupKey && props.itemCode) {
|
|
19
|
-
try {
|
|
20
|
-
const result = await useGraphQl().queryPromise<any>("masterItemByGroupKeyAndItemCode",['itemValue', 'itemValueAlternative'],{ groupKey: props.groupKey, itemCode: props.itemCode })
|
|
21
|
-
|
|
22
|
-
if (result) {
|
|
23
|
-
return props.locale === 'TH'
|
|
24
|
-
? result.itemValue
|
|
25
|
-
: result.itemValueAlternative || result.itemValue
|
|
26
|
-
}
|
|
27
|
-
} catch (e) {
|
|
28
|
-
console.error(e)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
15
|
+
const computedNotFoundText = computed(()=>{
|
|
31
16
|
return props.notFoundText || `${props.itemCode}`
|
|
32
|
-
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const computedPlaceholder = computed(()=>{
|
|
20
|
+
return props.placeholder || `${props.groupKey}(${props.itemCode})`
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const computedItemTitle = computed(()=>{
|
|
24
|
+
return (props.locale == 'TH')
|
|
25
|
+
? "itemValue"
|
|
26
|
+
: (result : Record<string,any>) => result["itemValueAlternative"] || result["itemValue"]
|
|
27
|
+
})
|
|
33
28
|
</script>
|
|
34
29
|
|
|
35
30
|
<template>
|
|
36
|
-
|
|
31
|
+
<model-label
|
|
32
|
+
model-name="masterItemByGroupKeyAndItemCode"
|
|
33
|
+
:model-by="{groupKey: props.groupKey, itemCode: props.itemCode}"
|
|
34
|
+
:item-title="computedItemTitle"
|
|
35
|
+
:not-found-text="computedNotFoundText"
|
|
36
|
+
:placeholder="computedPlaceholder"
|
|
37
|
+
></model-label>
|
|
37
38
|
</template>
|
|
@@ -8,7 +8,7 @@ import {useGraphQlOperation} from "../../composables/graphqlOperation";
|
|
|
8
8
|
|
|
9
9
|
interface Props extends /* @vue-ignore */ InstanceType<typeof VAutocomplete['$props']> {
|
|
10
10
|
fuzzy?: boolean
|
|
11
|
-
sortBy?: string
|
|
11
|
+
sortBy?: string | string[]
|
|
12
12
|
showCode?: boolean
|
|
13
13
|
|
|
14
14
|
modelName: string
|
|
@@ -48,21 +48,28 @@ watchEffect(()=>{
|
|
|
48
48
|
variables[props.serverSearchKey] = searchData.value
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
if (props.modelName) {
|
|
52
|
+
isLoading.value = true
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
useGraphQlOperation('Query',props.modelName,fields,variables,props.cache).then((result: any) => {
|
|
55
|
+
if (isArray(result)) {
|
|
56
|
+
modelItems.value = result
|
|
57
|
+
items.value = result
|
|
58
|
+
isErrorLoading.value = false
|
|
59
|
+
} else {
|
|
60
|
+
isErrorLoading.value = true
|
|
61
|
+
}
|
|
62
|
+
}).catch((_error) => {
|
|
63
|
+
modelItems.value = []
|
|
64
|
+
items.value = []
|
|
59
65
|
isErrorLoading.value = true
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
}).finally(() => {
|
|
67
|
+
isLoading.value = false
|
|
68
|
+
})
|
|
69
|
+
} else {
|
|
70
|
+
modelItems.value = []
|
|
71
|
+
items.value = []
|
|
72
|
+
}
|
|
66
73
|
})
|
|
67
74
|
|
|
68
75
|
async function fuzzySearch() {
|
|
@@ -88,12 +95,12 @@ async function fuzzySearch() {
|
|
|
88
95
|
watchDebounced(searchData, fuzzySearch, { debounce: 1000, maxWait: 5000 })
|
|
89
96
|
|
|
90
97
|
const computedItems = computed(()=>{
|
|
91
|
-
let sortByField = props.sortBy || ((props.showCode) ? props.itemValue : props.itemTitle)
|
|
98
|
+
let sortByField = (!props.sortBy || typeof props.sortBy === "string") ? [props.sortBy || ((props.showCode) ? props.itemValue : props.itemTitle)] : props.sortBy
|
|
92
99
|
|
|
93
100
|
if (props.fuzzy && !isEmpty(searchData.value)) {
|
|
94
101
|
return items.value
|
|
95
102
|
} else {
|
|
96
|
-
return sortBy(items.value,
|
|
103
|
+
return sortBy(items.value, sortByField)
|
|
97
104
|
}
|
|
98
105
|
})
|
|
99
106
|
</script>
|
|
@@ -117,7 +124,6 @@ const computedItems = computed(()=>{
|
|
|
117
124
|
<slot
|
|
118
125
|
:name="name"
|
|
119
126
|
v-bind="((slotData || {}) as object)"
|
|
120
|
-
:operation="operation"
|
|
121
127
|
/>
|
|
122
128
|
</template>
|
|
123
129
|
<template
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import { computedAsync } from '@vueuse/core'
|
|
3
3
|
import { useGraphQlOperation } from '../../composables/graphqlOperation'
|
|
4
|
+
import {concat} from "lodash-es";
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
modelName: string
|
|
7
8
|
modelBy?: object
|
|
8
|
-
itemTitle: string
|
|
9
|
+
itemTitle: string | ((result:Record<string,any>)=>void)
|
|
9
10
|
fields?: Array<string | object>
|
|
10
11
|
cache?: boolean
|
|
11
12
|
|
|
@@ -19,14 +20,16 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
19
20
|
|
|
20
21
|
const modelItemValue = computedAsync<string>(async () => {
|
|
21
22
|
if (props.modelName && props.itemTitle) {
|
|
22
|
-
let fields: any[] = [props.itemTitle]
|
|
23
|
+
let fields: any[] = (typeof props.itemTitle === "string") ? [props.itemTitle] : []
|
|
24
|
+
if (props.fields) fields = concat(fields, props.fields)
|
|
25
|
+
|
|
23
26
|
const variables: Record<string, any> = Object.assign({},props.modelBy)
|
|
27
|
+
const result : Record<string, any> = await useGraphQlOperation('Query',props.modelName,fields,variables,props.cache)
|
|
24
28
|
|
|
25
29
|
try {
|
|
26
|
-
const result : Record<string, any> = await useGraphQlOperation('Query',props.modelName,fields,variables,props.cache)
|
|
27
|
-
|
|
28
30
|
if (result) {
|
|
29
|
-
return result[props.itemTitle]
|
|
31
|
+
if (typeof props.itemTitle === "string") return result[props.itemTitle]
|
|
32
|
+
else return props.itemTitle(result)
|
|
30
33
|
}
|
|
31
34
|
} catch (e) {
|
|
32
35
|
console.error(e)
|
|
@@ -1,2 +1,24 @@
|
|
|
1
|
+
export interface DocumentTemplateItem {
|
|
2
|
+
inputType: string;
|
|
3
|
+
width?: string | number;
|
|
4
|
+
inputLabel?: string;
|
|
5
|
+
variableName?: string;
|
|
6
|
+
validationRules?: string;
|
|
7
|
+
inputOptions?: string | object;
|
|
8
|
+
inputAttributes?: string;
|
|
9
|
+
inputCustomCode?: string;
|
|
10
|
+
columnAttributes?: string;
|
|
11
|
+
conditionalDisplay?: string;
|
|
12
|
+
computedValue?: string;
|
|
13
|
+
retrievedValue?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ChoiceItem {
|
|
16
|
+
label: string;
|
|
17
|
+
value: string;
|
|
18
|
+
}
|
|
1
19
|
export declare const validationRulesRegex: RegExp;
|
|
2
|
-
export declare function useDocumentTemplate(items: string | object): string;
|
|
20
|
+
export declare function useDocumentTemplate(items: string | object, parentTemplates?: string | string[]): string;
|
|
21
|
+
export declare function optionStringToChoiceObject(option: string | object): ChoiceItem[];
|
|
22
|
+
export declare function escapeObjectForInlineBinding(obj: object): string;
|
|
23
|
+
export declare function buildValidationRules(validationString: string): string;
|
|
24
|
+
export declare function processDefaultTemplate(item: DocumentTemplateItem, insideTemplate?: string, optionString?: string, validationRules?: string): string;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { processTemplateFormTable } from "./templateFormTable.js";
|
|
2
|
+
import { processTemplateFormHidden } from "./templateFormHidden.js";
|
|
1
3
|
export const validationRulesRegex = /^(require(?:\([^)]*\))?|requireIf(?:\([^)]*\))?|requireTrue(?:\([^)]*\))?|requireTrueIf(?:\([^)]*\))?|numeric(?:\([^)]*\))?|range(?:\([^)]*\))?|integer(?:\([^)]*\))?|unique(?:\([^)]*\))?|length(?:\([^)]*\))?|lengthGreater(?:\([^)]*\))?|lengthLess(?:\([^)]*\))?|telephone(?:\([^)]*\))?|email(?:\([^)]*\))?|regex(?:\([^)]*\))?)(,(require(?:\([^)]*\))?|requireIf(?:\([^)]*\))?|requireTrue(?:\([^)]*\))?|requireTrueIf(?:\([^)]*\))?|numeric(?:\([^)]*\))?|range(?:\([^)]*\))?|integer(?:\([^)]*\))?|unique(?:\([^)]*\))?|length(?:\([^)]*\))?|lengthGreater(?:\([^)]*\))?|lengthLess(?:\([^)]*\))?|telephone(?:\([^)]*\))?|email(?:\([^)]*\))?|regex(?:\([^)]*\))?))*$/;
|
|
2
|
-
export function useDocumentTemplate(items) {
|
|
4
|
+
export function useDocumentTemplate(items, parentTemplates) {
|
|
3
5
|
if (!items) return "";
|
|
4
6
|
if (typeof items === "string") {
|
|
5
7
|
try {
|
|
@@ -11,94 +13,108 @@ export function useDocumentTemplate(items) {
|
|
|
11
13
|
if (!Array.isArray(items) || !items.every((item) => typeof item === "object" && item !== null)) {
|
|
12
14
|
return "";
|
|
13
15
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return templateString;
|
|
16
|
+
const templateString = items.map((item) => templateItemToString(item, parentTemplates || [])).join("");
|
|
17
|
+
return !parentTemplates || parentTemplates.length == 0 ? `<v-container fluid><v-row dense>${templateString}</v-row></v-container>` : `<v-row dense>${templateString}</v-row>`;
|
|
17
18
|
}
|
|
18
|
-
function templateItemToString(item) {
|
|
19
|
+
function templateItemToString(item, parentTemplates) {
|
|
19
20
|
let optionString = "";
|
|
20
|
-
let validationRules = "";
|
|
21
21
|
if (item.inputOptions) {
|
|
22
|
-
if (item.inputType
|
|
23
|
-
optionString +=
|
|
22
|
+
if (item.inputType === "MasterAutocomplete") {
|
|
23
|
+
optionString += `groupKey="${item.inputOptions}" `;
|
|
24
24
|
}
|
|
25
|
-
if (
|
|
25
|
+
if (["VSelect", "VAutocomplete", "VCombobox"].includes(item.inputType)) {
|
|
26
26
|
const choice = optionStringToChoiceObject(item.inputOptions);
|
|
27
|
-
optionString = `item-value="value" item-title="label" :items='
|
|
27
|
+
optionString = `item-value="value" item-title="label" :items='${escapeObjectForInlineBinding(choice)}' `;
|
|
28
28
|
}
|
|
29
|
-
if (
|
|
29
|
+
if (["VRadio", "VRadioInline"].includes(item.inputType)) {
|
|
30
30
|
const choice = optionStringToChoiceObject(item.inputOptions);
|
|
31
|
-
optionString = choice.map((choiceItem) => `<v-radio label="${choiceItem.label}" value="${choiceItem.value}" ${item.inputAttributes ? " " + item.inputAttributes : ""}></v-radio>`).join("");
|
|
31
|
+
optionString = choice.map((choiceItem) => `<v-radio label="${choiceItem.label || ""}" value="${choiceItem.value || ""}" ${item.inputAttributes ? " " + item.inputAttributes : ""}></v-radio>`).join("");
|
|
32
32
|
}
|
|
33
|
-
if (item.inputType
|
|
34
|
-
optionString = item.inputOptions.split(",").join(" ");
|
|
33
|
+
if (item.inputType === "Header") {
|
|
34
|
+
if (typeof item.inputOptions === "string") optionString = item.inputOptions.split(",").join(" ");
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
if (item.
|
|
38
|
-
|
|
37
|
+
if (item.conditionalDisplay) {
|
|
38
|
+
item.inputAttributes = `${item.inputAttributes?.trim() || ""} v-if="${item.conditionalDisplay}"`.trim();
|
|
39
|
+
}
|
|
40
|
+
if (item.inputType === "FormDateTime" && !item.inputAttributes?.includes("dense")) {
|
|
41
|
+
item.inputAttributes = `${item.inputAttributes?.trim() || ""} dense`.trim();
|
|
42
|
+
}
|
|
39
43
|
let templateString;
|
|
40
44
|
switch (item.inputType) {
|
|
41
45
|
case "CustomCode":
|
|
42
|
-
templateString =
|
|
46
|
+
templateString = item.inputCustomCode || "";
|
|
43
47
|
break;
|
|
44
48
|
case "VRadio":
|
|
45
|
-
templateString = `<v-radio-group v-model="data.${item.variableName}" label="${item.inputLabel || item.variableName}"${validationRules ? " " + validationRules.trim() : ""}>${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
|
|
46
|
-
break;
|
|
47
49
|
case "VRadioInline":
|
|
48
|
-
|
|
50
|
+
const inlineAttr = item.inputType === "VRadioInline" ? " inline" : "";
|
|
51
|
+
const validationRules = item.validationRules ? buildValidationRules(item.validationRules) || "" : "";
|
|
52
|
+
templateString = `<v-radio-group v-model="data.${item.variableName || ""}"${inlineAttr}${validationRules ? " " + validationRules.trim() : ""}>${item.inputLabel ? `<template #prepend>${item.inputLabel}</template>` : ""}${optionString ? " " + optionString.trim() : ""}</v-radio-group>`;
|
|
49
53
|
break;
|
|
50
54
|
case "Separator":
|
|
51
|
-
templateString =
|
|
55
|
+
templateString = "</v-row><v-row dense>";
|
|
52
56
|
break;
|
|
53
57
|
case "Header":
|
|
54
|
-
templateString = `<div class="${optionString
|
|
58
|
+
templateString = `<div class="${optionString || "text-h4 font-weight-medium"}">${item.inputLabel || item.variableName || ""}</div>`;
|
|
55
59
|
break;
|
|
56
60
|
case "FormTable":
|
|
57
|
-
templateString =
|
|
58
|
-
<template #form>${item.inputFormTable}</template>
|
|
59
|
-
</form-table>`;
|
|
61
|
+
templateString = processTemplateFormTable(item, parentTemplates);
|
|
60
62
|
break;
|
|
61
63
|
case "FormHidden":
|
|
62
|
-
templateString =
|
|
63
|
-
|
|
64
|
+
templateString = processTemplateFormHidden(item, parentTemplates);
|
|
65
|
+
break;
|
|
66
|
+
case "DocumentForm":
|
|
67
|
+
const parentTemplatesString = typeof parentTemplates === "string" ? parentTemplates : parentTemplates.join("|");
|
|
68
|
+
templateString = `<document-form v-model="data" templateCode="${item.inputOptions || ""}" parent-templates="${parentTemplatesString}"></document-form>`;
|
|
64
69
|
break;
|
|
65
70
|
default:
|
|
66
|
-
templateString =
|
|
71
|
+
templateString = processDefaultTemplate(item, void 0, optionString);
|
|
72
|
+
}
|
|
73
|
+
if (!["Separator"].includes(item.inputType)) templateString = `<v-col${item.width ? ` cols="${item.width}"` : ""}${item.columnAttributes ? " " + item.columnAttributes : ""}>${templateString}</v-col>`;
|
|
74
|
+
if (["Header", "DocumentForm"].includes(item.inputType)) templateString = `</v-row><v-row dense>${templateString}</v-row><v-row dense>`;
|
|
75
|
+
if (item.computedValue && item.variableName) {
|
|
76
|
+
templateString = `${templateString || ""}<FormHidden v-model="data.${item.variableName}" :item-value="data" :hook="()=>${item.computedValue}"/>`.trim();
|
|
67
77
|
}
|
|
68
|
-
|
|
69
|
-
if (["Header"].includes(item.inputType)) templateString = `</v-row><v-row dense><v-col${item.columnAttributes ? " " + item.columnAttributes : ""}>${templateString}</v-col></v-row><v-row dense>`;
|
|
70
|
-
return templateString;
|
|
78
|
+
return templateString || "";
|
|
71
79
|
}
|
|
72
|
-
function optionStringToChoiceObject(option) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
export function optionStringToChoiceObject(option) {
|
|
81
|
+
if (typeof option === "string") {
|
|
82
|
+
if (!/^[^'",]+(,[^'",]+)*$/.test(option.trim())) return [];
|
|
83
|
+
return option.split(",").map((item) => {
|
|
76
84
|
item = item.trim();
|
|
77
85
|
if (item.includes("|")) {
|
|
78
86
|
const [label, value] = item.split("|").map((str) => str.trim().replace(/^['"]|['"]$/g, ""));
|
|
79
|
-
return { label, value };
|
|
80
|
-
} else {
|
|
81
|
-
item = item.replace(/^['"]|['"]$/g, "");
|
|
82
|
-
return { label: item, value: item };
|
|
87
|
+
return { label: label || "", value: value || "" };
|
|
83
88
|
}
|
|
89
|
+
item = item.replace(/^['"]|['"]$/g, "");
|
|
90
|
+
return { label: item, value: item };
|
|
84
91
|
});
|
|
85
92
|
}
|
|
86
|
-
|
|
93
|
+
if (Array.isArray(option) && option.every((item) => typeof item === "object" && item !== null)) {
|
|
94
|
+
return option.sort((a, b) => (a.id ?? Infinity) - (b.id ?? Infinity)).map((item) => ({
|
|
95
|
+
label: item.label || item.value || "",
|
|
96
|
+
// Default to empty string if label is undefined
|
|
97
|
+
value: item.value || ""
|
|
98
|
+
// Default to empty string if value is undefined
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
export function escapeObjectForInlineBinding(obj) {
|
|
104
|
+
return JSON.stringify(obj).replace(/'/g, "'");
|
|
87
105
|
}
|
|
88
|
-
function buildValidationRules(validationString) {
|
|
89
|
-
validationString = validationString.trim();
|
|
90
|
-
if (validationString.startsWith("[")) validationString = validationString.substring(1);
|
|
91
|
-
if (validationString.endsWith("]")) validationString = validationString.substring(0, validationString.length - 1);
|
|
106
|
+
export function buildValidationRules(validationString) {
|
|
107
|
+
validationString = validationString.replace(/^\[|]$/g, "").trim();
|
|
92
108
|
if (!validationRulesRegex.test(validationString)) return "";
|
|
93
|
-
|
|
109
|
+
const rules = validationString.split(",").map((rule) => {
|
|
94
110
|
rule = rule.trim();
|
|
95
|
-
if (!rule.startsWith("rules.")) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return
|
|
111
|
+
if (!rule.startsWith("rules.")) rule = `rules.${rule}`;
|
|
112
|
+
if (!/\(.+\)$/.test(rule)) rule += "()";
|
|
113
|
+
return rule.replace(/"/g, "'");
|
|
114
|
+
});
|
|
115
|
+
return `:rules="[${rules.join(",")}]"`;
|
|
116
|
+
}
|
|
117
|
+
export function processDefaultTemplate(item, insideTemplate, optionString, validationRules) {
|
|
118
|
+
if (!validationRules) validationRules = item.validationRules ? buildValidationRules(item.validationRules) || "" : "";
|
|
119
|
+
return `<${item.inputType} v-model="data.${item.variableName || ""}" label="${item.inputLabel || item.variableName || ""}"${item.inputAttributes ? " " + item.inputAttributes : ""}${optionString ? " " + optionString.trim() : ""}${validationRules ? " " + validationRules.trim() : ""}>${insideTemplate || ""}</${item.inputType}>`;
|
|
104
120
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function processTemplateFormHidden(item, parentTemplates) {
|
|
2
|
+
let formHiddenOptions = Object.assign({ itemValue: "", hook: "" }, item.inputOptions);
|
|
3
|
+
if (formHiddenOptions.hook.trim()) {
|
|
4
|
+
formHiddenOptions.hook = formHiddenOptions.hook.replace(/\n/g, ";").trim();
|
|
5
|
+
if (!/^return\s/.test(formHiddenOptions.hook)) {
|
|
6
|
+
formHiddenOptions.hook = `return ${formHiddenOptions.hook}`;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
return `<form-hidden v-model="data.${item.variableName || ""}" :item-value="${formHiddenOptions.itemValue || "data"}" :hook="(data,modelValue) => {${formHiddenOptions.hook}}"></form-hidden>`;
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
escapeObjectForInlineBinding,
|
|
3
|
+
processDefaultTemplate,
|
|
4
|
+
useDocumentTemplate
|
|
5
|
+
} from "./template.js";
|
|
6
|
+
export function processTemplateFormTable(item, parentTemplates) {
|
|
7
|
+
let tableOptions = Object.assign({ title: item.inputLabel || "", formTemplate: "" }, item.inputOptions);
|
|
8
|
+
return processDefaultTemplate(item, `<template #form="{data,rules}">${useDocumentTemplate(tableOptions.formTemplate)}</template>`, `title="${tableOptions.title}" :headers='${escapeObjectForInlineBinding(tableOptions.headers || {})}'`);
|
|
9
|
+
}
|