fix(uploader): register FilePond component at module level and restore proper dropzone styling
deploy-chabokan / deploy (push) Canceled after 0s
deploy-chabokan / deploy (push) Canceled after 0s
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
name="filepond-image"
|
name="filepond-image"
|
||||||
:max-files="maxFiles"
|
:max-files="maxFiles"
|
||||||
:allow-multiple="allowMultiple"
|
:allow-multiple="allowMultiple"
|
||||||
:accepted-file-types="acceptedFileTypes"
|
store-as-file="true"
|
||||||
:server="{
|
:server="{
|
||||||
process: {
|
process: {
|
||||||
url: resolvedUploadUrl,
|
url: resolvedUploadUrl,
|
||||||
@@ -20,37 +20,38 @@
|
|||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
headers: authHeaders,
|
headers: authHeaders,
|
||||||
onload: (response) => handleRevertResponse(response)
|
onload: (response) => handleRevertResponse(response)
|
||||||
},
|
|
||||||
load: (source, load, error, progress, abort, headers) => {
|
|
||||||
fetch(source)
|
|
||||||
.then((res) => res.blob())
|
|
||||||
.then(load)
|
|
||||||
.catch((err) => {
|
|
||||||
console.warn('FilePond failed to load existing image:', source, err);
|
|
||||||
error(err.message);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
:files="initialFiles"
|
:label-idle="'برای آپلود کلیک کنید یا فایل را بکشید و رها کنید <span class=\'filepond--label-action\'>انتخاب فایل</span>'"
|
||||||
@removefile="handleRemoveFile"
|
|
||||||
label-idle="برای آپلود کلیک کنید یا فایل را بکشید و رها کنید"
|
|
||||||
label-tap-to-undo="برای حذف کلیک کنید"
|
label-tap-to-undo="برای حذف کلیک کنید"
|
||||||
label-file-processing="در حال آپلود"
|
label-file-processing="در حال آپلود..."
|
||||||
label-file-processing-complete="آپلود فایل تکمیل شد"
|
label-file-processing-complete="آپلود فایل تکمیل شد"
|
||||||
|
@removefile="handleRemoveFile"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script>
|
||||||
import { ref, computed } from 'vue';
|
|
||||||
import Cookies from 'js-cookie';
|
|
||||||
import vueFilePond from 'vue-filepond';
|
import vueFilePond from 'vue-filepond';
|
||||||
import 'filepond/dist/filepond.min.css';
|
import 'filepond/dist/filepond.min.css';
|
||||||
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css';
|
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css';
|
||||||
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type';
|
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type';
|
||||||
import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
|
import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
|
||||||
|
|
||||||
// Define props
|
// Create FilePond component at module level
|
||||||
|
const FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FilePond
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
bucketName: {
|
bucketName: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -95,16 +96,8 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'upload', 'uploaded', 'delete']);
|
const emit = defineEmits(['update:modelValue', 'upload', 'uploaded', 'delete']);
|
||||||
|
|
||||||
// Define FilePond component
|
|
||||||
const FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview);
|
|
||||||
const pond = ref(null);
|
const pond = ref(null);
|
||||||
|
|
||||||
const acceptedFileTypes = computed(() => {
|
|
||||||
if (!props.acceptedFiles) return undefined;
|
|
||||||
return props.acceptedFiles.split(',').map((s) => s.trim());
|
|
||||||
});
|
|
||||||
|
|
||||||
const API_URL = (import.meta.env.VITE_API_URL || '').replace(/\/$/, '');
|
const API_URL = (import.meta.env.VITE_API_URL || '').replace(/\/$/, '');
|
||||||
|
|
||||||
const authHeaders = computed(() => {
|
const authHeaders = computed(() => {
|
||||||
@@ -136,33 +129,6 @@ const resolvedDeleteUrl = computed(() => {
|
|||||||
return `${API_URL}${cleanEndpoint}`;
|
return `${API_URL}${cleanEndpoint}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
const initialFiles = ref([]);
|
|
||||||
let isInitialLoaded = false;
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
(val) => {
|
|
||||||
// Only populate initialFiles on first load with existing items (e.g. edit mode)
|
|
||||||
// to avoid resetting active FilePond state during live uploads
|
|
||||||
if (!isInitialLoaded && Array.isArray(val) && val.length > 0) {
|
|
||||||
isInitialLoaded = true;
|
|
||||||
initialFiles.value = val
|
|
||||||
.map((item) => {
|
|
||||||
const url = typeof item === 'string' ? item : item.url || item.source || '';
|
|
||||||
if (!url) return null;
|
|
||||||
return {
|
|
||||||
source: url,
|
|
||||||
options: {
|
|
||||||
type: 'local'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle server responses for file processing
|
// Handle server responses for file processing
|
||||||
function handleProcessResponse(response) {
|
function handleProcessResponse(response) {
|
||||||
let parsed;
|
let parsed;
|
||||||
@@ -249,39 +215,50 @@ function removeFromFileList(identifier) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Apply dark mode styles to FilePond component */
|
/* FilePond Container & Theme Styling */
|
||||||
|
.image-uploader-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.filepond--root {
|
.filepond--root {
|
||||||
font-family: 'Estedad FD', sans-serif;
|
font-family: inherit;
|
||||||
background-color: var(--surface-ground);
|
margin-bottom: 0 !important;
|
||||||
color: var(--text-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-dark .filepond--root {
|
.filepond--panel-root {
|
||||||
background-color: var(--code-background);
|
border-radius: 0.75rem !important;
|
||||||
border-radius: 8px;
|
background-color: var(--surface-100, #f1f5f9) !important;
|
||||||
|
border: 2px dashed var(--surface-300, #cbd5e1) !important;
|
||||||
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filepond--label {
|
.app-dark .filepond--panel-root,
|
||||||
color: var(--text-color-secondary);
|
.dark .filepond--panel-root {
|
||||||
|
background-color: var(--surface-800, #1e293b) !important;
|
||||||
|
border-color: var(--surface-700, #334155) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-dark .filepond--label {
|
.filepond--drop-label {
|
||||||
color: var(--code-color);
|
min-height: 5.5rem !important;
|
||||||
|
color: var(--text-color-secondary, #64748b) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-dark .filepond--drop-label {
|
.app-dark .filepond--drop-label,
|
||||||
background-color: var(--code-background);
|
.dark .filepond--drop-label {
|
||||||
color: var(--code-color);
|
color: var(--text-color-secondary, #94a3b8) !important;
|
||||||
border: 1px solid var(--surface-border);
|
}
|
||||||
border-radius: 8px;
|
|
||||||
|
.filepond--label-action {
|
||||||
|
color: var(--primary-color, #10b981) !important;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filepond--item-panel {
|
.filepond--item-panel {
|
||||||
background-color: var(--surface-card);
|
background-color: var(--primary-color, #10b981) !important;
|
||||||
}
|
|
||||||
|
|
||||||
.app-dark .filepond--item-panel {
|
|
||||||
background-color: var(--code-background);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user