This commit is contained in:
hayu 2025-07-31 18:49:42 +08:00
parent a19de9187a
commit 236d4e237c
2 changed files with 236 additions and 138 deletions

View File

@ -78,20 +78,6 @@
<!-- 相机预览页面 --> <!-- 相机预览页面 -->
<view v-if="showCamera" class="camera-container"> <view v-if="showCamera" class="camera-container">
<!-- 遮罩层,中间透明 -->
<view class="camera-overlay">
<!-- 顶部遮罩 -->
<view class="overlay-top"></view>
<!-- 中间行:左遮罩 + 透明区域 + 右遮罩 -->
<view class="overlay-middle">
<view class="overlay-left"></view>
<view class="overlay-transparent"></view>
<view class="overlay-right"></view>
</view>
<!-- 底部遮罩 -->
<view class="overlay-bottom"></view>
</view>
<!-- 顶部提示 --> <!-- 顶部提示 -->
<view class="top-tip"> <view class="top-tip">
<text class="tip-text">请将识别编码置于取景框内,完成扫描</text> <text class="tip-text">请将识别编码置于取景框内,完成扫描</text>
@ -389,7 +375,7 @@ export default {
console.log('开始自动聚焦...'); console.log('开始自动聚焦...');
await new Promise((resolve, reject) => { await new Promise((resolve, reject)=> {
// 设置聚焦超时 // 设置聚焦超时
this.focusTimeout = setTimeout(() => { this.focusTimeout = setTimeout(() => {
console.log('聚焦超时'); console.log('聚焦超时');
@ -468,7 +454,7 @@ export default {
clearTimeout(this.focusTimeout); clearTimeout(this.focusTimeout);
} }
await new Promise((resolve, reject) => { await new Promise((resolve, reject)=> {
// 设置聚焦超时 // 设置聚焦超时
this.focusTimeout = setTimeout(() => { this.focusTimeout = setTimeout(() => {
console.log('点击聚焦超时'); console.log('点击聚焦超时');
@ -506,7 +492,9 @@ export default {
resolve(); // 即使聚焦失败也继续 resolve(); // 即使聚焦失败也继续
} }
); );
}); }
)
;
} catch (error) { } catch (error) {
console.error('点击聚焦过程出错:', error); console.error('点击聚焦过程出错:', error);
this.isFocusing = false; this.isFocusing = false;
@ -591,7 +579,8 @@ export default {
await this.performFocusBeforeCapture(); await this.performFocusBeforeCapture();
console.log('开始拍照...'); console.log('开始拍照...');
const imageData = await new Promise((resolve, reject) => { const imageData = await new Promise((resolve, reject)=>
{
const options = { const options = {
quality: 85, quality: 85,
targetHeight: 1920, targetHeight: 1920,
@ -609,7 +598,9 @@ export default {
reject(new Error(`拍照失败: ${error}`)); reject(new Error(`拍照失败: ${error}`));
} }
); );
}); }
)
;
await this.processImage(imageData); await this.processImage(imageData);
} catch (error) { } catch (error) {
@ -636,7 +627,8 @@ export default {
console.log('拍照前执行聚焦...'); console.log('拍照前执行聚焦...');
await new Promise((resolve, reject) => { await new Promise((resolve, reject)=>
{
// 设置聚焦超时 // 设置聚焦超时
const focusTimeout = setTimeout(() => { const focusTimeout = setTimeout(() => {
console.log('拍照前聚焦超时,继续拍照'); console.log('拍照前聚焦超时,继续拍照');
@ -659,7 +651,9 @@ export default {
resolve(); // 即使聚焦失败也继续拍照 resolve(); // 即使聚焦失败也继续拍照
} }
); );
}); }
)
;
} catch (error) { } catch (error) {
console.error('拍照前聚焦过程出错:', error); console.error('拍照前聚焦过程出错:', error);
this.isFocusing = false; this.isFocusing = false;
@ -680,7 +674,8 @@ export default {
}); });
console.log('图片处理完成开始OCR识别...'); console.log('图片处理完成开始OCR识别...');
const response = await new Promise((resolve, reject) => { const response = await new Promise((resolve, reject)=>
{
uni.request({ uni.request({
url: '/material/app/ocr/getOcrCode', url: '/material/app/ocr/getOcrCode',
method: 'POST', method: 'POST',
@ -693,7 +688,9 @@ export default {
success: resolve, success: resolve,
fail: reject fail: reject
}); });
}); }
)
;
if (response.data?.data?.result) { if (response.data?.data?.result) {
this.queryCodeParams.maCode = response.data.data.result; this.queryCodeParams.maCode = response.data.data.result;
@ -754,7 +751,8 @@ export default {
}); });
console.log('相册图片处理完成开始OCR识别...'); console.log('相册图片处理完成开始OCR识别...');
const response = await new Promise((resolve, reject) => { const response = await new Promise((resolve, reject)=>
{
uni.request({ uni.request({
url: '/material/app/ocr/getOcrCode', url: '/material/app/ocr/getOcrCode',
method: 'POST', method: 'POST',
@ -767,7 +765,9 @@ export default {
success: resolve, success: resolve,
fail: reject fail: reject
}); });
}); }
)
;
if (response.data?.data?.result) { if (response.data?.data?.result) {
this.queryCodeParams.maCode = response.data.data.result; this.queryCodeParams.maCode = response.data.data.result;
@ -804,7 +804,8 @@ export default {
return; return;
} }
try { try {
const response = await new Promise((resolve, reject) => { const response = await new Promise((resolve, reject)=>
{
uni.request({ uni.request({
url: '/material/ma_machine/getHisByCode', url: '/material/ma_machine/getHisByCode',
method: 'GET', method: 'GET',
@ -812,7 +813,9 @@ export default {
success: resolve, success: resolve,
fail: reject fail: reject
}); });
}); }
)
;
if (response.data?.data && response.data.data.length > 0) { if (response.data?.data && response.data.data.length > 0) {
this.optionList = response.data.data.map(option => ({ this.optionList = response.data.data.map(option => ({
@ -841,7 +844,8 @@ export default {
async changeTag() { async changeTag() {
if (!this.queryCodeParams.maId) return; if (!this.queryCodeParams.maId) return;
try { try {
const response = await new Promise((resolve, reject) => { const response = await new Promise((resolve, reject)=>
{
uni.request({ uni.request({
url: '/material/ma_machine/getHisByCode', url: '/material/ma_machine/getHisByCode',
method: 'GET', method: 'GET',
@ -849,7 +853,9 @@ export default {
success: resolve, success: resolve,
fail: reject fail: reject
}); });
}); }
)
;
if (response.data?.data && response.data.data.length > 0) { if (response.data?.data && response.data.data.length > 0) {
this.codeData = response.data.data[0]; this.codeData = response.data.data[0];
@ -940,56 +946,10 @@ export default {
left: 0; left: 0;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
background-color: transparent; /* 改为透明,让相机预览显示 */ background-color: transparent;
z-index: 99999; z-index: 99999;
} }
/* 遮罩层样式 */
.camera-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.overlay-top {
width: 100%;
height: calc(50% - 300rpx); /* 取景框上方的遮罩 */
background-color: rgba(0, 0, 0, 0.6);
}
.overlay-middle {
display: flex;
width: 100%;
height: 600rpx; /* 取景框的高度 */
}
.overlay-left {
width: calc(50% - 250rpx); /* 取景框左侧的遮罩 */
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
}
.overlay-transparent {
width: 500rpx; /* 取景框的宽度 */
height: 100%;
background-color: transparent; /* 透明区域,显示相机预览 */
}
.overlay-right {
width: calc(50% - 250rpx); /* 取景框右侧的遮罩 */
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
}
.overlay-bottom {
width: 100%;
height: calc(50% - 300rpx); /* 取景框下方的遮罩 */
background-color: rgba(0, 0, 0, 0.6);
}
.top-tip { .top-tip {
position: absolute; position: absolute;
top: 80rpx; top: 80rpx;
@ -1001,7 +961,7 @@ export default {
} }
.tip-text { .tip-text {
background-color: rgba(0, 0, 0, 0.7); background-color: rgba(0, 0, 0, 0.5);
color: #fff; color: #fff;
font-size: 32rpx; font-size: 32rpx;
font-weight: bold; font-weight: bold;
@ -1040,9 +1000,10 @@ export default {
position: relative; position: relative;
width: 500rpx; width: 500rpx;
height: 600rpx; height: 600rpx;
background-color: transparent; /* 透明背景 */ background-color: transparent;
border: none; /* 移除边框 */ border: 2rpx solid rgba(255, 255, 255, 0.5);
border-radius: 20rpx; border-radius: 20rpx;
box-shadow: 0 0 20rpx rgba(75, 142, 255, 0.5);
} }
/* 扫描线动画 */ /* 扫描线动画 */
@ -1052,7 +1013,7 @@ export default {
left: 0; left: 0;
width: 100%; width: 100%;
height: 4rpx; height: 4rpx;
background: linear-gradient(90deg, transparent, #4b8eff, transparent); background: linear-gradient(90deg, transparent, rgba(75, 142, 255, 0.8), transparent);
animation: scan 2s linear infinite; animation: scan 2s linear infinite;
border-radius: 2rpx; border-radius: 2rpx;
} }
@ -1075,7 +1036,7 @@ export default {
position: absolute; position: absolute;
width: 60rpx; width: 60rpx;
height: 60rpx; height: 60rpx;
border: 6rpx solid #4b8eff; border: 6rpx solid rgba(75, 142, 255, 0.8);
background-color: transparent; background-color: transparent;
} }
@ -1165,7 +1126,7 @@ export default {
.control-btn { .control-btn {
width: 100rpx; width: 100rpx;
height: 100rpx; height: 100rpx;
background-color: rgba(255, 255, 255, 0.2); background-color: rgba(0, 0, 0, 0.3);
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -1182,12 +1143,12 @@ export default {
.photo-btn { .photo-btn {
width: 140rpx; width: 140rpx;
height: 140rpx; height: 140rpx;
background-color: rgba(255, 255, 255, 0.2); background-color: rgba(0, 0, 0, 0.3);
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border: 6rpx solid #fff; border: 6rpx solid rgba(255, 255, 255, 0.5);
transition: all 0.2s ease; transition: all 0.2s ease;
} }
@ -1204,7 +1165,7 @@ export default {
.photo-btn-inner { .photo-btn-inner {
width: 100rpx; width: 100rpx;
height: 100rpx; height: 100rpx;
background-color: #fff; background-color: rgba(255, 255, 255, 0.9);
border-radius: 50%; border-radius: 50%;
transition: all 0.2s ease; transition: all 0.2s ease;
} }

View File

@ -1,5 +1,17 @@
// 图片处理工具类 // 修复后的图片处理工具类
class ImageUtils { class ImageUtils {
/**
* 获取 uni 对象的引用
*/
static getUni() {
// 尝试多种方式获取 uni 对象
const uni = window.uni || global.uni
if (typeof uni !== "undefined") {
return uni
}
throw new Error("uni 对象不可用")
}
/** /**
* 将图片转换为Base64字符串相当于Android的bitmapToString * 将图片转换为Base64字符串相当于Android的bitmapToString
* @param {string} imagePath 图片路径 * @param {string} imagePath 图片路径
@ -8,6 +20,8 @@ class ImageUtils {
*/ */
static async imageToBase64(imagePath, quality = 50) { static async imageToBase64(imagePath, quality = 50) {
try { try {
const uniInstance = this.getUni()
// 如果已经是base64格式直接返回数据部分 // 如果已经是base64格式直接返回数据部分
if (imagePath.startsWith("data:image/")) { if (imagePath.startsWith("data:image/")) {
return imagePath.split(",")[1] return imagePath.split(",")[1]
@ -20,7 +34,7 @@ class ImageUtils {
// 压缩图片 // 压缩图片
const compressResult = await new Promise((resolve, reject) => { const compressResult = await new Promise((resolve, reject) => {
window.uni.compressImage({ uniInstance.compressImage({
src: imagePath, src: imagePath,
quality: quality, quality: quality,
success: resolve, success: resolve,
@ -29,7 +43,7 @@ class ImageUtils {
}) })
// 读取压缩后的文件并转换为base64 // 读取压缩后的文件并转换为base64
const fileManager = window.uni.getFileSystemManager() const fileManager = uniInstance.getFileSystemManager()
const base64Data = await new Promise((resolve, reject) => { const base64Data = await new Promise((resolve, reject) => {
fileManager.readFile({ fileManager.readFile({
filePath: compressResult.tempFilePath, filePath: compressResult.tempFilePath,
@ -42,10 +56,58 @@ class ImageUtils {
return base64Data return base64Data
} catch (error) { } catch (error) {
console.error("Image to base64 conversion failed:", error) console.error("Image to base64 conversion failed:", error)
throw error // 如果上述方法失败,尝试使用 Canvas 方法
return await this.canvasToBase64(imagePath, quality)
} }
} }
/**
* 使用 Canvas 将图片转换为 Base64
* @param {string} imagePath 图片路径
* @param {number} quality 质量
* @returns {Promise<string>} Base64字符串
*/
static async canvasToBase64(imagePath, quality = 50) {
return new Promise((resolve, reject) => {
try {
const uniInstance = this.getUni()
const canvasId = "base64Canvas_" + Date.now()
// 获取图片信息
this.getImageInfo(imagePath)
.then((imageInfo) => {
const { width, height } = imageInfo
const ctx = uniInstance.createCanvasContext(canvasId)
// 绘制图片到 canvas
ctx.drawImage(imagePath, 0, 0, width, height)
ctx.draw(false, () => {
// 导出为临时文件
uniInstance.canvasToTempFilePath({
canvasId: canvasId,
fileType: "jpg",
quality: quality / 100,
success: (res) => {
// 读取文件为 base64
const fileManager = uniInstance.getFileSystemManager()
fileManager.readFile({
filePath: res.tempFilePath,
encoding: "base64",
success: (fileRes) => resolve(fileRes.data),
fail: reject,
})
},
fail: reject,
})
})
})
.catch(reject)
} catch (error) {
reject(error)
}
})
}
/** /**
* 获取缩放后的图片相当于Android的getSmallBitmap * 获取缩放后的图片相当于Android的getSmallBitmap
* @param {string} imagePath 图片路径 * @param {string} imagePath 图片路径
@ -72,44 +134,48 @@ class ImageUtils {
*/ */
static async resizeImageWithCanvas(imagePath, maxWidth, maxHeight) { static async resizeImageWithCanvas(imagePath, maxWidth, maxHeight) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 获取图片信息 try {
window.uni.getImageInfo({ const uniInstance = this.getUni()
src: imagePath,
success: (imageInfo) => {
const { width, height } = imageInfo
// 计算缩放比例 // 获取图片信息
const scale = this.calculateScale(width, height, maxWidth, maxHeight) this.getImageInfo(imagePath)
const newWidth = Math.floor(width * scale) .then((imageInfo) => {
const newHeight = Math.floor(height * scale) const { width, height } = imageInfo
// 如果不需要缩放,直接返回原图 // 计算缩放比例
if (scale >= 1) { const scale = this.calculateScale(width, height, maxWidth, maxHeight)
resolve(imagePath) const newWidth = Math.floor(width * scale)
return const newHeight = Math.floor(height * scale)
}
// 创建canvas上下文 // 如果不需要缩放,直接返回原图
const canvasId = "imageProcessCanvas_" + Date.now() if (scale >= 1) {
const ctx = window.uni.createCanvasContext(canvasId) resolve(imagePath)
return
}
// 绘制缩放后的图片 // 创建canvas上下文
ctx.drawImage(imagePath, 0, 0, newWidth, newHeight) const canvasId = "imageProcessCanvas_" + Date.now()
ctx.draw(false, () => { const ctx = uniInstance.createCanvasContext(canvasId)
// 导出canvas为临时文件
window.uni.canvasToTempFilePath({ // 绘制缩放后的图片
canvasId: canvasId, ctx.drawImage(imagePath, 0, 0, newWidth, newHeight)
fileType: "jpg", ctx.draw(false, () => {
quality: 0.8, // 导出canvas为临时文件
success: (res) => { uniInstance.canvasToTempFilePath({
resolve(res.tempFilePath) canvasId: canvasId,
}, fileType: "jpg",
fail: reject, quality: 0.8,
success: (res) => {
resolve(res.tempFilePath)
},
fail: reject,
})
}) })
}) })
}, .catch(reject)
fail: reject, } catch (error) {
}) reject(error)
}
}) })
} }
@ -122,14 +188,19 @@ class ImageUtils {
*/ */
static async fallbackCompress(imagePath, maxWidth, maxHeight) { static async fallbackCompress(imagePath, maxWidth, maxHeight) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
window.uni.compressImage({ try {
src: imagePath, const uniInstance = this.getUni()
quality: 80, uniInstance.compressImage({
compressedWidth: maxWidth, src: imagePath,
compressedHeight: maxHeight, quality: 80,
success: (res) => resolve(res.tempFilePath), compressedWidth: maxWidth,
fail: reject, compressedHeight: maxHeight,
}) success: (res) => resolve(res.tempFilePath),
fail: reject,
})
} catch (error) {
reject(error)
}
}) })
} }
@ -145,10 +216,8 @@ class ImageUtils {
if (originalWidth <= maxWidth && originalHeight <= maxHeight) { if (originalWidth <= maxWidth && originalHeight <= maxHeight) {
return 1 // 不需要缩放 return 1 // 不需要缩放
} }
const widthScale = maxWidth / originalWidth const widthScale = maxWidth / originalWidth
const heightScale = maxHeight / originalHeight const heightScale = maxHeight / originalHeight
// 选择较小的缩放比例,确保图片完全适应限制尺寸 // 选择较小的缩放比例,确保图片完全适应限制尺寸
return Math.min(widthScale, heightScale) return Math.min(widthScale, heightScale)
} }
@ -163,17 +232,80 @@ class ImageUtils {
const { maxWidth = 1024, maxHeight = 1024, quality = 50, outputFormat = "base64" } = options const { maxWidth = 1024, maxHeight = 1024, quality = 50, outputFormat = "base64" } = options
try { try {
console.log("开始处理图片:", imagePath)
// 如果已经是 base64 格式,直接处理
if (imagePath.startsWith("data:image/")) {
if (outputFormat === "base64") {
return imagePath.split(",")[1]
} else {
return imagePath
}
}
// 1. 先缩放图片 // 1. 先缩放图片
const resizedImagePath = await this.getSmallImage(imagePath, maxWidth, maxHeight) const resizedImagePath = await this.getSmallImage(imagePath, maxWidth, maxHeight)
console.log("图片缩放完成:", resizedImagePath)
// 2. 根据输出格式返回结果 // 2. 根据输出格式返回结果
if (outputFormat === "base64") { if (outputFormat === "base64") {
return await this.imageToBase64(resizedImagePath, quality) const base64Result = await this.imageToBase64(resizedImagePath, quality)
console.log("图片转换为base64完成")
return base64Result
} else { } else {
return resizedImagePath return resizedImagePath
} }
} catch (error) { } catch (error) {
console.error("Process image failed:", error) console.error("Process image failed:", error)
// 如果所有方法都失败,尝试简单的压缩
return await this.simpleProcess(imagePath, options)
}
}
/**
* 简单处理方法备用
* @param {string} imagePath 图片路径
* @param {object} options 处理选项
* @returns {Promise<string>} 处理后的结果
*/
static async simpleProcess(imagePath, options = {}) {
const { quality = 50, outputFormat = "base64" } = options
try {
const uniInstance = this.getUni()
// 如果已经是 base64直接返回
if (imagePath.startsWith("data:image/")) {
return outputFormat === "base64" ? imagePath.split(",")[1] : imagePath
}
// 简单压缩
const compressResult = await new Promise((resolve, reject) => {
uniInstance.compressImage({
src: imagePath,
quality: quality,
success: resolve,
fail: reject,
})
})
if (outputFormat === "base64") {
// 转换为 base64
const fileManager = uniInstance.getFileSystemManager()
const base64Data = await new Promise((resolve, reject) => {
fileManager.readFile({
filePath: compressResult.tempFilePath,
encoding: "base64",
success: (res) => resolve(res.data),
fail: reject,
})
})
return base64Data
} else {
return compressResult.tempFilePath
}
} catch (error) {
console.error("Simple process failed:", error)
throw error throw error
} }
} }
@ -185,11 +317,16 @@ class ImageUtils {
*/ */
static async getImageInfo(imagePath) { static async getImageInfo(imagePath) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
window.uni.getImageInfo({ try {
src: imagePath, const uniInstance = this.getUni()
success: resolve, uniInstance.getImageInfo({
fail: reject, src: imagePath,
}) success: resolve,
fail: reject,
})
} catch (error) {
reject(error)
}
}) })
} }
} }