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

View File

@ -1,5 +1,17 @@
// 图片处理工具类
// 修复后的图片处理工具类
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
* @param {string} imagePath 图片路径
@ -8,6 +20,8 @@ class ImageUtils {
*/
static async imageToBase64(imagePath, quality = 50) {
try {
const uniInstance = this.getUni()
// 如果已经是base64格式直接返回数据部分
if (imagePath.startsWith("data:image/")) {
return imagePath.split(",")[1]
@ -20,7 +34,7 @@ class ImageUtils {
// 压缩图片
const compressResult = await new Promise((resolve, reject) => {
window.uni.compressImage({
uniInstance.compressImage({
src: imagePath,
quality: quality,
success: resolve,
@ -29,7 +43,7 @@ class ImageUtils {
})
// 读取压缩后的文件并转换为base64
const fileManager = window.uni.getFileSystemManager()
const fileManager = uniInstance.getFileSystemManager()
const base64Data = await new Promise((resolve, reject) => {
fileManager.readFile({
filePath: compressResult.tempFilePath,
@ -42,10 +56,58 @@ class ImageUtils {
return base64Data
} catch (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
* @param {string} imagePath 图片路径
@ -72,10 +134,12 @@ class ImageUtils {
*/
static async resizeImageWithCanvas(imagePath, maxWidth, maxHeight) {
return new Promise((resolve, reject) => {
try {
const uniInstance = this.getUni()
// 获取图片信息
window.uni.getImageInfo({
src: imagePath,
success: (imageInfo) => {
this.getImageInfo(imagePath)
.then((imageInfo) => {
const { width, height } = imageInfo
// 计算缩放比例
@ -91,13 +155,13 @@ class ImageUtils {
// 创建canvas上下文
const canvasId = "imageProcessCanvas_" + Date.now()
const ctx = window.uni.createCanvasContext(canvasId)
const ctx = uniInstance.createCanvasContext(canvasId)
// 绘制缩放后的图片
ctx.drawImage(imagePath, 0, 0, newWidth, newHeight)
ctx.draw(false, () => {
// 导出canvas为临时文件
window.uni.canvasToTempFilePath({
uniInstance.canvasToTempFilePath({
canvasId: canvasId,
fileType: "jpg",
quality: 0.8,
@ -107,9 +171,11 @@ class ImageUtils {
fail: reject,
})
})
},
fail: reject,
})
.catch(reject)
} catch (error) {
reject(error)
}
})
}
@ -122,7 +188,9 @@ class ImageUtils {
*/
static async fallbackCompress(imagePath, maxWidth, maxHeight) {
return new Promise((resolve, reject) => {
window.uni.compressImage({
try {
const uniInstance = this.getUni()
uniInstance.compressImage({
src: imagePath,
quality: 80,
compressedWidth: maxWidth,
@ -130,6 +198,9 @@ class ImageUtils {
success: (res) => resolve(res.tempFilePath),
fail: reject,
})
} catch (error) {
reject(error)
}
})
}
@ -145,10 +216,8 @@ class ImageUtils {
if (originalWidth <= maxWidth && originalHeight <= maxHeight) {
return 1 // 不需要缩放
}
const widthScale = maxWidth / originalWidth
const heightScale = maxHeight / originalHeight
// 选择较小的缩放比例,确保图片完全适应限制尺寸
return Math.min(widthScale, heightScale)
}
@ -163,17 +232,80 @@ class ImageUtils {
const { maxWidth = 1024, maxHeight = 1024, quality = 50, outputFormat = "base64" } = options
try {
console.log("开始处理图片:", imagePath)
// 如果已经是 base64 格式,直接处理
if (imagePath.startsWith("data:image/")) {
if (outputFormat === "base64") {
return imagePath.split(",")[1]
} else {
return imagePath
}
}
// 1. 先缩放图片
const resizedImagePath = await this.getSmallImage(imagePath, maxWidth, maxHeight)
console.log("图片缩放完成:", resizedImagePath)
// 2. 根据输出格式返回结果
if (outputFormat === "base64") {
return await this.imageToBase64(resizedImagePath, quality)
const base64Result = await this.imageToBase64(resizedImagePath, quality)
console.log("图片转换为base64完成")
return base64Result
} else {
return resizedImagePath
}
} catch (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
}
}
@ -185,11 +317,16 @@ class ImageUtils {
*/
static async getImageInfo(imagePath) {
return new Promise((resolve, reject) => {
window.uni.getImageInfo({
try {
const uniInstance = this.getUni()
uniInstance.getImageInfo({
src: imagePath,
success: resolve,
fail: reject,
})
} catch (error) {
reject(error)
}
})
}
}