|
|
@@ -8,7 +8,7 @@
|
|
|
@change="handleFileSelect"
|
|
|
/>
|
|
|
|
|
|
- <div class="combined-upload-btn">
|
|
|
+ <div class="combined-upload-btn" :title="resolutionHint || ''">
|
|
|
<span class="upload-part" @click="triggerFileSelect" :title="'上传参考图'">
|
|
|
上传参考图
|
|
|
<el-icon><Picture /></el-icon>
|
|
|
@@ -17,6 +17,7 @@
|
|
|
<span class="camera-part" @click="openCamera" :title="'使用相机拍照'">
|
|
|
<el-icon><Camera /></el-icon>
|
|
|
</span>
|
|
|
+ <div v-if="resolutionHint" class="resolution-hint">{{ resolutionHint }}</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 预览图显示区域 - 使用el-image组件 -->
|
|
|
@@ -94,11 +95,36 @@ import { Message } from '@/utils/message/Message.js';
|
|
|
// 导入axios用于文件上传
|
|
|
import axios from 'axios';
|
|
|
|
|
|
-// 定义props(保留,但在内部写死默认值)
|
|
|
+// 定义props
|
|
|
const props = defineProps({
|
|
|
modelValue: {
|
|
|
type: String,
|
|
|
default: ''
|
|
|
+ },
|
|
|
+ // 最小宽度(像素),0表示不限制
|
|
|
+ minWidth: {
|
|
|
+ type: Number,
|
|
|
+ default: 0
|
|
|
+ },
|
|
|
+ // 最小高度(像素),0表示不限制
|
|
|
+ minHeight: {
|
|
|
+ type: Number,
|
|
|
+ default: 0
|
|
|
+ },
|
|
|
+ // 最大宽度(像素),0表示不限制
|
|
|
+ maxWidth: {
|
|
|
+ type: Number,
|
|
|
+ default: 0
|
|
|
+ },
|
|
|
+ // 最大高度(像素),0表示不限制
|
|
|
+ maxHeight: {
|
|
|
+ type: Number,
|
|
|
+ default: 0
|
|
|
+ },
|
|
|
+ // 分辨率提示文字
|
|
|
+ resolutionHint: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -147,24 +173,64 @@ const triggerFileSelect = () => {
|
|
|
fileInput.value?.click();
|
|
|
};
|
|
|
|
|
|
+// 验证图片分辨率
|
|
|
+const validateImageResolution = (file) => {
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ if (!props.minWidth && !props.minHeight && !props.maxWidth && !props.maxHeight) {
|
|
|
+ resolve(true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const img = new Image();
|
|
|
+ const url = URL.createObjectURL(file);
|
|
|
+ img.onload = () => {
|
|
|
+ URL.revokeObjectURL(url);
|
|
|
+ const { width, height } = img;
|
|
|
+ if (props.minWidth && width < props.minWidth) {
|
|
|
+ Message().error(`图片宽度不能小于 ${props.minWidth}px(当前 ${width}px)`, true);
|
|
|
+ resolve(false); return;
|
|
|
+ }
|
|
|
+ if (props.minHeight && height < props.minHeight) {
|
|
|
+ Message().error(`图片高度不能小于 ${props.minHeight}px(当前 ${height}px)`, true);
|
|
|
+ resolve(false); return;
|
|
|
+ }
|
|
|
+ if (props.maxWidth && width > props.maxWidth) {
|
|
|
+ Message().error(`图片宽度不能大于 ${props.maxWidth}px(当前 ${width}px)`, true);
|
|
|
+ resolve(false); return;
|
|
|
+ }
|
|
|
+ if (props.maxHeight && height > props.maxHeight) {
|
|
|
+ Message().error(`图片高度不能大于 ${props.maxHeight}px(当前 ${height}px)`, true);
|
|
|
+ resolve(false); return;
|
|
|
+ }
|
|
|
+ resolve(true);
|
|
|
+ };
|
|
|
+ img.onerror = () => {
|
|
|
+ URL.revokeObjectURL(url);
|
|
|
+ Message().error('无法读取图片信息,请检查文件是否损坏', true);
|
|
|
+ resolve(false);
|
|
|
+ };
|
|
|
+ img.src = url;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
// 处理文件选择
|
|
|
const handleFileSelect = async (event) => {
|
|
|
const file = event.target.files[0];
|
|
|
if (!file) return;
|
|
|
|
|
|
- // 验证文件类型
|
|
|
if (!file.type.match('image.*')) {
|
|
|
Message().error('请选择有效的图片文件!', true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 验证文件大小
|
|
|
const maxSize = 50 * 1024 * 1024;
|
|
|
if (file.size > maxSize) {
|
|
|
Message().error('图片大小不能超过50MB!', true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ const valid = await validateImageResolution(file);
|
|
|
+ if (!valid) return;
|
|
|
+
|
|
|
await uploadFile(file);
|
|
|
};
|
|
|
|
|
|
@@ -247,14 +313,14 @@ const getCameraStream = async (facingMode) => {
|
|
|
// 获取相机容器的尺寸
|
|
|
const containerWidth = window.innerWidth * (isSmallScreen.value ? 0.9 : 0.8);
|
|
|
const containerHeight = window.innerHeight * (isSmallScreen.value ? 0.7 : 0.6);
|
|
|
-
|
|
|
+
|
|
|
// 定义相机视频的最大分辨率
|
|
|
const videoConstraints = {
|
|
|
width: { ideal: containerWidth, max: containerWidth * 1.2 },
|
|
|
height: { ideal: containerHeight, max: containerHeight * 1.2 },
|
|
|
frameRate: { ideal: 30, max: 60 }
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
// 尝试获取指定方向的摄像头
|
|
|
return await navigator.mediaDevices.getUserMedia({
|
|
|
video: { ...videoConstraints, facingMode }
|
|
|
@@ -283,15 +349,15 @@ const openCamera = async () => {
|
|
|
// 前摄像头也不可用,使用默认设置
|
|
|
const containerWidth = window.innerWidth * (isSmallScreen.value ? 0.9 : 0.8);
|
|
|
const containerHeight = window.innerHeight * (isSmallScreen.value ? 0.7 : 0.6);
|
|
|
-
|
|
|
+
|
|
|
const videoConstraints = {
|
|
|
width: { ideal: containerWidth, max: containerWidth * 1.2 },
|
|
|
height: { ideal: containerHeight, max: containerHeight * 1.2 },
|
|
|
frameRate: { ideal: 30, max: 60 }
|
|
|
};
|
|
|
-
|
|
|
- stream.value = await navigator.mediaDevices.getUserMedia({
|
|
|
- video: videoConstraints
|
|
|
+
|
|
|
+ stream.value = await navigator.mediaDevices.getUserMedia({
|
|
|
+ video: videoConstraints
|
|
|
});
|
|
|
currentCamera.value = 'default';
|
|
|
}
|
|
|
@@ -324,12 +390,12 @@ const switchCamera = async () => {
|
|
|
|
|
|
// 切换摄像头方向
|
|
|
const newCamera = currentCamera.value === 'environment' ? 'user' : 'environment';
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
// 尝试获取新的摄像头流
|
|
|
stream.value = await getCameraStream(newCamera);
|
|
|
currentCamera.value = newCamera;
|
|
|
-
|
|
|
+
|
|
|
// 更新视频源
|
|
|
if (videoRef.value) {
|
|
|
videoRef.value.srcObject = stream.value;
|
|
|
@@ -338,7 +404,7 @@ const switchCamera = async () => {
|
|
|
// 如果获取新摄像头失败,尝试切换回原来的摄像头
|
|
|
console.error(`切换到${newCamera === 'environment' ? '后' : '前'}摄像头失败:`, error);
|
|
|
Message().error(`无法切换到${newCamera === 'environment' ? '后' : '前'}摄像头,请检查设备是否支持!`, true);
|
|
|
-
|
|
|
+
|
|
|
// 尝试恢复原来的摄像头
|
|
|
try {
|
|
|
stream.value = await getCameraStream(currentCamera.value);
|
|
|
@@ -420,15 +486,14 @@ const retakePhoto = async () => {
|
|
|
const confirmAndUpload = async () => {
|
|
|
if (capturedImage.value) {
|
|
|
try {
|
|
|
- // 创建Blob对象
|
|
|
const response = await fetch(capturedImage.value);
|
|
|
const blob = await response.blob();
|
|
|
const file = new File([blob], `camera_photo_${Date.now()}.jpg`, { type: 'image/jpeg' });
|
|
|
|
|
|
- // 调用共用的上传函数
|
|
|
- await uploadFile(file);
|
|
|
+ const valid = await validateImageResolution(file);
|
|
|
+ if (!valid) return;
|
|
|
|
|
|
- // 关闭相机弹窗
|
|
|
+ await uploadFile(file);
|
|
|
closeCamera();
|
|
|
} catch (error) {
|
|
|
console.error('相机拍摄图片上传失败:', error);
|
|
|
@@ -450,6 +515,37 @@ const confirmAndUpload = async () => {
|
|
|
display: inline-block;
|
|
|
}
|
|
|
|
|
|
+.resolution-hint {
|
|
|
+ position: absolute;
|
|
|
+ bottom: calc(100% + 6px);
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ background: rgba(49, 37, 12, 0.92);
|
|
|
+ color: #fff7d8;
|
|
|
+ font-size: 11px;
|
|
|
+ padding: 3px 8px;
|
|
|
+ border-radius: 4px;
|
|
|
+ white-space: nowrap;
|
|
|
+ pointer-events: none;
|
|
|
+ z-index: 20;
|
|
|
+ opacity: 0;
|
|
|
+ transition: opacity 0.16s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.combined-upload-btn:hover .resolution-hint {
|
|
|
+ opacity: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.resolution-hint::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 100%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ border: 4px solid transparent;
|
|
|
+ border-top-color: rgba(49, 37, 12, 0.92);
|
|
|
+}
|
|
|
+
|
|
|
// 合并按钮样式
|
|
|
.combined-upload-btn {
|
|
|
display: inline-flex;
|
|
|
@@ -671,4 +767,4 @@ const confirmAndUpload = async () => {
|
|
|
border: none;
|
|
|
}
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|