修改拍照的大小以及增加放大缩小编辑等功能
This commit is contained in:
@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"axios": "^1.13.3",
|
||||
"cropperjs": "^1.6.2",
|
||||
"element-plus": "^2.13.1",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
"jspdf": "^2.5.1",
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div class="camera-container">
|
||||
<div class="camera-container is-fullscreen">
|
||||
<div v-if="error" class="error-message">
|
||||
{{ error }}
|
||||
<el-button style="margin-top: 20px" @click="handleCancel">关闭</el-button>
|
||||
</div>
|
||||
|
||||
<div v-else class="media-box">
|
||||
@ -10,60 +11,134 @@
|
||||
ref="videoRef"
|
||||
autoplay
|
||||
playsinline
|
||||
class="media-content"
|
||||
class="media-content video-feed"
|
||||
:style="{ transform: `scale(${cameraZoom})` }"
|
||||
></video>
|
||||
|
||||
<img
|
||||
v-if="imgSrc"
|
||||
:src="imgSrc"
|
||||
class="media-content preview-img"
|
||||
alt="Photo Preview"
|
||||
/>
|
||||
<div v-show="imgSrc" class="editor-container">
|
||||
<img
|
||||
ref="previewImgRef"
|
||||
:src="imgSrc"
|
||||
class="media-content preview-img"
|
||||
alt="Photo Preview"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<canvas ref="canvasRef" style="display: none;"></canvas>
|
||||
</div>
|
||||
|
||||
<div v-if="!imgSrc && isCameraReady" class="zoom-slider-container">
|
||||
<el-icon class="zoom-icon"><Remove /></el-icon>
|
||||
<el-slider
|
||||
v-model="cameraZoom"
|
||||
:min="1"
|
||||
:max="3"
|
||||
:step="0.1"
|
||||
:show-tooltip="false"
|
||||
class="custom-slider"
|
||||
/>
|
||||
<el-icon class="zoom-icon"><CirclePlus /></el-icon>
|
||||
</div>
|
||||
|
||||
<div class="camera-actions">
|
||||
|
||||
<template v-if="!imgSrc">
|
||||
<el-button @click="handleCancel" size="large">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="capture"
|
||||
size="large"
|
||||
:disabled="!isCameraReady"
|
||||
>
|
||||
<el-icon class="el-icon--left"><Camera /></el-icon>
|
||||
拍照
|
||||
<el-button circle size="large" @click="handleCancel" class="action-btn icon-btn">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
circle
|
||||
type="danger"
|
||||
size="large"
|
||||
@click="capture"
|
||||
:disabled="!isCameraReady"
|
||||
class="shutter-btn"
|
||||
>
|
||||
<div class="shutter-inner"></div>
|
||||
</el-button>
|
||||
|
||||
<div class="placeholder-btn"></div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-button @click="retake" size="large" type="warning" plain>
|
||||
<el-icon class="el-icon--left"><RefreshLeft /></el-icon>
|
||||
重拍
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
@click="confirmUse"
|
||||
size="large"
|
||||
>
|
||||
<el-icon class="el-icon--left"><Check /></el-icon>
|
||||
确认使用
|
||||
</el-button>
|
||||
|
||||
<div v-if="isEditing" class="edit-mode-bar">
|
||||
<div class="edit-tools">
|
||||
<el-tooltip content="切换移动图片/调整裁剪框" placement="top" :show-after="1000">
|
||||
<el-button
|
||||
circle
|
||||
@click="toggleDragMode"
|
||||
class="tool-btn"
|
||||
:class="{ 'is-active': isMoveMode }"
|
||||
>
|
||||
<el-icon><Rank /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-button circle @click="zoomCropper(0.1)" class="tool-btn"><el-icon><ZoomIn /></el-icon></el-button>
|
||||
<el-button circle @click="zoomCropper(-0.1)" class="tool-btn"><el-icon><ZoomOut /></el-icon></el-button>
|
||||
|
||||
<el-button circle @click="rotateLeft" class="tool-btn"><el-icon><RefreshLeft /></el-icon></el-button>
|
||||
<el-button circle @click="rotateRight" class="tool-btn"><el-icon><RefreshRight /></el-icon></el-button>
|
||||
<el-button circle @click="resetCrop" class="tool-btn"><el-icon><Refresh /></el-icon></el-button>
|
||||
</div>
|
||||
|
||||
<div class="edit-confirm">
|
||||
<el-button circle size="large" @click="handleCancel" class="action-btn icon-btn">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<el-button @click="stopEdit" class="text-btn">取消编辑</el-button>
|
||||
<el-button type="success" @click="confirmUse" class="confirm-btn">
|
||||
完成并上传
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="preview-mode-bar">
|
||||
<el-button circle size="large" @click="handleCancel" class="action-btn icon-btn">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<el-button @click="retake" size="large" class="text-btn" style="min-width: 80px;">重拍</el-button>
|
||||
|
||||
<el-button @click="startEdit" size="large" class="text-btn" style="min-width: 80px;">
|
||||
<el-icon style="margin-right: 4px"><Edit /></el-icon>编辑
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
type="success"
|
||||
@click="confirmUse"
|
||||
size="large"
|
||||
class="confirm-btn"
|
||||
>
|
||||
确认使用 <el-icon class="el-icon--right"><Check /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Camera, RefreshLeft, Check } from '@element-plus/icons-vue'
|
||||
import {
|
||||
Camera, RefreshLeft, RefreshRight, Check, Close, Refresh, Edit,
|
||||
ZoomIn, ZoomOut, Remove, CirclePlus, Rank
|
||||
} from '@element-plus/icons-vue'
|
||||
import Cropper from 'cropperjs'
|
||||
import 'cropperjs/dist/cropper.css'
|
||||
|
||||
const emit = defineEmits(['photo-submit', 'cancel'])
|
||||
|
||||
const videoRef = ref<HTMLVideoElement>()
|
||||
const canvasRef = ref<HTMLCanvasElement>()
|
||||
const previewImgRef = ref<HTMLImageElement>()
|
||||
const mediaStream = ref<MediaStream>()
|
||||
const error = ref('')
|
||||
const isCameraReady = ref(false)
|
||||
@ -71,11 +146,18 @@ const isCameraReady = ref(false)
|
||||
const imgSrc = ref('')
|
||||
const currentFile = ref<File | null>(null)
|
||||
|
||||
const isEditing = ref(false)
|
||||
const isMoveMode = ref(false)
|
||||
const cameraZoom = ref(1) // 控制拍摄时的变焦倍数
|
||||
let cropper: Cropper | null = null
|
||||
|
||||
const startCamera = async () => {
|
||||
stopCamera()
|
||||
error.value = ''
|
||||
imgSrc.value = ''
|
||||
isEditing.value = false
|
||||
currentFile.value = null
|
||||
cameraZoom.value = 1
|
||||
|
||||
try {
|
||||
const constraints = {
|
||||
@ -96,7 +178,7 @@ const startCamera = async () => {
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
|
||||
error.value = '无法访问摄像头: 浏览器限制非 HTTPS 环境调用摄像头,请使用 localhost 或 配置 HTTPS。'
|
||||
error.value = '无法访问摄像头: 请使用 HTTPS 环境。'
|
||||
} else {
|
||||
error.value = '无法访问摄像头: ' + (err.message || '请检查权限')
|
||||
}
|
||||
@ -115,126 +197,357 @@ const stopCamera = () => {
|
||||
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
|
||||
// 1. 获取视频原始尺寸
|
||||
const vW = video.videoWidth
|
||||
const vH = video.videoHeight
|
||||
if (vW === 0 || vH === 0) return
|
||||
|
||||
if (width === 0 || height === 0) return
|
||||
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
// 2. 设置画布为全尺寸(保持清晰度)
|
||||
canvas.width = vW
|
||||
canvas.height = vH
|
||||
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
ctx.drawImage(video, 0, 0, width, height)
|
||||
// 3. 计算基于 zoomLevel 的裁剪区域
|
||||
// zoom = 1: 裁剪宽 = vW
|
||||
// zoom = 2: 裁剪宽 = vW / 2
|
||||
const zoom = cameraZoom.value
|
||||
const cropW = vW / zoom
|
||||
const cropH = vH / zoom
|
||||
|
||||
// 4. 计算裁剪的起始点 (居中裁剪)
|
||||
const cropX = (vW - cropW) / 2
|
||||
const cropY = (vH - cropH) / 2
|
||||
|
||||
// 5. 将裁剪区域绘制到全尺寸画布上 (drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh))
|
||||
ctx.drawImage(
|
||||
video,
|
||||
cropX, cropY, cropW, cropH, // 源:截取中心部分
|
||||
0, 0, vW, vH // 目标:铺满整个画布
|
||||
)
|
||||
|
||||
canvas.toBlob((blob) => {
|
||||
if (!blob) {
|
||||
ElMessage.error('照片生成失败,请重试')
|
||||
ElMessage.error('拍照失败,请重试')
|
||||
return
|
||||
}
|
||||
|
||||
const timestamp = new Date().getTime()
|
||||
const filename = `photo_${timestamp}.jpg`
|
||||
const file = new File([blob], filename, { type: 'image/jpeg' })
|
||||
currentFile.value = new File([blob], filename, { type: 'image/jpeg' })
|
||||
|
||||
currentFile.value = file
|
||||
imgSrc.value = URL.createObjectURL(blob)
|
||||
console.log('✅ 照片已捕获:', filename, (file.size / 1024).toFixed(2) + 'KB')
|
||||
stopCamera()
|
||||
}, 'image/jpeg', 0.95)
|
||||
}
|
||||
|
||||
const retake = () => {
|
||||
if (imgSrc.value) {
|
||||
URL.revokeObjectURL(imgSrc.value)
|
||||
}
|
||||
destroyCropper()
|
||||
isEditing.value = false
|
||||
if (imgSrc.value) URL.revokeObjectURL(imgSrc.value)
|
||||
imgSrc.value = ''
|
||||
currentFile.value = null
|
||||
startCamera()
|
||||
}
|
||||
|
||||
if (!mediaStream.value || !mediaStream.value.active) {
|
||||
startCamera()
|
||||
const startEdit = () => {
|
||||
if (!imgSrc.value || !previewImgRef.value) return
|
||||
isEditing.value = true
|
||||
isMoveMode.value = false
|
||||
|
||||
nextTick(() => {
|
||||
if (cropper) cropper.destroy()
|
||||
|
||||
cropper = new Cropper(previewImgRef.value!, {
|
||||
viewMode: 1,
|
||||
dragMode: 'none',
|
||||
autoCropArea: 0.8,
|
||||
background: false,
|
||||
modal: true,
|
||||
guides: true,
|
||||
highlight: false,
|
||||
cropBoxMovable: true,
|
||||
cropBoxResizable: true,
|
||||
toggleDragModeOnDblclick: false,
|
||||
movable: true,
|
||||
zoomable: true,
|
||||
rotatable: true,
|
||||
scalable: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const toggleDragMode = () => {
|
||||
if (!cropper) return
|
||||
isMoveMode.value = !isMoveMode.value
|
||||
cropper.setDragMode(isMoveMode.value ? 'move' : 'none')
|
||||
}
|
||||
|
||||
const stopEdit = () => {
|
||||
destroyCropper()
|
||||
isEditing.value = false
|
||||
}
|
||||
|
||||
const destroyCropper = () => {
|
||||
if (cropper) {
|
||||
cropper.destroy()
|
||||
cropper = null
|
||||
}
|
||||
}
|
||||
|
||||
const rotateLeft = () => cropper?.rotate(-90)
|
||||
const rotateRight = () => cropper?.rotate(90)
|
||||
const resetCrop = () => {
|
||||
cropper?.reset()
|
||||
isMoveMode.value = false
|
||||
cropper?.setDragMode('none')
|
||||
}
|
||||
const zoomCropper = (ratio: number) => cropper?.zoom(ratio)
|
||||
|
||||
const confirmUse = () => {
|
||||
console.log('👆 点击了确认使用')
|
||||
if (currentFile.value) {
|
||||
try {
|
||||
console.log('📤 发送 photo-submit 事件', currentFile.value)
|
||||
emit('photo-submit', currentFile.value)
|
||||
} catch (err) {
|
||||
console.error('父组件处理事件失败:', err)
|
||||
console.log('👆 确认使用')
|
||||
|
||||
if (isEditing.value && cropper) {
|
||||
const croppedCanvas = cropper.getCroppedCanvas({
|
||||
imageSmoothingQuality: 'high'
|
||||
})
|
||||
|
||||
if (!croppedCanvas) {
|
||||
ElMessage.error('图片处理失败')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning('照片数据未就绪,请稍后或重拍')
|
||||
|
||||
croppedCanvas.toBlob((blob) => {
|
||||
if (!blob) {
|
||||
ElMessage.error('文件生成失败')
|
||||
return
|
||||
}
|
||||
const timestamp = new Date().getTime()
|
||||
const filename = `photo_crop_${timestamp}.jpg`
|
||||
const file = new File([blob], filename, { type: 'image/jpeg' })
|
||||
|
||||
emitFile(file)
|
||||
}, 'image/jpeg', 0.9)
|
||||
}
|
||||
else if (currentFile.value) {
|
||||
emitFile(currentFile.value)
|
||||
}
|
||||
else {
|
||||
ElMessage.warning('没有可用的照片')
|
||||
}
|
||||
}
|
||||
|
||||
const emitFile = (file: File) => {
|
||||
try {
|
||||
console.log('📤 提交文件:', file.name, (file.size/1024).toFixed(1)+'KB')
|
||||
emit('photo-submit', file)
|
||||
} catch (err) {
|
||||
console.error('父组件处理事件失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
destroyCropper()
|
||||
stopCamera()
|
||||
emit('cancel')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
startCamera()
|
||||
})
|
||||
|
||||
onMounted(() => startCamera())
|
||||
onBeforeUnmount(() => {
|
||||
destroyCropper()
|
||||
stopCamera()
|
||||
if (imgSrc.value) {
|
||||
URL.revokeObjectURL(imgSrc.value)
|
||||
}
|
||||
if (imgSrc.value) URL.revokeObjectURL(imgSrc.value)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
startCamera,
|
||||
stopCamera
|
||||
})
|
||||
defineExpose({ startCamera, stopCamera })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.camera-container {
|
||||
.camera-container.is-fullscreen {
|
||||
position: fixed;
|
||||
top: 0; left: 0; width: 100vw; height: 100vh;
|
||||
background-color: #000;
|
||||
z-index: 9999;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #fff;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 媒体显示区 */
|
||||
.media-box {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
height: 400px;
|
||||
background: #000;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.media-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
object-fit: cover;
|
||||
transition: transform 0.2s ease-out; /* 变焦平滑动画 */
|
||||
transform-origin: center center;
|
||||
}
|
||||
.error-message {
|
||||
color: #f56c6c;
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
background: #fef0f0;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
.camera-actions {
|
||||
margin-top: 20px;
|
||||
|
||||
.editor-container { width: 100%; height: 100%; }
|
||||
.preview-img { display: block; max-width: 100%; max-height: 100%; }
|
||||
|
||||
/* 变焦滑块 */
|
||||
.zoom-slider-container {
|
||||
position: absolute;
|
||||
bottom: 150px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 70%;
|
||||
max-width: 300px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
z-index: 20;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 5px 15px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.zoom-icon { color: #fff; font-size: 18px; }
|
||||
.custom-slider { flex: 1; }
|
||||
:deep(.el-slider__runway) { background-color: #555; }
|
||||
:deep(.el-slider__bar) { background-color: #fff; }
|
||||
:deep(.el-slider__button) { border-color: #fff; }
|
||||
|
||||
/* 底部操作栏 */
|
||||
.camera-actions {
|
||||
height: 140px;
|
||||
width: 100%;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-bottom: 10px;
|
||||
position: relative;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
/* 拍照按钮布局 */
|
||||
.camera-actions:has(.shutter-btn) {
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.placeholder-btn { width: 40px; }
|
||||
|
||||
/* 按钮样式 */
|
||||
.action-btn { background: rgba(255, 255, 255, 0.15); border: none; color: #fff; }
|
||||
|
||||
.text-btn {
|
||||
color: #fff;
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
font-size: 14px;
|
||||
}
|
||||
.confirm-btn { min-width: 120px; font-weight: bold; }
|
||||
|
||||
/* 快门按钮 */
|
||||
.shutter-btn {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border: 4px solid #fff;
|
||||
background: transparent !important;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.shutter-inner {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
.shutter-btn:active .shutter-inner {
|
||||
transform: scale(0.9);
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
/* 预览模式操作栏 */
|
||||
.preview-mode-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 编辑模式操作栏 */
|
||||
.edit-mode-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
.edit-tools {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.tool-btn {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.tool-btn.is-active {
|
||||
background-color: #409EFF;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.edit-confirm {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Cropper 样式定制 */
|
||||
:deep(.cropper-view-box) {
|
||||
outline: 3px solid #409EFF;
|
||||
outline-color: #409EFF;
|
||||
}
|
||||
:deep(.cropper-point) {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #409EFF;
|
||||
opacity: 0.9;
|
||||
}
|
||||
:deep(.cropper-line) {
|
||||
background-color: rgba(64, 158, 255, 0.5);
|
||||
}
|
||||
</style>
|
||||
@ -353,7 +353,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { Plus, Setting, Refresh, Search, Box, House, Link, InfoFilled, Printer, Camera, Picture } from '@element-plus/icons-vue'
|
||||
// 修复:引入 ElLoading
|
||||
// 修复点:引入 ElLoading
|
||||
import { ElMessage, ElLoading } from 'element-plus'
|
||||
import dayjs from 'dayjs'
|
||||
import { getProductList, createProductInbound, updateProductInbound, deleteProductInbound, searchMaterialBase } from '@/api/inbound/product'
|
||||
@ -389,6 +389,7 @@ const inspectionFileList = ref<any[]>([]) // 检测报告
|
||||
|
||||
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('')
|
||||
@ -573,6 +574,10 @@ const triggerCamera = (field: any) => {
|
||||
currentCameraField.value = field;
|
||||
cameraDialogVisible.value = true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 修复核心:拍照上传回调逻辑
|
||||
// ------------------------------------------------------------------------
|
||||
const handleCameraConfirm = async (file: File) => {
|
||||
if (!beforeAvatarUpload(file)) {
|
||||
cameraDialogVisible.value = false;
|
||||
@ -581,23 +586,33 @@ const handleCameraConfirm = async (file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
// 修复点:使用 ElLoading
|
||||
const loadingMsg = ElLoading.service({ text: '照片上传中...', background: 'rgba(0, 0, 0, 0.7)' });
|
||||
// 使用 ElLoading.service 替代报错的 ElMessage.loading
|
||||
const loadingMsg = ElLoading.service({
|
||||
lock: true,
|
||||
text: '照片处理中...',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
});
|
||||
|
||||
let success = false;
|
||||
try {
|
||||
const res: any = await uploadFile(formData);
|
||||
if (res.code === 200) {
|
||||
const newUrl = res.data.url;
|
||||
const field = currentCameraField.value;
|
||||
const field = currentCameraField.value; // 根据触发时记录的字段
|
||||
|
||||
// 添加到表单数据
|
||||
form[field].push(newUrl);
|
||||
|
||||
// 更新对应的显示列表
|
||||
const previewItem = { name: newUrl.split('/').pop(), url: getImageUrl(newUrl) };
|
||||
if (field === 'product_photo') {
|
||||
productPhotoList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
productPhotoList.value.push(previewItem);
|
||||
} else if (field === 'quality_report_link') {
|
||||
qualityFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
qualityFileList.value.push(previewItem);
|
||||
} else if (field === 'inspection_report_link') {
|
||||
inspectionFileList.value.push({ name: newUrl.split('/').pop(), url: getImageUrl(newUrl) });
|
||||
inspectionFileList.value.push(previewItem);
|
||||
}
|
||||
|
||||
ElMessage.success('拍照上传成功');
|
||||
success = true;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user