tantee-nuxt-commons 0.0.168 → 0.0.170
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
CHANGED
|
@@ -317,5 +317,5 @@ defineExpose({
|
|
|
317
317
|
/>
|
|
318
318
|
</template>
|
|
319
319
|
<style>
|
|
320
|
-
.form-data-dirty
|
|
320
|
+
.form-data-dirty:not(.v-input--error) :not(.v-chip):not(.v-chip *){color:color-mix(in srgb,currentColor 70%,rgb(var(--v-theme-primary)))!important;text-shadow:0 0 .02em currentColor}
|
|
321
321
|
</style>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { VueSignaturePad } from 'vue-signature-pad'
|
|
3
3
|
import { VInput } from 'vuetify/components/VInput'
|
|
4
4
|
import { ref, computed, withDefaults, defineProps, defineExpose, watch } from 'vue'
|
|
5
|
-
import { useAssetFile, type Base64Asset } from '../../composables/assetFile'
|
|
5
|
+
import { useAssetFile, type Base64Asset, type Base64File } from '../../composables/assetFile'
|
|
6
6
|
|
|
7
7
|
interface SignatureProps extends /* @vue-ignore */ InstanceType<typeof VInput['$props']> {
|
|
8
8
|
title?: string
|
|
@@ -44,14 +44,7 @@ const imageDataUrl = computed(() => {
|
|
|
44
44
|
const val = normalized.value
|
|
45
45
|
if (!val?.base64String) return null
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (base.startsWith("data:")) {
|
|
50
|
-
return base
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const mime = (val as Base64File).fileType || "image/png"
|
|
54
|
-
return `data:${mime};base64,${base}`
|
|
47
|
+
return useAssetFile().ensureDataUrl(val.base64String.trim(),(val as Base64File).fileType || "image/png")
|
|
55
48
|
})
|
|
56
49
|
|
|
57
50
|
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { ref, watch } from 'vue'
|
|
3
3
|
import { isEqual } from 'lodash-es'
|
|
4
4
|
import { useAlert } from '../../../composables/alert'
|
|
5
|
-
import {
|
|
5
|
+
import {useAssetFile, type Base64Image, type Base64Asset, type Base64File} from '../../../composables/assetFile'
|
|
6
|
+
import {useRuntimeConfig} from "#imports";
|
|
6
7
|
|
|
7
8
|
const emit = defineEmits<{
|
|
8
9
|
(e: 'update:modelValue', value: Base64Image[]): void
|
|
@@ -17,11 +18,13 @@ interface Props {
|
|
|
17
18
|
label?: string
|
|
18
19
|
accept?: string
|
|
19
20
|
autoHydrate?: boolean
|
|
21
|
+
maxFileSize?: number
|
|
20
22
|
}
|
|
21
23
|
const props = withDefaults(defineProps<Props>(), {
|
|
22
24
|
modelValue: () => [] as Base64Image[],
|
|
23
25
|
accept: '.jpg,.jpeg,.png,.webp,.gif,.bmp,.tiff,.tif',
|
|
24
26
|
autoHydrate: false,
|
|
27
|
+
maxFileSize: 10,
|
|
25
28
|
})
|
|
26
29
|
|
|
27
30
|
/** Internal state (always Base64Image[]) */
|
|
@@ -35,7 +38,7 @@ const dataUpdate = ref<Base64Image>({ imageData: {}, imageTitle: '', imageProps:
|
|
|
35
38
|
|
|
36
39
|
/** Fullscreen preview */
|
|
37
40
|
const dialogImageFullScreen = ref(false)
|
|
38
|
-
const imageFullScreen = ref({ title: '', image: '' })
|
|
41
|
+
const imageFullScreen = ref<{title: string,image: string|undefined}>({ title: '', image: '' })
|
|
39
42
|
|
|
40
43
|
/** ---------- Stable keys + guards ---------- */
|
|
41
44
|
let internalSync = false
|
|
@@ -90,23 +93,29 @@ const setDataUpdate = (img: Base64Image) => {
|
|
|
90
93
|
dialogUpdate.value = true
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
const checkDuplicationName = (name: string) =>
|
|
94
|
-
images.value.some(({ imageTitle }) => isEqual(imageTitle, name))
|
|
96
|
+
const checkDuplicationName = (name: string) => images.value.some(({ imageTitle }) => isEqual(imageTitle, name))
|
|
95
97
|
|
|
96
98
|
const isImageDataUrl = (dataUrl: string) => /^data:image\//i.test(dataUrl)
|
|
97
99
|
|
|
100
|
+
const imageSrcFromImageData = (imageData: Base64Image) => {
|
|
101
|
+
let assetUrl = useRuntimeConfig().public.ASSET_URL as string
|
|
102
|
+
if (imageData?.imageData?.base64String) return useAssetFile().ensureDataUrl(imageData?.imageData?.base64String.trim(),(imageData?.imageData as Base64File).fileType || "image/png")
|
|
103
|
+
if (imageData?.imageData?.id) return (assetUrl || '/asset').replace(/\/+$/, '') +"/"+imageData?.imageData?.id
|
|
104
|
+
return undefined
|
|
105
|
+
}
|
|
106
|
+
|
|
98
107
|
/** File → Base64Image using composable */
|
|
99
108
|
const fileToBase64Image = async (file: File): Promise<Base64Image | null> => {
|
|
100
109
|
try {
|
|
101
|
-
const
|
|
102
|
-
const dataUrl =
|
|
110
|
+
const base64 = await fileToBase64(file,props.maxFileSize)
|
|
111
|
+
const dataUrl = base64.base64String || ''
|
|
103
112
|
if (!isImageDataUrl(dataUrl)) {
|
|
104
|
-
alert?.addAlert({ message:
|
|
113
|
+
alert?.addAlert({ message: `File "${base64.fileName}" is not supported image type.`, alertType: 'error' })
|
|
105
114
|
return null
|
|
106
115
|
}
|
|
107
116
|
return {
|
|
108
117
|
imageData: { base64String: dataUrl } as Base64Asset,
|
|
109
|
-
imageTitle:
|
|
118
|
+
imageTitle: base64.fileName,
|
|
110
119
|
imageProps: {},
|
|
111
120
|
}
|
|
112
121
|
} catch (e: any) {
|
|
@@ -116,7 +125,7 @@ const fileToBase64Image = async (file: File): Promise<Base64Image | null> => {
|
|
|
116
125
|
}
|
|
117
126
|
|
|
118
127
|
/** Handle upload button update */
|
|
119
|
-
const
|
|
128
|
+
const uploadImageFile = async () => {
|
|
120
129
|
const duplicated: string[] = []
|
|
121
130
|
for (const file of uploadImages.value) {
|
|
122
131
|
if (checkDuplicationName(file.name)) {
|
|
@@ -128,7 +137,7 @@ const update = async () => {
|
|
|
128
137
|
}
|
|
129
138
|
uploadImages.value = []
|
|
130
139
|
if (duplicated.length) {
|
|
131
|
-
alert?.addAlert({ message:
|
|
140
|
+
alert?.addAlert({ message: `File(s) are duplicated. ${duplicated.join(', ')}`, alertType: 'error' })
|
|
132
141
|
}
|
|
133
142
|
}
|
|
134
143
|
|
|
@@ -139,7 +148,7 @@ const modelData = ref()
|
|
|
139
148
|
const captureImage = (payload: any, cb: FormDialogCallback) => {
|
|
140
149
|
const dataUrl: string = payload?.imageCapture ?? ''
|
|
141
150
|
if (!dataUrl || !isImageDataUrl(dataUrl)) {
|
|
142
|
-
alert?.addAlert({ message: '
|
|
151
|
+
alert?.addAlert({ message: 'Invalid image.', alertType: 'error' })
|
|
143
152
|
return
|
|
144
153
|
}
|
|
145
154
|
addImage({
|
|
@@ -154,7 +163,7 @@ const captureImage = (payload: any, cb: FormDialogCallback) => {
|
|
|
154
163
|
const openImageFullScreen = (img: Base64Image) => {
|
|
155
164
|
dialogImageFullScreen.value = true
|
|
156
165
|
imageFullScreen.value.title = img.imageTitle ?? ''
|
|
157
|
-
imageFullScreen.value.image = img
|
|
166
|
+
imageFullScreen.value.image = imageSrcFromImageData(img)
|
|
158
167
|
}
|
|
159
168
|
|
|
160
169
|
/** ---------- Watchers (signature-based) ---------- */
|
|
@@ -217,7 +226,7 @@ watch(
|
|
|
217
226
|
icon-only
|
|
218
227
|
multiple
|
|
219
228
|
variant="text"
|
|
220
|
-
@update:model-value="
|
|
229
|
+
@update:model-value="uploadImageFile"
|
|
221
230
|
/>
|
|
222
231
|
<v-btn color="primary" icon @click="dialog = true">
|
|
223
232
|
<v-icon>mdi mdi-camera-plus</v-icon>
|
|
@@ -238,16 +247,16 @@ watch(
|
|
|
238
247
|
<v-btn icon @click="remove(index)">
|
|
239
248
|
<v-icon>mdi mdi-delete-outline</v-icon>
|
|
240
249
|
</v-btn>
|
|
241
|
-
<v-btn color="primary" icon @click="setDataUpdate(image)">
|
|
250
|
+
<v-btn color="primary" icon @click="setDataUpdate(image)" v-if="!image.imageData?.id">
|
|
242
251
|
<v-icon>mdi mdi-image-edit-outline</v-icon>
|
|
243
252
|
</v-btn>
|
|
244
253
|
</VToolbarItems>
|
|
245
254
|
</VToolbar>
|
|
246
255
|
|
|
247
256
|
<v-img
|
|
248
|
-
:src="image
|
|
257
|
+
:src="imageSrcFromImageData(image)"
|
|
249
258
|
height="250"
|
|
250
|
-
@click="() => { props.readonly ? openImageFullScreen(image) : setDataUpdate(image) }"
|
|
259
|
+
@click="() => { (props.readonly || image.imageData?.id) ? openImageFullScreen(image) : setDataUpdate(image) }"
|
|
251
260
|
/>
|
|
252
261
|
</VCard>
|
|
253
262
|
</VCol>
|