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数组格式输出,不要将多个药品信息放到一个药品名称字段内,跟药品无关的信息不要" "text": "请只返回提取好的药品名称、每日服用次数、单次用药剂量、饭前服用或饭后服用,格式为 JSON 数组,每个元素包含 'name','jiliang','cishu1', 'time' 四个字段。例如:[{\"name\": \"阿莫西林\", \"jiliang\": \"2片\", \"cishu\": \"3\", \"time\": \"饭后\"}]。不要返回包含 rotate_rect、text 等字段的原始 OCR 结构化表格数据。" } ] }] }, success(res) { let data = parseJsonBlock(res.data.choices[0].message.content) if(data.length == 0){ wx.showToast({ title: '识别失败,请重新选择照片!', icon: 'none', duration: 5000 }); 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: 5000 }); 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} */ 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); }