ocr
This commit is contained in:
parent
a19de9187a
commit
236d4e237c
|
|
@ -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>
|
||||
|
|
@ -389,7 +375,7 @@ export default {
|
|||
|
||||
console.log('开始自动聚焦...');
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
await new Promise((resolve, reject)=> {
|
||||
// 设置聚焦超时
|
||||
this.focusTimeout = setTimeout(() => {
|
||||
console.log('聚焦超时');
|
||||
|
|
@ -468,7 +454,7 @@ export default {
|
|||
clearTimeout(this.focusTimeout);
|
||||
}
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
await new Promise((resolve, reject)=> {
|
||||
// 设置聚焦超时
|
||||
this.focusTimeout = setTimeout(() => {
|
||||
console.log('点击聚焦超时');
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,44 +134,48 @@ class ImageUtils {
|
|||
*/
|
||||
static async resizeImageWithCanvas(imagePath, maxWidth, maxHeight) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 获取图片信息
|
||||
window.uni.getImageInfo({
|
||||
src: imagePath,
|
||||
success: (imageInfo) => {
|
||||
const { width, height } = imageInfo
|
||||
try {
|
||||
const uniInstance = this.getUni()
|
||||
|
||||
// 计算缩放比例
|
||||
const scale = this.calculateScale(width, height, maxWidth, maxHeight)
|
||||
const newWidth = Math.floor(width * scale)
|
||||
const newHeight = Math.floor(height * scale)
|
||||
// 获取图片信息
|
||||
this.getImageInfo(imagePath)
|
||||
.then((imageInfo) => {
|
||||
const { width, height } = imageInfo
|
||||
|
||||
// 如果不需要缩放,直接返回原图
|
||||
if (scale >= 1) {
|
||||
resolve(imagePath)
|
||||
return
|
||||
}
|
||||
// 计算缩放比例
|
||||
const scale = this.calculateScale(width, height, maxWidth, maxHeight)
|
||||
const newWidth = Math.floor(width * scale)
|
||||
const newHeight = Math.floor(height * scale)
|
||||
|
||||
// 创建canvas上下文
|
||||
const canvasId = "imageProcessCanvas_" + Date.now()
|
||||
const ctx = window.uni.createCanvasContext(canvasId)
|
||||
// 如果不需要缩放,直接返回原图
|
||||
if (scale >= 1) {
|
||||
resolve(imagePath)
|
||||
return
|
||||
}
|
||||
|
||||
// 绘制缩放后的图片
|
||||
ctx.drawImage(imagePath, 0, 0, newWidth, newHeight)
|
||||
ctx.draw(false, () => {
|
||||
// 导出canvas为临时文件
|
||||
window.uni.canvasToTempFilePath({
|
||||
canvasId: canvasId,
|
||||
fileType: "jpg",
|
||||
quality: 0.8,
|
||||
success: (res) => {
|
||||
resolve(res.tempFilePath)
|
||||
},
|
||||
fail: reject,
|
||||
// 创建canvas上下文
|
||||
const canvasId = "imageProcessCanvas_" + Date.now()
|
||||
const ctx = uniInstance.createCanvasContext(canvasId)
|
||||
|
||||
// 绘制缩放后的图片
|
||||
ctx.drawImage(imagePath, 0, 0, newWidth, newHeight)
|
||||
ctx.draw(false, () => {
|
||||
// 导出canvas为临时文件
|
||||
uniInstance.canvasToTempFilePath({
|
||||
canvasId: canvasId,
|
||||
fileType: "jpg",
|
||||
quality: 0.8,
|
||||
success: (res) => {
|
||||
resolve(res.tempFilePath)
|
||||
},
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
fail: reject,
|
||||
})
|
||||
.catch(reject)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -122,14 +188,19 @@ class ImageUtils {
|
|||
*/
|
||||
static async fallbackCompress(imagePath, maxWidth, maxHeight) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.uni.compressImage({
|
||||
src: imagePath,
|
||||
quality: 80,
|
||||
compressedWidth: maxWidth,
|
||||
compressedHeight: maxHeight,
|
||||
success: (res) => resolve(res.tempFilePath),
|
||||
fail: reject,
|
||||
})
|
||||
try {
|
||||
const uniInstance = this.getUni()
|
||||
uniInstance.compressImage({
|
||||
src: imagePath,
|
||||
quality: 80,
|
||||
compressedWidth: maxWidth,
|
||||
compressedHeight: maxHeight,
|
||||
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({
|
||||
src: imagePath,
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
})
|
||||
try {
|
||||
const uniInstance = this.getUni()
|
||||
uniInstance.getImageInfo({
|
||||
src: imagePath,
|
||||
success: resolve,
|
||||
fail: reject,
|
||||
})
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue