推送
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"* text=auto eol=lf"
|
@@ -3,7 +3,7 @@ module.exports = {
|
|||||||
// baseUrl: 'http://172.16.129.101:8080/dev-api/',
|
// baseUrl: 'http://172.16.129.101:8080/dev-api/',
|
||||||
//baseUrl: 'http://pasd.gxsdxy.cn/prod-api/',
|
//baseUrl: 'http://pasd.gxsdxy.cn/prod-api/',
|
||||||
// baseUrl: 'http://172.16.129.101:8080',//172.16.129.101
|
// baseUrl: 'http://172.16.129.101:8080',//172.16.129.101
|
||||||
//baseUrl: 'http://localhost:8080',
|
baseUrl: 'http://192.168.100.106:8080',
|
||||||
// 应用信息
|
// 应用信息
|
||||||
appInfo: {
|
appInfo: {
|
||||||
// 应用名称
|
// 应用名称
|
||||||
|
110
manifest.json
110
manifest.json
@@ -1,23 +1,31 @@
|
|||||||
{
|
{
|
||||||
"name" : "平安水电移动端",
|
"name": "平安水电移动端",
|
||||||
"appid" : "__UNI__3CA192A",
|
"appid": "__UNI__3CA192A",
|
||||||
"description" : "",
|
"description": "",
|
||||||
"versionName" : "1.1.0",
|
"versionName": "1.1.0",
|
||||||
"versionCode" : "100",
|
"versionCode": "100",
|
||||||
"transformPx" : false,
|
"transformPx": false,
|
||||||
"app-plus" : {
|
"app-plus": {
|
||||||
"usingComponents" : true,
|
"usingComponents": true,
|
||||||
"nvueCompiler" : "uni-app",
|
"nvueCompiler": "uni-app",
|
||||||
"splashscreen" : {
|
"splashscreen": {
|
||||||
"alwaysShowBeforeRender" : true,
|
"alwaysShowBeforeRender": true,
|
||||||
"waiting" : true,
|
"waiting": true,
|
||||||
"autoclose" : true,
|
"autoclose": true,
|
||||||
"delay" : 0
|
"delay": 0
|
||||||
},
|
},
|
||||||
"modules" : {},
|
"modules": {},
|
||||||
"distribute" : {
|
"permission": {
|
||||||
"android" : {
|
"Camera": {
|
||||||
"permissions" : [
|
"description": "需要访问相机以实现扫码功能"
|
||||||
|
},
|
||||||
|
"Flashlight": {
|
||||||
|
"description": "需要访问闪光灯以支持扫码补光"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"distribute": {
|
||||||
|
"android": {
|
||||||
|
"permissions": [
|
||||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
@@ -32,38 +40,54 @@
|
|||||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ios" : {},
|
"ios": {
|
||||||
"sdkConfigs" : {}
|
"permissions": {
|
||||||
|
"Camera": {
|
||||||
|
"description": "需要访问相机以实现扫码功能"
|
||||||
|
},
|
||||||
|
"PhotoLibraryAdd": {
|
||||||
|
"description": "需要访问相册以保存图片"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sdkConfigs": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"quickapp" : {},
|
"quickapp": {},
|
||||||
"mp-weixin" : {
|
"mp-weixin": {
|
||||||
"appid" : "wx6caa681653417faa",
|
"appid": "wx6caa681653417faa",
|
||||||
"setting" : {
|
"setting": {
|
||||||
"urlCheck" : false,
|
"urlCheck": false,
|
||||||
"es6" : false,
|
"es6": false,
|
||||||
"minified" : true,
|
"minified": true,
|
||||||
"postcss" : true
|
"postcss": true
|
||||||
},
|
},
|
||||||
"optimization" : {
|
"optimization": {
|
||||||
"subPackages" : true
|
"subPackages": true
|
||||||
},
|
},
|
||||||
"usingComponents" : true
|
"permission": {
|
||||||
|
"scope.camera": {
|
||||||
|
"desc": "需要访问相机以实现扫码功能"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"vueVersion" : "2",
|
"vueVersion": "2",
|
||||||
"h5" : {
|
"h5": {
|
||||||
"template" : "static/index.html",
|
"template": "static/index.html",
|
||||||
"devServer" : {
|
"devServer": {
|
||||||
"port" : 9090,
|
"port": 9090,
|
||||||
"https" : false
|
"https": false
|
||||||
},
|
},
|
||||||
"title" : "RuoYi-App",
|
"title": "RuoYi-App",
|
||||||
"router" : {
|
"router": {
|
||||||
"mode" : "hash",
|
"mode": "hash",
|
||||||
"base" : "./"
|
"base": "./"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,6 +14,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"echarts": "^5.5.1"
|
"@zxing/library": "^0.21.3",
|
||||||
|
"echarts": "^5.5.1",
|
||||||
|
"jsqr": "^1.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
</uni-forms>
|
</uni-forms>
|
||||||
<view class="button-group">
|
<view class="button-group">
|
||||||
<button type="primary" size="mini" @click="cancel()">取消</button>
|
<button type="primary" size="mini" @click="cancel()">取消</button>
|
||||||
<button type="primary" size="mini" @click="submit()">提交</button>
|
<button type="primary" size="mini" @click="submit()">提交1</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<!-- 备注 -->
|
<!-- 备注 -->
|
||||||
<uni-forms-item label="备注" name="remark">
|
<uni-forms-item label="备注1" name="remark">
|
||||||
<uni-easyinput type="textarea" v-model="form.remark" placeholder="请输入备注"></uni-easyinput>
|
<uni-easyinput type="textarea" v-model="form.remark" placeholder="请输入备注"></uni-easyinput>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
</uni-forms>
|
</uni-forms>
|
||||||
@@ -47,149 +47,204 @@
|
|||||||
addWatermarkToImage
|
addWatermarkToImage
|
||||||
} from "@/utils/watermark.js"
|
} from "@/utils/watermark.js"
|
||||||
export default {
|
export default {
|
||||||
// vue
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
inspectionType: 0, //默认常规巡检
|
inspectionType: 0,
|
||||||
inspectorId: this.$store.state.user.nickName, //巡检人
|
inspectorId: this.$store.state.user.nickName,
|
||||||
// 巡检点
|
|
||||||
inspectionPoint: "",
|
inspectionPoint: "",
|
||||||
// 巡检要求
|
|
||||||
inspectionRequirements: "",
|
inspectionRequirements: "",
|
||||||
inspectionImg: "",
|
inspectionImg: "",
|
||||||
ImgUrl: []
|
ImgUrl: [],
|
||||||
|
remark: ""
|
||||||
},
|
},
|
||||||
img: [],
|
img: [],
|
||||||
sourceType: ['camera'],
|
sourceType: ['camera'],
|
||||||
//存图片文件
|
|
||||||
imgFiles: "",
|
imgFiles: "",
|
||||||
// 消息提示框
|
|
||||||
msgType: '',
|
msgType: '',
|
||||||
messageText: '',
|
messageText: '',
|
||||||
isUploading: false, // 上传中标志
|
isUploading: false,
|
||||||
|
isMobileBrowser: false // 改为更通用的移动浏览器检测标志
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 提交
|
|
||||||
submit() {
|
submit() {
|
||||||
if (this.isUploading) {
|
if (this.isUploading) {
|
||||||
this.msgType = 'warning'
|
this.showMessage('warning', '图片正在上传中,请稍等');
|
||||||
this.messageText = '图片正在上传中,请稍等'
|
|
||||||
this.$refs.alertDialog.open()
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Array.isArray(this.form.ImgUrl) || this.form.ImgUrl.length === 0) {
|
if (!Array.isArray(this.form.ImgUrl) || this.form.ImgUrl.length === 0) {
|
||||||
this.msgType = 'error'
|
this.showMessage('error', '请选择要上传的图片');
|
||||||
this.messageText = `请选择要上传的图片`
|
|
||||||
this.$refs.alertDialog.open()
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.form.inspectionImg = this.joinList()
|
this.form.inspectionImg = this.joinList();
|
||||||
addRecord(this.form).then(res => {
|
addRecord(this.form).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
// console.log("sdfsadfsdfsdfsdfsdf");
|
this.showMessage('success', '打卡成功');
|
||||||
this.msgType = 'success'
|
|
||||||
this.messageText = `打卡成功`
|
|
||||||
this.$refs.alertDialog.open()
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.msgType = 'error'
|
this.showMessage('error', '打卡失败');
|
||||||
this.messageText = `打卡失败`
|
|
||||||
this.$refs.alertDialog.open()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
}).catch(err => {
|
||||||
|
this.showMessage('error', `提交失败: ${err.message}`);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
// 图片删除
|
|
||||||
deleteImg(e) {
|
deleteImg(e) {
|
||||||
//const index = this.img.findIndex(f => f.uuid === e.tempFile.uuid); // 假设文件通过 url 唯一标识
|
const index = this.img.findIndex(f => f.path === e.tempFile.path);
|
||||||
const index = this.img.findIndex(f => f.path === e.tempFile.path); // 假设文件通过 url 唯一标识
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.form.ImgUrl.splice(index, 1)
|
this.form.ImgUrl.splice(index, 1);
|
||||||
this.img.splice(index, 1)
|
this.img.splice(index, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 上传图片
|
|
||||||
upload(e) {
|
upload(e) {
|
||||||
console.log(e);
|
if (e.tempFiles && e.tempFiles.length > 0) {
|
||||||
// this.img.push(e.tempFiles[0])
|
this.handleImageUpload(e.tempFiles[0]);
|
||||||
// e.tempFilePaths.forEach(item => {
|
}
|
||||||
// uploadImg({
|
|
||||||
// filePath: item
|
|
||||||
// }).then(res => {
|
|
||||||
// this.form.ImgUrl.push(res.fileName)
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
this.handleImageUpload(e.tempFiles[0]);
|
|
||||||
},
|
},
|
||||||
// 取消
|
|
||||||
cancel() {
|
cancel() {
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: '/pages/work/index'
|
url: '/pages/work/index'
|
||||||
})
|
|
||||||
uni.showToast({
|
|
||||||
title: '打卡失败',
|
|
||||||
icon: 'error', // 成功图标
|
|
||||||
duration: 1000 // 持续时间为2000ms
|
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
// 定义一个方法,用于将list拼接成字符串
|
|
||||||
joinList() {
|
joinList() {
|
||||||
// 使用数组的join方法,以逗号分隔元素
|
|
||||||
return this.form.ImgUrl.join(',');
|
return this.form.ImgUrl.join(',');
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 处理图片上传 --知无涯 */
|
|
||||||
async handleImageUpload(file) {
|
async handleImageUpload(file) {
|
||||||
this.isUploading = true; // 开始上传
|
this.isUploading = true;
|
||||||
|
|
||||||
const that = this;
|
|
||||||
try {
|
try {
|
||||||
if (!file) {
|
if (!file || !file.file) {
|
||||||
throw new Error('无效的文件对象');
|
throw new Error('无效的文件对象');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成水印文字:巡检地点 + 巡检时间
|
// 生成水印文字
|
||||||
const times = this.getCurrentDate();
|
const watermarkText = `${this.form.inspectionPoint || '未命名地点'} \n${this.getCurrentDate()}`;
|
||||||
|
|
||||||
const watermarkText = `${this.form.inspectionPoint || '未命名地点'} \n${times || '未设置时间'}`;
|
// 添加水印
|
||||||
|
let processedFile = await addWatermarkToImage(file.file, watermarkText);
|
||||||
// 添加水印 - 水印配置
|
|
||||||
// const watermarkedFile = await addWatermarkToImage(file.file, watermarkText, {
|
// 如果是移动浏览器且文件大于300KB,则进行压缩
|
||||||
// font: 'bold 20px Arial', // 加大字号并加粗
|
if (this.isMobileBrowser && processedFile.size > 300 * 1024) {
|
||||||
// color: 'rgba(0, 0, 0, 0.7)' // 黑色,70%不透明度
|
console.log('原始文件大小:', (processedFile.size / 1024).toFixed(2), 'KB');
|
||||||
// });
|
processedFile = await this.compressImageForMobile(processedFile);
|
||||||
const watermarkedFile = await addWatermarkToImage(file.file, watermarkText);
|
console.log('压缩后文件大小:', (processedFile.size / 1024).toFixed(2), 'KB');
|
||||||
|
}
|
||||||
// 创建预览URL
|
|
||||||
const previewUrl = URL.createObjectURL(watermarkedFile);
|
|
||||||
|
|
||||||
// 更新表单数据
|
|
||||||
that.imgFiles = {
|
|
||||||
raw: watermarkedFile,
|
|
||||||
name: watermarkedFile.name,
|
|
||||||
url: previewUrl
|
|
||||||
};
|
|
||||||
//上传图片,获取图片路径
|
|
||||||
that.img.push(that.imgFiles)
|
|
||||||
await uploadImg({
|
|
||||||
filePath: previewUrl
|
|
||||||
}).then(res => {
|
|
||||||
that.form.ImgUrl.push(res.fileName)
|
|
||||||
// console.log(res);
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// 上传处理后的文件
|
||||||
|
const fileUrl = await this.uploadFile(processedFile);
|
||||||
|
|
||||||
|
// 更新UI显示
|
||||||
|
this.img.push({
|
||||||
|
raw: processedFile,
|
||||||
|
name: processedFile.name,
|
||||||
|
url: fileUrl
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("水印处理失败:", error);
|
console.error("图片处理失败:", error);
|
||||||
proxy.$modal.msgError("图片处理失败: " + error.message);
|
this.showMessage('error', `图片处理失败: ${error.message}`);
|
||||||
} finally {
|
} finally {
|
||||||
this.isUploading = false; // 上传完毕
|
this.isUploading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 获取当前时间
|
|
||||||
|
// 专门为移动浏览器优化的压缩方法
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getCurrentDate() {
|
getCurrentDate() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const year = now.getFullYear();
|
const year = now.getFullYear();
|
||||||
@@ -202,34 +257,42 @@
|
|||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
//对话框确定按钮
|
|
||||||
dialogConfirm() {
|
dialogConfirm() {
|
||||||
if (this.msgType == 'success') {
|
if (this.msgType == 'success') {
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: '/pages/work/index'
|
url: '/pages/work/index'
|
||||||
})
|
});
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// uniapp
|
|
||||||
//uniapp方法,在最开始的时候拿到传值过来的值
|
|
||||||
onLoad(option) {
|
onLoad(option) {
|
||||||
const _this = this
|
this.form.inspectionPoint = option.inspectionPoint || '';
|
||||||
// console.log('传递的值',option)
|
this.form.inspectionRequirements = option.inspectionRequirements || '';
|
||||||
_this.form.inspectionPoint = option.inspectionPoint
|
this.form.inspectionPointId = option.inspectionPointId || '';
|
||||||
_this.form.inspectionRequirements = option.inspectionRequirements
|
|
||||||
_this.form.inspectionPointId = option.inspectionPointId
|
|
||||||
},
|
},
|
||||||
created() {
|
mounted() {
|
||||||
// console.log("this.$store.state.user",this.$store.state.user);
|
this.checkMobileBrowser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
page {
|
page {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@@ -1,145 +1,728 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<button type="default" @click="scanCode">扫码</button>
|
<!-- 自定义扫码界面 -->
|
||||||
<!-- <view>
|
<view class="scanner-container" v-if="isScanning">
|
||||||
扫码结果:{{qrCodeRes}}
|
<!-- 顶部操作栏 -->
|
||||||
</view>
|
<view class="scanner-header">
|
||||||
|
<view class="back-btn" @click="cancelScan">返回</view>
|
||||||
<image :src="qc"></image> -->
|
<view class="title">扫码</view>
|
||||||
</view>
|
<view class="empty"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 相机预览区域 -->
|
||||||
|
<view class="camera-preview">
|
||||||
|
<!-- 遮挡浏览器自带的相册按钮 -->
|
||||||
|
<view class="browser-button-cover"></view>
|
||||||
|
|
||||||
|
<video
|
||||||
|
v-if="showVideo"
|
||||||
|
:src="videoSrc"
|
||||||
|
autoplay
|
||||||
|
playsinline
|
||||||
|
muted
|
||||||
|
class="preview-video"
|
||||||
|
@error="handleVideoError"
|
||||||
|
></video>
|
||||||
|
|
||||||
|
<!-- 扫描框 -->
|
||||||
|
<view class="scan-frame">
|
||||||
|
<!-- 扫描线动画 -->
|
||||||
|
<view class="scan-line" :style="{ top: scanLinePosition + 'px' }"></view>
|
||||||
|
|
||||||
|
<!-- 扫描框边角 -->
|
||||||
|
<view class="corner top-left"></view>
|
||||||
|
<view class="corner top-right"></view>
|
||||||
|
<view class="corner bottom-left"></view>
|
||||||
|
<view class="corner bottom-right"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 扫描提示文字 -->
|
||||||
|
<view class="scan-tip">{{cameraStatusText}}</view>
|
||||||
|
|
||||||
|
<!-- 浏览器不支持提示 -->
|
||||||
|
<view v-if="showBrowserNotSupported" class="browser-not-supported">
|
||||||
|
<view class="error-title">浏览器不支持</view>
|
||||||
|
<view class="error-desc">您的浏览器不支持直接访问摄像头</view>
|
||||||
|
<view class="suggestions">
|
||||||
|
<view>建议:</view>
|
||||||
|
<view>1. 使用Chrome、Firefox等现代浏览器</view>
|
||||||
|
<view>2. 确保网站使用HTTPS协议</view>
|
||||||
|
<view>3. 检查浏览器摄像头权限设置</view>
|
||||||
|
</view>
|
||||||
|
<button class="retry-btn" @click="retryCamera">重试</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 扫码结果展示 -->
|
||||||
|
<view v-if="qrCodeRes" class="result-container">
|
||||||
|
<view>扫码成功</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let Qrcode = require('../../../../utils/reqrcode.js')
|
let Qrcode = require('../../../../utils/reqrcode.js')
|
||||||
import {
|
import {
|
||||||
getInspectionManage
|
getInspectionManage
|
||||||
} from '@/api/inspection/inspectionManage.js'
|
} from '@/api/inspection/inspectionManage.js'
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
qrCodeRes: '',
|
|
||||||
qc: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 扫码
|
|
||||||
scanCode() {
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
this.scanCodeAPP()
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef H5
|
// 二维码优化处理器
|
||||||
this.scanCodeH5()
|
const QROptimizer = {
|
||||||
// #endif
|
// 图像预处理
|
||||||
},
|
preprocessImage: function(base64) {
|
||||||
// APP直接调用 uni.scanCode 接口
|
return new Promise((resolve) => {
|
||||||
scanCodeAPP() {
|
const img = new Image()
|
||||||
uni.scanCode({
|
img.src = base64
|
||||||
scanType: ['qrCode'],
|
img.onload = () => {
|
||||||
success: (res) => {
|
const canvas = document.createElement('canvas')
|
||||||
this.qrCodeRes = res.result
|
const ctx = canvas.getContext('2d')
|
||||||
}
|
canvas.width = img.width
|
||||||
})
|
canvas.height = img.height
|
||||||
},
|
|
||||||
// H5通过拉起相机拍照来识别二维码
|
// 增强对比度和亮度
|
||||||
scanCodeH5() {
|
ctx.filter = 'contrast(1.3) brightness(1.1)'
|
||||||
let that = this;
|
ctx.drawImage(img, 0, 0)
|
||||||
uni.chooseImage({
|
|
||||||
count: 1,
|
// 转换为灰度并二值化
|
||||||
sourceType: ['camera'],
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
||||||
success: imgRes => {
|
this.binarize(imageData)
|
||||||
that.qc = imgRes.tempFiles[0].path; // 预览图片
|
ctx.putImageData(imageData, 0, 0)
|
||||||
const tempFile = imgRes.tempFiles[0];
|
|
||||||
const reader = new FileReader();
|
resolve(canvas.toDataURL('image/jpeg'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图像二值化处理
|
||||||
|
binarize: function(imageData) {
|
||||||
|
const data = imageData.data
|
||||||
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
|
const gray = 0.299 * data[i] + 0.587 * data[i+1] + 0.114 * data[i+2]
|
||||||
|
const value = gray > 128 ? 255 : 0
|
||||||
|
data[i] = data[i+1] = data[i+2] = value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 增强版解码
|
||||||
|
enhancedDecode: function(base64) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.preprocessImage(base64).then(processed => {
|
||||||
|
Qrcode.qrcode.decode(processed)
|
||||||
|
Qrcode.qrcode.callback = (res) => {
|
||||||
|
if (res.indexOf('error') === -1) {
|
||||||
|
resolve(res)
|
||||||
|
} else {
|
||||||
|
// 尝试原始图像解码
|
||||||
|
Qrcode.qrcode.decode(base64)
|
||||||
|
Qrcode.qrcode.callback = resolve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reader.onload = function(event) {
|
export default {
|
||||||
const base64 = event.target.result;
|
data() {
|
||||||
try {
|
return {
|
||||||
Qrcode.qrcode.decode(base64);
|
qrCodeRes: '',
|
||||||
Qrcode.qrcode.callback = (codeRes) => {
|
videoSrc: '',
|
||||||
if (codeRes.indexOf('error') >= 0) {
|
isScanning: false,
|
||||||
that.qrCodeRes = '不合法二维码:' + codeRes;
|
stream: null,
|
||||||
uni.showToast({
|
scanLinePosition: 0,
|
||||||
title: '二维码识别失败!',
|
scanAnimation: null,
|
||||||
icon: 'none'
|
isCameraReady: false,
|
||||||
});
|
cameraStatusText: '将二维码/条形码放入框内,即可自动扫描',
|
||||||
} else {
|
permissionDenied: false,
|
||||||
let r = that.decodeStr(codeRes);
|
showVideo: true,
|
||||||
that.qrCodeRes = r;
|
showBrowserNotSupported: false,
|
||||||
getInspectionManage(that.qrCodeRes)
|
scanInterval: null,
|
||||||
.then(res => {
|
lastScanTime: 0,
|
||||||
if (res.data.inspectionStatus === "1") {
|
scanFrameRect: null
|
||||||
console.log(res)
|
}
|
||||||
let inspectionPoint = res.data.inspectionPoint;
|
},
|
||||||
let inspectionRequirements = res.data
|
onShow() {
|
||||||
.inspectionRequirements;
|
this.scanCode();
|
||||||
let inspectionPointId = res.data.id
|
},
|
||||||
uni.reLaunch({
|
mounted() {
|
||||||
url: `/pages/work/inspection/scanSign/index?inspectionPoint=${inspectionPoint}&inspectionRequirements=${inspectionRequirements}&inspectionPointId=${inspectionPointId}`
|
this.initScanAnimation();
|
||||||
});
|
},
|
||||||
} else {
|
beforeDestroy() {
|
||||||
uni.showToast({
|
if (this.scanAnimation) {
|
||||||
title: '验证码已失效!',
|
cancelAnimationFrame(this.scanAnimation);
|
||||||
icon: 'none'
|
}
|
||||||
});
|
this.stopCamera();
|
||||||
uni.reLaunch({
|
this.stopAutoScan();
|
||||||
url: '/pages/work/index'
|
},
|
||||||
});
|
methods: {
|
||||||
}
|
scanCode() {
|
||||||
})
|
// #ifdef APP-PLUS
|
||||||
.catch(err => {
|
this.scanCodeAPP()
|
||||||
console.log("请求错误", err);
|
// #endif
|
||||||
uni.showToast({
|
|
||||||
title: '服务器错误',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.log("解析失败", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: '二维码解析失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.readAsDataURL(tempFile);
|
// #ifdef H5
|
||||||
},
|
this.startCustomScanner()
|
||||||
fail: (err) => {
|
// #endif
|
||||||
console.log("图片选择失败", err);
|
},
|
||||||
uni.showToast({
|
|
||||||
title: '拍照失败,请重试',
|
initScanAnimation() {
|
||||||
icon: 'none'
|
const scanFrameHeight = 210;
|
||||||
});
|
const speed = 2;
|
||||||
}
|
|
||||||
});
|
const animate = () => {
|
||||||
},
|
if (this.scanLinePosition >= scanFrameHeight) {
|
||||||
// 获取文件地址函数
|
this.scanLinePosition = 0;
|
||||||
getObjectURL(file) {
|
} else {
|
||||||
if (window.URL && window.URL.createObjectURL) {
|
this.scanLinePosition += speed;
|
||||||
return window.URL.createObjectURL(file);
|
}
|
||||||
} else {
|
this.scanAnimation = requestAnimationFrame(animate);
|
||||||
console.warn("当前浏览器不支持 createObjectURL 方法");
|
};
|
||||||
return null;
|
|
||||||
}
|
animate();
|
||||||
},
|
},
|
||||||
// 解码,输出:中文
|
|
||||||
decodeStr(str) {
|
startCustomScanner() {
|
||||||
try {
|
this.isScanning = true;
|
||||||
return decodeURIComponent(escape(str));
|
this.qrCodeRes = '';
|
||||||
} catch (e) {
|
this.isCameraReady = false;
|
||||||
console.warn("解码失败,返回原字符串:", str);
|
this.cameraStatusText = '正在请求摄像头权限...';
|
||||||
return str;
|
this.showBrowserNotSupported = false;
|
||||||
}
|
this.showVideo = true;
|
||||||
},
|
|
||||||
}
|
if (!this.checkBrowserSupport()) {
|
||||||
}
|
this.handleBrowserNotSupported();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkCameraPermission();
|
||||||
|
},
|
||||||
|
|
||||||
|
checkBrowserSupport() {
|
||||||
|
return !!(
|
||||||
|
navigator.mediaDevices &&
|
||||||
|
navigator.mediaDevices.getUserMedia &&
|
||||||
|
window.URL &&
|
||||||
|
window.URL.createObjectURL
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleBrowserNotSupported() {
|
||||||
|
this.showVideo = false;
|
||||||
|
this.showBrowserNotSupported = true;
|
||||||
|
this.cameraStatusText = '浏览器不支持摄像头访问';
|
||||||
|
this.isCameraReady = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
checkCameraPermission() {
|
||||||
|
if (navigator.permissions && navigator.permissions.query) {
|
||||||
|
navigator.permissions.query({ name: 'camera' })
|
||||||
|
.then(permissionStatus => {
|
||||||
|
if (permissionStatus.state === 'denied') {
|
||||||
|
this.handleCameraError('摄像头权限已被拒绝,请在浏览器设置中启用');
|
||||||
|
this.permissionDenied = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.getUserMediaWithFallback();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.getUserMediaWithFallback();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.getUserMediaWithFallback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getUserMediaWithFallback() {
|
||||||
|
const constraints = {
|
||||||
|
video: {
|
||||||
|
facingMode: { ideal: 'environment' },
|
||||||
|
width: { ideal: window.innerWidth },
|
||||||
|
height: { ideal: window.innerHeight }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
navigator.mediaDevices.getUserMedia(constraints)
|
||||||
|
.then(stream => {
|
||||||
|
this.handleStreamSuccess(stream);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const scanFrame = document.querySelector('.scan-frame');
|
||||||
|
if (scanFrame) {
|
||||||
|
this.scanFrameRect = scanFrame.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
this.startAutoScan();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.warn('后置摄像头访问失败,尝试前置摄像头:', err);
|
||||||
|
|
||||||
|
const frontConstraints = {
|
||||||
|
video: {
|
||||||
|
facingMode: 'user',
|
||||||
|
width: { ideal: window.innerWidth },
|
||||||
|
height: { ideal: window.innerHeight }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
navigator.mediaDevices.getUserMedia(frontConstraints)
|
||||||
|
.then(stream => {
|
||||||
|
this.handleStreamSuccess(stream);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const scanFrame = document.querySelector('.scan-frame');
|
||||||
|
if (scanFrame) {
|
||||||
|
this.scanFrameRect = scanFrame.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
this.startAutoScan();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err2 => {
|
||||||
|
console.error('所有摄像头访问失败:', err2);
|
||||||
|
this.handleCameraError('无法访问摄像头,请检查设备是否有摄像头并授予权限');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleStreamSuccess(stream) {
|
||||||
|
this.stream = stream;
|
||||||
|
this.videoSrc = URL.createObjectURL(stream);
|
||||||
|
this.isCameraReady = true;
|
||||||
|
|
||||||
|
if (this.stream.getVideoTracks()[0].label.toLowerCase().includes('front')) {
|
||||||
|
this.cameraStatusText = '正在使用前置摄像头,建议使用后置摄像头扫码';
|
||||||
|
} else {
|
||||||
|
this.cameraStatusText = '将二维码放入框内,自动扫描';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
startAutoScan() {
|
||||||
|
this.stopAutoScan();
|
||||||
|
|
||||||
|
const settings = this.getVideoStreamSettings();
|
||||||
|
const interval = settings?.height > 720 ? 800 : 500;
|
||||||
|
|
||||||
|
this.scanInterval = setInterval(() => {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - this.lastScanTime > interval) {
|
||||||
|
this.captureAndDecode();
|
||||||
|
this.lastScanTime = now;
|
||||||
|
}
|
||||||
|
}, interval);
|
||||||
|
},
|
||||||
|
|
||||||
|
getVideoStreamSettings() {
|
||||||
|
if (this.stream) {
|
||||||
|
const track = this.stream.getVideoTracks()[0];
|
||||||
|
return track.getSettings();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
stopAutoScan() {
|
||||||
|
if (this.scanInterval) {
|
||||||
|
clearInterval(this.scanInterval);
|
||||||
|
this.scanInterval = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async captureAndDecode() {
|
||||||
|
if (!this.isCameraReady || !this.scanFrameRect) return;
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const video = document.querySelector('.preview-video');
|
||||||
|
|
||||||
|
if (!video || video.readyState !== 4) return;
|
||||||
|
|
||||||
|
const frameWidth = this.scanFrameRect.width;
|
||||||
|
const frameHeight = this.scanFrameRect.height;
|
||||||
|
canvas.width = frameWidth;
|
||||||
|
canvas.height = frameHeight;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
ctx.drawImage(
|
||||||
|
video,
|
||||||
|
this.scanFrameRect.left, this.scanFrameRect.top, frameWidth, frameHeight,
|
||||||
|
0, 0, frameWidth, frameHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
const base64 = canvas.toDataURL('image/jpeg');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const codeRes = await QROptimizer.enhancedDecode(base64);
|
||||||
|
|
||||||
|
if (codeRes.indexOf('error') === -1) {
|
||||||
|
this.stopAutoScan();
|
||||||
|
this.stopCamera();
|
||||||
|
this.isScanning = false;
|
||||||
|
|
||||||
|
let r = this.decodeStr(codeRes);
|
||||||
|
this.qrCodeRes = r;
|
||||||
|
this.handleScanResult(r);
|
||||||
|
} else {
|
||||||
|
const qrSize = await this.estimateQRCodeSize(base64);
|
||||||
|
if (qrSize < 0.3) {
|
||||||
|
this.cameraStatusText = '二维码太小,请靠近些';
|
||||||
|
} else if (qrSize > 0.9) {
|
||||||
|
this.cameraStatusText = '二维码太大,请离远些';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("解析失败", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
estimateQRCodeSize(base64) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = base64;
|
||||||
|
img.onload = () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
|
||||||
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
const data = imageData.data;
|
||||||
|
|
||||||
|
let activePixels = 0;
|
||||||
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
|
if (data[i] < 128) activePixels++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ratio = activePixels / (canvas.width * canvas.height);
|
||||||
|
resolve(Math.min(ratio * 10, 1));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCameraError(message) {
|
||||||
|
this.cameraStatusText = message;
|
||||||
|
this.isCameraReady = false;
|
||||||
|
uni.showToast({
|
||||||
|
title: message,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
this.redirectToWorkbench(); // 摄像头错误跳回工作台
|
||||||
|
},
|
||||||
|
|
||||||
|
handleVideoError(error) {
|
||||||
|
console.error('视频元素错误:', error);
|
||||||
|
this.handleCameraError('视频加载失败,请重试');
|
||||||
|
},
|
||||||
|
|
||||||
|
retryCamera() {
|
||||||
|
this.stopCamera();
|
||||||
|
if (this.permissionDenied) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '权限不足',
|
||||||
|
content: '请在浏览器设置中允许摄像头权限,然后重试',
|
||||||
|
showCancel: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.startCustomScanner();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
stopCamera() {
|
||||||
|
this.stopAutoScan();
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.getTracks().forEach(track => {
|
||||||
|
track.stop();
|
||||||
|
});
|
||||||
|
this.stream = null;
|
||||||
|
}
|
||||||
|
this.videoSrc = '';
|
||||||
|
this.isCameraReady = false;
|
||||||
|
this.scanFrameRect = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelScan() {
|
||||||
|
this.isScanning = false;
|
||||||
|
this.stopCamera();
|
||||||
|
this.redirectToWorkbench(); // 取消扫码跳回工作台
|
||||||
|
},
|
||||||
|
|
||||||
|
scanCodeAPP() {
|
||||||
|
uni.scanCode({
|
||||||
|
scanType: ['qrCode'],
|
||||||
|
onlyFromCamera: true,
|
||||||
|
success: (res) => {
|
||||||
|
this.qrCodeRes = res.result
|
||||||
|
this.handleScanResult(res.result);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.log("扫码失败", err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '扫码失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
this.redirectToWorkbench(); // APP扫码失败跳回工作台
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleScanResult(result) {
|
||||||
|
getInspectionManage(result)
|
||||||
|
.then(res => {
|
||||||
|
if (res.data.inspectionStatus === "1") {
|
||||||
|
let inspectionPoint = res.data.inspectionPoint;
|
||||||
|
let inspectionRequirements = res.data.inspectionRequirements;
|
||||||
|
let inspectionPointId = res.data.id
|
||||||
|
uni.reLaunch({
|
||||||
|
url: `/pages/work/inspection/scanSign/index?inspectionPoint=${inspectionPoint}&inspectionRequirements=${inspectionRequirements}&inspectionPointId=${inspectionPointId}`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '验证码已失效!',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
this.redirectToWorkbench(); // 验证码失效跳回工作台
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log("请求错误", err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '服务器错误',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
this.redirectToWorkbench(); // 请求错误跳回工作台
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增方法:跳转回工作台
|
||||||
|
redirectToWorkbench() {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/work/index'
|
||||||
|
});
|
||||||
|
}, 1500); // 1.5秒后跳转,让用户看到提示信息
|
||||||
|
},
|
||||||
|
|
||||||
|
decodeStr(str) {
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(escape(str));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("解码失败,返回原字符串:", str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.container {
|
.container {
|
||||||
padding: 10px;
|
padding: 0;
|
||||||
}
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanner-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanner-header {
|
||||||
|
height: 45px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 15px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-preview {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 45px);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-button-cover {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 999;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
background-color: #000;
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-preview::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1;
|
||||||
|
clip-path: polygon(
|
||||||
|
0% 0%,
|
||||||
|
0% 100%,
|
||||||
|
calc(50% - 140px) 100%,
|
||||||
|
calc(50% - 140px) calc(50% - 105px),
|
||||||
|
calc(50% + 140px) calc(50% - 105px),
|
||||||
|
calc(50% + 140px) calc(50% + 105px),
|
||||||
|
calc(50% - 140px) calc(50% + 105px),
|
||||||
|
calc(50% - 140px) 100%,
|
||||||
|
100% 100%,
|
||||||
|
100% 0%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-frame {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 280px;
|
||||||
|
height: 210px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
border: 1px solid rgba(0, 255, 0, 0.5);
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-line {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #0f0;
|
||||||
|
transition: top 0.05s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.corner {
|
||||||
|
position: absolute;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-color: #0f0;
|
||||||
|
border-style: solid;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-left {
|
||||||
|
top: -1px;
|
||||||
|
left: -1px;
|
||||||
|
border-width: 3px 0 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-right {
|
||||||
|
top: -1px;
|
||||||
|
right: -1px;
|
||||||
|
border-width: 3px 3px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-left {
|
||||||
|
bottom: -1px;
|
||||||
|
left: -1px;
|
||||||
|
border-width: 0 0 3px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-right {
|
||||||
|
bottom: -1px;
|
||||||
|
right: -1px;
|
||||||
|
border-width: 0 3px 3px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-tip {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(50% + 120px);
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-not-supported {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #ff5252;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-desc {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions {
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retry-btn {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 8px 20px;
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-container {
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #fff;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
Reference in New Issue
Block a user