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} */ 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} - 格式化的检测项目数组 */ export const parseBUltraReportText = (text) => { return parseBUltraReport(text); }; /** * 解析B超报告文本内容,提取关键信息 * @param {string} text - B超报告文本内容 * @returns {Array} - 格式化的检测项目数组 */ 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; }