Files
pasd_app/pages/work/inspection/scanSign/index.vue

637 lines
17 KiB
Vue
Raw Normal View History

2025-07-28 14:57:35 +08:00
<template>
<view class="container">
<view class="example">
<uni-forms ref="dynamicForm" :model="form" label-width="80px">
<uni-forms-item label="巡检点" name="inspectionPoint">
<uni-easyinput disabled :value="form.inspectionPoint" placeholder="请输入巡检点"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="巡检要求" name="inspectionRequirements">
<uni-easyinput type="textarea" disabled :value="form.inspectionRequirements" placeholder="请输入巡检要求"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="图片上传">
<view class="example-body">
2025-09-04 20:26:46 +08:00
<view class="custom-image-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>
2025-07-28 14:57:35 +08:00
</view>
</uni-forms-item>
<!-- 备注 -->
2025-08-07 13:01:29 +08:00
<uni-forms-item label="备注1" name="remark">
2025-07-28 14:57:35 +08:00
<uni-easyinput type="textarea" v-model="form.remark" placeholder="请输入备注"></uni-easyinput>
</uni-forms-item>
</uni-forms>
<view class="button-group">
<button class="btn btn-primary" @click="submit()">提交</button>
<button class="btn btn-cancel" @click="cancel()">取消</button>
</view>
</view>
<!-- 消息提示框 -->
<view>
<uni-popup ref="alertDialog" type="dialog">
<uni-popup-dialog :type="msgType" title="消息" :content="messageText" @confirm="dialogConfirm"
showClose="false"></uni-popup-dialog>
</uni-popup>
</view>
</view>
</template>
<script>
import {
uploadImg
} from "@/api/system/user"
import {
addRecord
} from "@/api/inspection/record.js"
import {
addWatermarkToImage
} from "@/utils/watermark.js"
export default {
data() {
return {
form: {
2025-08-07 13:01:29 +08:00
inspectionType: 0,
inspectorId: this.$store.state.user.nickName,
2025-07-28 14:57:35 +08:00
inspectionPoint: "",
inspectionRequirements: "",
inspectionImg: "",
2025-08-07 13:01:29 +08:00
ImgUrl: [],
remark: ""
2025-07-28 14:57:35 +08:00
},
img: [],
imgFiles: "",
msgType: '',
messageText: '',
2025-08-07 13:01:29 +08:00
isUploading: false,
isMobileBrowser: false // 改为更通用的移动浏览器检测标志
2025-07-28 14:57:35 +08:00
}
},
methods: {
submit() {
if (this.isUploading) {
2025-08-07 13:01:29 +08:00
this.showMessage('warning', '图片正在上传中,请稍等');
2025-07-28 14:57:35 +08:00
return;
}
if (!Array.isArray(this.form.ImgUrl) || this.form.ImgUrl.length === 0) {
2025-08-07 13:01:29 +08:00
this.showMessage('error', '请选择要上传的图片');
2025-07-28 14:57:35 +08:00
return;
}
2025-08-07 13:01:29 +08:00
this.form.inspectionImg = this.joinList();
2025-07-28 14:57:35 +08:00
addRecord(this.form).then(res => {
if (res.code === 200) {
2025-08-07 13:01:29 +08:00
this.showMessage('success', '打卡成功');
2025-07-28 14:57:35 +08:00
} else {
2025-08-07 13:01:29 +08:00
this.showMessage('error', '打卡失败');
2025-07-28 14:57:35 +08:00
}
2025-08-07 13:01:29 +08:00
}).catch(err => {
this.showMessage('error', `提交失败: ${err.message}`);
});
2025-07-28 14:57:35 +08:00
},
2025-08-07 13:01:29 +08:00
2025-09-04 20:26:46 +08:00
// 自定义图片选择方法
chooseImage() {
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.img.splice(index, 1);
}
},
// 兼容旧的删除方法
2025-07-28 14:57:35 +08:00
deleteImg(e) {
2025-08-07 13:01:29 +08:00
const index = this.img.findIndex(f => f.path === e.tempFile.path);
2025-07-28 14:57:35 +08:00
if (index !== -1) {
2025-09-04 20:26:46 +08:00
this.deleteCustomImg(index);
2025-07-28 14:57:35 +08:00
}
},
2025-08-07 13:01:29 +08:00
2025-09-04 20:26:46 +08:00
// 兼容旧的上传方法
2025-07-28 14:57:35 +08:00
upload(e) {
2025-08-07 13:01:29 +08:00
if (e.tempFiles && e.tempFiles.length > 0) {
this.handleImageUpload(e.tempFiles[0]);
}
2025-07-28 14:57:35 +08:00
},
2025-08-07 13:01:29 +08:00
2025-09-04 20:26:46 +08:00
// 处理自定义图片上传
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
});
},
2025-07-28 14:57:35 +08:00
cancel() {
uni.reLaunch({
url: '/pages/work/index'
});
},
2025-08-07 13:01:29 +08:00
2025-07-28 14:57:35 +08:00
joinList() {
return this.form.ImgUrl.join(',');
},
2025-09-04 20:26:46 +08:00
// 处理原有的图片上传兼容uni-file-picker
2025-07-28 14:57:35 +08:00
async handleImageUpload(file) {
2025-08-07 13:01:29 +08:00
this.isUploading = true;
2025-07-28 14:57:35 +08:00
try {
2025-08-07 13:01:29 +08:00
if (!file || !file.file) {
2025-07-28 14:57:35 +08:00
throw new Error('无效的文件对象');
}
2025-08-07 13:01:29 +08:00
// 生成水印文字
const watermarkText = `${this.form.inspectionPoint || '未命名地点'} \n${this.getCurrentDate()}`;
2025-07-28 14:57:35 +08:00
2025-08-07 13:01:29 +08:00
// 添加水印
let processedFile = await addWatermarkToImage(file.file, watermarkText);
2025-09-04 20:26:46 +08:00
// 使用新的压缩方法
processedFile = await this.compressImage(processedFile);
2025-07-28 14:57:35 +08:00
2025-08-07 13:01:29 +08:00
// 上传处理后的文件
const fileUrl = await this.uploadFile(processedFile);
// 更新UI显示
this.img.push({
raw: processedFile,
name: processedFile.name,
url: fileUrl
});
2025-07-28 14:57:35 +08:00
} catch (error) {
2025-08-07 13:01:29 +08:00
console.error("图片处理失败:", error);
this.showMessage('error', `图片处理失败: ${error.message}`);
2025-07-28 14:57:35 +08:00
} finally {
2025-08-07 13:01:29 +08:00
this.isUploading = false;
2025-07-28 14:57:35 +08:00
}
},
2025-08-07 13:01:29 +08:00
2025-09-04 20:26:46 +08:00
// 通用图片压缩方法
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);
});
},
// 专门为移动浏览器优化的压缩方法(保留兼容性)
2025-08-07 13:01:29 +08:00
compressImageForMobile(file, quality = 0.6, maxWidth = 1200, maxHeight = 1200) {
return new Promise((resolve, reject) => {
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的polyfill保证兼容性
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()
});
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);
});
},
async uploadFile(file) {
return new Promise((resolve, reject) => {
// 创建预览URL用于显示
const previewUrl = URL.createObjectURL(file);
// 实际上传
uploadImg({
filePath: previewUrl
}).then(res => {
this.form.ImgUrl.push(res.fileName);
resolve(previewUrl);
}).catch(err => {
reject(err);
});
});
},
2025-07-28 14:57:35 +08:00
getCurrentDate() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
dialogConfirm() {
if (this.msgType == 'success') {
uni.reLaunch({
url: '/pages/work/index'
2025-08-07 13:01:29 +08:00
});
2025-07-28 14:57:35 +08:00
}
},
2025-08-07 13:01:29 +08:00
showMessage(type, text) {
this.msgType = type;
this.messageText = text;
this.$refs.alertDialog.open();
},
// 检测移动浏览器环境
checkMobileBrowser() {
// 更全面的移动设备检测
const userAgent = navigator.userAgent.toLowerCase();
const isMobile = /iphone|ipod|android|windows phone|mobile|blackberry/.test(userAgent);
// 如果是通过uni-app的web-view打开也认为是移动环境
this.isMobileBrowser = isMobile || window.__uniAppWebview;
}
2025-07-28 14:57:35 +08:00
},
onLoad(option) {
2025-08-07 13:01:29 +08:00
this.form.inspectionPoint = option.inspectionPoint || '';
this.form.inspectionRequirements = option.inspectionRequirements || '';
this.form.inspectionPointId = option.inspectionPointId || '';
2025-07-28 14:57:35 +08:00
},
2025-08-07 13:01:29 +08:00
mounted() {
this.checkMobileBrowser();
2025-07-28 14:57:35 +08:00
}
}
</script>
2025-08-07 13:01:29 +08:00
2025-07-28 14:57:35 +08:00
<style lang="scss">
page {
display: flex;
flex-direction: column;
box-sizing: border-box;
background-color: #fff;
height: 90vh;
/* 使页面高度占满整个视口 */
}
.container {
flex: 1;
/* 让 container 占满剩余空间 */
display: flex;
flex-direction: column;
/* 设置为列方向 */
}
.example {
flex: 1;
/* 让 example 占满 container 的剩余空间 */
display: flex;
flex-direction: column;
/* 设置为列方向,确保子元素垂直排列 */
padding: 15px;
background-color: #fff;
}
// 样式沉底
.button-group {
position: fixed;
bottom: 20px;
left: 0;
/* 使用 margin-top: auto 来将按钮组推到 example 容器的底部 */
display: flex;
width: 100%;
justify-content: space-around;
}
.button-group button {
flex: 1;
background: #fff;
color: #000;
/* 使按钮平分可用空间 */
/* 可能还需要设置一些其他的样式来确保按钮看起来正确,比如 text-align, padding 等 */
}
2025-09-04 20:26:46 +08:00
/* 自定义图片选择器样式 */
.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;
}
2025-07-28 14:57:35 +08:00
</style>