2
This commit is contained in:
@@ -10,9 +10,25 @@
|
|||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="图片上传">
|
<uni-forms-item label="图片上传">
|
||||||
<view class="example-body">
|
<view class="example-body">
|
||||||
<uni-file-picker limit="3" :sourceType="sourceType" :value="img" title="最多选择3张图片"
|
<view class="custom-image-picker">
|
||||||
file-mediatype="image" @delete="deleteImg" @select="upload"
|
<!-- 图片预览区域 -->
|
||||||
:auto-upload="false"></uni-file-picker>
|
<view class="image-preview-container">
|
||||||
|
<view v-for="(item, index) in img" :key="index" class="image-preview-item">
|
||||||
|
<image :src="item.url" class="preview-image" mode="aspectFill"></image>
|
||||||
|
<view class="delete-btn" @click="deleteCustomImg(index)">
|
||||||
|
<text class="delete-icon">×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 添加图片按钮 -->
|
||||||
|
<view v-if="img.length < 3" class="add-image-btn" @click="chooseImage">
|
||||||
|
<text class="add-icon">+</text>
|
||||||
|
<text class="add-text">添加图片</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="image-tip">
|
||||||
|
<text>最多选择3张图片,支持自动压缩</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<!-- 备注 -->
|
<!-- 备注 -->
|
||||||
@@ -59,7 +75,6 @@
|
|||||||
remark: ""
|
remark: ""
|
||||||
},
|
},
|
||||||
img: [],
|
img: [],
|
||||||
sourceType: ['camera'],
|
|
||||||
imgFiles: "",
|
imgFiles: "",
|
||||||
msgType: '',
|
msgType: '',
|
||||||
messageText: '',
|
messageText: '',
|
||||||
@@ -90,20 +105,132 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteImg(e) {
|
// 自定义图片选择方法
|
||||||
const index = this.img.findIndex(f => f.path === e.tempFile.path);
|
chooseImage() {
|
||||||
if (index !== -1) {
|
if (this.isUploading) {
|
||||||
|
this.showMessage('warning', '图片正在上传中,请稍等');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 3 - this.img.length, // 最多选择剩余可选数量
|
||||||
|
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
|
||||||
|
sourceType: ['camera', 'album'], // 可以指定来源是相册还是相机,默认二者都有
|
||||||
|
success: (res) => {
|
||||||
|
// 处理选择的图片
|
||||||
|
res.tempFilePaths.forEach((filePath, index) => {
|
||||||
|
this.handleCustomImageUpload(filePath, res.tempFiles[index]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('选择图片失败:', err);
|
||||||
|
this.showMessage('error', '选择图片失败');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除自定义图片
|
||||||
|
deleteCustomImg(index) {
|
||||||
|
if (index >= 0 && index < this.img.length) {
|
||||||
|
// 释放URL对象
|
||||||
|
if (this.img[index].url && this.img[index].url.startsWith('blob:')) {
|
||||||
|
URL.revokeObjectURL(this.img[index].url);
|
||||||
|
}
|
||||||
|
|
||||||
this.form.ImgUrl.splice(index, 1);
|
this.form.ImgUrl.splice(index, 1);
|
||||||
this.img.splice(index, 1);
|
this.img.splice(index, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 兼容旧的删除方法
|
||||||
|
deleteImg(e) {
|
||||||
|
const index = this.img.findIndex(f => f.path === e.tempFile.path);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.deleteCustomImg(index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 兼容旧的上传方法
|
||||||
upload(e) {
|
upload(e) {
|
||||||
if (e.tempFiles && e.tempFiles.length > 0) {
|
if (e.tempFiles && e.tempFiles.length > 0) {
|
||||||
this.handleImageUpload(e.tempFiles[0]);
|
this.handleImageUpload(e.tempFiles[0]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 处理自定义图片上传
|
||||||
|
async handleCustomImageUpload(filePath, fileInfo) {
|
||||||
|
this.isUploading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 将uni.chooseImage返回的文件转换为File对象
|
||||||
|
const file = await this.convertToFile(filePath, fileInfo);
|
||||||
|
|
||||||
|
// 生成水印文字
|
||||||
|
const watermarkText = `${this.form.inspectionPoint || '未命名地点'} \n${this.getCurrentDate()}`;
|
||||||
|
|
||||||
|
// 添加水印
|
||||||
|
let processedFile = await addWatermarkToImage(file, watermarkText);
|
||||||
|
|
||||||
|
// 自动压缩图片(所有图片都进行压缩优化)
|
||||||
|
processedFile = await this.compressImage(processedFile);
|
||||||
|
|
||||||
|
// 上传处理后的文件
|
||||||
|
const fileUrl = await this.uploadFile(processedFile);
|
||||||
|
|
||||||
|
// 更新UI显示
|
||||||
|
this.img.push({
|
||||||
|
raw: processedFile,
|
||||||
|
name: processedFile.name,
|
||||||
|
url: fileUrl,
|
||||||
|
path: filePath
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("图片处理失败:", error);
|
||||||
|
this.showMessage('error', `图片处理失败: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
this.isUploading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 将uni.chooseImage的结果转换为File对象
|
||||||
|
async convertToFile(filePath, fileInfo) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 在uni-app中,可以直接使用filePath创建File对象
|
||||||
|
// 对于H5平台,需要特殊处理
|
||||||
|
// #ifdef H5
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', filePath, true);
|
||||||
|
xhr.responseType = 'blob';
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const blob = xhr.response;
|
||||||
|
const file = new File([blob], fileInfo.name || 'image.jpg', {
|
||||||
|
type: fileInfo.type || 'image/jpeg'
|
||||||
|
});
|
||||||
|
resolve(file);
|
||||||
|
} else {
|
||||||
|
reject(new Error('文件读取失败'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.onerror = () => reject(new Error('文件读取失败'));
|
||||||
|
xhr.send();
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef H5
|
||||||
|
// 对于非H5平台,创建一个模拟的File对象
|
||||||
|
const file = {
|
||||||
|
name: fileInfo.name || 'image.jpg',
|
||||||
|
type: fileInfo.type || 'image/jpeg',
|
||||||
|
size: fileInfo.size || 0,
|
||||||
|
path: filePath,
|
||||||
|
lastModified: Date.now()
|
||||||
|
};
|
||||||
|
resolve(file);
|
||||||
|
// #endif
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: '/pages/work/index'
|
url: '/pages/work/index'
|
||||||
@@ -114,6 +241,7 @@
|
|||||||
return this.form.ImgUrl.join(',');
|
return this.form.ImgUrl.join(',');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 处理原有的图片上传(兼容uni-file-picker)
|
||||||
async handleImageUpload(file) {
|
async handleImageUpload(file) {
|
||||||
this.isUploading = true;
|
this.isUploading = true;
|
||||||
|
|
||||||
@@ -128,12 +256,8 @@
|
|||||||
// 添加水印
|
// 添加水印
|
||||||
let processedFile = await addWatermarkToImage(file.file, watermarkText);
|
let processedFile = await addWatermarkToImage(file.file, watermarkText);
|
||||||
|
|
||||||
// 如果是移动浏览器且文件大于300KB,则进行压缩
|
// 使用新的压缩方法
|
||||||
if (this.isMobileBrowser && processedFile.size > 300 * 1024) {
|
processedFile = await this.compressImage(processedFile);
|
||||||
console.log('原始文件大小:', (processedFile.size / 1024).toFixed(2), 'KB');
|
|
||||||
processedFile = await this.compressImageForMobile(processedFile);
|
|
||||||
console.log('压缩后文件大小:', (processedFile.size / 1024).toFixed(2), 'KB');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传处理后的文件
|
// 上传处理后的文件
|
||||||
const fileUrl = await this.uploadFile(processedFile);
|
const fileUrl = await this.uploadFile(processedFile);
|
||||||
@@ -153,7 +277,92 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 专门为移动浏览器优化的压缩方法
|
// 通用图片压缩方法
|
||||||
|
compressImage(file, quality = 0.7, maxWidth = 1200, maxHeight = 1200) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 如果文件小于200KB,直接返回
|
||||||
|
if (file.size < 200 * 1024) {
|
||||||
|
console.log('文件较小,无需压缩');
|
||||||
|
resolve(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('原始文件大小:', (file.size / 1024).toFixed(2), 'KB');
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = (event) => {
|
||||||
|
img.onload = () => {
|
||||||
|
// 计算压缩比例
|
||||||
|
let width = img.width;
|
||||||
|
let height = img.height;
|
||||||
|
|
||||||
|
// 限制最大尺寸
|
||||||
|
if (width > maxWidth || height > maxHeight) {
|
||||||
|
const ratio = Math.min(maxWidth / width, maxHeight / height);
|
||||||
|
width = Math.floor(width * ratio);
|
||||||
|
height = Math.floor(height * ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建canvas
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// 设置更高的压缩质量
|
||||||
|
ctx.imageSmoothingQuality = 'high';
|
||||||
|
ctx.drawImage(img, 0, 0, width, height);
|
||||||
|
|
||||||
|
// 使用toBlob进行压缩
|
||||||
|
if (canvas.toBlob) {
|
||||||
|
canvas.toBlob(
|
||||||
|
(blob) => {
|
||||||
|
if (!blob) {
|
||||||
|
console.warn('压缩失败,返回原始文件');
|
||||||
|
resolve(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建压缩后的文件对象
|
||||||
|
const compressedFile = new File([blob], file.name, {
|
||||||
|
type: 'image/jpeg',
|
||||||
|
lastModified: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('压缩后文件大小:', (compressedFile.size / 1024).toFixed(2), 'KB');
|
||||||
|
resolve(compressedFile);
|
||||||
|
},
|
||||||
|
'image/jpeg',
|
||||||
|
quality
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 不支持toBlob的浏览器直接返回原文件
|
||||||
|
console.warn('浏览器不支持toBlob方法,无法压缩');
|
||||||
|
resolve(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
img.onerror = () => {
|
||||||
|
console.warn('图片加载失败,返回原始文件');
|
||||||
|
resolve(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src = event.target.result;
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = () => {
|
||||||
|
console.warn('文件读取失败,返回原始文件');
|
||||||
|
resolve(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 专门为移动浏览器优化的压缩方法(保留兼容性)
|
||||||
compressImageForMobile(file, quality = 0.6, maxWidth = 1200, maxHeight = 1200) {
|
compressImageForMobile(file, quality = 0.6, maxWidth = 1200, maxHeight = 1200) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
@@ -339,4 +548,90 @@
|
|||||||
/* 使按钮平分可用空间 */
|
/* 使按钮平分可用空间 */
|
||||||
/* 可能还需要设置一些其他的样式来确保按钮看起来正确,比如 text-align, padding 等 */
|
/* 可能还需要设置一些其他的样式来确保按钮看起来正确,比如 text-align, padding 等 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 自定义图片选择器样式 */
|
||||||
|
.custom-image-picker {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-item {
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: -5px;
|
||||||
|
right: -5px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #ff4757;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-icon {
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-image-btn {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border: 2px dashed #c0c0c0;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #fafafa;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-image-btn:hover {
|
||||||
|
border-color: #007aff;
|
||||||
|
background-color: #f0f8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #c0c0c0;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
Reference in New Issue
Block a user