增加读卡器功能
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 })
|
return await request.put({ url: `/inspect/patient/update`, data })
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -141,5 +141,9 @@ export const PatientApi = {
|
|||||||
//生成体检报告
|
//生成体检报告
|
||||||
generateReport: async (medicalSn: string) => {
|
generateReport: async (medicalSn: string) => {
|
||||||
return await request.get({ url: `/inspect/patient/generateReport?medicalSn=` + medicalSn })
|
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
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:loading="connecting"
|
:loading="connecting"
|
||||||
@click="showReader"
|
@click="readIdCard"
|
||||||
>
|
>
|
||||||
读取身份证
|
读取身份证
|
||||||
</el-button>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, onUnmounted } from 'vue';
|
import { defineComponent, ref, onUnmounted } from 'vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
|
import sdk from './IDCardReader/sdk';
|
||||||
|
|
||||||
interface IDCardInfo {
|
interface IDCardInfo {
|
||||||
name: string;
|
name: string;
|
||||||
@ -75,6 +22,9 @@ interface IDCardInfo {
|
|||||||
birthday: string;
|
birthday: string;
|
||||||
address: string;
|
address: string;
|
||||||
idNumber: string;
|
idNumber: string;
|
||||||
|
certOrg: string;
|
||||||
|
effDate: string;
|
||||||
|
expDate: string;
|
||||||
photo?: string;
|
photo?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,162 +38,96 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
emits: ['update:cardId', 'success'],
|
emits: ['update:cardId', 'success'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const device = ref<USBDevice | null>(null);
|
|
||||||
const connecting = ref(false);
|
const connecting = ref(false);
|
||||||
const cardInfo = ref<IDCardInfo | null>(null);
|
const deviceConnected = ref(false);
|
||||||
const dialogVisible = ref(false);
|
|
||||||
|
|
||||||
// 华视 CVR-100UC 的 USB 设备过滤器
|
// 初始化SDK
|
||||||
const USB_FILTERS = [
|
const initSdk = () => {
|
||||||
{
|
try {
|
||||||
vendorId: 0x0483,
|
sdk.init_sdk();
|
||||||
productId: 0x5750
|
console.log('SDK初始化成功');
|
||||||
}
|
} catch (error) {
|
||||||
];
|
console.error('SDK初始化失败:', error);
|
||||||
|
ElMessage.error('SDK初始化失败');
|
||||||
// 华视读卡器命令
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 连接设备
|
||||||
const connectDevice = async () => {
|
const connectDevice = async () => {
|
||||||
try {
|
try {
|
||||||
connecting.value = true;
|
connecting.value = true;
|
||||||
|
|
||||||
const selectedDevice = await navigator.usb.requestDevice({
|
// 连接设备
|
||||||
filters: USB_FILTERS
|
await sdk.open_device();
|
||||||
});
|
|
||||||
|
deviceConnected.value = true;
|
||||||
await selectedDevice.open();
|
console.log('读卡器连接成功');
|
||||||
await selectedDevice.selectConfiguration(1);
|
return true;
|
||||||
await selectedDevice.claimInterface(0);
|
} catch (error: any) {
|
||||||
|
|
||||||
await selectedDevice.controlTransferOut({
|
|
||||||
requestType: 'vendor',
|
|
||||||
recipient: 'device',
|
|
||||||
request: 0x01,
|
|
||||||
value: 0x01,
|
|
||||||
index: 0x00
|
|
||||||
});
|
|
||||||
|
|
||||||
device.value = selectedDevice;
|
|
||||||
ElMessage.success('读卡器连接成功');
|
|
||||||
// 连接成功后自动读卡
|
|
||||||
readCard();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('连接设备失败:', error);
|
console.error('连接设备失败:', error);
|
||||||
ElMessage.error('连接读卡器失败');
|
// 显示详细错误信息
|
||||||
|
ElMessage.error(error instanceof Error ? error.message : '连接读卡器失败');
|
||||||
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
connecting.value = false;
|
connecting.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendCommand = async (command: Uint8Array): Promise<Uint8Array | null> => {
|
// 读取身份证
|
||||||
if (!device.value) return null;
|
const readIdCard = async () => {
|
||||||
|
if (connecting.value) {
|
||||||
try {
|
ElMessage.warning('正在连接读卡器,请稍候...');
|
||||||
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('请先连接读卡器');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 寻卡
|
// 初始化SDK
|
||||||
const findResult = await sendCommand(COMMANDS.FIND_CARD);
|
initSdk();
|
||||||
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);
|
|
||||||
|
|
||||||
// 5. 读取照片(可选)
|
// 检查设备是否已连接
|
||||||
try {
|
if (!deviceConnected.value) {
|
||||||
const photoResult = await sendCommand(COMMANDS.READ_PHOTO);
|
console.log('设备未连接,尝试连接...');
|
||||||
if (photoResult) {
|
const connected = await connectDevice();
|
||||||
info.photo = btoa(String.fromCharCode.apply(null, Array.from(photoResult.slice(5, -1))));
|
if (!connected) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (photoError) {
|
ElMessage.success('读卡器连接成功');
|
||||||
console.warn('读取照片失败:', photoError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cardInfo.value = info;
|
// 通知用户正在读取身份证
|
||||||
// 更新身份证号
|
|
||||||
emit('update:cardId', info.idNumber);
|
// 读取身份证信息
|
||||||
// 发送成功事件
|
const result = await sdk.read_card();
|
||||||
emit('success', info);
|
|
||||||
// 关闭弹窗
|
if (result) {
|
||||||
dialogVisible.value = false;
|
// 添加调试信息
|
||||||
ElMessage.success('读取身份证成功');
|
console.log('组件接收到的身份证信息:', result);
|
||||||
} catch (error) {
|
|
||||||
|
// 发送成功事件,触发查询,并传递身份证号
|
||||||
|
emit('success', result);
|
||||||
|
|
||||||
|
// 读取成功后断开连接
|
||||||
|
await disconnect();
|
||||||
|
|
||||||
|
ElMessage.success('读取身份证成功,正在查询患者信息...');
|
||||||
|
} else {
|
||||||
|
throw new Error('读取身份证失败');
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
console.error('读卡失败:', error);
|
console.error('读卡失败:', error);
|
||||||
ElMessage.error(error instanceof Error ? error.message : '读取身份证失败');
|
ElMessage.error(error instanceof Error ? error.message : '读取身份证失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 断开连接
|
||||||
const disconnect = async () => {
|
const disconnect = async () => {
|
||||||
if (device.value) {
|
if (deviceConnected.value) {
|
||||||
try {
|
try {
|
||||||
await device.value.close();
|
await sdk.close_device();
|
||||||
device.value = null;
|
deviceConnected.value = false;
|
||||||
cardInfo.value = null;
|
console.log('已断开读卡器连接');
|
||||||
ElMessage.success('已断开连接');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('断开连接失败:', error);
|
console.error('断开连接失败:', error);
|
||||||
ElMessage.error('断开连接失败');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -253,14 +137,8 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
device,
|
|
||||||
connecting,
|
connecting,
|
||||||
cardInfo,
|
readIdCard
|
||||||
dialogVisible,
|
|
||||||
showReader,
|
|
||||||
connectDevice,
|
|
||||||
readCard,
|
|
||||||
disconnect
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -270,78 +148,4 @@ export default defineComponent({
|
|||||||
.id-card-reader {
|
.id-card-reader {
|
||||||
display: inline-block;
|
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>
|
</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',
|
itemStatus: item.itemStatus || '0',
|
||||||
sectionID: item.sectionID
|
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 ''
|
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) => {
|
const getSectionName = (sectionId) => {
|
||||||
// 获取该科室下的所有检查项目
|
// 获取该科室下的所有检查项目
|
||||||
@ -1526,6 +1687,18 @@ const handleResultChange = (item) => {
|
|||||||
|
|
||||||
item.originalValue = item.value
|
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') {
|
if (item.reference && item.reference !== 'null-null') {
|
||||||
const value = parseFloat(item.value)
|
const value = parseFloat(item.value)
|
||||||
const [low, high] = item.reference
|
const [low, high] = item.reference
|
||||||
@ -1811,7 +1984,7 @@ const handleSaveAllResults = async () => {
|
|||||||
ElMessage.warning('请选择总检医生')
|
ElMessage.warning('请选择总检医生')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userProfile = await getUserProfile()
|
const userProfile = await getUserProfile()
|
||||||
user.value = userProfile
|
user.value = userProfile
|
||||||
@ -1930,7 +2103,7 @@ const handleSaveAllResults = async () => {
|
|||||||
await PatientApi.updatePatient({
|
await PatientApi.updatePatient({
|
||||||
id: selectedPatient.value.id,
|
id: selectedPatient.value.id,
|
||||||
status: 1, // 设置为已检查状态
|
status: 1, // 设置为已检查状态
|
||||||
medicalDateTime: currentTimestamp // 更新体检日期为当前时间
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
@ -2191,7 +2364,19 @@ const debouncedStatusChange = debounce(async (value) => {
|
|||||||
// 修改状态筛选变化处理函数
|
// 修改状态筛选变化处理函数
|
||||||
const handleStatusFilterChange = (value) => {
|
const handleStatusFilterChange = (value) => {
|
||||||
statusFilter.value = 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 = {
|
const params = {
|
||||||
pageNo: pageNo.value,
|
pageNo: pageNo.value,
|
||||||
pageSize: pageSize.value,
|
pageSize: pageSize.value,
|
||||||
isprint: 1 // 保持已打印状态
|
isprint: 1, // 保持已打印状态
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只有在有日期范围时才添加日期条件
|
// 只有在有日期范围时才添加日期条件
|
||||||
@ -2543,7 +2728,6 @@ const resetPatientStatus = async (patient) => {
|
|||||||
await PatientApi.updatePatient({
|
await PatientApi.updatePatient({
|
||||||
id: patient.id,
|
id: patient.id,
|
||||||
status: 0, // 设置为待检查状态
|
status: 0, // 设置为待检查状态
|
||||||
medicalDateTime: Date.now() // 更新体检日期为当前时间
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
|
@ -42,12 +42,12 @@ v-loading.fullscreen.lock="fullscreenLoading"
|
|||||||
class="!w-200px"
|
class="!w-200px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item prop="cardId">
|
<el-form-item prop="cardId">
|
||||||
<IDCardReader
|
<IDCardReader
|
||||||
v-model:cardId="queryParams.cardId"
|
v-model:cardId="queryParams.cardId"
|
||||||
@success="handleQuery"
|
@success="handleIdCardSuccess"
|
||||||
/>
|
/>
|
||||||
</el-form-item> -->
|
</el-form-item>
|
||||||
<el-form-item label="打印日期" prop="printTimeRange">
|
<el-form-item label="打印日期" prop="printTimeRange">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="queryParams.printTimeRange"
|
v-model="queryParams.printTimeRange"
|
||||||
@ -260,6 +260,15 @@ const handleQuery = () => {
|
|||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 处理身份证读卡成功事件 */
|
||||||
|
const handleIdCardSuccess = (cardInfo) => {
|
||||||
|
// 直接使用身份证号进行查询,不更新搜索框
|
||||||
|
const tempCardId = queryParams.cardId;
|
||||||
|
queryParams.cardId = cardInfo.idNumber;
|
||||||
|
getList();
|
||||||
|
// 查询完成后恢复原来的值
|
||||||
|
queryParams.cardId = tempCardId;
|
||||||
|
}
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
/** 添加/修改操作 */
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
@ -276,6 +285,7 @@ const handlePrint = async (row: PatientVO) => {
|
|||||||
await createPrint(row.medicalSn)
|
await createPrint(row.medicalSn)
|
||||||
// 打印完成后更新状态,添加当前时间戳
|
// 打印完成后更新状态,添加当前时间戳
|
||||||
await PatientApi.updatePrintStatus(row.medicalSn, new Date())
|
await PatientApi.updatePrintStatus(row.medicalSn, new Date())
|
||||||
|
await PatientApi.updateMedicalDateTime(row.medicalSn, new Date())
|
||||||
// 刷新列表
|
// 刷新列表
|
||||||
await getList()
|
await getList()
|
||||||
message.success('打印成功')
|
message.success('打印成功')
|
||||||
|
Loading…
Reference in New Issue
Block a user