feat: add WebRTC camera component for in-app photo capture
Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
This commit is contained in:
134
inventory-web/src/components/Camera/WebRtcCamera.vue
Normal file
134
inventory-web/src/components/Camera/WebRtcCamera.vue
Normal file
@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div class="camera-container">
|
||||
<div v-if="error" class="error-message">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<video ref="videoRef" autoplay playsinline class="video-preview"></video>
|
||||
<canvas ref="canvasRef" style="display: none;"></canvas>
|
||||
</div>
|
||||
<div class="camera-actions">
|
||||
<el-button @click="handleCancel" size="large">取消</el-button>
|
||||
<el-button type="primary" @click="capture" size="large" :disabled="!isCameraReady">拍照</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const props = defineProps<{
|
||||
visible?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
confirm: [file: File]
|
||||
cancel: []
|
||||
}>()
|
||||
|
||||
const videoRef = ref<HTMLVideoElement>()
|
||||
const canvasRef = ref<HTMLCanvasElement>()
|
||||
const mediaStream = ref<MediaStream>()
|
||||
const error = ref('')
|
||||
const isCameraReady = ref(false)
|
||||
|
||||
const startCamera = async () => {
|
||||
stopCamera()
|
||||
error.value = ''
|
||||
try {
|
||||
const constraints = {
|
||||
video: { facingMode: 'environment' }
|
||||
}
|
||||
const stream = await navigator.mediaDevices.getUserMedia(constraints)
|
||||
mediaStream.value = stream
|
||||
if (videoRef.value) {
|
||||
videoRef.value.srcObject = stream
|
||||
await videoRef.value.play()
|
||||
isCameraReady.value = true
|
||||
}
|
||||
} catch (err: any) {
|
||||
error.value = '无法访问摄像头: ' + (err.message || '请检查权限或连接')
|
||||
ElMessage.error(error.value)
|
||||
emit('cancel')
|
||||
}
|
||||
}
|
||||
|
||||
const stopCamera = () => {
|
||||
if (mediaStream.value) {
|
||||
mediaStream.value.getTracks().forEach(track => track.stop())
|
||||
mediaStream.value = undefined
|
||||
}
|
||||
if (videoRef.value) {
|
||||
videoRef.value.srcObject = null
|
||||
}
|
||||
isCameraReady.value = false
|
||||
}
|
||||
|
||||
const capture = () => {
|
||||
const video = videoRef.value
|
||||
const canvas = canvasRef.value
|
||||
if (!video || !canvas) return
|
||||
|
||||
const width = video.videoWidth
|
||||
const height = video.videoHeight
|
||||
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
ctx.drawImage(video, 0, 0, width, height)
|
||||
|
||||
canvas.toBlob((blob) => {
|
||||
if (!blob) return
|
||||
const file = new File([blob], 'camera.jpg', { type: 'image/jpeg' })
|
||||
emit('confirm', file)
|
||||
stopCamera()
|
||||
}, 'image/jpeg', 0.95)
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
stopCamera()
|
||||
emit('cancel')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
startCamera()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopCamera()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
startCamera,
|
||||
stopCamera
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.camera-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.video-preview {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
max-height: 360px;
|
||||
background: #000;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.error-message {
|
||||
color: #f56c6c;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.camera-actions {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
</style>
|
||||
@ -333,8 +333,14 @@
|
||||
<el-dialog v-model="dialogVisibleImage" append-to-body width="50%">
|
||||
<img style="width: 100%" :src="dialogImageUrl" alt="Preview Image" />
|
||||
</el-dialog>
|
||||
<el-dialog v-model="cameraDialogVisible" title="拍照上传" width="500px" append-to-body destroy-on-close>
|
||||
<WebRtcCamera
|
||||
ref="cameraRef"
|
||||
@confirm="handleCameraConfirm"
|
||||
@cancel="cameraDialogVisible = false"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<input type="file" ref="cameraInputRef" accept="image/*" capture="environment" style="display: none" @change="handleCameraFile"/>
|
||||
|
||||
</el-card>
|
||||
</div>
|
||||
@ -353,6 +359,7 @@ import {
|
||||
delMaterialBase
|
||||
} from '@/api/material_base';
|
||||
import { uploadFile, deleteFile } from '@/api/common/upload'; // 假设通用上传接口在此
|
||||
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue';
|
||||
|
||||
// --- 类型定义 ---
|
||||
interface MaterialBaseVO {
|
||||
@ -393,7 +400,8 @@ const imageExternalUrl = ref('');
|
||||
const manualExternalUrl = ref('');
|
||||
const dialogVisibleImage = ref(false);
|
||||
const dialogImageUrl = ref('');
|
||||
const cameraInputRef = ref<HTMLInputElement | null>(null);
|
||||
const cameraDialogVisible = ref(false);
|
||||
const cameraRef = ref<InstanceType<typeof WebRtcCamera> | null>(null);
|
||||
const currentCameraField = ref<'generalImage' | 'generalManual'>('generalImage');
|
||||
|
||||
|
||||
@ -739,29 +747,39 @@ const handlePreviewPicture = (uploadFile: any) => {
|
||||
|
||||
const triggerCamera = (field: 'generalImage' | 'generalManual') => {
|
||||
currentCameraField.value = field;
|
||||
if (cameraInputRef.value) cameraInputRef.value.click()
|
||||
cameraDialogVisible.value = true;
|
||||
}
|
||||
|
||||
const handleCameraFile = async (event: Event) => {
|
||||
const input = event.target as HTMLInputElement
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0]
|
||||
if (!beforeAvatarUpload(file)) { input.value = ''; return }
|
||||
const formData = new FormData(); formData.append('file', file)
|
||||
const loadingMsg = ElMessage.loading({ message: '照片上传中...', duration: 0 })
|
||||
try {
|
||||
const res: any = await uploadFile(formData)
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url;
|
||||
const field = currentCameraField.value
|
||||
form.value[field].push(newUrl)
|
||||
if (field === 'generalImage') fileListImage.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
else fileListManual.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
ElMessage.success('拍照上传成功')
|
||||
} else { ElMessage.error(res.msg || '上传失败') }
|
||||
} catch (e) { ElMessage.error('网络错误,上传失败') } finally { loadingMsg.close(); input.value = '' }
|
||||
const handleCameraConfirm = async (file: File) => {
|
||||
if (!beforeAvatarUpload(file)) {
|
||||
cameraDialogVisible.value = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const loadingMsg = ElMessage.loading({ message: '照片上传中...', duration: 0 });
|
||||
try {
|
||||
const res: any = await uploadFile(formData);
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url;
|
||||
const field = currentCameraField.value;
|
||||
form.value[field].push(newUrl);
|
||||
if (field === 'generalImage') {
|
||||
fileListImage.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
} else if (field === 'generalManual') {
|
||||
fileListManual.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
}
|
||||
ElMessage.success('拍照上传成功');
|
||||
} else {
|
||||
ElMessage.error(res.msg || '上传失败');
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('网络错误,上传失败');
|
||||
} finally {
|
||||
loadingMsg.close();
|
||||
cameraDialogVisible.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
@ -806,4 +824,4 @@ onMounted(() => {
|
||||
/* 表格缩略图样式 */
|
||||
.file-preview-cell { display: flex; align-items: center; justify-content: center; position: relative; }
|
||||
.more-badge { position: absolute; top: -5px; right: -5px; background: #909399; color: #fff; border-radius: 10px; padding: 0 4px; font-size: 10px; transform: scale(0.9); }
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -374,9 +374,15 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<input type="file" ref="cameraInputRef" accept="image/*" capture="environment" style="display: none" @change="handleCameraFile"/>
|
||||
|
||||
<el-dialog v-model="dialogVisibleImage" append-to-body width="50%"><img style="width: 100%" :src="dialogImageUrl" alt="Preview Image" /></el-dialog>
|
||||
<el-dialog v-model="cameraDialogVisible" title="拍照上传" width="500px" append-to-body destroy-on-close>
|
||||
<WebRtcCamera
|
||||
ref="cameraRef"
|
||||
@confirm="handleCameraConfirm"
|
||||
@cancel="cameraDialogVisible = false"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="printVisible" title="标签打印预览" width="400px" destroy-on-close append-to-body>
|
||||
<div style="text-align: center;">
|
||||
@ -408,6 +414,7 @@ import {
|
||||
deleteFile
|
||||
} from '@/api/inbound/buy'
|
||||
import {getLabelPreview, executePrint} from '@/api/common/print'
|
||||
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue'
|
||||
|
||||
// ------------------------------------
|
||||
// 状态与变量
|
||||
@ -779,25 +786,36 @@ const handleRemoveImage = async (uploadFile: any, targetField: 'arrival_photo' |
|
||||
}
|
||||
const handlePreviewPicture = (uploadFile: any) => { dialogImageUrl.value = uploadFile.url!; dialogVisibleImage.value = true }
|
||||
const triggerCamera = (field: 'arrival_photo' | 'inspection_report') => { currentCameraField.value = field; if (cameraInputRef.value) cameraInputRef.value.click() }
|
||||
const handleCameraFile = async (event: Event) => {
|
||||
const input = event.target as HTMLInputElement
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0]
|
||||
if (!beforeAvatarUpload(file)) { input.value = ''; return }
|
||||
const formData = new FormData(); formData.append('file', file)
|
||||
const loadingMsg = ElMessage.loading({ message: '照片上传中...', duration: 0 })
|
||||
try {
|
||||
const res: any = await uploadFile(formData)
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url; const field = currentCameraField.value
|
||||
form[field].push(newUrl)
|
||||
if (field === 'arrival_photo') arrivalFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
else reportFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
ElMessage.success('拍照上传成功')
|
||||
} else { ElMessage.error(res.msg || '上传失败') }
|
||||
} catch (e) { ElMessage.error('网络错误,上传失败') } finally { loadingMsg.close(); input.value = '' }
|
||||
const handleCameraConfirm = async (file: File) => {
|
||||
if (!beforeAvatarUpload(file)) {
|
||||
cameraDialogVisible.value = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const loadingMsg = ElMessage.loading({ message: '照片上传中...', duration: 0 });
|
||||
try {
|
||||
const res: any = await uploadFile(formData);
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url;
|
||||
const field = currentCameraField.value;
|
||||
form[field].push(newUrl);
|
||||
if (field === 'arrival_photo') {
|
||||
arrivalFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
} else if (field === 'inspection_report') {
|
||||
reportFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
}
|
||||
ElMessage.success('拍照上传成功');
|
||||
} else {
|
||||
ElMessage.error(res.msg || '上传失败');
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('网络错误,上传失败');
|
||||
} finally {
|
||||
loadingMsg.close();
|
||||
cameraDialogVisible.value = false;
|
||||
}
|
||||
};
|
||||
const handleDelete = async (row: any) => { try { await deleteBuyInbound(row.id); ElMessage.success('删除成功'); fetchData() } catch (e) { ElMessage.error('删除失败') } }
|
||||
const handlePrint = async (row: any) => {
|
||||
printVisible.value = true; printLoading.value = true; previewUrl.value = ''
|
||||
@ -872,4 +890,4 @@ onMounted(() => fetchData())
|
||||
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
||||
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
||||
.camera-card .el-icon { font-size: 24px; }
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -317,11 +317,17 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<input type="file" ref="cameraInputRef" accept="image/*" capture="environment" style="display: none" @change="handleCameraFile" />
|
||||
|
||||
<el-dialog v-model="dialogVisibleImage" append-to-body width="50%">
|
||||
<img style="width: 100%" :src="dialogImageUrl" alt="Preview Image" />
|
||||
</el-dialog>
|
||||
<el-dialog v-model="cameraDialogVisible" title="拍照上传" width="500px" append-to-body destroy-on-close>
|
||||
<WebRtcCamera
|
||||
ref="cameraRef"
|
||||
@confirm="handleCameraConfirm"
|
||||
@cancel="cameraDialogVisible = false"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="printVisible" title="标签打印预览" width="400px" destroy-on-close append-to-body>
|
||||
<div style="text-align: center;">
|
||||
@ -351,6 +357,7 @@ import { ElMessage } from 'element-plus'
|
||||
import dayjs from 'dayjs'
|
||||
import { getProductList, createProductInbound, updateProductInbound, deleteProductInbound, searchMaterialBase } from '@/api/inbound/product'
|
||||
import { uploadFile, deleteFile } from '@/api/inbound/buy'
|
||||
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue'
|
||||
import { getLabelPreview, executePrint } from '@/api/common/print'
|
||||
|
||||
const loading = ref(false)
|
||||
@ -379,7 +386,8 @@ const productPhotoList = ref<any[]>([]) // 成品实拍
|
||||
const qualityFileList = ref<any[]>([]) // 质量报告
|
||||
const inspectionFileList = ref<any[]>([]) // 检测报告
|
||||
|
||||
const cameraInputRef = ref<HTMLInputElement | null>(null)
|
||||
const cameraDialogVisible = ref(false)
|
||||
const cameraRef = ref<InstanceType<typeof WebRtcCamera> | null>(null)
|
||||
const currentCameraField = ref<'product_photo' | 'quality_report_link' | 'inspection_report_link'>('product_photo')
|
||||
const quality_url = ref('')
|
||||
const inspection_url = ref('')
|
||||
@ -558,23 +566,42 @@ const handleRemoveImage = async (uploadFile: any, targetField: 'product_photo' |
|
||||
ElMessage.success('已删除')
|
||||
} catch (e) { console.error(e) }
|
||||
}
|
||||
const triggerCamera = (field: any) => { currentCameraField.value = field; if (cameraInputRef.value) cameraInputRef.value.click() }
|
||||
const handleCameraFile = async (event: Event) => {
|
||||
const input = event.target as HTMLInputElement; if (input.files && input.files[0]) {
|
||||
const file = input.files[0]; if (!beforeAvatarUpload(file)) { input.value = ''; return }
|
||||
const formData = new FormData(); formData.append('file', file); const loadingMsg = ElMessage.loading({ message: '上传中...', duration: 0 })
|
||||
try {
|
||||
const res: any = await uploadFile(formData)
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url; const field = currentCameraField.value; form[field].push(newUrl)
|
||||
if (field === 'product_photo') productPhotoList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
else if (field === 'quality_report_link') qualityFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
else inspectionFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
ElMessage.success('拍照上传成功')
|
||||
} else { ElMessage.error(res.msg || '上传失败') }
|
||||
} catch (e) { ElMessage.error('网络错误') } finally { loadingMsg.close(); input.value = '' }
|
||||
}
|
||||
const triggerCamera = (field: any) => {
|
||||
currentCameraField.value = field;
|
||||
cameraDialogVisible.value = true;
|
||||
}
|
||||
const handleCameraConfirm = async (file: File) => {
|
||||
if (!beforeAvatarUpload(file)) {
|
||||
cameraDialogVisible.value = false;
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const loadingMsg = ElMessage.loading({ message: '上传中...', duration: 0 });
|
||||
try {
|
||||
const res: any = await uploadFile(formData);
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url;
|
||||
const field = currentCameraField.value;
|
||||
form[field].push(newUrl);
|
||||
if (field === 'product_photo') {
|
||||
productPhotoList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
} else if (field === 'quality_report_link') {
|
||||
qualityFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
} else if (field === 'inspection_report_link') {
|
||||
inspectionFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
}
|
||||
ElMessage.success('拍照上传成功');
|
||||
} else {
|
||||
ElMessage.error(res.msg || '上传失败');
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('网络错误');
|
||||
} finally {
|
||||
loadingMsg.close();
|
||||
cameraDialogVisible.value = false;
|
||||
}
|
||||
};
|
||||
const handlePreviewPicture = (uploadFile: any) => { dialogImageUrl.value = uploadFile.url!; dialogVisibleImage.value = true }
|
||||
|
||||
const submitForm = async () => {
|
||||
@ -664,4 +691,4 @@ onMounted(() => fetchData())
|
||||
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
||||
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
||||
.camera-card .el-icon { font-size: 24px; }
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -409,8 +409,14 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<input type="file" ref="cameraInputRef" accept="image/*" capture="environment" style="display: none" @change="handleCameraFile"/>
|
||||
<el-dialog v-model="dialogVisibleImage" append-to-body width="50%"><img style="width: 100%" :src="dialogImageUrl" alt="Preview Image" /></el-dialog>
|
||||
<el-dialog v-model="cameraDialogVisible" title="拍照上传" width="500px" append-to-body destroy-on-close>
|
||||
<WebRtcCamera
|
||||
ref="cameraRef"
|
||||
@confirm="handleCameraConfirm"
|
||||
@cancel="cameraDialogVisible = false"
|
||||
/>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="printVisible" title="标签打印预览" width="400px" destroy-on-close append-to-body>
|
||||
<div style="text-align: center;">
|
||||
<div v-loading="printLoading" class="preview-box">
|
||||
@ -439,6 +445,7 @@ import {
|
||||
searchMaterialBase
|
||||
} from '@/api/inbound/semi'
|
||||
import { uploadFile, deleteFile } from '@/api/inbound/buy'
|
||||
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue'
|
||||
import {getLabelPreview, executePrint} from '@/api/common/print'
|
||||
|
||||
// ------------------------------------
|
||||
@ -467,7 +474,8 @@ const dialogImageUrl = ref('')
|
||||
const dialogVisibleImage = ref(false)
|
||||
const arrivalFileList = ref<any[]>([])
|
||||
const reportFileList = ref<any[]>([])
|
||||
const cameraInputRef = ref<HTMLInputElement | null>(null)
|
||||
const cameraDialogVisible = ref(false)
|
||||
const cameraRef = ref<InstanceType<typeof WebRtcCamera> | null>(null)
|
||||
const currentCameraField = ref<'arrival_photo' | 'quality_report_link'>('arrival_photo')
|
||||
const quality_report_url = ref('')
|
||||
|
||||
@ -734,26 +742,40 @@ const handleRemoveImage = async (uploadFile: any, targetField: 'arrival_photo' |
|
||||
} catch (e) { console.error(e) }
|
||||
}
|
||||
const handlePreviewPicture = (uploadFile: any) => { dialogImageUrl.value = uploadFile.url!; dialogVisibleImage.value = true }
|
||||
const triggerCamera = (field: 'arrival_photo' | 'quality_report_link') => { currentCameraField.value = field; if (cameraInputRef.value) cameraInputRef.value.click() }
|
||||
const handleCameraFile = async (event: Event) => {
|
||||
const input = event.target as HTMLInputElement
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0]
|
||||
if (!beforeAvatarUpload(file)) { input.value = ''; return }
|
||||
const formData = new FormData(); formData.append('file', file)
|
||||
const loadingMsg = ElMessage.loading({ message: '照片上传中...', duration: 0 })
|
||||
try {
|
||||
const res: any = await uploadFile(formData)
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url; const field = currentCameraField.value
|
||||
form[field].push(newUrl)
|
||||
if (field === 'arrival_photo') arrivalFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
else reportFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) })
|
||||
ElMessage.success('拍照上传成功')
|
||||
} else { ElMessage.error(res.msg || '上传失败') }
|
||||
} catch (e) { ElMessage.error('网络错误,上传失败') } finally { loadingMsg.close(); input.value = '' }
|
||||
}
|
||||
const triggerCamera = (field: 'arrival_photo' | 'quality_report_link') => {
|
||||
currentCameraField.value = field;
|
||||
cameraDialogVisible.value = true;
|
||||
}
|
||||
const handleCameraConfirm = async (file: File) => {
|
||||
if (!beforeAvatarUpload(file)) {
|
||||
cameraDialogVisible.value = false;
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const loadingMsg = ElMessage.loading({ message: '照片上传中...', duration: 0 });
|
||||
try {
|
||||
const res: any = await uploadFile(formData);
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url;
|
||||
const field = currentCameraField.value;
|
||||
form[field].push(newUrl);
|
||||
if (field === 'arrival_photo') {
|
||||
arrivalFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
} else if (field === 'quality_report_link') {
|
||||
reportFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
}
|
||||
ElMessage.success('拍照上传成功');
|
||||
} else {
|
||||
ElMessage.error(res.msg || '上传失败');
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('网络错误,上传失败');
|
||||
} finally {
|
||||
loadingMsg.close();
|
||||
cameraDialogVisible.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const submitForm = async () => {
|
||||
if (!formRef.value) return
|
||||
@ -853,4 +875,4 @@ onMounted(() => fetchData())
|
||||
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
||||
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
||||
.camera-card .el-icon { font-size: 24px; }
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user