186 lines
5.3 KiB
Vue
186 lines
5.3 KiB
Vue
|
|
<script>
|
||
|
|
export default {
|
||
|
|
name: 'ImageUploadButton',
|
||
|
|
components: {},
|
||
|
|
props: {
|
||
|
|
onUpload: {
|
||
|
|
type: Function,
|
||
|
|
default: () => {},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
isUploading: false,
|
||
|
|
};
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
handleFileSelect(event) {
|
||
|
|
const file = event.target.files[0];
|
||
|
|
if (!file) return;
|
||
|
|
|
||
|
|
// Validate file type
|
||
|
|
if (!file.type.startsWith('image/')) {
|
||
|
|
alert('Please select an image file');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Validate file size (max 10MB)
|
||
|
|
const maxSize = 10 * 1024 * 1024;
|
||
|
|
if (file.size > maxSize) {
|
||
|
|
alert('Image size must be less than 10MB');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.uploadImage(file);
|
||
|
|
},
|
||
|
|
|
||
|
|
async uploadImage(file) {
|
||
|
|
this.isUploading = true;
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Get MALL_URL from window.chatwootWebChannel
|
||
|
|
const mallUrl = window.chatwootWebChannel?.mallUrl || 'https://apicn.qa1.gaia888.com';
|
||
|
|
const uploadUrl = `${mallUrl}/mall/api/upload/upload-resource`;
|
||
|
|
|
||
|
|
// Create FormData
|
||
|
|
const formData = new FormData();
|
||
|
|
formData.append('file', file);
|
||
|
|
|
||
|
|
// Get auth token from cookie
|
||
|
|
const token = this.getTokenFromCookie();
|
||
|
|
|
||
|
|
console.log('[ImageUpload] Uploading to:', uploadUrl);
|
||
|
|
console.log('[ImageUpload] Token:', token ? 'Present' : 'Missing');
|
||
|
|
|
||
|
|
// Upload to mall API
|
||
|
|
const response = await fetch(uploadUrl, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Authorization': `Bearer ${token}`,
|
||
|
|
},
|
||
|
|
body: formData,
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('[ImageUpload] Response status:', response.status);
|
||
|
|
|
||
|
|
if (!response.ok) {
|
||
|
|
throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const result = await response.json();
|
||
|
|
console.log('[ImageUpload] Response data:', result);
|
||
|
|
|
||
|
|
// Handle different response formats
|
||
|
|
let imageUrl = null;
|
||
|
|
|
||
|
|
if (result.code === 200 && result.result && result.result.url) {
|
||
|
|
// Mall API format: { code: 200, msg: "success", result: { url: "..." } }
|
||
|
|
imageUrl = result.result.url;
|
||
|
|
} else if (result.success && result.data) {
|
||
|
|
// Format: { success: true, data: { url: "..." } }
|
||
|
|
imageUrl = result.data.url || result.data.path;
|
||
|
|
} else if (result.code === 200 && result.data) {
|
||
|
|
// Format: { code: 200, data: { url: "..." } }
|
||
|
|
imageUrl = result.data.url || result.data.path || result.data;
|
||
|
|
} else if (result.url) {
|
||
|
|
// Format: { url: "..." }
|
||
|
|
imageUrl = result.url;
|
||
|
|
} else if (typeof result === 'string') {
|
||
|
|
// Format: just the URL string
|
||
|
|
imageUrl = result;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (imageUrl) {
|
||
|
|
console.log('[ImageUpload] Image URL:', imageUrl);
|
||
|
|
// Send with search_image content_type, empty content, and imageUrl in content_attributes
|
||
|
|
this.onUpload({
|
||
|
|
imageUrl: imageUrl,
|
||
|
|
contentType: 'search_image'
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
console.error('[ImageUpload] Unknown response format:', result);
|
||
|
|
throw new Error(`Unknown response format: ${JSON.stringify(result)}`);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Image upload error:', error);
|
||
|
|
alert(`Failed to upload image: ${error.message}`);
|
||
|
|
} finally {
|
||
|
|
this.isUploading = false;
|
||
|
|
// Reset file input
|
||
|
|
this.$refs.fileInput.value = '';
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
getTokenFromCookie() {
|
||
|
|
const getCookie = (name) => {
|
||
|
|
const value = `; ${document.cookie}`;
|
||
|
|
const parts = value.split(`; ${name}=`);
|
||
|
|
if (parts.length === 2) return parts.pop().split(';').shift();
|
||
|
|
return null;
|
||
|
|
};
|
||
|
|
return getCookie('token') || '';
|
||
|
|
},
|
||
|
|
|
||
|
|
triggerFileInput() {
|
||
|
|
if (this.isUploading) return;
|
||
|
|
this.$refs.fileInput.click();
|
||
|
|
},
|
||
|
|
},
|
||
|
|
};
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div class="image-upload-button">
|
||
|
|
<input
|
||
|
|
ref="fileInput"
|
||
|
|
type="file"
|
||
|
|
accept="image/*"
|
||
|
|
style="display: none;"
|
||
|
|
@change="handleFileSelect"
|
||
|
|
/>
|
||
|
|
<button
|
||
|
|
class="flex items-center justify-center min-h-8 min-w-8 text-n-slate-12"
|
||
|
|
:disabled="isUploading"
|
||
|
|
:aria-label="isUploading ? 'Uploading...' : 'Upload image'"
|
||
|
|
@click="triggerFileInput"
|
||
|
|
>
|
||
|
|
<svg
|
||
|
|
v-if="!isUploading"
|
||
|
|
xmlns="http://www.w3.org/2000/svg"
|
||
|
|
class="h-5 w-5"
|
||
|
|
fill="none"
|
||
|
|
viewBox="0 0 24 24"
|
||
|
|
stroke="currentColor"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
stroke-linecap="round"
|
||
|
|
stroke-linejoin="round"
|
||
|
|
stroke-width="2"
|
||
|
|
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
<svg
|
||
|
|
v-else
|
||
|
|
xmlns="http://www.w3.org/2000/svg"
|
||
|
|
class="h-5 w-5 animate-spin"
|
||
|
|
fill="none"
|
||
|
|
viewBox="0 0 24 24"
|
||
|
|
>
|
||
|
|
<circle
|
||
|
|
class="opacity-25"
|
||
|
|
cx="12"
|
||
|
|
cy="12"
|
||
|
|
r="10"
|
||
|
|
stroke="currentColor"
|
||
|
|
stroke-width="4"
|
||
|
|
/>
|
||
|
|
<path
|
||
|
|
class="opacity-75"
|
||
|
|
fill="currentColor"
|
||
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</template>
|