@ramathibodi/nuxt-commons 0.1.34 → 0.1.36
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/SplitterPanel.vue +1 -1
- package/dist/runtime/components/document/TemplateBuilder.vue +49 -19
- package/dist/runtime/components/form/ActionPad.vue +133 -0
- package/dist/runtime/components/form/Dialog.vue +1 -2
- package/dist/runtime/components/form/File.vue +123 -89
- package/dist/runtime/components/form/Iterator.vue +7 -1
- package/dist/runtime/components/form/SignPad.vue +160 -121
- package/dist/runtime/components/form/Table.vue +27 -6
- package/dist/runtime/components/form/images/Field.vue +1 -1
- package/dist/runtime/components/master/label.vue +37 -0
- package/dist/runtime/components/model/Pad.vue +35 -29
- package/dist/runtime/components/model/label.vue +41 -0
- package/dist/runtime/components/pdf/View.vue +32 -12
- package/dist/runtime/composables/document/template.js +9 -0
- package/dist/runtime/composables/graphqlOperation.js +1 -1
- package/package.json +2 -2
package/dist/module.json
CHANGED
|
@@ -73,10 +73,14 @@ async function convertToAdvanceMode() {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
const inputTypeChoice = ref(['VTextField', 'VTextarea', 'VSelect', 'VAutocomplete', 'FormDate', 'FormTime', 'FormDateTime', 'VCombobox', 'VRadio', 'VRadioInline', 'VCheckbox', 'VSwitch', 'MasterAutocomplete', 'Header', 'Separator', 'CustomCode'])
|
|
76
|
+
const inputTypeChoice = ref(['VTextField', 'VTextarea', 'VSelect', 'VAutocomplete', 'FormDate', 'FormTime', 'FormDateTime', 'VCombobox', 'VRadio', 'VRadioInline', 'VCheckbox', 'VSwitch', 'MasterAutocomplete', 'Header', 'Separator', 'CustomCode', 'FormTable', 'FormHidden', 'FormFile', 'FormSignPad'])
|
|
77
77
|
const requireOption = ref(['VSelect', 'VAutocomplete', 'VCombobox', 'VRadio', 'VRadioInline', 'MasterAutocomplete'])
|
|
78
78
|
const notRequireVariable = ref(['Header', 'Separator', 'CustomCode'])
|
|
79
|
-
const notRequireLabel = ref(['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'])
|
|
80
84
|
|
|
81
85
|
const choiceOption = ref(['VSelect', 'VRadio', 'VRadioInline'])
|
|
82
86
|
|
|
@@ -128,25 +132,23 @@ const ruleOptions = (inputType: string) => (value: any) => {
|
|
|
128
132
|
type="number"
|
|
129
133
|
/>
|
|
130
134
|
</v-col>
|
|
131
|
-
<v-col cols="4">
|
|
135
|
+
<v-col cols="4" v-if="!notRequireLabel.includes(data.inputType)">
|
|
132
136
|
<v-text-field
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
:rules="[rules.require()]"
|
|
137
|
+
v-model="data.inputLabel"
|
|
138
|
+
label="Input Label"
|
|
139
|
+
:rules="[rules.require()]"
|
|
137
140
|
/>
|
|
138
141
|
</v-col>
|
|
139
|
-
<v-col cols="4">
|
|
142
|
+
<v-col cols="4" v-if="!notRequireVariable.includes(data.inputType)">
|
|
140
143
|
<v-text-field
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
:rules="[rules.require()]"
|
|
144
|
+
v-model="data.variableName"
|
|
145
|
+
label="Variable Name"
|
|
146
|
+
:rules="[rules.require()]"
|
|
145
147
|
/>
|
|
146
148
|
</v-col>
|
|
147
149
|
<v-col
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
v-if="!notRequireOptions.includes(data.inputType)"
|
|
151
|
+
cols="12"
|
|
150
152
|
>
|
|
151
153
|
<v-textarea
|
|
152
154
|
v-model="data.inputOptions"
|
|
@@ -179,9 +181,29 @@ const ruleOptions = (inputType: string) => (value: any) => {
|
|
|
179
181
|
cols="12"
|
|
180
182
|
>
|
|
181
183
|
<FormCodeEditor
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
v-model="data.inputCustomCode"
|
|
185
|
+
label="Custom Code"
|
|
186
|
+
:rules="[rules.require()]"
|
|
187
|
+
/>
|
|
188
|
+
</v-col>
|
|
189
|
+
<v-col
|
|
190
|
+
v-if="data.inputType=='FormTable'"
|
|
191
|
+
cols="12"
|
|
192
|
+
>
|
|
193
|
+
<FormCodeEditor
|
|
194
|
+
v-model="data.inputFormTable"
|
|
195
|
+
label="Form Table"
|
|
196
|
+
:rules="[rules.require()]"
|
|
197
|
+
/>
|
|
198
|
+
</v-col>
|
|
199
|
+
<v-col
|
|
200
|
+
v-if="data.inputType=='FormHidden'"
|
|
201
|
+
cols="12"
|
|
202
|
+
>
|
|
203
|
+
<FormCodeEditor
|
|
204
|
+
v-model="data.inputFormHidden"
|
|
205
|
+
label="Form Table"
|
|
206
|
+
:rules="[rules.require()]"
|
|
185
207
|
/>
|
|
186
208
|
</v-col>
|
|
187
209
|
</v-row>
|
|
@@ -201,10 +223,18 @@ const ruleOptions = (inputType: string) => (value: any) => {
|
|
|
201
223
|
<template v-if="props.item.inputAttributes">
|
|
202
224
|
<b>Input Attributes :</b> {{ props.item.inputAttributes }}<br>
|
|
203
225
|
</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>
|
|
204
234
|
</template>
|
|
205
235
|
</FormTable>
|
|
206
236
|
<FormCodeEditor
|
|
207
|
-
|
|
208
|
-
|
|
237
|
+
v-else
|
|
238
|
+
v-model="modelValue"
|
|
209
239
|
/>
|
|
210
240
|
</template>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import {computed, defineExpose, ref, watchEffect} from 'vue'
|
|
3
|
+
import {cloneDeep, isEqual} from 'lodash-es'
|
|
4
|
+
import type {FormDialogCallback} from '../../types/formDialog'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
title?: string
|
|
8
|
+
initialData?: object
|
|
9
|
+
createCaption?: string
|
|
10
|
+
updateCaption?: string
|
|
11
|
+
cancelCaption?: string
|
|
12
|
+
showTitle?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
16
|
+
createCaption: 'Add',
|
|
17
|
+
updateCaption: 'Save',
|
|
18
|
+
cancelCaption: 'Cancel',
|
|
19
|
+
showTitle: false,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const isSaving = ref<boolean>(false)
|
|
23
|
+
const formPadRef = ref()
|
|
24
|
+
const formOriginalData = ref<object>()
|
|
25
|
+
const formData = ref<object>({})
|
|
26
|
+
|
|
27
|
+
const emit = defineEmits(['create', 'update'])
|
|
28
|
+
|
|
29
|
+
function save() {
|
|
30
|
+
if (formPadRef.value.isValid) {
|
|
31
|
+
isSaving.value = true
|
|
32
|
+
emit((isCreating.value) ? 'create' : 'update', cloneDeep(formData.value), callback)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function cancel() {
|
|
37
|
+
reset()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function reset() {
|
|
41
|
+
formOriginalData.value = undefined
|
|
42
|
+
loadFormData()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const callback: FormDialogCallback = {
|
|
46
|
+
done: function () {
|
|
47
|
+
isSaving.value = false
|
|
48
|
+
reset()
|
|
49
|
+
},
|
|
50
|
+
error: function () {
|
|
51
|
+
isSaving.value = false
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const isDataChange = computed(() => {
|
|
56
|
+
return !((isCreating.value) ? isEqual(formData.value, createOriginalValue.value) : isEqual(formData.value, formOriginalData.value))
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const isCreating = computed(() => {
|
|
60
|
+
return !formOriginalData.value
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const createOriginalValue = computed(() => {
|
|
64
|
+
return Object.assign({}, props.initialData)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const loadFormData = () => {
|
|
68
|
+
if (formOriginalData.value) {
|
|
69
|
+
formData.value = cloneDeep(formOriginalData.value)
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
formData.value = Object.assign({}, props.initialData)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
watchEffect(loadFormData)
|
|
77
|
+
|
|
78
|
+
function setOriginalData(originalData?: object) {
|
|
79
|
+
formOriginalData.value = originalData
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
defineExpose({setOriginalData,reset})
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<template>
|
|
86
|
+
<VCard flat>
|
|
87
|
+
<VToolbar v-if="showTitle">
|
|
88
|
+
<VToolbarTitle>
|
|
89
|
+
<slot name="title">
|
|
90
|
+
{{ (isCreating) ? "New" : "Edit" }} {{ title }}
|
|
91
|
+
</slot>
|
|
92
|
+
</VToolbarTitle>
|
|
93
|
+
<VSpacer />
|
|
94
|
+
</VToolbar>
|
|
95
|
+
<VCardText>
|
|
96
|
+
<form-pad
|
|
97
|
+
ref="formPadRef"
|
|
98
|
+
v-model="formData"
|
|
99
|
+
isolated
|
|
100
|
+
>
|
|
101
|
+
<template #default="slotData">
|
|
102
|
+
<slot
|
|
103
|
+
v-bind="slotData"
|
|
104
|
+
:is-creating="isCreating"
|
|
105
|
+
:is-data-change="isDataChange"
|
|
106
|
+
/>
|
|
107
|
+
</template>
|
|
108
|
+
</form-pad>
|
|
109
|
+
</VCardText>
|
|
110
|
+
<VCardActions>
|
|
111
|
+
<slot name="action" :save="save" :cancel="cancel">
|
|
112
|
+
<VSpacer />
|
|
113
|
+
<VBtn
|
|
114
|
+
color="primary"
|
|
115
|
+
variant="flat"
|
|
116
|
+
:loading="isSaving"
|
|
117
|
+
:disabled="!isDataChange"
|
|
118
|
+
@click="save"
|
|
119
|
+
>
|
|
120
|
+
{{ (isCreating) ? createCaption : updateCaption }}
|
|
121
|
+
</VBtn>
|
|
122
|
+
<VBtn
|
|
123
|
+
color="error"
|
|
124
|
+
variant="flat"
|
|
125
|
+
:disabled="isSaving"
|
|
126
|
+
@click="cancel"
|
|
127
|
+
>
|
|
128
|
+
{{ cancelCaption }}
|
|
129
|
+
</VBtn>
|
|
130
|
+
</slot>
|
|
131
|
+
</VCardActions>
|
|
132
|
+
</VCard>
|
|
133
|
+
</template>
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { uniqWith
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import {isEqual, uniqWith} from 'lodash-es'
|
|
3
|
+
import {ref, watch} from 'vue'
|
|
4
|
+
import {VTextField} from 'vuetify/components/VTextField'
|
|
5
|
+
import {useAlert} from '../../composables/alert'
|
|
6
6
|
|
|
7
7
|
const alert = useAlert()
|
|
8
8
|
|
|
9
9
|
interface Base64String {
|
|
10
10
|
base64String?: string
|
|
11
11
|
fileName: string
|
|
12
|
+
originalFileName?: string
|
|
12
13
|
id?: number
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -16,22 +17,24 @@ interface Props extends /* @vue-ignore */ InstanceType<typeof VTextField['$props
|
|
|
16
17
|
accept?: string
|
|
17
18
|
multiple?: boolean
|
|
18
19
|
maxSize?: number
|
|
19
|
-
modelValue?: Base64String[]
|
|
20
|
+
modelValue?: Base64String | Base64String[]
|
|
21
|
+
downloadable?: boolean
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
const props = withDefaults(defineProps<Props>(), {
|
|
23
25
|
accept: '*',
|
|
24
26
|
multiple: false,
|
|
25
27
|
maxSize: 5,
|
|
28
|
+
downloadable: false
|
|
26
29
|
})
|
|
27
30
|
|
|
28
31
|
const emit = defineEmits<{
|
|
29
|
-
(e: 'update:modelValue', value: Base64String[]): void
|
|
32
|
+
(e: 'update:modelValue', value: Base64String | Base64String[]): void
|
|
30
33
|
}>()
|
|
31
34
|
|
|
32
35
|
const allFiles = ref<File[]>([])
|
|
33
36
|
const allAssets = ref<Base64String[]>([])
|
|
34
|
-
const combinedBase64String = ref<Base64String[]>([])
|
|
37
|
+
const combinedBase64String = ref<Base64String[] | Base64String>([])
|
|
35
38
|
const fileInput = ref()
|
|
36
39
|
|
|
37
40
|
function openWindowUpload() {
|
|
@@ -47,141 +50,172 @@ function addFiles(files: File | File[]) {
|
|
|
47
50
|
|
|
48
51
|
function removeFileByIndex(i: number | string) {
|
|
49
52
|
const index = Number(i)
|
|
50
|
-
if (
|
|
51
|
-
if (index >= 0 && index < allFiles.value.length) allFiles.value.splice(index, 1)
|
|
52
|
-
}
|
|
53
|
+
if (index >= 0 && index < allFiles.value.length) allFiles.value.splice(index, 1)
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
function removeAssetByIndex(i: number | string) {
|
|
56
57
|
const index = Number(i)
|
|
57
|
-
if (
|
|
58
|
-
if (index >= 0 && index < allAssets.value.length) allAssets.value.splice(index, 1)
|
|
59
|
-
}
|
|
58
|
+
if (index >= 0 && index < allAssets.value.length) allAssets.value.splice(index, 1)
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
function fileToBase64(file: File) {
|
|
63
62
|
const maxSize = props.maxSize * 1048576
|
|
64
63
|
|
|
65
64
|
return new Promise<Base64String>((resolve, reject) => {
|
|
66
|
-
if (file.size > maxSize) reject
|
|
65
|
+
if (file.size > maxSize) reject(`File (${file.name}) size exceeds the ${props.maxSize} MB limit.`)
|
|
67
66
|
|
|
68
67
|
const reader = new FileReader()
|
|
69
|
-
reader.onload =
|
|
68
|
+
reader.onload = (event) => {
|
|
70
69
|
resolve({ fileName: file.name, base64String: event.target?.result as string })
|
|
71
70
|
}
|
|
72
|
-
reader.onerror =
|
|
73
|
-
reject(error)
|
|
74
|
-
}
|
|
71
|
+
reader.onerror = reject
|
|
75
72
|
reader.readAsDataURL(file)
|
|
76
73
|
})
|
|
77
74
|
}
|
|
78
75
|
|
|
79
|
-
function base64ToFile(base64Data: string, filename: string) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
function base64ToFile(base64Data: string, filename: string, defaultContentType: string = "application/octet-stream") {
|
|
77
|
+
const matchResult = base64Data.match(/data:([^;]*);base64,(.*)/);
|
|
78
|
+
let contentType: string;
|
|
79
|
+
let base64Payload: string;
|
|
80
|
+
|
|
81
|
+
if (matchResult) {
|
|
82
|
+
[contentType, base64Payload] = matchResult.slice(1)
|
|
83
|
+
} else {
|
|
84
|
+
contentType = defaultContentType
|
|
85
|
+
base64Payload = base64Data
|
|
84
86
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
bytes
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const binaryStr = atob(base64Payload)
|
|
90
|
+
const bytes = new Uint8Array(binaryStr.length)
|
|
91
|
+
for (let i = 0; i < binaryStr.length; i++) {
|
|
92
|
+
bytes[i] = binaryStr.charCodeAt(i)
|
|
93
|
+
}
|
|
94
|
+
return new File([bytes], filename, { type: contentType });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error("Invalid base64 data", error);
|
|
97
|
+
return undefined;
|
|
93
98
|
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function downloadBase64File(base64Data: string, filename: string): void {
|
|
102
|
+
const file = base64ToFile(base64Data,filename)
|
|
94
103
|
|
|
95
|
-
|
|
104
|
+
if (file) {
|
|
105
|
+
const link = document.createElement("a");
|
|
106
|
+
link.href = URL.createObjectURL(file);
|
|
107
|
+
link.download = filename;
|
|
108
|
+
|
|
109
|
+
// Append the link to the body temporarily and trigger the download
|
|
110
|
+
document.body.appendChild(link);
|
|
111
|
+
link.click();
|
|
112
|
+
|
|
113
|
+
// Cleanup
|
|
114
|
+
document.body.removeChild(link);
|
|
115
|
+
URL.revokeObjectURL(link.href);
|
|
116
|
+
}
|
|
96
117
|
}
|
|
97
118
|
|
|
119
|
+
|
|
98
120
|
watch(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
() => props.modelValue,
|
|
122
|
+
() => {
|
|
123
|
+
if (Array.isArray(props.modelValue)) {
|
|
124
|
+
allAssets.value = props.modelValue.filter((item) => item.id !== undefined)
|
|
125
|
+
allFiles.value = props.modelValue
|
|
126
|
+
.filter((item) => item.id === undefined && item.base64String !== undefined)
|
|
127
|
+
.map((base64) => base64ToFile(base64.base64String as string, base64.fileName))
|
|
128
|
+
.filter((item) => item !== undefined) as File[]
|
|
129
|
+
} else if (props.modelValue) {
|
|
130
|
+
allAssets.value = props.modelValue.id !== undefined ? [props.modelValue] : []
|
|
131
|
+
allFiles.value =
|
|
132
|
+
props.modelValue.id === undefined && props.modelValue.base64String !== undefined
|
|
133
|
+
? [base64ToFile(props.modelValue.base64String, props.modelValue.fileName)].filter(
|
|
134
|
+
(item) => item !== undefined
|
|
135
|
+
) as File[]
|
|
136
|
+
: []
|
|
137
|
+
} else {
|
|
138
|
+
allAssets.value = []
|
|
139
|
+
allFiles.value = []
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{ deep: true, immediate: true }
|
|
111
143
|
)
|
|
112
144
|
|
|
113
145
|
watch([allAssets, allFiles], () => {
|
|
114
|
-
if (allFiles.value
|
|
115
|
-
const base64Promises = allFiles.value
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
146
|
+
if (allFiles.value.length) {
|
|
147
|
+
const base64Promises = allFiles.value.map(fileToBase64)
|
|
148
|
+
Promise.all(base64Promises)
|
|
149
|
+
.then((base64Strings) => {
|
|
150
|
+
combinedBase64String.value = props.multiple
|
|
151
|
+
? [...allAssets.value, ...base64Strings]
|
|
152
|
+
: base64Strings[0] || allAssets.value[0] || null
|
|
153
|
+
})
|
|
154
|
+
.catch((error) => {
|
|
155
|
+
alert?.addAlert({ message: error, alertType: 'error' })
|
|
156
|
+
allFiles.value = []
|
|
157
|
+
})
|
|
158
|
+
} else {
|
|
159
|
+
combinedBase64String.value = props.multiple
|
|
160
|
+
? [...allAssets.value]
|
|
161
|
+
: allAssets.value[0] || null
|
|
126
162
|
}
|
|
127
163
|
}, { deep: true, immediate: true })
|
|
128
164
|
|
|
129
165
|
watch(combinedBase64String, (newValue, oldValue) => {
|
|
130
166
|
if (!isEqual(newValue, oldValue)) {
|
|
131
|
-
emit('update:modelValue', uniqWith(newValue, isEqual))
|
|
167
|
+
emit('update:modelValue', props.multiple ? uniqWith(newValue as Base64String[], isEqual) : newValue)
|
|
132
168
|
}
|
|
133
169
|
}, { deep: true })
|
|
134
170
|
</script>
|
|
135
171
|
|
|
136
172
|
<template>
|
|
137
173
|
<v-text-field
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
174
|
+
v-bind="$attrs"
|
|
175
|
+
label="Upload files"
|
|
176
|
+
readonly
|
|
177
|
+
:dirty="Array.isArray(combinedBase64String) ? combinedBase64String.length > 0 : !!combinedBase64String"
|
|
178
|
+
v-on="Array.isArray(combinedBase64String) && combinedBase64String.length > 0 ? {} : { click: openWindowUpload }"
|
|
143
179
|
>
|
|
144
180
|
<template #default>
|
|
145
181
|
<v-chip
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
182
|
+
v-for="(asset, index) in allAssets"
|
|
183
|
+
:key="asset.fileName"
|
|
184
|
+
color="green"
|
|
185
|
+
variant="flat"
|
|
186
|
+
closable
|
|
187
|
+
@click:close="removeAssetByIndex(index)"
|
|
152
188
|
>
|
|
153
|
-
{{ asset.fileName }}
|
|
189
|
+
{{ asset.originalFileName || asset.fileName }}
|
|
190
|
+
<template #append v-if="downloadable">
|
|
191
|
+
<slot name="download" :item="asset">
|
|
192
|
+
<v-icon @click="downloadBase64File(asset.base64String || '',asset.originalFileName || asset.fileName)" v-if="asset.base64String">mdi mdi-download</v-icon>
|
|
193
|
+
</slot>
|
|
194
|
+
</template>
|
|
154
195
|
</v-chip>
|
|
155
196
|
<v-chip
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
197
|
+
v-for="(file, index) in allFiles"
|
|
198
|
+
:key="file.name"
|
|
199
|
+
color="primary"
|
|
200
|
+
variant="flat"
|
|
201
|
+
closable
|
|
202
|
+
@click:close="removeFileByIndex(index)"
|
|
162
203
|
>
|
|
163
204
|
{{ file.name }}
|
|
164
205
|
</v-chip>
|
|
165
206
|
</template>
|
|
166
207
|
|
|
167
|
-
<template
|
|
168
|
-
|
|
169
|
-
#append-inner
|
|
170
|
-
>
|
|
171
|
-
<VBtn
|
|
172
|
-
variant="text"
|
|
173
|
-
:icon="true"
|
|
174
|
-
@click="openWindowUpload"
|
|
175
|
-
>
|
|
208
|
+
<template v-if="props.multiple && Array.isArray(combinedBase64String) && combinedBase64String.length > 0" #append-inner>
|
|
209
|
+
<VBtn variant="text" :icon="true" @click="openWindowUpload">
|
|
176
210
|
<v-icon>mdi mdi-plus</v-icon>
|
|
177
211
|
</VBtn>
|
|
178
212
|
</template>
|
|
179
213
|
</v-text-field>
|
|
180
214
|
<v-file-input
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
215
|
+
ref="fileInput"
|
|
216
|
+
:accept="props.accept"
|
|
217
|
+
:multiple="props.multiple"
|
|
218
|
+
style="display: none"
|
|
219
|
+
@update:model-value="addFiles"
|
|
186
220
|
/>
|
|
187
221
|
</template>
|
|
@@ -22,6 +22,8 @@ interface Props extends /* @vue-ignore */ InstanceType<typeof VDataIterator['$pr
|
|
|
22
22
|
insertable?: boolean
|
|
23
23
|
searchable?: boolean
|
|
24
24
|
|
|
25
|
+
loading?: boolean
|
|
26
|
+
|
|
25
27
|
viewSwitch?: boolean
|
|
26
28
|
viewSwitchMultiple?: boolean
|
|
27
29
|
|
|
@@ -44,6 +46,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
44
46
|
insertable: true,
|
|
45
47
|
searchable: true,
|
|
46
48
|
|
|
49
|
+
loading: false,
|
|
50
|
+
|
|
47
51
|
viewSwitch: false,
|
|
48
52
|
viewSwitchMultiple:false,
|
|
49
53
|
|
|
@@ -208,6 +212,7 @@ defineExpose({operation})
|
|
|
208
212
|
:items="items"
|
|
209
213
|
:item-value="modelKey"
|
|
210
214
|
:search="search"
|
|
215
|
+
:loading="loading"
|
|
211
216
|
>
|
|
212
217
|
<template #default="defaultProps" v-if="viewType.includes('iterator')">
|
|
213
218
|
<slot
|
|
@@ -244,7 +249,7 @@ defineExpose({operation})
|
|
|
244
249
|
<v-container fluid>
|
|
245
250
|
<v-row>
|
|
246
251
|
<v-col
|
|
247
|
-
v-for="key in
|
|
252
|
+
v-for="key in itemsPerPage"
|
|
248
253
|
:key="key"
|
|
249
254
|
:cols="cols"
|
|
250
255
|
:sm="sm"
|
|
@@ -340,6 +345,7 @@ defineExpose({operation})
|
|
|
340
345
|
color="primary"
|
|
341
346
|
:items="items"
|
|
342
347
|
:search="search"
|
|
348
|
+
:loading="loading"
|
|
343
349
|
v-if="viewType.includes('table')"
|
|
344
350
|
>
|
|
345
351
|
<!-- @ts-ignore -->
|