增加读卡器功能
This commit is contained in:
parent
9cf90e5423
commit
a46a73e8b2
@ -56,7 +56,7 @@ export const PatientApi = {
|
||||
},
|
||||
|
||||
// 修改患者信息
|
||||
updatePatient: async (data: PatientVO) => {
|
||||
updatePatient: async (data: Object) => {
|
||||
return await request.put({ url: `/inspect/patient/update`, data })
|
||||
},
|
||||
|
||||
@ -141,5 +141,9 @@ export const PatientApi = {
|
||||
//生成体检报告
|
||||
generateReport: async (medicalSn: string) => {
|
||||
return await request.get({ url: `/inspect/patient/generateReport?medicalSn=` + medicalSn })
|
||||
},
|
||||
//更新体检日期
|
||||
updateMedicalDateTime: async (medicalSn: string, medicalDateTime: Date) => {
|
||||
return await request.put({ url: `/inspect/patient/updateMedicalDateTime?medicalSn=${medicalSn}&medicalDateTime=${medicalDateTime.toISOString()}` })
|
||||
}
|
||||
}
|
||||
|
@ -3,70 +3,17 @@
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="connecting"
|
||||
@click="showReader"
|
||||
@click="readIdCard"
|
||||
>
|
||||
读取身份证
|
||||
</el-button>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="身份证信息"
|
||||
width="700px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="reader-controls" v-if="!device">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="connecting"
|
||||
@click="connectDevice"
|
||||
>
|
||||
连接读卡器
|
||||
</el-button>
|
||||
<span v-if="connecting">正在连接读卡器...</span>
|
||||
</div>
|
||||
|
||||
<div v-if="cardInfo" class="card-info">
|
||||
<div class="info-container">
|
||||
<div class="info-left">
|
||||
<p>姓名:{{ cardInfo.name }}</p>
|
||||
<p>性别:{{ cardInfo.gender }}</p>
|
||||
<p>民族:{{ cardInfo.nation }}</p>
|
||||
<p>出生日期:{{ cardInfo.birthday }}</p>
|
||||
<p>地址:{{ cardInfo.address }}</p>
|
||||
</div>
|
||||
<div class="info-right">
|
||||
<div class="id-card-back">
|
||||
<div class="id-number">
|
||||
<p class="id-label">公民身份号码</p>
|
||||
<p class="id-value">{{ cardInfo.idNumber }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="cardInfo.photo" class="photo">
|
||||
<img :src="'data:image/jpeg;base64,' + cardInfo.photo" alt="身份证照片" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">关 闭</el-button>
|
||||
<el-button v-if="device" type="primary" @click="readCard">
|
||||
重新读取
|
||||
</el-button>
|
||||
<el-button v-if="device" type="warning" @click="disconnect">
|
||||
断开连接
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onUnmounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import sdk from './IDCardReader/sdk';
|
||||
|
||||
interface IDCardInfo {
|
||||
name: string;
|
||||
@ -75,6 +22,9 @@ interface IDCardInfo {
|
||||
birthday: string;
|
||||
address: string;
|
||||
idNumber: string;
|
||||
certOrg: string;
|
||||
effDate: string;
|
||||
expDate: string;
|
||||
photo?: string;
|
||||
}
|
||||
|
||||
@ -88,162 +38,96 @@ export default defineComponent({
|
||||
},
|
||||
emits: ['update:cardId', 'success'],
|
||||
setup(props, { emit }) {
|
||||
const device = ref<USBDevice | null>(null);
|
||||
const connecting = ref(false);
|
||||
const cardInfo = ref<IDCardInfo | null>(null);
|
||||
const dialogVisible = ref(false);
|
||||
const deviceConnected = ref(false);
|
||||
|
||||
// 华视 CVR-100UC 的 USB 设备过滤器
|
||||
const USB_FILTERS = [
|
||||
{
|
||||
vendorId: 0x0483,
|
||||
productId: 0x5750
|
||||
}
|
||||
];
|
||||
|
||||
// 华视读卡器命令
|
||||
const COMMANDS = {
|
||||
FIND_CARD: new Uint8Array([0x02, 0x01, 0x01, 0x03]), // 寻卡命令
|
||||
SELECT_CARD: new Uint8Array([0x02, 0x02, 0x01, 0x03]), // 选卡命令
|
||||
READ_BASEINFO: new Uint8Array([0x02, 0x03, 0x01, 0x03]), // 读基本信息
|
||||
READ_PHOTO: new Uint8Array([0x02, 0x04, 0x01, 0x03]) // 读照片
|
||||
};
|
||||
|
||||
const showReader = () => {
|
||||
dialogVisible.value = true;
|
||||
if (!device.value) {
|
||||
connectDevice();
|
||||
// 初始化SDK
|
||||
const initSdk = () => {
|
||||
try {
|
||||
sdk.init_sdk();
|
||||
console.log('SDK初始化成功');
|
||||
} catch (error) {
|
||||
console.error('SDK初始化失败:', error);
|
||||
ElMessage.error('SDK初始化失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 连接设备
|
||||
const connectDevice = async () => {
|
||||
try {
|
||||
connecting.value = true;
|
||||
|
||||
const selectedDevice = await navigator.usb.requestDevice({
|
||||
filters: USB_FILTERS
|
||||
});
|
||||
|
||||
await selectedDevice.open();
|
||||
await selectedDevice.selectConfiguration(1);
|
||||
await selectedDevice.claimInterface(0);
|
||||
|
||||
await selectedDevice.controlTransferOut({
|
||||
requestType: 'vendor',
|
||||
recipient: 'device',
|
||||
request: 0x01,
|
||||
value: 0x01,
|
||||
index: 0x00
|
||||
});
|
||||
|
||||
device.value = selectedDevice;
|
||||
ElMessage.success('读卡器连接成功');
|
||||
// 连接成功后自动读卡
|
||||
readCard();
|
||||
} catch (error) {
|
||||
// 连接设备
|
||||
await sdk.open_device();
|
||||
|
||||
deviceConnected.value = true;
|
||||
console.log('读卡器连接成功');
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
console.error('连接设备失败:', error);
|
||||
ElMessage.error('连接读卡器失败');
|
||||
// 显示详细错误信息
|
||||
ElMessage.error(error instanceof Error ? error.message : '连接读卡器失败');
|
||||
return false;
|
||||
} finally {
|
||||
connecting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const sendCommand = async (command: Uint8Array): Promise<Uint8Array | null> => {
|
||||
if (!device.value) return null;
|
||||
|
||||
try {
|
||||
await device.value.transferOut(1, command);
|
||||
const result = await device.value.transferIn(1, 1024); // 增加缓冲区大小
|
||||
|
||||
if (result.data) {
|
||||
return new Uint8Array(result.data.buffer);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发送命令失败:', error);
|
||||
throw error;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const parseCardInfo = (data: Uint8Array): IDCardInfo => {
|
||||
// 解析返回的数据,华视读卡器返回的是 GB2312 编码的数据
|
||||
const decoder = new TextDecoder('gb2312');
|
||||
const text = decoder.decode(data.slice(5, -1)); // 去掉头尾标识符
|
||||
|
||||
// 按照华视读卡器的数据格式解析
|
||||
const fields = text.split('\x1f');
|
||||
return {
|
||||
name: fields[0] || '',
|
||||
gender: fields[1] || '',
|
||||
nation: fields[2] || '',
|
||||
birthday: fields[3]?.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3') || '',
|
||||
address: fields[4] || '',
|
||||
idNumber: fields[5] || ''
|
||||
};
|
||||
};
|
||||
|
||||
const readCard = async () => {
|
||||
if (!device.value) {
|
||||
ElMessage.error('请先连接读卡器');
|
||||
// 读取身份证
|
||||
const readIdCard = async () => {
|
||||
if (connecting.value) {
|
||||
ElMessage.warning('正在连接读卡器,请稍候...');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 寻卡
|
||||
const findResult = await sendCommand(COMMANDS.FIND_CARD);
|
||||
if (!findResult || findResult[0] !== 0x02) {
|
||||
throw new Error('未找到身份证');
|
||||
}
|
||||
|
||||
// 2. 选卡
|
||||
const selectResult = await sendCommand(COMMANDS.SELECT_CARD);
|
||||
if (!selectResult || selectResult[0] !== 0x02) {
|
||||
throw new Error('选卡失败');
|
||||
}
|
||||
|
||||
// 3. 读取基本信息
|
||||
const infoResult = await sendCommand(COMMANDS.READ_BASEINFO);
|
||||
if (!infoResult) {
|
||||
throw new Error('读取信息失败');
|
||||
}
|
||||
|
||||
// 4. 解析基本信息
|
||||
const info = parseCardInfo(infoResult);
|
||||
// 初始化SDK
|
||||
initSdk();
|
||||
|
||||
// 5. 读取照片(可选)
|
||||
try {
|
||||
const photoResult = await sendCommand(COMMANDS.READ_PHOTO);
|
||||
if (photoResult) {
|
||||
info.photo = btoa(String.fromCharCode.apply(null, Array.from(photoResult.slice(5, -1))));
|
||||
// 检查设备是否已连接
|
||||
if (!deviceConnected.value) {
|
||||
console.log('设备未连接,尝试连接...');
|
||||
const connected = await connectDevice();
|
||||
if (!connected) {
|
||||
return;
|
||||
}
|
||||
} catch (photoError) {
|
||||
console.warn('读取照片失败:', photoError);
|
||||
ElMessage.success('读卡器连接成功');
|
||||
}
|
||||
|
||||
cardInfo.value = info;
|
||||
// 更新身份证号
|
||||
emit('update:cardId', info.idNumber);
|
||||
// 发送成功事件
|
||||
emit('success', info);
|
||||
// 关闭弹窗
|
||||
dialogVisible.value = false;
|
||||
ElMessage.success('读取身份证成功');
|
||||
} catch (error) {
|
||||
|
||||
// 通知用户正在读取身份证
|
||||
|
||||
// 读取身份证信息
|
||||
const result = await sdk.read_card();
|
||||
|
||||
if (result) {
|
||||
// 添加调试信息
|
||||
console.log('组件接收到的身份证信息:', result);
|
||||
|
||||
// 发送成功事件,触发查询,并传递身份证号
|
||||
emit('success', result);
|
||||
|
||||
// 读取成功后断开连接
|
||||
await disconnect();
|
||||
|
||||
ElMessage.success('读取身份证成功,正在查询患者信息...');
|
||||
} else {
|
||||
throw new Error('读取身份证失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('读卡失败:', error);
|
||||
ElMessage.error(error instanceof Error ? error.message : '读取身份证失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 断开连接
|
||||
const disconnect = async () => {
|
||||
if (device.value) {
|
||||
if (deviceConnected.value) {
|
||||
try {
|
||||
await device.value.close();
|
||||
device.value = null;
|
||||
cardInfo.value = null;
|
||||
ElMessage.success('已断开连接');
|
||||
await sdk.close_device();
|
||||
deviceConnected.value = false;
|
||||
console.log('已断开读卡器连接');
|
||||
} catch (error) {
|
||||
console.error('断开连接失败:', error);
|
||||
ElMessage.error('断开连接失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -253,14 +137,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
return {
|
||||
device,
|
||||
connecting,
|
||||
cardInfo,
|
||||
dialogVisible,
|
||||
showReader,
|
||||
connectDevice,
|
||||
readCard,
|
||||
disconnect
|
||||
readIdCard
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -270,78 +148,4 @@ export default defineComponent({
|
||||
.id-card-reader {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.reader-controls {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
border: 1px solid #e8e8e8;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.info-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.info-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.info-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.id-card-back {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
height: 200px;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.id-number {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.id-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.id-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
font-family: monospace;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.photo {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.photo img {
|
||||
max-width: 200px;
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
161
src/components/IDCardReader/sdk.js
Normal file
161
src/components/IDCardReader/sdk.js
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* @Author: Hikari
|
||||
* @Date: 2021-04-26 18:52:16
|
||||
* @LastEditTime: 2021-04-26 20:16:36
|
||||
* @LastEditors: Please set LastEditors
|
||||
* @Description: 读取身份证信息
|
||||
* @FilePath: /cloud-desk-top/components/common/idcard/sdk.js
|
||||
*/
|
||||
import axios from "axios";
|
||||
|
||||
const sdk = new (function () {
|
||||
var type = null;
|
||||
|
||||
// 初始化SDK 默认使用华视
|
||||
this.init_sdk = function (qudao = "hs") {
|
||||
this.type = qudao;
|
||||
};
|
||||
|
||||
// 读取身份证信息
|
||||
this.read_card = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: "get",
|
||||
url: "http://localhost:19196/readcard",
|
||||
})
|
||||
.then((res) => {
|
||||
let data = res.data;
|
||||
let result = data;
|
||||
if (result.resultFlag == 0) {
|
||||
// 解析身份证信息
|
||||
const idCardInfo = {
|
||||
name: result.partyName || '',
|
||||
gender: this.parseGender(result.gender),
|
||||
nation: result.nation || '',
|
||||
birthday: this.formatDate(result.bornDay) || '',
|
||||
address: result.certAddress || '',
|
||||
idNumber: result.certNumber || '',
|
||||
certOrg: result.certOrg || '',
|
||||
effDate: this.formatDate(result.effDate) || '',
|
||||
expDate: result.expDate === '长期' ? '长期' : this.formatDate(result.expDate) || '',
|
||||
photo: result.photo || ''
|
||||
};
|
||||
resolve(idCardInfo);
|
||||
} else {
|
||||
console.error('读取身份证失败:', result.errorMsg);
|
||||
reject(new Error(result.errorMsg || '读取身份证失败'));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('读取身份证请求失败:', err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 格式化日期 YYYYMMDD -> YYYY-MM-DD
|
||||
this.formatDate = function(dateStr) {
|
||||
if (!dateStr || dateStr.length !== 8) return '';
|
||||
const year = dateStr.substring(0, 4);
|
||||
const month = dateStr.substring(4, 6);
|
||||
const day = dateStr.substring(6, 8);
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
// 解析性别信息
|
||||
this.parseGender = function(gender) {
|
||||
if (!gender) return '';
|
||||
|
||||
// 处理字符串类型的性别
|
||||
if (typeof gender === 'string') {
|
||||
// 如果已经是"男"或"女",直接返回
|
||||
if (gender === '男' || gender === '女') return gender;
|
||||
|
||||
// 处理数字字符串
|
||||
if (gender === '1' || gender === 'M' || gender.toUpperCase() === 'MALE') return '男';
|
||||
if (gender === '2' || gender === 'F' || gender.toUpperCase() === 'FEMALE') return '女';
|
||||
}
|
||||
|
||||
// 处理数字类型的性别
|
||||
if (typeof gender === 'number') {
|
||||
return gender === 1 ? '男' : '女';
|
||||
}
|
||||
|
||||
// 如果无法解析,返回空字符串
|
||||
return '';
|
||||
};
|
||||
|
||||
// 链接
|
||||
this.open_device = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: "get",
|
||||
url: "http://localhost:19196/OpenDevice",
|
||||
})
|
||||
.then((res) => {
|
||||
let data = res.data;
|
||||
let result = data;
|
||||
if (result.resultFlag == 0) {
|
||||
resolve(result);
|
||||
} else {
|
||||
console.error('连接设备失败:', result.errorMsg);
|
||||
reject(new Error(result.errorMsg || '连接设备失败'));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('连接设备请求失败:', err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 断开
|
||||
this.close_device = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: "get",
|
||||
url: "http://localhost:19196/CloseDevice",
|
||||
})
|
||||
.then((res) => {
|
||||
let data = res.data;
|
||||
let result = data;
|
||||
if (result.resultFlag == 0) {
|
||||
resolve(result);
|
||||
} else {
|
||||
console.error('断开设备失败:', result.errorMsg);
|
||||
reject(new Error(result.errorMsg || '断开设备失败'));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('断开设备请求失败:', err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 获取安全模块号
|
||||
this.get_samid = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: "get",
|
||||
url: "http://localhost:19196/getsamid",
|
||||
})
|
||||
.then((res) => {
|
||||
let data = res.data;
|
||||
let result = data;
|
||||
if (result.resultFlag == 0) {
|
||||
resolve(result.samid);
|
||||
} else {
|
||||
console.error('获取安全模块号失败:', result.errorMsg);
|
||||
reject(new Error(result.errorMsg || '获取安全模块号失败'));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('获取安全模块号请求失败:', err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
||||
export default sdk;
|
@ -963,6 +963,57 @@ const processItemData = (item) => {
|
||||
itemStatus: item.itemStatus || '0',
|
||||
sectionID: item.sectionID
|
||||
}
|
||||
|
||||
// 对体质指数(BMI)进行特殊处理
|
||||
if (item.itemName && item.itemName.includes('体质指数') && item.itemName.includes('BMI') && item.itemResult) {
|
||||
// 应用BMI特定的判断逻辑
|
||||
const bmiValue = parseFloat(item.itemResult)
|
||||
if (!isNaN(bmiValue)) {
|
||||
// BMI判断标准:体重过低<18.5,正常18.5≤BMI<24,超重24≤BMI<28,肥胖28≤BMI
|
||||
if (bmiValue < 18.5) {
|
||||
processedItem.note = '↓'
|
||||
processedItem.risk = '体重过低'
|
||||
processedItem.status = 'danger'
|
||||
} else if (bmiValue >= 18.5 && bmiValue < 24) {
|
||||
processedItem.note = '-'
|
||||
processedItem.risk = '正常'
|
||||
processedItem.status = ''
|
||||
} else if (bmiValue >= 24 && bmiValue < 28) {
|
||||
processedItem.note = '↑'
|
||||
processedItem.risk = '超重'
|
||||
processedItem.status = 'danger'
|
||||
} else if (bmiValue >= 28) {
|
||||
processedItem.note = '↑↑'
|
||||
processedItem.risk = '肥胖'
|
||||
processedItem.status = 'danger'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对血压进行特殊处理
|
||||
if (item.itemName && item.itemName.includes('血压') && item.itemResult) {
|
||||
// 血压值通常是systolic/diastolic格式,例如 125/90
|
||||
const bpMatch = item.itemResult.match(/(\d+)\/(\d+)/)
|
||||
if (bpMatch) {
|
||||
const systolic = parseInt(bpMatch[1]) // 收缩压
|
||||
const diastolic = parseInt(bpMatch[2]) // 舒张压
|
||||
|
||||
if (!isNaN(systolic) && !isNaN(diastolic)) {
|
||||
// 判断标准: 收缩压<130 且 舒张压<85为正常
|
||||
if (systolic >= 130 || diastolic >= 85) {
|
||||
processedItem.note = '↑'
|
||||
processedItem.risk = '血压偏高'
|
||||
processedItem.status = 'danger'
|
||||
} else {
|
||||
processedItem.note = '-'
|
||||
processedItem.risk = '正常'
|
||||
processedItem.status = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return processedItem
|
||||
}
|
||||
|
||||
// 辅助函数:处理小结数据
|
||||
@ -1052,6 +1103,116 @@ const getRowStatus = (item) => {
|
||||
return ''
|
||||
}
|
||||
|
||||
// 添加处理BMI指数的函数
|
||||
const handleBmiResult = (item) => {
|
||||
if (!item.value) return
|
||||
|
||||
const bmiValue = parseFloat(item.value)
|
||||
if (isNaN(bmiValue)) return
|
||||
|
||||
let bmiStatus = ''
|
||||
// BMI判断标准:体重过低<18.5,正常18.5≤BMI<24,超重24≤BMI<28,肥胖28≤BMI
|
||||
if (bmiValue < 18.5) {
|
||||
item.note = '↓'
|
||||
item.risk = '体重过低'
|
||||
item.status = 'danger'
|
||||
bmiStatus = `【体质指数(BMI)】${bmiValue},体重过低`
|
||||
} else if (bmiValue >= 18.5 && bmiValue < 24) {
|
||||
item.note = '-'
|
||||
item.risk = '正常'
|
||||
item.status = ''
|
||||
// 正常值不添加到小结
|
||||
} else if (bmiValue >= 24 && bmiValue < 28) {
|
||||
item.note = '↑'
|
||||
item.risk = '超重'
|
||||
item.status = 'danger'
|
||||
bmiStatus = `【体质指数(BMI)】${bmiValue},超重`
|
||||
} else if (bmiValue >= 28) {
|
||||
item.note = '↑↑'
|
||||
item.risk = '肥胖'
|
||||
item.status = 'danger'
|
||||
bmiStatus = `【体质指数(BMI)】${bmiValue},肥胖`
|
||||
}
|
||||
|
||||
// 直接更新一般检查小结
|
||||
updateGeneralSummary(bmiStatus)
|
||||
}
|
||||
|
||||
// 添加处理血压的函数
|
||||
const handleBloodPressureResult = (item) => {
|
||||
if (!item.value) return
|
||||
|
||||
// 血压值通常是systolic/diastolic格式,例如 125/90
|
||||
const bpMatch = item.value.match(/(\d+)\/(\d+)/)
|
||||
if (!bpMatch) return
|
||||
|
||||
const systolic = parseInt(bpMatch[1]) // 收缩压
|
||||
const diastolic = parseInt(bpMatch[2]) // 舒张压
|
||||
|
||||
let bpStatus = ''
|
||||
if (isNaN(systolic) || isNaN(diastolic)) return
|
||||
|
||||
// 判断标准: 收缩压<130 且 舒张压<85为正常
|
||||
if (systolic >= 130 || diastolic >= 85) {
|
||||
item.note = '↑'
|
||||
item.risk = '血压偏高'
|
||||
item.status = 'danger'
|
||||
bpStatus = `【血压】${item.value},偏高`
|
||||
} else {
|
||||
item.note = '-'
|
||||
item.risk = '正常'
|
||||
item.status = ''
|
||||
// 正常值不添加到小结
|
||||
}
|
||||
|
||||
// 直接更新一般检查小结
|
||||
updateGeneralSummary(bpStatus)
|
||||
}
|
||||
|
||||
// 添加更新一般检查小结的函数
|
||||
const updateGeneralSummary = (newStatus) => {
|
||||
if (!newStatus) return // 如果没有异常状态,不更新小结
|
||||
|
||||
// 获取当前小结内容
|
||||
let currentSummary = conclusionData.value.general.summary || '未见异常'
|
||||
|
||||
// 处理关键词,提取括号中内容作为判断依据
|
||||
const keywordMatch = newStatus.match(/【(.+?)】/)
|
||||
if (!keywordMatch) return
|
||||
|
||||
const keyword = keywordMatch[1]
|
||||
|
||||
// 如果当前是"未见异常",直接替换为新状态
|
||||
if (currentSummary === '未见异常') {
|
||||
conclusionData.value.general.summary = newStatus
|
||||
return
|
||||
}
|
||||
|
||||
// 检查小结中是否已包含相同关键词的内容
|
||||
if (currentSummary.includes(`【${keyword}】`)) {
|
||||
// 如果已包含相同关键词,替换该部分内容
|
||||
const regex = new RegExp(`【${keyword}】[^;。]+[;。]?`, 'g')
|
||||
currentSummary = currentSummary.replace(regex, '')
|
||||
|
||||
// 清理可能留下的多余分隔符
|
||||
currentSummary = currentSummary.replace(/;;+/g, ';').trim()
|
||||
|
||||
// 如果替换后内容为空,设置为新状态
|
||||
if (!currentSummary || currentSummary === ';') {
|
||||
conclusionData.value.general.summary = newStatus
|
||||
} else {
|
||||
// 否则添加到末尾
|
||||
conclusionData.value.general.summary = currentSummary + (currentSummary.endsWith(';') ? '' : ';') + newStatus
|
||||
}
|
||||
} else {
|
||||
// 如果不包含相同关键词,添加到末尾
|
||||
conclusionData.value.general.summary = currentSummary + (currentSummary.endsWith(';') ? '' : ';') + newStatus
|
||||
}
|
||||
|
||||
// 清理多余的分隔符
|
||||
conclusionData.value.general.summary = conclusionData.value.general.summary.replace(/;{2,}/g, ';')
|
||||
}
|
||||
|
||||
// 修改科室名称映射函数
|
||||
const getSectionName = (sectionId) => {
|
||||
// 获取该科室下的所有检查项目
|
||||
@ -1526,6 +1687,18 @@ const handleResultChange = (item) => {
|
||||
|
||||
item.originalValue = item.value
|
||||
|
||||
// 对体质指数(BMI)进行特殊处理
|
||||
if (item.name && item.name.includes('体质指数') && item.name.includes('BMI')) {
|
||||
handleBmiResult(item)
|
||||
return
|
||||
}
|
||||
|
||||
// 对血压进行特殊处理
|
||||
if (item.name && item.name.includes('血压')) {
|
||||
handleBloodPressureResult(item)
|
||||
return
|
||||
}
|
||||
|
||||
if (item.reference && item.reference !== 'null-null') {
|
||||
const value = parseFloat(item.value)
|
||||
const [low, high] = item.reference
|
||||
@ -1811,7 +1984,7 @@ const handleSaveAllResults = async () => {
|
||||
ElMessage.warning('请选择总检医生')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const userProfile = await getUserProfile()
|
||||
user.value = userProfile
|
||||
@ -1930,7 +2103,7 @@ const handleSaveAllResults = async () => {
|
||||
await PatientApi.updatePatient({
|
||||
id: selectedPatient.value.id,
|
||||
status: 1, // 设置为已检查状态
|
||||
medicalDateTime: currentTimestamp // 更新体检日期为当前时间
|
||||
|
||||
})
|
||||
|
||||
// 更新本地状态
|
||||
@ -2191,7 +2364,19 @@ const debouncedStatusChange = debounce(async (value) => {
|
||||
// 修改状态筛选变化处理函数
|
||||
const handleStatusFilterChange = (value) => {
|
||||
statusFilter.value = value
|
||||
debouncedStatusChange(value)
|
||||
|
||||
// 如果选择的是"已打印"状态,自动将日期范围设置为今天
|
||||
if (value === '2') {
|
||||
const today = new Date().toISOString().split('T')[0] // 获取当前日期,格式为YYYY-MM-DD
|
||||
dateRange.value = [today, today] // 设置日期范围为今天到今天
|
||||
// 直接使用dateRange进行筛选,不调用debouncedStatusChange以避免重复请求
|
||||
handleDateRangeChange(dateRange.value)
|
||||
} else {
|
||||
// 非已打印状态清空日期范围
|
||||
dateRange.value = []
|
||||
// 调用状态变化处理函数
|
||||
debouncedStatusChange(value)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加检查是否已完成的响应式引用
|
||||
@ -2501,7 +2686,7 @@ const handleDateRangeChange = async (val) => {
|
||||
const params = {
|
||||
pageNo: pageNo.value,
|
||||
pageSize: pageSize.value,
|
||||
isprint: 1 // 保持已打印状态
|
||||
isprint: 1, // 保持已打印状态
|
||||
}
|
||||
|
||||
// 只有在有日期范围时才添加日期条件
|
||||
@ -2543,7 +2728,6 @@ const resetPatientStatus = async (patient) => {
|
||||
await PatientApi.updatePatient({
|
||||
id: patient.id,
|
||||
status: 0, // 设置为待检查状态
|
||||
medicalDateTime: Date.now() // 更新体检日期为当前时间
|
||||
})
|
||||
|
||||
// 更新本地状态
|
||||
|
@ -42,12 +42,12 @@ v-loading.fullscreen.lock="fullscreenLoading"
|
||||
class="!w-200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item prop="cardId">
|
||||
<el-form-item prop="cardId">
|
||||
<IDCardReader
|
||||
v-model:cardId="queryParams.cardId"
|
||||
@success="handleQuery"
|
||||
@success="handleIdCardSuccess"
|
||||
/>
|
||||
</el-form-item> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="打印日期" prop="printTimeRange">
|
||||
<el-date-picker
|
||||
v-model="queryParams.printTimeRange"
|
||||
@ -260,6 +260,15 @@ const handleQuery = () => {
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 处理身份证读卡成功事件 */
|
||||
const handleIdCardSuccess = (cardInfo) => {
|
||||
// 直接使用身份证号进行查询,不更新搜索框
|
||||
const tempCardId = queryParams.cardId;
|
||||
queryParams.cardId = cardInfo.idNumber;
|
||||
getList();
|
||||
// 查询完成后恢复原来的值
|
||||
queryParams.cardId = tempCardId;
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
@ -276,6 +285,7 @@ const handlePrint = async (row: PatientVO) => {
|
||||
await createPrint(row.medicalSn)
|
||||
// 打印完成后更新状态,添加当前时间戳
|
||||
await PatientApi.updatePrintStatus(row.medicalSn, new Date())
|
||||
await PatientApi.updateMedicalDateTime(row.medicalSn, new Date())
|
||||
// 刷新列表
|
||||
await getList()
|
||||
message.success('打印成功')
|
||||
|
Loading…
Reference in New Issue
Block a user