@finema/core 1.4.92 → 1.4.93
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/module.mjs +1 -1
- package/dist/runtime/components/Form/Fields.vue +9 -0
- package/dist/runtime/components/Form/InputUploadDropzoneAuto/index.vue +7 -1
- package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/Item.vue +283 -282
- package/dist/runtime/components/Form/InputUploadDropzoneAutoMultiple/index.vue +13 -4
- package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/index.vue +134 -0
- package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/item.vue +198 -0
- package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/types.d.ts +20 -0
- package/dist/runtime/components/Form/InputUploadDropzoneImageAutoMultiple/types.mjs +0 -0
- package/dist/runtime/components/Form/InputUploadFileClassic/index.vue +5 -3
- package/dist/runtime/components/Form/InputUploadFileClassicAuto/index.vue +9 -5
- package/dist/runtime/components/Form/types.d.ts +4 -2
- package/dist/runtime/components/Form/types.mjs +1 -0
- package/dist/runtime/composables/useNotification.d.ts +7 -0
- package/dist/runtime/composables/useNotification.mjs +39 -0
- package/dist/runtime/types/config.d.ts +1 -1
- package/dist/runtime/ui.config/index.d.ts +1 -0
- package/dist/runtime/ui.config/index.mjs +1 -0
- package/dist/runtime/ui.config/uploadDropzoneImage.d.ts +83 -0
- package/dist/runtime/ui.config/uploadDropzoneImage.mjs +26 -0
- package/dist/runtime/ui.config/uploadFileDropzone.d.ts +4 -0
- package/dist/runtime/ui.config/uploadFileDropzone.mjs +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FieldWrapper v-bind="wrapperProps">
|
|
3
|
+
<div class="space-y-3">
|
|
4
|
+
<div
|
|
5
|
+
ref="dropzoneRef"
|
|
6
|
+
:class="[
|
|
7
|
+
ui.base,
|
|
8
|
+
{
|
|
9
|
+
[ui.disabled]: isDisabled,
|
|
10
|
+
[ui.background.default]: !isOverDropZone && !isDisabled,
|
|
11
|
+
[ui.background.dragover]: isOverDropZone && !isDisabled,
|
|
12
|
+
},
|
|
13
|
+
]"
|
|
14
|
+
>
|
|
15
|
+
<input
|
|
16
|
+
ref="fileInputRef"
|
|
17
|
+
type="file"
|
|
18
|
+
class="hidden"
|
|
19
|
+
:name="name"
|
|
20
|
+
:accept="acceptFile"
|
|
21
|
+
:disabled="isDisabled"
|
|
22
|
+
multiple
|
|
23
|
+
@change="handleChange"
|
|
24
|
+
/>
|
|
25
|
+
<div :class="[ui.wrapper]">
|
|
26
|
+
<div v-if="selectedFiles.length === 0" :class="[ui.placeholderWrapper]">
|
|
27
|
+
<Icon :name="ui.default.uploadIcon" :class="[ui.labelIcon]" />
|
|
28
|
+
<div :class="[ui.labelWrapper]">
|
|
29
|
+
<p class="text-primary cursor-pointer" @click="handleOpenFile">
|
|
30
|
+
{{ selectFileLabel }}
|
|
31
|
+
</p>
|
|
32
|
+
<p>{{ selectFileSubLabel }}</p>
|
|
33
|
+
</div>
|
|
34
|
+
<p v-if="placeholder" :class="[ui.placeholder]">{{ placeholder }}</p>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div v-else :class="ui.imageItemWrapper">
|
|
38
|
+
<Item
|
|
39
|
+
v-for="(file, index) in selectedFiles"
|
|
40
|
+
:key="file.name + index"
|
|
41
|
+
v-bind="$props"
|
|
42
|
+
:ui="ui"
|
|
43
|
+
:selected-file="file"
|
|
44
|
+
@success="handleSuccess(index, $event)"
|
|
45
|
+
@delete="handleDeleteFile(index)"
|
|
46
|
+
@error="handleError(index, $event)"
|
|
47
|
+
/>
|
|
48
|
+
|
|
49
|
+
<Item v-bind="$props" is-adding-btn :ui="ui" @add="handleOpenFile" />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</FieldWrapper>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script lang="ts" setup>
|
|
58
|
+
import { useDropZone } from '@vueuse/core'
|
|
59
|
+
import { type IUploadDropzoneImageAutoMultipleProps } from './types'
|
|
60
|
+
import FieldWrapper from '#core/components/Form/FieldWrapper.vue'
|
|
61
|
+
import { useFieldHOC } from '#core/composables/useForm'
|
|
62
|
+
import { _get, computed, ref, toRef, useUI, useUiConfig } from '#imports'
|
|
63
|
+
import { uploadFileDropzoneImage } from '#core/ui.config'
|
|
64
|
+
import Item from './item.vue'
|
|
65
|
+
|
|
66
|
+
const config = useUiConfig<typeof uploadFileDropzoneImage>(
|
|
67
|
+
uploadFileDropzoneImage,
|
|
68
|
+
'uploadFileDropzoneImage'
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const props = withDefaults(defineProps<IUploadDropzoneImageAutoMultipleProps>(), {
|
|
72
|
+
accept: 'image/*',
|
|
73
|
+
bodyKey: 'file',
|
|
74
|
+
responseKey: 'url',
|
|
75
|
+
selectFileLabel: 'คลิกเพื่อเลือกไฟล์',
|
|
76
|
+
selectFileSubLabel: 'หรือ ลากและวางที่นี่',
|
|
77
|
+
uploadAddLabel: 'อัพโหลด',
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const emits = defineEmits(['change', 'success', 'delete'])
|
|
81
|
+
const selectedFiles = ref<File[]>([])
|
|
82
|
+
|
|
83
|
+
const { wrapperProps, handleChange: onChange, value } = useFieldHOC<File[]>(props)
|
|
84
|
+
|
|
85
|
+
const { ui } = useUI('uploadFileDropzoneImage', toRef(props, 'ui'), config)
|
|
86
|
+
|
|
87
|
+
const fileInputRef = ref<HTMLInputElement>()
|
|
88
|
+
const dropzoneRef = ref<HTMLDivElement>()
|
|
89
|
+
|
|
90
|
+
const acceptFile = computed(() =>
|
|
91
|
+
typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
const onDrop = (files: File[] | null) => {
|
|
95
|
+
if (props.isDisabled || files?.length === 0 || !files) return
|
|
96
|
+
|
|
97
|
+
for (const file of files) {
|
|
98
|
+
selectedFiles.value = [...selectedFiles.value, file]
|
|
99
|
+
|
|
100
|
+
emits('change', value.value)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const { isOverDropZone } = useDropZone(dropzoneRef as unknown as HTMLElement, {
|
|
105
|
+
onDrop,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const handleChange = (e: Event) => {
|
|
109
|
+
if (props.isDisabled) return
|
|
110
|
+
|
|
111
|
+
for (const file of (e.target as HTMLInputElement).files ?? []) {
|
|
112
|
+
selectedFiles.value = [...selectedFiles.value, file]
|
|
113
|
+
emits('change', value.value)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const handleOpenFile = () => {
|
|
118
|
+
fileInputRef.value?.click()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const handleDeleteFile = (index: number) => {
|
|
122
|
+
selectedFiles.value.splice(index, 1)
|
|
123
|
+
onChange(undefined)
|
|
124
|
+
emits('delete')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const handleError = (index: number, error: any) => {}
|
|
128
|
+
|
|
129
|
+
const handleSuccess = (index: number, any: any) => {
|
|
130
|
+
value.value = [_get(any, props.responseKey), ...(value.value || [])]
|
|
131
|
+
emits('change', value.value)
|
|
132
|
+
emits('success', value.value)
|
|
133
|
+
}
|
|
134
|
+
</script>
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="[ui.imageItem.wrapper]">
|
|
3
|
+
<div v-if="isAddingBtn" class="w-full">
|
|
4
|
+
<div :class="[ui.action.addingWrapper]" @click="$emit('add')">
|
|
5
|
+
<Icon :name="ui.action.addingIcon" :class="[ui.action.addingBtnClass]" />
|
|
6
|
+
<p :class="[ui.action.addingTextClass]">{{ uploadAddLabel }}</p>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<!-- Loading State -->
|
|
11
|
+
<div v-if="selectedFile && upload.status.value.isLoading" class="w-full">
|
|
12
|
+
<div :class="[ui.imageItem.onLoading.wrapper]">
|
|
13
|
+
<div :class="[ui.imageItem.onLoading.percentClass]">{{ percent }}%</div>
|
|
14
|
+
<UProgress :value="percent" />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Success State -->
|
|
19
|
+
<div v-if="selectedFile && upload.status.value.isSuccess" class="w-full">
|
|
20
|
+
<div :class="[ui.imageItem.onPreview.wrapper]">
|
|
21
|
+
<img
|
|
22
|
+
:class="[ui.imageItem.onPreview.previewImgClass]"
|
|
23
|
+
:src="upload.data.value[responseKey]"
|
|
24
|
+
alt="img"
|
|
25
|
+
/>
|
|
26
|
+
<div :class="[ui.imageItem.onPreview.previewActionWrapper]">
|
|
27
|
+
<Icon
|
|
28
|
+
title="ดูตัวอย่าง"
|
|
29
|
+
:name="ui.action.previewIcon"
|
|
30
|
+
:class="[ui.imageItem.onPreview.actionBtnClass]"
|
|
31
|
+
@click="() => (isPreviewOpen = true)"
|
|
32
|
+
/>
|
|
33
|
+
<Icon
|
|
34
|
+
title="ลบไฟล์"
|
|
35
|
+
:name="ui.action.deleteIcon"
|
|
36
|
+
:class="[ui.imageItem.onPreview.actionBtnClass]"
|
|
37
|
+
@click="handleDeleteFile"
|
|
38
|
+
/>
|
|
39
|
+
<Modal v-model="isPreviewOpen" :title="selectedFile?.name">
|
|
40
|
+
<img :src="upload.data.value[responseKey]" alt="image-preview" />
|
|
41
|
+
</Modal>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div :class="[ui.imageItem.onPreview.previewTextWrapper]">
|
|
46
|
+
<p :class="[ui.imageItem.onPreview.previewText]">{{ selectedFile.name }}</p>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Failed State -->
|
|
51
|
+
<div v-if="selectedFile && upload.status.value.isError" class="w-full">
|
|
52
|
+
<div :class="[ui.imageItem.onFailed.wrapper]">
|
|
53
|
+
<img
|
|
54
|
+
:class="[ui.imageItem.onFailed.failedImgClass]"
|
|
55
|
+
:src="generateURL(selectedFile)"
|
|
56
|
+
alt="img"
|
|
57
|
+
/>
|
|
58
|
+
<div :class="[ui.imageItem.onFailed.failedActionWrapper]">
|
|
59
|
+
<Icon
|
|
60
|
+
title="ลบไฟล์"
|
|
61
|
+
:name="ui.action.deleteIcon"
|
|
62
|
+
:class="[ui.imageItem.onFailed.actionBtnClass]"
|
|
63
|
+
@click="handleDeleteFile"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<script lang="ts" setup>
|
|
72
|
+
import {
|
|
73
|
+
checkFileType,
|
|
74
|
+
checkMaxSize,
|
|
75
|
+
generateURL,
|
|
76
|
+
getFileAllocate,
|
|
77
|
+
} from '#core/helpers/componentHelper'
|
|
78
|
+
import { type uploadFileDropzoneImage } from '#core/ui.config'
|
|
79
|
+
import type { IUploadDropzoneImageAutoMultipleProps } from '#core/components/Form/InputUploadDropzoneImageAutoMultiple/types'
|
|
80
|
+
import {
|
|
81
|
+
computed,
|
|
82
|
+
type IUploadRequest,
|
|
83
|
+
onMounted,
|
|
84
|
+
ref,
|
|
85
|
+
StringHelper,
|
|
86
|
+
useUploadLoader,
|
|
87
|
+
useWatchTrue,
|
|
88
|
+
} from '#imports'
|
|
89
|
+
import i18next from 'i18next'
|
|
90
|
+
|
|
91
|
+
const emits = defineEmits(['success', 'error', 'delete', 'add'])
|
|
92
|
+
const props = defineProps<
|
|
93
|
+
{
|
|
94
|
+
ui: typeof uploadFileDropzoneImage
|
|
95
|
+
selectedFile?: File
|
|
96
|
+
isAddingBtn?: boolean
|
|
97
|
+
} & IUploadDropzoneImageAutoMultipleProps
|
|
98
|
+
>()
|
|
99
|
+
|
|
100
|
+
const request: IUploadRequest = {
|
|
101
|
+
pathURL: props.uploadPathURL,
|
|
102
|
+
requestOptions: props.requestOptions,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const isPreviewOpen = ref<boolean>(false)
|
|
106
|
+
const percent = ref<number | string>(0)
|
|
107
|
+
const upload = useUploadLoader(request)
|
|
108
|
+
const errMsg = ref<string>('')
|
|
109
|
+
|
|
110
|
+
const acceptFile = computed(() =>
|
|
111
|
+
typeof props.accept === 'string' ? props.accept : props.accept?.join(',')
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const fileAllocate = computed(() =>
|
|
115
|
+
getFileAllocate(props.maxSize ?? 0, props.selectedFile?.size ?? 0)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
const handleDeleteFile = () => {
|
|
119
|
+
emits('delete')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const onUploadProgress = (progressEvent: ProgressEvent) => {
|
|
123
|
+
percent.value = StringHelper.withFixed(
|
|
124
|
+
(Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.8
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const onDownloadProgress = (progressEvent: ProgressEvent) => {
|
|
129
|
+
if (progressEvent.total === 0) {
|
|
130
|
+
percent.value = 100
|
|
131
|
+
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
percent.value = StringHelper.withFixed(
|
|
136
|
+
(Math.floor((progressEvent.loaded * 100) / progressEvent.total) || 0) * 0.2 + 80
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const handleCheckFileCondition = (file: File | undefined): boolean => {
|
|
141
|
+
if (!file) return false
|
|
142
|
+
|
|
143
|
+
const fileType = checkFileType(file, acceptFile.value ?? '')
|
|
144
|
+
|
|
145
|
+
if (!fileType) {
|
|
146
|
+
errMsg.value = i18next.t('custom:invalid_file_type')
|
|
147
|
+
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const maxSize = checkMaxSize(file, fileAllocate.value.acceptFileSizeKb ?? 0)
|
|
152
|
+
|
|
153
|
+
if (!maxSize) {
|
|
154
|
+
if (fileAllocate.value.isAcceptFileUseMb) {
|
|
155
|
+
errMsg.value = i18next.t('custom:invalid_file_size_mb', {
|
|
156
|
+
size: fileAllocate.value.acceptFileSizeMb,
|
|
157
|
+
})
|
|
158
|
+
} else {
|
|
159
|
+
errMsg.value = i18next.t('custom:invalid_file_size_kb', {
|
|
160
|
+
size: fileAllocate.value.acceptFileSizeKb,
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return false
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
errMsg.value = ''
|
|
168
|
+
|
|
169
|
+
return true
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
onMounted(() => {
|
|
173
|
+
if (props.isAddingBtn) return
|
|
174
|
+
|
|
175
|
+
const result = handleCheckFileCondition(props.selectedFile)
|
|
176
|
+
|
|
177
|
+
if (result) {
|
|
178
|
+
const formData = new FormData()
|
|
179
|
+
|
|
180
|
+
formData.append(props.bodyKey!, props.selectedFile!)
|
|
181
|
+
upload.run(formData, { data: { onUploadProgress, onDownloadProgress } })
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
useWatchTrue(
|
|
186
|
+
() => upload.status.value.isSuccess,
|
|
187
|
+
() => {
|
|
188
|
+
emits('success', upload.data.value)
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
useWatchTrue(
|
|
193
|
+
() => upload.status.value.isError,
|
|
194
|
+
() => {
|
|
195
|
+
emits('error', upload.status.value.errorData)
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type AxiosRequestConfig } from 'axios';
|
|
2
|
+
import { type IFieldProps, type IFormFieldBase, type INPUT_TYPES } from '../types';
|
|
3
|
+
export interface IUploadDropzoneImageAutoMultipleProps extends IFieldProps {
|
|
4
|
+
requestOptions: Omit<AxiosRequestConfig, 'baseURL'> & {
|
|
5
|
+
baseURL: string;
|
|
6
|
+
};
|
|
7
|
+
uploadPathURL?: string;
|
|
8
|
+
selectFileLabel?: string;
|
|
9
|
+
selectFileSubLabel?: string;
|
|
10
|
+
uploadAddLabel?: string;
|
|
11
|
+
accept?: string[] | string;
|
|
12
|
+
bodyKey?: string;
|
|
13
|
+
responseKey?: string;
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
}
|
|
16
|
+
export type IUploadDropzoneImageAutoMultipleField = IFormFieldBase<INPUT_TYPES.UPLOAD_DROPZONE_IMAGE_AUTO_MULTIPLE, IUploadDropzoneImageAutoMultipleProps, {
|
|
17
|
+
change: (value: File[] | undefined) => void;
|
|
18
|
+
success: (res: any) => void;
|
|
19
|
+
delete: () => void;
|
|
20
|
+
}>;
|
|
File without changes
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
/>
|
|
13
13
|
<div :class="[ui.wrapper]">
|
|
14
14
|
<div :class="[ui.selectFileBox]">
|
|
15
|
-
<Button size="2xs" @click="handleOpenFile">{{ selectFileLabel
|
|
15
|
+
<Button size="2xs" @click="handleOpenFile">{{ selectFileLabel }}</Button>
|
|
16
16
|
<p :class="ui.placeholder">
|
|
17
|
-
{{ value?.name ?? placeholder ?? '
|
|
17
|
+
{{ value?.name ?? placeholder ?? 'ยังไม่ได้เลือกไฟล์' }}
|
|
18
18
|
</p>
|
|
19
19
|
<Badge v-if="value" size="xs" variant="outline">
|
|
20
20
|
{{ isSelectedFileUseMb ? `${selectedFileSizeMb} MB` : `${selectedFileSizeKb} KB` }}
|
|
@@ -39,7 +39,9 @@ const config = useUiConfig<typeof uploadFileInputClassicAuto>(
|
|
|
39
39
|
)
|
|
40
40
|
|
|
41
41
|
const emits = defineEmits(['change'])
|
|
42
|
-
const props = withDefaults(defineProps<IUploadFileClassicFieldProps>(), {
|
|
42
|
+
const props = withDefaults(defineProps<IUploadFileClassicFieldProps>(), {
|
|
43
|
+
selectFileLabel: 'เลือกไฟล์',
|
|
44
|
+
})
|
|
43
45
|
|
|
44
46
|
const { value, wrapperProps, setErrors } = useFieldHOC<File | undefined>(props)
|
|
45
47
|
const selectedFileSizeKb = computed(() => ((value.value?.size || 0) / 1000).toFixed(2))
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
/>
|
|
13
13
|
<div :class="[ui.wrapper]">
|
|
14
14
|
<div :class="[ui.selectFileBox]">
|
|
15
|
-
<Button size="2xs" @click="handleOpenFile">{{ selectFileLabel
|
|
15
|
+
<Button size="2xs" @click="handleOpenFile">{{ selectFileLabel }}</Button>
|
|
16
16
|
<p :class="ui.placeholder">
|
|
17
|
-
{{ selectedFile?.name ?? placeholder ?? '
|
|
17
|
+
{{ selectedFile?.name ?? placeholder ?? 'ยังไม่ได้เลือกไฟล์' }}
|
|
18
18
|
</p>
|
|
19
19
|
<Badge v-if="selectedFile" size="xs" variant="outline">
|
|
20
20
|
{{ isSelectedFileUseMb ? `${selectedFileSizeMb} MB` : `${selectedFileSizeKb} KB` }}
|
|
@@ -67,7 +67,11 @@ const config = useUiConfig<typeof uploadFileInputClassicAuto>(
|
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
const emits = defineEmits(['success'])
|
|
70
|
-
const props = withDefaults(defineProps<IUploadFileProps>(), {
|
|
70
|
+
const props = withDefaults(defineProps<IUploadFileProps>(), {
|
|
71
|
+
bodyKey: 'file',
|
|
72
|
+
responseKey: 'url',
|
|
73
|
+
selectFileLabel: 'เลือกไฟล์',
|
|
74
|
+
})
|
|
71
75
|
|
|
72
76
|
const { wrapperProps, setErrors, value } = useFieldHOC<string>(props)
|
|
73
77
|
|
|
@@ -110,7 +114,7 @@ const handleChange = (e: Event) => {
|
|
|
110
114
|
selectedFile.value = file
|
|
111
115
|
const formData = new FormData()
|
|
112
116
|
|
|
113
|
-
formData.append(props.bodyKey
|
|
117
|
+
formData.append(props.bodyKey, file)
|
|
114
118
|
upload.run(formData, { data: { onUploadProgress, onDownloadProgress } })
|
|
115
119
|
}
|
|
116
120
|
}
|
|
@@ -160,7 +164,7 @@ const onDownloadProgress = (progressEvent: ProgressEvent) => {
|
|
|
160
164
|
useWatchTrue(
|
|
161
165
|
() => upload.status.value.isSuccess,
|
|
162
166
|
() => {
|
|
163
|
-
value.value = _get(upload.data.value, props.responseKey
|
|
167
|
+
value.value = _get(upload.data.value, props.responseKey)
|
|
164
168
|
emits('success', upload.data.value)
|
|
165
169
|
}
|
|
166
170
|
)
|
|
@@ -14,6 +14,7 @@ import { type IUploadDropzoneField } from '#core/components/Form/InputUploadDrop
|
|
|
14
14
|
import { type IUploadDropzoneAutoField } from '#core/components/Form/InputUploadDropzoneAuto/types';
|
|
15
15
|
import type { INumberField } from '#core/components/Form/InputNumber/types';
|
|
16
16
|
import type { IUploadDropzoneAutoMultipleField } from '#core/components/Form/InputUploadDropzoneAutoMultiple/types';
|
|
17
|
+
import type { IUploadDropzoneImageAutoMultipleField } from '#core/components/Form/InputUploadDropzoneImageAutoMultiple/types';
|
|
17
18
|
export declare const enum INPUT_TYPES {
|
|
18
19
|
TEXT = "TEXT",
|
|
19
20
|
NUMBER = "NUMBER",
|
|
@@ -31,7 +32,8 @@ export declare const enum INPUT_TYPES {
|
|
|
31
32
|
UPLOAD_FILE_CLASSIC_AUTO = "UPLOAD_FILE_CLASSIC_AUTO",
|
|
32
33
|
UPLOAD_DROPZONE = "UPLOAD_DROPZONE",
|
|
33
34
|
UPLOAD_DROPZONE_AUTO = "UPLOAD_DROPZONE_AUTO",
|
|
34
|
-
UPLOAD_DROPZONE_AUTO_MULTIPLE = "UPLOAD_DROPZONE_AUTO_MULTIPLE"
|
|
35
|
+
UPLOAD_DROPZONE_AUTO_MULTIPLE = "UPLOAD_DROPZONE_AUTO_MULTIPLE",
|
|
36
|
+
UPLOAD_DROPZONE_IMAGE_AUTO_MULTIPLE = "UPLOAD_DROPZONE_IMAGE_AUTO_MULTIPLE"
|
|
35
37
|
}
|
|
36
38
|
export interface IFieldProps {
|
|
37
39
|
form?: FormContext;
|
|
@@ -60,4 +62,4 @@ export interface IFormFieldBase<I extends INPUT_TYPES, P extends IFieldProps, O>
|
|
|
60
62
|
props: P;
|
|
61
63
|
on?: O;
|
|
62
64
|
}
|
|
63
|
-
export type IFormField = ITextField | INumberField | IStaticField | ICheckboxField | IRadioField | ISelectField | IToggleField | ITextareaField | IDateTimeField | IUploadFileClassicField | IUploadFileField | IUploadDropzoneField | IUploadDropzoneAutoField | IUploadDropzoneAutoMultipleField;
|
|
65
|
+
export type IFormField = ITextField | INumberField | IStaticField | ICheckboxField | IRadioField | ISelectField | IToggleField | ITextareaField | IDateTimeField | IUploadFileClassicField | IUploadFileField | IUploadDropzoneField | IUploadDropzoneAutoField | IUploadDropzoneAutoMultipleField | IUploadDropzoneImageAutoMultipleField;
|
|
@@ -16,5 +16,6 @@ export var INPUT_TYPES = /* @__PURE__ */ ((INPUT_TYPES2) => {
|
|
|
16
16
|
INPUT_TYPES2["UPLOAD_DROPZONE"] = "UPLOAD_DROPZONE";
|
|
17
17
|
INPUT_TYPES2["UPLOAD_DROPZONE_AUTO"] = "UPLOAD_DROPZONE_AUTO";
|
|
18
18
|
INPUT_TYPES2["UPLOAD_DROPZONE_AUTO_MULTIPLE"] = "UPLOAD_DROPZONE_AUTO_MULTIPLE";
|
|
19
|
+
INPUT_TYPES2["UPLOAD_DROPZONE_IMAGE_AUTO_MULTIPLE"] = "UPLOAD_DROPZONE_IMAGE_AUTO_MULTIPLE";
|
|
19
20
|
return INPUT_TYPES2;
|
|
20
21
|
})(INPUT_TYPES || {});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useToast } from "#imports";
|
|
2
|
+
export const useNotification = () => {
|
|
3
|
+
const toast = useToast();
|
|
4
|
+
const info = (notification) => {
|
|
5
|
+
toast.add({
|
|
6
|
+
icon: "ph:info",
|
|
7
|
+
color: "info",
|
|
8
|
+
...notification
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
const success = (notification) => {
|
|
12
|
+
toast.add({
|
|
13
|
+
icon: "ph:check-circle",
|
|
14
|
+
color: "success",
|
|
15
|
+
...notification
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
const warning = (notification) => {
|
|
19
|
+
toast.add({
|
|
20
|
+
icon: "ph:warning",
|
|
21
|
+
color: "warning",
|
|
22
|
+
...notification
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const error = (notification) => {
|
|
26
|
+
toast.add({
|
|
27
|
+
icon: "ph:x-circle",
|
|
28
|
+
color: "danger",
|
|
29
|
+
...notification
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
info,
|
|
34
|
+
success,
|
|
35
|
+
warning,
|
|
36
|
+
error,
|
|
37
|
+
remove: toast.remove
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type UIComponentList = 'modal' | 'slideover' | 'dropdown' | 'icon' | 'button' | 'buttonGroup' | 'tabs' | 'card' | 'breadcrumb' | 'badge' | 'input' | 'pagination' | 'notification' | 'uploadFileInputClassicAuto' | 'uploadFileDropzone';
|
|
1
|
+
export type UIComponentList = 'modal' | 'slideover' | 'dropdown' | 'icon' | 'button' | 'buttonGroup' | 'tabs' | 'card' | 'breadcrumb' | 'badge' | 'input' | 'pagination' | 'notification' | 'uploadFileInputClassicAuto' | 'uploadFileDropzone' | 'uploadFileDropzoneImage';
|
|
@@ -11,3 +11,4 @@ export { slideover } from './slideover';
|
|
|
11
11
|
export { breadcrumb } from './breadcrumb';
|
|
12
12
|
export { uploadFileInputClassicAuto } from './uploadFileInputClassicAuto';
|
|
13
13
|
export { uploadFileDropzone } from './uploadFileDropzone';
|
|
14
|
+
export { uploadFileDropzoneImage } from './uploadDropzoneImage';
|
|
@@ -11,3 +11,4 @@ export { slideover } from "./slideover.mjs";
|
|
|
11
11
|
export { breadcrumb } from "./breadcrumb.mjs";
|
|
12
12
|
export { uploadFileInputClassicAuto } from "./uploadFileInputClassicAuto.mjs";
|
|
13
13
|
export { uploadFileDropzone } from "./uploadFileDropzone.mjs";
|
|
14
|
+
export { uploadFileDropzoneImage } from "./uploadDropzoneImage.mjs";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export declare const uploadFileDropzoneImage: {
|
|
2
|
+
imageItemWrapper: string;
|
|
3
|
+
imageItem: {
|
|
4
|
+
wrapper: string;
|
|
5
|
+
onLoading: {
|
|
6
|
+
wrapper: string;
|
|
7
|
+
percentClass: string;
|
|
8
|
+
};
|
|
9
|
+
onPreview: {
|
|
10
|
+
wrapper: string;
|
|
11
|
+
previewImgClass: string;
|
|
12
|
+
previewActionWrapper: string;
|
|
13
|
+
actionBtnClass: string;
|
|
14
|
+
previewTextWrapper: string;
|
|
15
|
+
previewText: string;
|
|
16
|
+
};
|
|
17
|
+
onFailed: {
|
|
18
|
+
wrapper: string;
|
|
19
|
+
failedImgClass: string;
|
|
20
|
+
failedActionWrapper: string;
|
|
21
|
+
actionBtnClass: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
base: string;
|
|
25
|
+
wrapper: string;
|
|
26
|
+
disabled: string;
|
|
27
|
+
failed: string;
|
|
28
|
+
placeholderWrapper: string;
|
|
29
|
+
placeholder: string;
|
|
30
|
+
labelWrapper: string;
|
|
31
|
+
onLoading: {
|
|
32
|
+
wrapper: string;
|
|
33
|
+
placeholderWrapper: string;
|
|
34
|
+
placeholderImgIcon: string;
|
|
35
|
+
placeholderFileIcon: string;
|
|
36
|
+
placeholderIconClass: string;
|
|
37
|
+
textWrapper: string;
|
|
38
|
+
loadingIcon: string;
|
|
39
|
+
loadingIconClass: string;
|
|
40
|
+
};
|
|
41
|
+
onPreview: {
|
|
42
|
+
wrapper: string;
|
|
43
|
+
previewImgWrapper: string;
|
|
44
|
+
previewImgClass: string;
|
|
45
|
+
previewFileIcon: string;
|
|
46
|
+
previewFileClass: string;
|
|
47
|
+
textWrapper: string;
|
|
48
|
+
};
|
|
49
|
+
onFailed: {
|
|
50
|
+
wrapper: string;
|
|
51
|
+
failedImgWrapper: string;
|
|
52
|
+
failedImgIcon: string;
|
|
53
|
+
failedFileIcon: string;
|
|
54
|
+
failedIconClass: string;
|
|
55
|
+
textWrapper: string;
|
|
56
|
+
};
|
|
57
|
+
action: {
|
|
58
|
+
wrapper: string;
|
|
59
|
+
addingWrapper: string;
|
|
60
|
+
iconClass: string;
|
|
61
|
+
deleteIconClass: string;
|
|
62
|
+
retryBtnClass: string;
|
|
63
|
+
addingBtnClass: string;
|
|
64
|
+
addingTextClass: string;
|
|
65
|
+
previewIcon: string;
|
|
66
|
+
downloadIcon: string;
|
|
67
|
+
deleteIcon: string;
|
|
68
|
+
retryIcon: string;
|
|
69
|
+
addingIcon: string;
|
|
70
|
+
};
|
|
71
|
+
background: {
|
|
72
|
+
default: string;
|
|
73
|
+
dragover: string;
|
|
74
|
+
};
|
|
75
|
+
labelIcon: string;
|
|
76
|
+
default: {
|
|
77
|
+
filePreviewIcon: string;
|
|
78
|
+
uploadIcon: string;
|
|
79
|
+
placeholderImgIcon: string;
|
|
80
|
+
failedImgIcon: string;
|
|
81
|
+
loadingIcon: string;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { uploadFileDropzone } from "./uploadFileDropzone.mjs";
|
|
2
|
+
export const uploadFileDropzoneImage = {
|
|
3
|
+
...uploadFileDropzone,
|
|
4
|
+
imageItemWrapper: "flex w-full flex-wrap gap-4",
|
|
5
|
+
imageItem: {
|
|
6
|
+
wrapper: "max-w-[96px]",
|
|
7
|
+
onLoading: {
|
|
8
|
+
wrapper: "flex size-24 flex-col items-center justify-center overflow-hidden rounded-lg border-[1px] border-dashed p-2",
|
|
9
|
+
percentClass: "text-primary"
|
|
10
|
+
},
|
|
11
|
+
onPreview: {
|
|
12
|
+
wrapper: "relative size-24 overflow-hidden rounded-lg ring-[1px] ring-gray-100",
|
|
13
|
+
previewImgClass: "size-full object-cover",
|
|
14
|
+
previewActionWrapper: "absolute inset-0 z-10 flex items-center justify-center space-x-2 opacity-0 transition hover:bg-black/50 hover:opacity-100",
|
|
15
|
+
actionBtnClass: "size-7 cursor-pointer text-white",
|
|
16
|
+
previewTextWrapper: "mt-2 truncate",
|
|
17
|
+
previewText: "truncate text-center text-sm"
|
|
18
|
+
},
|
|
19
|
+
onFailed: {
|
|
20
|
+
wrapper: "ring-danger relative size-24 overflow-hidden rounded-lg ring-[1px]",
|
|
21
|
+
failedImgClass: "size-full object-cover",
|
|
22
|
+
failedActionWrapper: "absolute inset-0 z-10 flex items-center justify-center space-x-2 bg-white/50",
|
|
23
|
+
actionBtnClass: "text-danger size-7 cursor-pointer"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
@@ -34,13 +34,17 @@ export declare const uploadFileDropzone: {
|
|
|
34
34
|
};
|
|
35
35
|
action: {
|
|
36
36
|
wrapper: string;
|
|
37
|
+
addingWrapper: string;
|
|
37
38
|
iconClass: string;
|
|
38
39
|
deleteIconClass: string;
|
|
39
40
|
retryBtnClass: string;
|
|
41
|
+
addingBtnClass: string;
|
|
42
|
+
addingTextClass: string;
|
|
40
43
|
previewIcon: string;
|
|
41
44
|
downloadIcon: string;
|
|
42
45
|
deleteIcon: string;
|
|
43
46
|
retryIcon: string;
|
|
47
|
+
addingIcon: string;
|
|
44
48
|
};
|
|
45
49
|
background: {
|
|
46
50
|
default: string;
|
|
@@ -34,13 +34,17 @@ export const uploadFileDropzone = {
|
|
|
34
34
|
},
|
|
35
35
|
action: {
|
|
36
36
|
wrapper: "flex items-center space-x-4",
|
|
37
|
+
addingWrapper: "flex size-24 flex-col items-center justify-center overflow-hidden rounded-lg border-[1px] border-dashed p-2 cursor-pointer transition hover:bg-gray-100",
|
|
37
38
|
iconClass: "size-6 text-gray-400 cursor-pointer",
|
|
38
39
|
deleteIconClass: "size-6 text-danger cursor-pointer",
|
|
39
40
|
retryBtnClass: "px-0",
|
|
41
|
+
addingBtnClass: "size-7 text-gray-500",
|
|
42
|
+
addingTextClass: "text-center text-sm text-gray-500",
|
|
40
43
|
previewIcon: "i-ic:outline-remove-red-eye",
|
|
41
44
|
downloadIcon: "i-ic:outline-file-download",
|
|
42
45
|
deleteIcon: "ph:trash",
|
|
43
|
-
retryIcon: "ph:arrow-counter-clockwise"
|
|
46
|
+
retryIcon: "ph:arrow-counter-clockwise",
|
|
47
|
+
addingIcon: "i-material-symbols:add"
|
|
44
48
|
},
|
|
45
49
|
background: {
|
|
46
50
|
default: "bg-white border-gray-border",
|