135 lines
5.1 KiB
JavaScript
135 lines
5.1 KiB
JavaScript
export const getOcr = (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": "要求准确无误的提取图片中的药品名称、每次用量、每日服用次数等信息,模糊或者强光遮挡的单个文字可以用英文问号?代替,药品名称的key为'name',每次用量key为'jiliang',每日服用次数的key为'cishu',返回数据格式以JSON数组格式输出,不要将多个药品信息放到一个药品名称字段内,跟药品无关的信息不要"
|
||
}
|
||
]
|
||
}]
|
||
},
|
||
success(res) {
|
||
let data = parseJsonBlock(res.data.choices[0].message.content)
|
||
if(data.length == 0){
|
||
wx.showToast({
|
||
title: '识别失败,请重新选择照片!',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
reject('识别失败,请重新选择照片!')
|
||
return
|
||
}
|
||
if(data.some((item)=>{
|
||
return !item.name && item.text;
|
||
})){
|
||
let sss = extractDrugsFromOcr(data)
|
||
if(sss.length == 0){
|
||
wx.showToast({
|
||
title: '识别失败,请重新选择照片!',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
reject('识别失败!')
|
||
return
|
||
}
|
||
resolve(sss);
|
||
} else {
|
||
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 extractDrugsFromOcr(ocrArray) {
|
||
// 1. 合并所有 text 字段
|
||
const lines = ocrArray.map(item => item.text.trim()).filter(Boolean);
|
||
|
||
// 2. 分组药品(以“1、”“2、”等开头)
|
||
const drugGroups = [];
|
||
let currentDrug = null;
|
||
|
||
lines.forEach(line => {
|
||
// 判断是否为新药品的开始
|
||
if (/^\d+、/.test(line)) {
|
||
if (currentDrug) drugGroups.push(currentDrug);
|
||
currentDrug = { name: line.replace(/^\d+、/, '').trim(), jiliang: '', cishu: '' };
|
||
} else if (currentDrug) {
|
||
// 判断是否为用量
|
||
if (/每次|mg|片|粒|ml|g|单位/.test(line) && !currentDrug.jiliang) {
|
||
currentDrug.jiliang = line;
|
||
}
|
||
// 判断是否为次数
|
||
else if (/每日|每天|次|早|晚|中/.test(line) && !currentDrug.cishu) {
|
||
currentDrug.cishu = line;
|
||
}
|
||
// 其他信息可根据需要扩展
|
||
}
|
||
});
|
||
if (currentDrug) drugGroups.push(currentDrug);
|
||
|
||
// 过滤掉无效项
|
||
return drugGroups.filter(d => d.name);
|
||
} |