@zuopngfei 746614c2cc dsdd
2025-08-04 18:11:46 +08:00

208 lines
7.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export const getBOcr = (url) => {
return new Promise((resolve, reject) => {
wx.request({
url: 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions',
method: 'POST',
dataType: 'json', // 微信官方文档中介绍会对数据进行一次JSON.parse
header: {
'Authorization': 'Bearer sk-52414b887aee47e4883caf16cbf801bd',
'Content-Type': 'application/json'
},
data: {
"model": "qwen-vl-ocr-latest",
"messages": [{
"role": "user",
"content": [{
"type": "image_url",
"image_url": {
"url": url
},
"min_pixels": 3136,
"max_pixels": 6422528
},
{
"type": "text",
"text": "要求准确无误的提取上述关键信息、不要遗漏和捏造虚假信息,模糊或者强光遮挡的单个文字可以用英文问号?代替。"
}
]
}]
},
success(res) {
// console.log(res.data)
// return
let data = parseBUltraReportText(res.data.choices[0].message.content)
if(data.length == 0){
wx.showToast({
title: '识别失败,请重新选择照片!',
icon: 'none',
duration: 5000
});
reject('识别失败!')
return
}
// 判断是否为 rotate_rect 格式
if (Array.isArray(data) && data.length && data[0].rotate_rect) {
let items = extractCheckItemsFromOcrTable(data);
if(items.length==0){
wx.showToast({
title: '识别失败,请重新选择照片!',
icon: 'none',
duration: 5000
});
reject('识别失败!')
return
}
resolve(items);
return;
}
resolve(data);
},
fail(err) {
wx.showToast({
title: '识别失败,请重新选择照片!',
icon: 'none',
duration: 2000
})
console.log(err)
// 断网、服务器挂了都会fail回调直接reject即可
reject(err);
},
});
})
}
/**
* 解析类似 ```json ... ``` 格式的字符串,提取检测项目数组
* @param {string} str
* @returns {Array<Object>}
*/
function parseJsonBlock(str) {
// 匹配 ```json ... ``` 或 ``` ... ```
const match = str.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
let jsonStr = match ? match[1] : str;
jsonStr = jsonStr.trim();
// 尝试直接解析
try {
return JSON.parse(jsonStr);
} catch (e) {
// 替换单引号包裹的 key 和 value 为双引号
let fixedStr = jsonStr
// 替换 key 的单引号(允许有空格、括号等)
.replace(/'([\w\u4e00-\u9fa5\(\)\s]+)'(?=\s*:)/g, '"$1"')
// 替换 value 的单引号(允许有空格、括号等,非贪婪匹配)
.replace(/:\s*'([^']*?)'/g, ': "$1"');
try {
return JSON.parse(fixedStr);
} catch (e2) {
console.error('JSON parse error:', e2, fixedStr);
return [];
}
}
}
function extractCheckItemsFromOcrTable(ocrArray) {
// 1. 找到表头的 x 坐标
const nameHeader = ocrArray.find(item => item.text.includes('检验项目'));
const valueHeader = ocrArray.find(item => item.text.includes('结果'));
if (!nameHeader || !valueHeader) return [];
const nameX = nameHeader.rotate_rect[0];
const valueX = valueHeader.rotate_rect[0];
// 2. 过滤掉表头,按 y 坐标分组(每一行)
const rows = {};
ocrArray.forEach(item => {
if (item.text && !['检验项目', '结果', '单位', '提示', '参考区间'].includes(item.text)) {
// 以 y 坐标为 key允许有一定误差如±5
const y = Math.round(item.rotate_rect[1] / 5) * 5;
if (!rows[y]) rows[y] = [];
rows[y].push(item);
}
});
// 3. 每一行找 name/value
const result = [];
Object.values(rows).forEach(items => {
let name = null, value = null;
items.forEach(item => {
if (Math.abs(item.rotate_rect[0] - nameX) < 50) name = item.text.trim();
if (Math.abs(item.rotate_rect[0] - valueX) < 50) value = item.text.trim();
});
if (name && value) result.push({ name, value });
});
return result;
}
/**
* 导出B超报告解析函数
* @param {string} text - B超报告文本内容
* @returns {Array<Object>} - 格式化的检测项目数组
*/
export const parseBUltraReportText = (text) => {
return parseBUltraReport(text);
};
/**
* 解析B超报告文本内容提取关键信息
* @param {string} text - B超报告文本内容
* @returns {Array<Object>} - 格式化的检测项目数组
*/
function parseBUltraReport(text) {
if (!text) return [];
const result = [];
const lines = text.split(/[\n\r]+/);
lines.forEach(line => {
line = line.trim();
if (!line) return;
// 匹配 "名称: 数值" 格式,提取数值部分(不包含单位)
const colonMatch = line.match(/^([^:]+)[:]\s*(.+)$/);
if (colonMatch) {
const name = colonMatch[1].trim();
let value = colonMatch[2].trim();
// 提取数值部分,去除单位
const numberMatch = value.match(/^(\d+(?:\.\d+)?)/);
if (numberMatch) {
value = numberMatch[1];
}
if (name && value) {
result.push({ name, value });
}
return;
}
// 匹配包含数值的项目(如 "肝肋下: 32mm"),只提取数值
const valueMatch = line.match(/([^0-9]+?)(\d+(?:\.\d+)?)/);
if (valueMatch) {
const name = valueMatch[1].trim();
const value = valueMatch[2].trim();
if (name && value) {
result.push({ name, value });
}
return;
}
// 处理特殊格式,如 "未触及"、"显示不清" 等
const specialMatch = line.match(/^([^:]+)[:]\s*(未触及|显示不清|.+)$/);
if (specialMatch) {
const name = specialMatch[1].trim();
const value = specialMatch[2].trim();
if (name && value) {
result.push({ name, value });
}
}
});
return result;
}