Fix Bug #550: AI修复

This commit is contained in:
2026-05-27 03:00:08 +08:00
parent 8e6cb5c79f
commit 16c42ca108
5433 changed files with 171 additions and 778731 deletions

View File

@@ -1,700 +0,0 @@
<template>
<el-form :model="row" :rules="rules" :ref="(el) => (formRef = el)" :label-width="100">
<div class="expend_div" style="padding: 16px; background: #f8f9fa; border-radius: 8px">
<template v-if="row.adviceType == 1">
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span class="medicine-title">
{{
row.adviceName +
' ' +
row.volume +
' [' +
Number(row.unitPrice).toFixed(2) +
' 元' +
'/' +
row.unitCode_dictText +
']'
}}
</span>
<el-form-item prop="lotNumber" label="药房:">
<el-select
v-model="row.inventoryId"
style="width: 330px; margin-right: 20px"
placeholder="药房"
>
<el-option
v-for="item in row.stockList"
:key="item.inventoryId"
:value="item.inventoryId"
:label="`${item.locationName} 批次号: ${item.lotNumber ?? '-'} 库存:${stockFormat(
row.partPercent,
row.unitCodeList,
item.quantity
)}`"
@click="handleNumberClick(item)"
>
<div style="display: flex; gap: 8px; align-items: center">
<span>{{ item.locationName }}</span>
<span>批次号: {{ item.lotNumber ?? '-' }}</span>
<span>
库存{{ stockFormat(row.partPercent, row.unitCodeList, item.quantity) }}
</span>
</div>
</el-option>
</el-select>
</el-form-item>
<el-form-item
label="执行次数:"
prop="executeNum"
class="required-field"
data-prop="executeNum"
v-if="row.injectFlag == 1"
>
<el-input-number
:min="1"
v-model="row.executeNum"
controls-position="right"
:controls="false"
:ref="(el) => setInputRef('executeNum', el)"
@keyup.enter.prevent="handleEnter('executeNum')"
style="width: 70px; margin-right: 20px"
/>
</el-form-item>
<span class="medicine-info"> 诊断{{ config.diagnosisName }} </span>
<span class="medicine-info"> 皮试{{ row.skinTestFlag_enumText }} </span>
<span class="medicine-info"> 注射药品{{ row.injectFlag_enumText }} </span>
<span class="total-amount">
总金额{{ row.totalPrice ? Number(row.totalPrice).toFixed(2) + ' 元' : '0.00 元' }}
</span>
</div>
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap">
<div class="form-group">
<!-- 单次剂量 -->
<el-form-item
label="单次用量:"
prop="doseQuantity"
class="required-field"
data-prop="doseQuantity"
>
<el-input-number
:min="0"
v-model="row.doseQuantity"
controls-position="right"
:controls="false"
style="width: 70px"
:ref="(el) => setInputRef('doseQuantity', el)"
@input="() => { convertValues(); calculateTotalAmount(); }"
@keyup.enter.prevent="handleEnter('doseQuantity')"
/>
</el-form-item>
<!-- 剂量单位 -->
<el-select
v-model="row.minUnitCode"
style="width: 70px; margin-right: 32px"
placeholder=" "
>
<template v-for="item in row.unitCodeList" :key="item.value">
<el-option
v-if="item.type == config.unitMap['minUnit']"
:value="item.value"
:label="item.label"
/>
</template>
</el-select>
<span>=</span>
<!-- 单次剂量 -->
<el-form-item prop="dose" class="required-field" data-prop="dose" label-width="0">
<el-input-number
v-model="row.dose"
controls-position="right"
:controls="false"
style="width: 70px; margin-left: 32px"
:ref="(el) => setInputRef('dose', el)"
@input="() => { convertDoseValues(); calculateTotalAmount(); }"
@keyup.enter.prevent="handleEnter('dose')"
/>
</el-form-item>
<!-- 全部单位 -->
<el-select
v-model="row.doseUnitCode"
style="width: 70px"
placeholder=" "
@change="() => { convertValues(); calculateTotalAmount(); }"
>
<el-option
v-for="item in row.unitCodeList"
:value="item.value"
:label="item.label"
:key="item.value"
/>
</el-select>
</div>
<div class="form-group">
<el-form-item
label="给药途径:"
prop="methodCode"
class="required-field"
data-prop="methodCode"
>
<el-select
v-model="row.methodCode"
placeholder="给药途径"
clearable
filterable
:ref="(el) => setInputRef('methodCode', el)"
style="width: 120px"
@keyup.enter.prevent="
() => {
if (row.methodCode) {
handleEnter('methodCode');
}
}
"
>
<el-option
v-for="dict in config.methodCode"
@click="() => (row.methodCode_dictText = dict.label)"
@keyup="handleEnter('methodCode')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item
label="用药频次:"
prop="rateCode"
class="required-field"
data-prop="rateCode"
>
<el-select
v-model="row.rateCode"
placeholder="频次"
style="width: 120px"
filterable
:disabled="row.therapyEnum == '2'"
@keyup.enter.prevent="
() => {
if (row.rateCode) {
handleEnter('rateCode');
}
}
"
:ref="(el) => setInputRef('rateCode', el)"
>
<el-option
v-for="dict in config.rateCode"
@click="() => (row.rateCode_dictText = dict.label)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</div>
</div>
<div
style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 10px"
>
<div class="form-group">
<template v-if="row.therapyEnum == '2'">
<el-form-item
label="用药天数:"
prop="dispensePerDuration"
class="required-field"
data-prop="dispensePerDuration"
>
<el-input-number
v-model="row.dispensePerDuration"
style="width: 148px"
:min="1"
controls-position="right"
:controls="false"
:ref="(el) => setInputRef('dispensePerDuration', el)"
@keyup.enter.prevent="handleEnter('dispensePerDuration')"
>
<template #suffix></template>
</el-input-number>
</el-form-item>
<el-form-item
label="总量:"
prop="quantity"
class="required-field"
data-prop="quantity"
label-width="80"
>
<el-input-number
v-model="row.quantity"
style="width: 70px"
controls-position="right"
:controls="false"
:ref="(el) => setInputRef('quantity', el)"
@keyup.enter.prevent="handleEnter('quantity')"
@input="calculateTotalPrice"
/>
</el-form-item>
<el-select
v-model="row.unitCode"
style="width: 70px"
placeholder=" "
@change="calculateTotalAmount"
>
<template v-for="item in row.unitCodeList" :key="item.value">
<el-option
v-if="checkUnit(item)"
:value="item.value"
:label="item.label"
@click="
() => {
if (item.type == config.unitMap['minUnit']) {
row.unitPrice = row.minUnitPrice;
} else {
row.unitPrice = row.unitTempPrice;
}
row.unitCode_dictText = item.label;
}
"
/>
</template>
</el-select>
</template>
<template v-if="row.therapyEnum == '1'">
<el-form-item
label="首次用量:"
prop="firstDose"
class="required-field"
data-prop="firstDose"
>
<el-input-number
v-model="row.firstDose"
style="width: 70px"
controls-position="right"
:controls="false"
:ref="(el) => setInputRef('firstDose', el)"
@input="calculateTotalAmount"
@keyup.enter.prevent="handleEnter('firstDose')"
/>
</el-form-item>
<el-select v-model="row.doseUnitCode" style="width: 70px" placeholder=" ">
<el-option
v-for="item in row.unitCodeList"
:value="item.value"
:label="item.label"
:key="item.value"
/>
</el-select>
</template>
</div>
<div class="form-actions">
<el-button type="primary" @click="handleSave">确定</el-button>
<el-button @click="handleCancel">取消</el-button>
</div>
</div>
</template>
<template v-else-if="row.adviceType == 2">
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span style="font-size: 16px; font-weight: 600">
{{
row.adviceName +
' ' +
row.volume +
' ' +
row.unitPrice +
' 元/' +
row.unitCode_dictText
}}
</span>
<div class="form-group">
<el-select
v-model="row.lotNumber"
style="width: 180px; margin-right: 20px"
placeholder="药房"
>
<el-option
v-for="item in row.stockList"
:key="item.inventoryId"
:value="item.inventoryId"
:label="`${item.locationName} 批次号: ${item.lotNumber ?? '-'} 库存:${stockFormat(
row.partPercent,
row.unitCodeList,
item.quantity
)}`"
@click="handleNumberClick(item)"
>
<div style="display: flex; gap: 8px; align-items: center">
<span>{{ item.locationName }}</span>
<span>批次号: {{ item.lotNumber ?? '-' }}</span>
<span>
库存{{ stockFormat(row.partPercent, row.unitCodeList, item.quantity) }}
</span>
</div>
</el-option>
</el-select>
<el-form-item
label="数量:"
prop="quantity"
class="required-field"
data-prop="quantity"
>
<el-input-number
placeholder="数量"
v-model="row.quantity"
style="width: 70px"
controls-position="right"
:controls="false"
@keyup.enter.prevent="handleEnter('quantity')"
@input="calculateTotalAmount"
/>
</el-form-item>
<el-select
v-model="row.unitCode"
style="width: 70px; margin-right: 20px"
placeholder=" "
@change="calculateTotalAmount"
>
<template v-for="item in row.unitCodeList" :key="item.value">
<el-option
v-if="item.type != config.unitMap['dose']"
:value="item.value"
:label="item.label"
@click="() => (row.unitCode_dictText = item.label)"
/>
</template>
</el-select>
<span class="total-amount" v-if="row.therapyEnum == '2'">
总金额{{ row.totalPrice ? Number(row.totalPrice).toFixed(2) + ' ' : '0.00 ' }}
</span>
</div>
<div class="form-actions">
<el-button type="primary" @click="handleSave">确定</el-button>
<el-button @click="handleCancel">取消</el-button>
</div>
</div>
</template>
<template v-else>
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span style="font-size: 16px; font-weight: 600">
{{ row.adviceName }}
<template v-if="row.unitPrice != null && row.unitPrice !== ''">
{{ Number(row.unitPrice).toFixed(2) }}
</template>
</span>
<div class="form-group">
<el-form-item
label="执行次数:"
prop="quantity"
class="required-field"
data-prop="quantity"
>
<el-input-number
placeholder="执行次数"
style="width: 100px; margin: 0 20px"
v-model="row.quantity"
controls-position="right"
:controls="false"
@keyup.enter.prevent="handleEnter('quantity')"
@input="calculateTotalPrice"
/>
</el-form-item>
<el-form-item label="执行科室:" prop="orgId" class="required-field" data-prop="orgId">
<el-tree-select
clearable
v-model="row.orgId"
style="width: 200px"
:data="orgTreeData"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
check-strictly
default-expand-all
:fallback-option="orgFallbackOption"
@change="handleOrgChange"
placeholder="请选择执行科室"
/>
</el-form-item>
<span class="total-amount" v-if="row.therapyEnum == '2'">
总金额{{ row.totalPrice ? Number(row.totalPrice).toFixed(2) + ' ' : '0.00 ' }}
</span>
<span style="font-size: 16px; font-weight: 600">
<!-- 金额: {{ row.priceList[0].price }} -->
</span>
</div>
<div class="form-actions">
<el-button type="primary" @click="handleSave">确定</el-button>
<el-button @click="handleCancel">取消</el-button>
</div>
</div>
</template>
</div>
</el-form>
</template>
<script setup lang="ts">
import {computed, getCurrentInstance, nextTick, onMounted, ref, watch} from 'vue';
import Decimal from 'decimal.js';
interface Config {
diagnosisName: string; // 仅用于显示
methodCode: any[];
rateCode: any[];
organization: any[];
unitMap: {
dose: string;
minUnit: string;
unit: string;
};
}
interface Handlers {
handleEnter: (prop: string, row: any, index: number) => void;
handleNumberClick: (item: any, index: number, row: any) => void;
handleOrgChange: (value: any, index: number) => void;
convertValue: (type: 'dose' | 'doseQuantity', row: any, index: number) => void;
calculateTotal: (type: 'price' | 'amount', row: any, index: number) => void;
setInputRef: (prop: string, el: any) => void;
}
interface Props {
row: any;
index: number;
formRefName: string;
rules: any;
config: Config;
handlers: Handlers;
}
interface Emits {
(e: 'save', row: any, index: number): void;
(e: 'cancel', row: any, index: number): void;
}
const props = defineProps<Props>();
const emit = defineEmits<Emits>();
const { proxy } = getCurrentInstance() as any;
// 创建表单 ref
const formRef = ref();
// 将表单 ref 注册到父组件的 $refs 上,以便父组件可以通过 proxy.$refs['formRef' + index] 访问
const registerFormRef = () => {
if (formRef.value && proxy?.$parent?.$refs) {
proxy.$parent.$refs[props.formRefName] = formRef.value;
}
};
// 监听 formRef 变化,确保注册
watch(
() => formRef.value,
() => {
if (formRef.value) {
nextTick(() => {
registerFormRef();
});
}
},
{ immediate: true }
);
onMounted(() => {
nextTick(() => {
registerFormRef();
});
if (props.row.therapyEnum == '2' && !props.row.rateCode) {
setRateCodeToST();
}
});
watch(
() => props.row.therapyEnum,
(newVal) => {
if (newVal == '2') {
setRateCodeToST();
} else if (newVal == '1') {
props.row.rateCode = '';
props.row.rateCode_dictText = '';
}
}
);
const setRateCodeToST = () => {
if (Array.isArray(props.config.rateCode)) {
const stOption = props.config.rateCode.find((item) => item.value === 'ST');
if (stOption) {
props.row.rateCode = 'ST';
props.row.rateCode_dictText = stOption.label;
} else {
props.row.rateCode = 'ST';
props.row.rateCode_dictText = '立即';
}
}
};
// 格式化库存显示
const stockFormat = (partPercent: number, unitList: any[], quantity: number): string => {
const unitCode = unitList.find((item) => item.type == 'unit')?.label;
const minUnitCode = unitList.find((item) => item.type == 'minUnit')?.label;
const a = quantity % partPercent;
const b = Math.floor(quantity / partPercent);
if (a == 0) {
return b + ' ' + unitCode;
}
return b + ' ' + unitCode + ' ' + a + ' ' + minUnitCode;
};
// 检查单位
const checkUnit = (item: any): boolean => {
if (item.type == 'dose') {
return false;
} else if (props.row.partAttributeEnum == '2' && item.type == 'minUnit') {
return false;
} else {
return true;
}
};
const handleEnter = (prop: string) => props.handlers.handleEnter(prop, props.row, props.index);
const handleSave = () => {
nextTick(() => {
if (!formRef.value) {
console.error('Form ref not found');
return;
}
formRef.value.validate((valid: boolean) => {
if (valid) {
emit('save', props.row, props.index);
} else {
if (proxy?.$modal?.msgWarning) {
proxy.$modal.msgWarning('请完善必填信息');
}
}
});
});
};
const handleCancel = () => {
emit('cancel', props.row, props.index);
};
const handleNumberClick = (item: any) =>
props.handlers.handleNumberClick(item, props.index, props.row);
const handleOrgChange = (value: any) => props.handlers.handleOrgChange(value, props.index);
// 🔧 关键修复:计算属性,确保当前行的 orgId 始终存在于树数据中
// 当 orgId 不在科室树中时,注入一个临时节点,让 el-tree-select 能正确显示中文名称
const orgTreeData = computed(() => {
const tree = props.config.organization || [];
const orgId = props.row?.orgId;
const orgName = props.row?.orgName || props.row?.positionName;
if (!orgId || !orgName) return tree;
// 检查 orgId 是否已在树中存在
const existsInTree = findOrgName(orgId);
if (existsInTree) return tree;
// 注入临时节点到树根层级
return [...tree, { id: String(orgId), name: orgName, children: [] }];
});
// 🔧 从 organization 树中根据 orgId 查找科室名称(供 fallback-option 使用)
function findOrgName(orgId: any): string {
if (!orgId || !props.config.organization || props.config.organization.length === 0) return '';
const strId = String(orgId);
function walk(nodes: any[]): string {
if (!nodes) return '';
for (const node of nodes) {
if (String(node.id) === strId) return node.name;
// 模糊匹配:处理大 Long 精度丢失的情况
if (typeof node.id === 'string' && node.id.length >= 16 && strId.length >= 16
&& node.id.substring(0, 15) === strId.substring(0, 15)) {
return node.name;
}
if (node.children) {
const found = walk(node.children);
if (found) return found;
}
}
return '';
}
return walk(props.config.organization);
}
// 🔧 el-tree-select 的 fallback-option当 orgId 匹配不到树节点时,
// 仍然显示对应的中文科室名称而非原始数字 ID
// 优先从科室树查找,其次用 row 上保存的 orgName/positionName 兜底
const orgFallbackOption = (value: any) => {
const name = findOrgName(value);
const fallbackName = props.row?.orgName || props.row?.positionName;
return { label: name || fallbackName || value, value };
};
const convertValues = () => props.handlers.convertValue('doseQuantity', props.row, props.index);
const convertDoseValues = () => props.handlers.convertValue('dose', props.row, props.index);
const calculateTotalPrice = () => props.handlers.calculateTotal('price', props.row, props.index);
// 直接用 row 计算总金额:数量 * 单价,避免父组件索引不匹配的问题
const calculateTotalAmount = () => {
nextTick(() => {
const row = props.row;
const qty = new Decimal(row.doseQuantity || 0);
// 根据首次用量单位类型决定使用哪个单价
const unitType = row.unitCodeList?.find((k) => k.value == row.doseUnitCode)?.type;
const price = unitType == 'unit' ? row.unitPrice : row.minUnitPrice;
const roundedPrice = new Decimal(price || 0).toDecimalPlaces(2, Decimal.ROUND_HALF_UP);
row.totalPrice = qty.mul(roundedPrice).toDecimalPlaces(2, Decimal.ROUND_HALF_UP).toString();
});
};
const setInputRef = props.handlers.setInputRef;
defineExpose({
stockFormat,
checkUnit,
formRef,
});
</script>
<style scoped lang="scss">
.medicine-title {
font-size: 16px;
font-weight: 600;
min-width: 280px;
display: inline-block;
}
.total-amount {
font-size: 16px;
font-weight: 600;
color: #409eff;
white-space: nowrap;
}
.medicine-info {
font-size: 15px;
font-weight: 600;
color: #606266;
white-space: nowrap;
}
.form-group {
display: flex;
align-items: center;
gap: 8px;
background: #fff;
padding: 6px 10px;
border-radius: 4px;
border: 1px solid #ebeef5;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}
.form-actions {
display: inline-flex;
align-items: center;
gap: 0px;
}
.expend_div {
:deep(.el-form-item--default) {
margin-bottom: 0px;
}
:deep(.el-input-number .el-input__inner) {
text-align: center;
}
}
</style>

View File

@@ -1,123 +0,0 @@
import request from '@/utils/request';
// 申请单相关接口
// 手术项目专用分页查询(仅手术 + 定价,无库存/草稿库存等无关逻辑)
export function getSurgeryPage(params) {
return request({
url: '/doctor-station/advice/surgery-page',
method: 'get',
params: params,
});
}
// 检查项目专用分页查询(仅检查(23) + 定价,绕过 AOP 校验提升加载速度)
export function getExaminationPage(params) {
return request({
url: '/doctor-station/advice/examination-page',
method: 'get',
params: params,
});
}
//医嘱大下拉
export function getApplicationList(queryParams) {
return request({
url: '/doctor-station/advice/advice-base-info',
method: 'get',
params: queryParams,
});
}
/**
* 保存检查申请单
*/
export function saveCheckd(data) {
return request({
url: '/reg-doctorstation/request-form/save-check',
method: 'post',
data: data,
});
}
/**
* 保存检验申请单
*/
export function saveInspection(data) {
return request({
url: '/reg-doctorstation/request-form/save-inspection',
method: 'post',
data: data,
});
}
/**
* 保存输血申请单
*/
export function saveBloodTransfusio(data) {
return request({
url: '/reg-doctorstation/request-form/save-blood-transfusion',
method: 'post',
data: data,
});
}
/**
* 保存手术申请单
*/
export function saveSurgery(data) {
return request({
url: '/reg-doctorstation/request-form/save-surgery',
method: 'post',
data: data,
});
}
// =====
/**
* 查询检查申请单
*/
export function getCheck(data) {
return request({
url: '/reg-doctorstation/request-form/get-check',
method: 'get',
params: data,
});
}
/**
* 查询检验申请单
*/
export function getInspection(data) {
return request({
url: '/reg-doctorstation/request-form/get-inspection',
method: 'get',
params: data,
});
}
/**
* 查询输血申请单
*/
export function getBloodTransfusion(data) {
return request({
url: '/reg-doctorstation/request-form/get-blood-transfusion',
method: 'get',
params: data,
});
}
/**
* 查询手术申请单
*/
export function getSurgery(data) {
return request({
url: '/reg-doctorstation/request-form/get-surgery',
method: 'get',
params: data,
});
}
/**
* 住院患者转科
*/
export function transferOrganization(data) {
return request({
url: '/reg-doctorstation/special-advice/transfer-organization-orders',
method: 'post',
data: data,
});
}

View File

@@ -1,168 +0,0 @@
<!--
* @Author: sjjh
* @Date: 2025-09-05 22:32:17
* @Description: 申请单 检验检查输血手术
-->
<template>
<div>
<div class="applicationForm-bottom-btn">
<el-button-group>
<el-button
type="primary"
@click="showApplicationFormDialog('LaboratoryTests')"
:disabled="!props.patientInfo?.inHospitalOrgId"
>检验</el-button
>
<el-button
type="primary"
@click="showApplicationFormDialog('MedicalExaminations')"
:disabled="!props.patientInfo?.inHospitalOrgId"
>检查</el-button
>
<el-button
type="primary"
@click="showApplicationFormDialog('BloodTransfusion')"
:disabled="!props.patientInfo?.inHospitalOrgId"
>输血</el-button
>
<el-button
type="primary"
@click="showApplicationFormDialog('Surgery')"
:disabled="!props.patientInfo?.inHospitalOrgId"
>手术</el-button
>
</el-button-group>
</div>
<el-dialog
v-model="applicationFormDialogVisible"
destroy-on-close
width="1200px"
:close-on-click-modal="false"
:title="applicationFormTitle"
@close="closeDialog"
>
<component
:is="applicationFormName"
@submitOk="submitOk"
ref="applicationFormNameRef"
></component>
<template #footer>
<div class="dialog-footer">
<el-button @click="applicationFormDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitApplicationForm"> 确认 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, reactive, ref,} from 'vue';
import BloodTransfusion from './bloodTransfusion.vue';
import Surgery from './surgery.vue';
import LaboratoryTests from './laboratoryTests.vue';
import MedicalExaminations from './medicalExaminations.vue';
const { proxy } = getCurrentInstance();
const emits = defineEmits(['refResh']);
const props = defineProps({
patientInfo: {
type: Object,
required: false,
default: () => ({}),
},
});
const state = reactive({});
const components = ref({
BloodTransfusion,
Surgery,
LaboratoryTests,
MedicalExaminations,
});
const applicationFormName = ref(null);
const applicationFormDialogVisible = ref(false);
const applicationFormTitle = computed(() => {
const titleMap = {
BloodTransfusion: '输血申请单',
Surgery: '手术申请单',
LaboratoryTests: '检验申请单',
MedicalExaminations: '检查申请单',
};
return titleMap[applicationFormName.value?.name] || '申请单';
});
const closeDialog = () => {
applicationFormName.value = null;
applicationFormDialogVisible.value = false;
};
const showApplicationFormDialog = (name) => {
if (!components.value[name]) {
console.warn(`未找到组件: ${name}`);
return;
}
// 如果点击的是当前已打开的组件,则关闭
if (applicationFormName.value === components.value[name]) {
applicationFormDialogVisible.value = false;
applicationFormName.value = null;
return;
}
// 如果当前弹窗已打开,先关闭当前弹窗,延迟后打开新的弹窗
if (applicationFormDialogVisible.value) {
applicationFormDialogVisible.value = false;
setTimeout(() => {
applicationFormName.value = components.value[name];
applicationFormDialogVisible.value = true;
// 列表(项目列表)
applicationFormNameRef?.value.getList?.();
// 科室列表
applicationFormNameRef?.value.getLocationInfo();
// 诊断目录列表
applicationFormNameRef?.value.getDiagnosisList();
}, 150);
} else {
applicationFormName.value = components.value[name];
applicationFormDialogVisible.value = true;
nextTick(() => {
// 列表(项目列表)
applicationFormNameRef?.value.getList?.();
// 科室列表
applicationFormNameRef?.value.getLocationInfo();
// 诊断目录列表
applicationFormNameRef?.value.getDiagnosisList();
});
}
};
onBeforeMount(() => {});
onMounted(() => {});
const applicationFormNameRef = ref();
const submitApplicationForm = () => {
console.log(applicationFormNameRef.value);
if (applicationFormNameRef.value?.submit) {
applicationFormNameRef.value.submit();
}
};
const submitOk = () => {
applicationFormDialogVisible.value = false;
applicationFormName.value = null;
// 🔧 BugFix#318: 延迟刷新,确保后端数据已提交
setTimeout(() => {
emits('refResh');
}, 500);
};
defineExpose({ state });
</script>
<style lang="scss" scoped>
.applicationForm-bottom-btn {
display: flex;
justify-content: center;
align-items: center;
padding: 8px 0;
.el-button-group {
.el-button {
margin: 0 2px;
}
}
}
</style>

View File

@@ -1,570 +0,0 @@
<!--
* @Author: sjjh
* @Date: 2025-09-05 22:37:10
* @Description: 输血申请
-->
<template>
<div class="bloodTransfusion-container">
<div v-loading="loading" class="transfer-wrapper">
<el-transfer
v-model="transferValue"
:data="applicationList"
filter-placeholder="项目代码/名称"
filterable
:titles="['未选择', '已选择']"
/>
</div>
<div class="bloodTransfusion-form">
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="demo-ruleForm">
<el-row :gutter="20">
<!-- <el-col :span="12">
<el-form-item label="项目类别" prop="categoryType" style="width: 100%">
<el-input v-model="form.categoryType" autocomplete="off" />
</el-form-item>
</el-col> -->
<el-col :span="12">
<el-form-item label="发往科室" prop="targetDepartment" style="width: 100%">
<el-select
v-model="form.targetDepartment"
filterable
clearable
placeholder="请选择科室"
style="width: 100%"
>
<el-option
v-for="opt in flatOrgOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="症状" prop="symptom" style="width: 100%">
<el-input v-model="form.symptom" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="体征" prop="sign" style="width: 100%">
<el-input v-model="form.sign" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="临床诊断" prop="clinicalDiagnosis" style="width: 100%">
<el-input disabled v-model="form.clinicalDiagnosis" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="其他诊断" prop="otherDiagnosis" style="width: 100%">
<el-input disabled v-model="form.otherDiagnosis" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="相关结果" prop="relatedResult" style="width: 100%">
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="注意事项" prop="attention" style="width: 100%">
<el-input v-model="form.attention" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
</template>
<script setup name="BloodTransfusion">
import {computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, reactive, ref, watch} from 'vue';
import {ElMessage} from 'element-plus';
import {patientInfo} from '../../../store/patient.js';
import {getDepartmentList} from '@/api/public.js';
import request from '@/utils/request';
import {getDiagnosisTreatmentOne} from '@/views/catalog/diagnosistreatment/components/diagnosistreatment';
import {getEncounterDiagnosis} from '../../api.js';
import {getApplicationList, saveBloodTransfusio} from './api';
const { proxy } = getCurrentInstance();
/** 科室树节点 id 统一为字符串,避免大整数精度丢失导致 tree-select 无法匹配 */
const normalizeOrgTreeIds = (nodes) => {
if (!Array.isArray(nodes)) return [];
return nodes.map((node) => ({
...node,
id: node.id != null ? String(node.id) : node.id,
children: node.children?.length ? normalizeOrgTreeIds(node.children) : undefined,
}));
};
// 递归查找树形科室节点
const findTreeItem = (list, id) => {
if (!list || list.length === 0 || id == null || id === '') return null;
const strId = String(id);
for (const item of list) {
if (String(item.id) === strId) return item;
if (item.children && item.children.length > 0) {
const found = findTreeItem(item.children, id);
if (found) return found;
}
}
return null;
};
/** 在科室树中解析 orgId兼容 Long 转 Number 后的精度丢失) */
const resolveOrgIdInTree = (rawOrgId) => {
if (rawOrgId == null || rawOrgId === '') return '';
const strOrgId = String(rawOrgId);
const findInTree = (nodes) => {
if (!nodes?.length) return null;
for (const node of nodes) {
if (String(node.id) === strOrgId) return String(node.id);
if (
typeof node.id === 'string' &&
node.id.length >= 16 &&
strOrgId.length >= 16 &&
node.id.substring(0, 15) === strOrgId.substring(0, 15)
) {
return String(node.id);
}
if (node.children?.length) {
const found = findInTree(node.children);
if (found) return found;
}
}
return null;
};
return findInTree(orgOptions.value) || strOrgId;
};
const resolveTargetDepartmentId = (rawId) => {
if (rawId == null || rawId === '') return '';
const resolved = resolveOrgIdInTree(rawId);
const node = findTreeItem(orgOptions.value, resolved);
return node ? String(node.id) : resolved;
};
/** 诊疗目录「所属科室」→ AdviceBaseDto.orgId */
const getBelongingOrgId = (item) => {
if (!item) return null;
return item.orgId ?? item.org_id ?? null;
};
/** 诊疗目录所属科室名称(字典翻译字段) */
const getBelongingOrgName = (item) => {
if (!item) return '';
return item.orgId_dictText || item.orgName || item.org_name || '';
};
/** 按机构 ID 拉取科室名称(树中无节点时兜底) */
const fetchOrgNameById = async (orgId) => {
if (orgId == null || orgId === '') return '';
const fromTree = findOrgName(orgId);
if (fromTree) return fromTree;
try {
const res = await request({
url: '/base-data-manage/organization/organization-getById',
method: 'get',
params: { orgId },
});
if (res.code === 200 && res.data?.name) {
return res.data.name;
}
} catch (e) {
console.error('查询科室名称失败', e);
}
return '';
};
/** 从机构树解析科室名称 */
const findOrgName = (orgId) => {
if (orgId == null || orgId === '') return '';
const node = findTreeItem(orgOptions.value, orgId);
if (node?.name) return node.name;
const resolved = resolveOrgIdInTree(orgId);
const resolvedNode = findTreeItem(orgOptions.value, resolved);
return resolvedNode?.name || '';
};
/** 自动填充时缓存的科室名称 */
const targetDepartmentName = ref('');
/** 扁平化科室选项,保证 el-select 能稳定显示 label */
const flatOrgOptions = computed(() => {
const options = [];
const seen = new Set();
const walk = (nodes) => {
for (const node of nodes || []) {
if (node?.id == null) continue;
const value = String(node.id);
if (seen.has(value)) continue;
seen.add(value);
options.push({ value, label: node.name || value });
if (node.children?.length) walk(node.children);
}
};
walk(orgOptions.value);
const curId = form.targetDepartment;
const curName = targetDepartmentName.value || findOrgName(curId);
if (curId && curName && !seen.has(String(curId))) {
options.unshift({ value: String(curId), label: curName });
}
return options;
});
/** 从诊疗目录详情补全所属科室(医嘱下拉接口不带 orgId_dictText */
const resolveProjectOrgInfo = async (item) => {
if (!item) return { orgId: null, orgName: '' };
let orgId = getBelongingOrgId(item);
let orgName = getBelongingOrgName(item);
if ((!orgId || !orgName) && item.adviceDefinitionId) {
try {
const res = await getDiagnosisTreatmentOne(item.adviceDefinitionId);
const detail = res?.data;
if (detail) {
orgId = orgId ?? detail.orgId ?? detail.org_id ?? null;
orgName = orgName || detail.orgId_dictText || detail.orgName || '';
}
} catch (e) {
console.error('查询诊疗目录所属科室失败', e);
}
}
if (orgId && !orgName) {
orgName = await fetchOrgNameById(orgId);
}
return { orgId, orgName };
};
/** 写入发往科室 */
const applyTargetDepartment = async (belongOrgId, nameHint = '') => {
if (belongOrgId == null || belongOrgId === '') {
form.targetDepartment = '';
targetDepartmentName.value = '';
return;
}
const resolvedDeptId = resolveTargetDepartmentId(belongOrgId);
const deptName =
nameHint || findOrgName(belongOrgId) || findOrgName(resolvedDeptId) || (await fetchOrgNameById(belongOrgId));
targetDepartmentName.value = deptName;
form.targetDepartment = resolvedDeptId;
};
const emits = defineEmits(['submitOk']);
const props = defineProps({});
const state = reactive({});
const applicationListAll = ref([]);
const applicationList = ref([]);
const loading = ref(false);
const orgOptions = ref([]); // 科室选项
const getList = () => {
if (!patientInfo.value?.inHospitalOrgId) {
applicationList.value = [];
return;
}
loading.value = true;
getApplicationList({
pageSize: 500,
pageNum: 1,
categoryCode: '28',
organizationId: patientInfo.value.inHospitalOrgId,
adviceTypes: [3], //1 药品 2耗材 3诊疗
})
.then((res) => {
if (res.code === 200 && Array.isArray(res.data?.records)) {
const records = res.data.records.filter((item) => item.adviceDefinitionId != null);
applicationListAll.value = records;
applicationList.value = records.map((item) => {
const priceInfo = item.priceList?.[0] || {};
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
const unit = item.unitCode_dictText || item.unitCode || '';
const id = item.adviceDefinitionId;
return {
adviceDefinitionId: id,
orgId: item.orgId,
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
key: id,
};
});
} else {
proxy.$message.error(res.message || '加载输血项目失败');
applicationListAll.value = [];
applicationList.value = [];
}
})
.finally(() => {
loading.value = false;
if (transferValue.value.length > 0) {
nextTick(async () => {
const valid = await validateTransferOrgConsistency(transferValue.value);
if (valid) {
lastValidTransferValue.value = [...transferValue.value];
fillTargetDepartmentFromSelection(transferValue.value, 1);
} else {
transferValue.value = [];
lastValidTransferValue.value = [];
}
});
}
});
};
const transferValue = ref([]);
/** 上一次通过校验的已选项目(科室不一致时回滚到此状态) */
const lastValidTransferValue = ref([]);
const isRevertingTransfer = ref(false);
let transferValidateSeq = 0;
const form = reactive({
// categoryType: '', // 项目类别
targetDepartment: '', // 发往科室
symptom: '', // 症状
sign: '', // 体征
clinicalDiagnosis: '', // 临床诊断
otherDiagnosis: '', // 其他诊断
relatedResult: '', // 相关结果
attention: '', // 注意事项
primaryDiagnosisList: [], //主诊断目录
otherDiagnosisList: [], //其他断目录
});
const rules = reactive({});
onBeforeMount(() => {});
onMounted(() => {
getList();
getLocationInfo();
});
const collectSelectedProjects = (selectProjectIds) => {
return (selectProjectIds || [])
.map((element) =>
applicationListAll.value.find((item) => String(item.adviceDefinitionId) === String(element))
)
.filter(Boolean);
};
/** 校验已选项目的所属科室是否一致(超过 1 项时才校验) */
const validateTransferOrgConsistency = async (selectProjectIds) => {
const arr = collectSelectedProjects(selectProjectIds);
if (arr.length <= 1) {
return true;
}
const orgInfoList = await Promise.all(arr.map((item) => resolveProjectOrgInfo(item)));
const firstOrgId = orgInfoList[0]?.orgId;
return orgInfoList.every((info) => String(info?.orgId ?? '') === String(firstOrgId ?? ''));
};
/**
* type(1watch监听类型 2:点击保存类型)
*/
const fillTargetDepartmentFromSelection = async (selectProjectIds, type) => {
const manualDept = type === 2 && form.targetDepartment ? form.targetDepartment : '';
const arr = collectSelectedProjects(selectProjectIds);
if (arr.length === 0) {
// 项目列表尚未加载完时,已选 ID 存在则先不清空(避免误清发往科室)
if ((selectProjectIds || []).length > 0 && applicationListAll.value.length === 0) {
return type === 2 ? !!manualDept : true;
}
form.targetDepartment = '';
targetDepartmentName.value = '';
return type === 2 ? !!manualDept : true;
}
const orgInfoList = await Promise.all(arr.map((item) => resolveProjectOrgInfo(item)));
const firstOrg = orgInfoList[0];
const belongOrgId = firstOrg?.orgId;
const allSameOrg = orgInfoList.every((info) => String(info?.orgId ?? '') === String(belongOrgId ?? ''));
if (!allSameOrg) {
if (type === 2) {
ElMessage.error('所选项目的所属科室不一致,请分开申请');
}
return false;
}
if (belongOrgId == null || belongOrgId === '') {
if (type === 2 && manualDept) {
await applyTargetDepartment(manualDept, findOrgName(manualDept));
return true;
}
if (type === 2) {
ElMessage.warning('所选项目未在诊疗目录配置所属科室,请手动选择发往科室');
return false;
}
form.targetDepartment = '';
targetDepartmentName.value = '';
return true;
}
if (type === 2 && manualDept) {
await applyTargetDepartment(manualDept, findOrgName(manualDept));
return true;
}
await applyTargetDepartment(belongOrgId, firstOrg?.orgName || '');
return true;
};
// 选中项目:先校验所属科室一致,不通过则回滚穿梭框,不允许进入「已选择」
watch(
() => transferValue.value,
async (newValue) => {
if (isRevertingTransfer.value) return;
const seq = ++transferValidateSeq;
const valid = await validateTransferOrgConsistency(newValue);
if (seq !== transferValidateSeq) return;
if (!valid) {
ElMessage.error('所选项目的所属科室不一致,请分开申请');
isRevertingTransfer.value = true;
transferValue.value = [...lastValidTransferValue.value];
await nextTick();
isRevertingTransfer.value = false;
return;
}
lastValidTransferValue.value = [...newValue];
await fillTargetDepartmentFromSelection(newValue, 1);
}
);
watch(
() => orgOptions.value,
() => {
if (transferValue.value.length > 0) {
nextTick(() => {
fillTargetDepartmentFromSelection(transferValue.value, 1);
});
}
},
{ deep: true }
);
const submit = async () => {
if (transferValue.value.length == 0) {
return proxy.$message.error('请选择申请单');
}
if (!(await fillTargetDepartmentFromSelection(transferValue.value, 2))) {
return;
}
if (!form.targetDepartment) {
return proxy.$message.error('请选择发往科室');
}
let applicationListAllFilter = applicationListAll.value.filter((item) => {
return transferValue.value.some((id) => String(id) === String(item.adviceDefinitionId));
});
applicationListAllFilter = applicationListAllFilter.map((item) => {
const priceInfo = item.priceList?.[0] || {};
return {
adviceDefinitionId: item.adviceDefinitionId /** 诊疗定义id */,
quantity: 1, // /** 请求数量 */
unitCode: priceInfo.unitCode /** 请求单位编码 */,
unitPrice: priceInfo.price /** 单价 */,
totalPrice: priceInfo.price /** 总价 */,
positionId: form.targetDepartment || item.positionId, //执行科室id
ybClassEnum: item.ybClassEnum, //类别医保编码
conditionId: item.conditionId, //诊断ID
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
adviceType: item.adviceType, ///** 医嘱类型 */
definitionId: priceInfo.definitionId, //费用定价主表ID */
definitionDetailId: item.definitionDetailId, //费用定价子表ID */
accountId: patientInfo.value.accountId, // // 账户id
};
});
saveBloodTransfusio({
activityList: applicationListAllFilter,
patientId: patientInfo.value.patientId, //患者ID
encounterId: patientInfo.value.encounterId, // 就诊ID
organizationId: patientInfo.value.inHospitalOrgId, // 医疗机构ID
requestFormId: '', // 申请单ID
name: '输血申请单',
descJson: JSON.stringify(form),
categoryEnum: '23', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
}).then((res) => {
if (res.code === 200) {
proxy.$message.success(res.msg);
applicationList.value = [];
applicationListAll.value = [];
transferValue.value = [];
lastValidTransferValue.value = [];
emits('submitOk');
} else {
proxy.$message.error(res.message);
}
});
};
/** 查询科室(与检验申请单一致) */
const getLocationInfo = () => {
return getDepartmentList().then((res) => {
orgOptions.value = normalizeOrgTreeIds(res?.data || []);
if (transferValue.value.length > 0) {
nextTick(() => fillTargetDepartmentFromSelection(transferValue.value, 1));
}
});
};
// 获取诊断目录
function getDiagnosisList() {
getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => {
console.log('诊断目录========>', JSON.stringify(res.data));
if (res.code == 200) {
const datas = (res.data || []).map((item) => {
let obj = {
...item,
};
if (obj.diagSrtNo == null) {
obj.diagSrtNo = '1';
}
return obj;
});
// 主诊断
form.primaryDiagnosisList = datas.filter((item) => {
return item?.maindiseFlag == 1;
});
console.log('@@@@@@========>', form.primaryDiagnosisList);
if (form.primaryDiagnosisList.length == 1) {
const obj = form.primaryDiagnosisList[0];
form.clinicalDiagnosis = obj.name;
}
//其他诊断
form.otherDiagnosisList = datas.filter((item) => {
return item?.maindiseFlag !== 1;
});
const otherDiagnosisNameList = form.otherDiagnosisList.map((item) => {
return item.name;
});
form.otherDiagnosis = otherDiagnosisNameList.join(',');
}
});
}
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
</script>
<style lang="scss" scoped>
.bloodTransfusion-container {
height: 100%;
width: 100%;
.transfer-wrapper {
position: relative;
min-height: 300px;
}
:deep(.el-transfer) {
--el-transfer-panel-width: 480px !important;
display: flex !important;
flex-direction: row !important;
}
:deep(.el-transfer__buttons) {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 4px;
}
:deep(.el-transfer__button) {
margin: 4px 0;
}
.bloodTransfusion-form {
padding: 8px 8px 0 8px;
height: 100%;
width: 100%;
display: flex;
}
}
</style>

View File

@@ -1,604 +0,0 @@
<!--
* @Author: sjjh
* @Date: 2025-09-05 22:31:58
* @Description: 检验
-->
<template>
<div class="LaboratoryTests-container">
<div v-loading="loading" class="transfer-wrapper">
<!-- 远程搜索框 -->
<div class="search-bar">
<el-input
v-model="searchKey"
placeholder="输入项目代码/名称搜索"
clearable
@keyup.enter="handleSearch"
@clear="handleSearch"
style="width: 300px; margin-bottom: 10px"
>
<template #append>
<el-button @click="handleSearch" :loading="loading">搜索</el-button>
</template>
</el-input>
<span class="total-count"> {{ totalCount }} </span>
</div>
<el-transfer
v-model="transferValue"
:data="transferData"
:titles="['未选择', '已选择']"
/>
</div>
<div class="bloodTransfusion-form">
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="demo-ruleForm">
<el-row :gutter="20">
<!-- <el-col :span="12">
<el-form-item label="项目类别" prop="categoryType" style="width: 100%">
<el-input v-model="form.categoryType" autocomplete="off" />
</el-form-item>
</el-col> -->
<el-col :span="12">
<el-form-item label="发往科室" prop="targetDepartment" style="width: 100%">
<!-- <el-input v-model="form.targetDepartment" autocomplete="off" /> -->
<el-tree-select
clearable
style="width: 100%"
v-model="form.targetDepartment"
filterable
:data="orgOptions"
:props="{
value: 'id',
label: 'name',
children: 'children',
}"
value-key="id"
check-strictly
placeholder="请选择科室"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="症状" prop="symptom" style="width: 100%">
<el-input v-model="form.symptom" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="体征" prop="sign" style="width: 100%">
<el-input v-model="form.sign" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="临床诊断" prop="clinicalDiagnosis" style="width: 100%">
<el-input disabled v-model="form.clinicalDiagnosis" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="其他诊断" prop="otherDiagnosis" style="width: 100%">
<el-input disabled v-model="form.otherDiagnosis" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="相关结果" prop="relatedResult" style="width: 100%">
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="注意事项" prop="attention" style="width: 100%">
<el-input v-model="form.attention" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<!-- 申请类型 -->
<el-col :span="12">
<el-form-item label="申请类型" prop="applicationType" style="width: 100%">
<el-radio-group v-model="form.applicationType">
<el-radio :value="0">普通</el-radio>
<el-radio :value="1">急诊</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<!-- 标本类型 -->
<el-col :span="12">
<el-form-item label="标本类型" prop="specimenName" style="width: 100%">
<el-select v-model="form.specimenName" placeholder="请选择标本类型" style="width: 100%">
<el-option label="血液" value="血液" />
<el-option label="尿液" value="尿液" />
<el-option label="粪便" value="粪便" />
<el-option label="痰液" value="痰液" />
<el-option label="咽拭子" value="咽拭子" />
<el-option label="脑脊液" value="脑脊液" />
<el-option label="胸腹水" value="胸腹水" />
<el-option label="关节液" value="关节液" />
<el-option label="分泌物" value="分泌物" />
<el-option label="其他" value="其他" />
</el-select>
</el-form-item>
</el-col>
<!-- 执行时间 -->
<el-col :span="12">
<el-form-item label="执行时间" prop="executeTime" style="width: 100%">
<el-date-picker
v-model="form.executeTime"
type="datetime"
placeholder="选择执行时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
</template>
<script setup name="LaboratoryTests">
import {getCurrentInstance, nextTick, onMounted, reactive, ref, watch, computed} from 'vue';
import {patientInfo} from '../../../store/patient.js';
import {getExaminationPage, saveInspection} from './api';
import {ActivityCategory} from '@/utils/medicalConstants';
import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js';
import {ElMessage} from 'element-plus';
const { proxy } = getCurrentInstance();
// 递归查找树形科室节点
const findTreeItem = (list, id) => {
if (!list || list.length === 0) return null;
for (const item of list) {
if (item.id == id) return item;
if (item.children && item.children.length > 0) {
const found = findTreeItem(item.children, id);
if (found) return found;
}
}
return null;
};
const emits = defineEmits(['submitOk']);
const props = defineProps({
editData: {
type: Object,
default: null,
},
});
const isEditMode = computed(() => !!props.editData?.requestFormId);
const state = reactive({});
const applicationListAll = ref([]);
const loading = ref(false);
const orgOptions = ref([]);
const searchKey = ref('');
const totalCount = ref(0);
const skipDeptAutoFill = ref(false);
// 将已加载的全部数据转为 transfer 组件所需的格式
const buildTransferData = (records) => {
return records.map((item) => {
const price = item.price != null ? Number(item.price).toFixed(2) : '0.00';
const unit = item.unitCodeDictText || item.unitCode || '';
return {
adviceDefinitionId: item.adviceDefinitionId,
orgId: item.orgId,
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
key: item.adviceDefinitionId,
};
});
};
const selectedItemsCache = ref(new Map());
const loadAllData = async () => {
if (!patientInfo.value?.inHospitalOrgId) {
applicationListAll.value = [];
return;
}
loading.value = true;
try {
const res = await getExaminationPage({
pageSize: 100,
pageNo: 1,
categoryCode: ActivityCategory.PROOF,
organizationId: patientInfo.value.inHospitalOrgId,
searchKey: searchKey.value,
});
if (res.code !== 200) {
proxy.$message.error(res.message);
applicationListAll.value = [];
return;
}
applicationListAll.value = res.data?.records || [];
totalCount.value = res.data?.total || 0;
if (!searchKey.value) {
applyEditTransferSelection();
}
} catch (e) {
proxy.$message.error('获取检验项目列表失败');
applicationListAll.value = [];
} finally {
loading.value = false;
}
};
const transferData = computed(() => buildTransferData(applicationListAll.value));
const getList = async () => {
await loadAllData();
};
let searchTimer = null;
const handleSearch = () => {
clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
loadAllData();
}, 300);
};
// 编辑初始化标志:避免 applyEditTransferSelection 设置 transferValue 时触发 projectWithDepartment 覆盖 descJson 中的科室值
const isInitializing = ref(false);
const transferValue = ref([]);
const form = reactive({
// categoryType: '', // 项目类别
targetDepartment: '', // 发往科室
symptom: '', // 症状
sign: '', // 体征
clinicalDiagnosis: '', // 临床诊断
otherDiagnosis: '', // 其他诊断
relatedResult: '', // 相关结果
attention: '', // 注意事项
applicationType: 0, // 申请类型 0-普通 1-急诊
specimenName: '血液', // 标本类型
executeTime: null, // 执行时间
primaryDiagnosisList: [], //主诊断目录
otherDiagnosisList: [], //其他断目录
});
const rules = reactive({});
const normalizeOrgTreeIds = (nodes) => {
if (!Array.isArray(nodes)) return [];
return nodes.map((node) => ({
...node,
id: node.id != null ? String(node.id) : node.id,
children: node.children?.length ? normalizeOrgTreeIds(node.children) : undefined,
}));
};
const resolveTargetDepartmentId = (rawId) => {
if (rawId == null || rawId === '') return '';
const node = findTreeItem(orgOptions.value, rawId);
return node ? String(node.id) : String(rawId);
};
const applyTargetDepartmentEcho = () => {
if (form.targetDepartment) {
form.targetDepartment = resolveTargetDepartmentId(form.targetDepartment);
}
};
onMounted(() => {
getLocationInfo();
getDiagnosisList();
getList();
});
/**
* type(1watch监听类型 2:点击保存类型)
* selectProjectIds(选中项目的id数组)
* */
const projectWithDepartment = (selectProjectIds, type) => {
//1.获取选中的项目 2.判断项目的执行科室是否相同 3.判断执行科室是否配置 4.将项目的执行科室复值到执行科室下拉选位置
let isRelease = true;
// 选中项目的数组
const arr = [];
// 根据选中的项目id查找对应的项目从全部原始数据中查找
selectProjectIds.forEach((element) => {
let searchData = applicationListAll.value.find((item) => {
return element == item.adviceDefinitionId;
});
if (!searchData) {
searchData = selectedItemsCache.value.get(element);
}
if (searchData) {
const priceInfo = searchData.priceList?.[0] || {};
const price = searchData.price != null ? Number(searchData.price).toFixed(2)
: priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
const unit = searchData.unitCodeDictText || searchData.unitCode_dictText || searchData.unitCode || '';
arr.push({
adviceDefinitionId: searchData.adviceDefinitionId,
orgId: searchData.orgId,
label: searchData.adviceName + ' (¥' + price + '/' + unit + ')',
key: searchData.adviceDefinitionId,
});
}
});
// 保存用户手动选择/回显的发往科室(提交、编辑回显时需要保留)
const manualDept =
type === 2 || (isEditMode.value && form.targetDepartment) ? form.targetDepartment : '';
// 清空科室
form.targetDepartment = '';
if (arr.length > 0) {
const obj = arr[0];
// 判断科室是否相同
const isCompare = arr.every((item) => {
return item.orgId == obj.orgId;
});
if (!isCompare) {
ElMessage({
type: 'error',
message: '执行科室不同',
});
isRelease = false;
}
// 选中项目中的执行科室id与全部科室数据做匹配
const findItem = findTreeItem(orgOptions.value, obj.orgId);
if (!findItem) {
// type=2(提交)时,若用户已手动选择发往科室,则允许提交
if ((type === 2 || isEditMode.value) && manualDept) {
form.targetDepartment = resolveTargetDepartmentId(manualDept);
isRelease = true;
} else if (type === 2 && !manualDept) {
// 提交时用户未手动选择科室,才提示错误
isRelease = false;
ElMessage({
type: 'error',
message: '未找到项目执行的科室',
});
} else {
// type=1(选择项目变化)时,不弹窗,仅清空科室让用户自行选择
isRelease = false;
}
}
if (findItem && isRelease) {
// 提交时若用户已选「发往科室」,不得用项目默认执行科室覆盖
if ((type === 2 || isEditMode.value) && manualDept) {
form.targetDepartment = resolveTargetDepartmentId(manualDept);
} else {
form.targetDepartment = String(findItem.id);
}
}
}
return isRelease;
};
// 监听选择项目变化
watch(
() => transferValue.value,
(newValue) => {
if (skipDeptAutoFill.value) return;
if (isInitializing.value) return;
newValue.forEach((id) => {
if (!selectedItemsCache.value.has(id)) {
const item = applicationListAll.value.find((i) => i.adviceDefinitionId == id);
if (item) selectedItemsCache.value.set(id, item);
}
});
projectWithDepartment(newValue, 1);
}
);
/** 编辑弹窗:根据申请单明细把右侧「已选择」与 transferValue 对齐(依赖 applicationListAll 已加载) */
const applyEditTransferSelection = () => {
const newData = props.editData
if (!newData?.requestFormId || !newData.requestFormDetailList?.length) {
return
}
if (!applicationListAll.value.length) {
return
}
const selectedIds = []
for (const detail of newData.requestFormDetailList) {
const idFromDetail = detail.activityId ?? detail.adviceDefinitionId
let matched = null
if (idFromDetail != null && idFromDetail !== '') {
matched = applicationListAll.value.find(
(item) => String(item.adviceDefinitionId) === String(idFromDetail)
)
}
if (!matched && detail.adviceName) {
matched = applicationListAll.value.find((item) => item.adviceName === detail.adviceName)
}
if (!matched && detail.adviceName) {
const norm = (s) => String(s || '').trim()
matched = applicationListAll.value.find(
(item) => norm(item.adviceName) === norm(detail.adviceName)
)
}
if (matched) {
selectedIds.push(matched.adviceDefinitionId)
}
}
const uniq = [...new Set(selectedIds)]
// 设置初始化标志,防止 transferValue 变化触发 projectWithDepartment 覆盖 descJson 中的科室值
isInitializing.value = true
skipDeptAutoFill.value = true
transferValue.value = uniq
nextTick(() => {
skipDeptAutoFill.value = false
})
isInitializing.value = false
if (newData.requestFormDetailList.length && uniq.length === 0) {
console.warn(
'[LaboratoryTests] 申请单明细未能在项目字典中匹配到项,请核对 activityId / 项目名称',
newData.requestFormDetailList
)
}
}
// 编辑模式下,回显已有数据(表单来自 descJson项目选择在字典加载后由 applyEditTransferSelection 完成)
watch(
() => props.editData,
(newData) => {
if (!newData || !newData.requestFormId) return
if (newData.descJson) {
try {
const obj = JSON.parse(newData.descJson)
Object.keys(form).forEach((key) => {
if (obj[key] !== undefined) {
form[key] = obj[key]
}
})
applyTargetDepartmentEcho()
} catch (e) {
console.error('解析 descJson 失败:', e)
}
}
applyEditTransferSelection()
},
{ immediate: true, deep: true }
)
watch(
() => orgOptions.value,
() => {
applyTargetDepartmentEcho()
}
)
// 编辑模式下,项目字典首次加载完成后回显已选项目(搜索刷新不重置)
watch(
() => applicationListAll.value,
() => {
if (!props.editData?.requestFormId) return;
if (!props.editData.requestFormDetailList?.length) return;
if (!applicationListAll.value.length) return;
if (searchKey.value) return;
const selectedIds = [];
props.editData.requestFormDetailList.forEach((detail) => {
const matched = applicationListAll.value.find(
(item) => item.adviceName === detail.adviceName
);
if (matched) {
selectedIds.push(matched.adviceDefinitionId);
}
});
isInitializing.value = true;
transferValue.value = selectedIds;
isInitializing.value = false;
applyEditTransferSelection();
}
);
const submit = () => {
if (transferValue.value.length == 0) {
return proxy.$message.error('请选择申请单');
}
if (!projectWithDepartment(transferValue.value, 2)) {
return;
}
let applicationListAllFilter = transferValue.value.map((id) => {
let item = applicationListAll.value.find((i) => i.adviceDefinitionId == id);
if (!item) {
item = selectedItemsCache.value.get(id);
}
if (!item) return null;
const priceInfo = item.priceList?.[0] || {};
return {
adviceDefinitionId: item.adviceDefinitionId /** 诊疗定义id */,
quantity: 1, // /** 请求数量 */
unitCode: item.unitCode || priceInfo.unitCode || '' /** 请求单位编码 */,
unitPrice: item.price ?? priceInfo.price ?? 0 /** 单价 */,
totalPrice: item.price ?? priceInfo.price ?? 0 /** 总价 */,
positionId: form.targetDepartment || item.positionId, // 用户指定发往科室优先于项目默认执行科室
ybClassEnum: item.ybClassEnum || '', //类别医保编码
conditionId: item.conditionId || '', //诊断ID
encounterDiagnosisId: item.encounterDiagnosisId || '', //就诊诊断id
adviceType: item.adviceType || 3, ///** 医嘱类型 */
definitionId: item.chargeItemDefinitionId || priceInfo.definitionId || '', //费用定价主表ID */
definitionDetailId: item.definitionDetailId || priceInfo.definitionDetailId || '', //费用定价子表ID */
accountId: patientInfo.value.accountId, // // 账户id
};
}).filter(Boolean);
const params = {
activityList: applicationListAllFilter,
patientId: patientInfo.value.patientId, //患者ID
encounterId: patientInfo.value.encounterId, // 就诊ID
organizationId: patientInfo.value.inHospitalOrgId, // 医疗机构ID
requestFormId: isEditMode.value ? props.editData.requestFormId : '', // 申请单ID编辑模式传入新增为空
name: '检验申请单',
descJson: JSON.stringify(form),
categoryEnum: '21', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
};
saveInspection(params).then((res) => {
if (res.code === 200) {
proxy.$message.success(isEditMode.value ? '修改成功' : res.msg);
transferValue.value = [];
selectedItemsCache.value.clear();
emits('submitOk');
} else {
proxy.$message.error(res.message);
}
});
};
/** 查询科室 */
const getLocationInfo = () => {
return getDepartmentList().then((res) => {
orgOptions.value = normalizeOrgTreeIds(res.data || []);
applyTargetDepartmentEcho();
});
};
// 获取诊断目录
function getDiagnosisList() {
getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => {
if (res.code == 200) {
const datas = (res.data || []).map((item) => {
let obj = {
...item,
};
if (obj.diagSrtNo == null) {
obj.diagSrtNo = '1';
}
return obj;
});
// 主诊断
form.primaryDiagnosisList = datas.filter((item) => {
return item?.maindiseFlag == 1;
});
console.log('@@@@@@========>', form.primaryDiagnosisList);
if (form.primaryDiagnosisList.length == 1) {
const obj = form.primaryDiagnosisList[0];
form.clinicalDiagnosis = obj.name;
}
//其他诊断
form.otherDiagnosisList = datas.filter((item) => {
return item?.maindiseFlag !== 1;
});
const otherDiagnosisNameList = form.otherDiagnosisList.map((item) => {
return item.name;
});
form.otherDiagnosis = otherDiagnosisNameList.join(',');
}
});
}
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
</script>
<style lang="scss" scoped>
.LaboratoryTests-container {
height: 100%;
width: 100%;
.transfer-wrapper {
position: relative;
min-height: 300px;
.search-bar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
.total-count {
font-size: 13px;
color: #909399;
white-space: nowrap;
}
}
}
.el-transfer {
--el-transfer-panel-width: 480px !important;
}
.bloodTransfusion-form {
padding: 8px 8px 0 8px;
height: 100%;
width: 100%;
display: flex;
}
}
</style>

View File

@@ -1,127 +0,0 @@
<template>
<el-dialog
v-model="dialogVisible"
destroy-on-close
width="600px"
:close-on-click-modal="false"
title="出院申请"
@close="closeDialog"
>
<div style="padding: 0 80px">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item label="出院方式" prop="outpatientType">
<el-select v-model="form.outpatientType">
<el-option
v-for="(item, index) in dscg_way"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="出院时间" prop="outpatientTime">
<el-radio-group v-model="form.outpatientTime">
<el-radio value="1">今日</el-radio>
<el-radio value="2">明日</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="出院描述" prop="outpatientDescription"
><el-input v-model="form.outpatientDescription" type="textarea" rows="5" />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitApplicationForm"> 确认 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {leaveHospital} from '../../api';
import {dayjs} from 'element-plus';
const props = defineProps({
encounterId: {
type: [String, Number],
required: false,
},
patientId: {
type: [String, Number],
required: false,
},
conditionId: {
type: [String, Number],
required: false,
},
encounterDiagnosisId: {
type: [String, Number],
required: false,
},
});
const { proxy } = getCurrentInstance();
const { dscg_way } = proxy.useDict('dscg_way');
const dialogVisible = ref(false);
const formRef = ref(null);
const emit = defineEmits(['success']);
const form = reactive({
outpatientType: [],
outpatientTime: '',
outpatientDescription: '',
});
const rules = {
outpatientType: [{ required: true, message: '请选择出院方式', trigger: 'change' }],
outpatientTime: [{ required: true, message: '请选择出院时间', trigger: 'change' }],
outpatientDescription: [{ required: true, message: '请输入出院描述', trigger: 'blur' }],
};
function getEndTime() {
let date = dayjs();
if (form.outpatientTime === '2') {
date = date.add(1, 'day');
}
return date.format('YYYY-MM-DD HH:mm:ss');
}
function openDialog() {
dialogVisible.value = true;
}
const submitApplicationForm = async () => {
if (!formRef.value) return;
try {
await formRef.value.validate();
const res = await leaveHospital({
endTime: getEndTime(),
outWayCode: form.outpatientType,
reasonText: form.outpatientDescription,
encounterId: props.encounterId,
patientId: props.patientId,
conditionId: props.conditionId,
encounterDiagnosisId: props.encounterDiagnosisId,
});
proxy.$modal.msgSuccess(res.msg);
closeDialog();
emit('success');
} catch (error) {
console.log(error);
}
};
const closeDialog = () => {
dialogVisible.value = false;
Object.assign(form, {
outpatientType: [],
outpatientTime: '',
outpatientDescription: '',
});
formRef.value?.resetFields();
};
defineExpose({
openDialog,
});
</script>

View File

@@ -1,349 +0,0 @@
<!--
* @Author: sjjh
* @Date: 2025-09-05 22:38:55
* @Description: 手术
-->
<template>
<div class="surgery-container">
<div class="transfer-wrapper" style="min-height: 300px;">
<!-- 搜索框3字触发后端搜索 -->
<div style="padding: 6px 0;">
<el-input
v-model="searchKey"
placeholder="请输入3个字及以上搜索"
clearable
@input="onSearchInput"
style="width: 320px;"
/>
</div>
<!-- 加载提示不阻塞穿梭框操作 -->
<div v-if="loading" style="padding:8px 0; color:#909399; font-size:13px;">
<el-icon class="is-loading"><Loading /></el-icon> 手术项目加载中...
</div>
<el-transfer
ref="transferRef"
v-model="transferValue"
:data="applicationList"
:titles="['待选择', '已选择']"
:format="leftPanelFormat"
/>
</div>
<div class="bloodTransfusion-form">
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="demo-ruleForm">
<el-row :gutter="20">
<!-- <el-col :span="12">
<el-form-item label="项目类别" prop="categoryType" style="width: 100%">
<el-input v-model="form.categoryType" autocomplete="off" />
</el-form-item>
</el-col> -->
<el-col :span="12">
<el-form-item label="发往科室" prop="targetDepartment" style="width: 100%">
<!-- <el-input v-model="form.targetDepartment" autocomplete="off" /> -->
<el-tree-select
clearable
style="width: 100%"
v-model="form.targetDepartment"
filterable
:data="orgOptions"
:props="{
value: 'id',
label: 'name',
children: 'children',
}"
value-key="id"
check-strictly
placeholder="请选择科室"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="症状" prop="symptom" style="width: 100%">
<el-input v-model="form.symptom" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="体征" prop="sign" style="width: 100%">
<el-input v-model="form.sign" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="临床诊断" prop="clinicalDiagnosis" style="width: 100%">
<el-input disabled v-model="form.clinicalDiagnosis" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="其他诊断" prop="otherDiagnosis" style="width: 100%">
<el-input disabled v-model="form.otherDiagnosis" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="相关结果" prop="relatedResult" style="width: 100%">
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="注意事项" prop="attention" style="width: 100%">
<el-input v-model="form.attention" autocomplete="off" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
</template>
<script setup name="Surgery">
import {computed, getCurrentInstance, onBeforeMount, onMounted, reactive, ref, watch} from 'vue';
import {patientInfo} from '../../../store/patient.js';
import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js';
import {getSurgeryPage, saveSurgery} from './api';
import {ElMessage} from 'element-plus';
const { proxy } = getCurrentInstance();
// 模块级缓存:避免每次打开弹窗都重新请求手术项目列表
let surgeryRecordsCache = null; // 原始 API 记录
let surgeryMappedCache = null; // 映射后的 el-transfer 数据
let searchDebounceTimer = null; // 搜索防抖
const transferRef = ref(null);
const dbTotal = ref(0); // 数据库中的手术项目总数
const searchKey = ref(''); // 搜索关键字
const checkedCount = computed(() => transferValue.value.length);
const leftPanelFormat = computed(() => ({
noChecked: ` 0/${dbTotal.value}`,
hasChecked: ` \${checked}/${dbTotal.value}`,
}));
// 递归查找树形科室节点
const findTreeItem = (list, id) => {
if (!list || list.length === 0) return null;
for (const item of list) {
if (item.id == id) return item;
if (item.children && item.children.length > 0) {
const found = findTreeItem(item.children, id);
if (found) return found;
}
}
return null;
};
const emits = defineEmits(['submitOk']);
const props = defineProps({});
const state = reactive({});
const applicationListAll = ref();
const applicationList = ref([]);
const orgOptions = ref([]); // 科室选项
const loading = ref(false); // 加载状态
const mapToTransferItem = (item) => {
const price = item.price != null ? Number(item.price).toFixed(2) : '0.00';
const unit = item.unitCodeDictText || item.unitCode || '';
return {
adviceDefinitionId: item.adviceDefinitionId,
orgId: item.orgId,
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
key: item.adviceDefinitionId,
};
};
const getList = () => {
if (!patientInfo.value?.inHospitalOrgId) {
applicationList.value = [];
return;
}
// 命中内存缓存时直接使用
if (surgeryMappedCache && surgeryMappedCache.length > 0) {
applicationList.value = surgeryMappedCache;
applicationListAll.value = surgeryRecordsCache;
loading.value = false;
return;
}
loadPage('');
};
/**
* 加载手术项目分页数据
* @param {string} key 搜索关键字(可选)
*/
const loadPage = (key) => {
const orgId = patientInfo.value.inHospitalOrgId;
loading.value = true;
getSurgeryPage({ organizationId: orgId, pageNo: 1, pageSize: 100, searchKey: key || '' })
.then((res) => {
if (res.code !== 200 || !res.data?.records) {
applicationList.value = [];
dbTotal.value = 0;
loading.value = false;
return;
}
dbTotal.value = res.data.total || 0;
const records = res.data.records;
applicationListAll.value = records;
applicationList.value = records
.filter(item => item.adviceDefinitionId != null)
.map(mapToTransferItem);
// 仅在无搜索时缓存
if (!key) {
surgeryRecordsCache = records;
surgeryMappedCache = applicationList.value;
}
loading.value = false;
})
.catch((e) => {
console.error('手术项目加载失败:', e);
applicationList.value = [];
dbTotal.value = 0;
loading.value = false;
});
};
/**
* 搜索输入框变化处理防抖300ms≥3字触发后端搜索
*/
const onSearchInput = () => {
clearTimeout(searchDebounceTimer);
const val = searchKey.value.trim();
if (!val) {
// 清空搜索框,恢复初始数据
loadPage('');
return;
}
if (val.length >= 3) {
searchDebounceTimer = setTimeout(() => {
loadPage(val);
}, 300);
}
};
const transferValue = ref([]);
const form = reactive({
// categoryType: '', // 项目类别
targetDepartment: '', // 发往科室
symptom: '', // 症状
sign: '', // 体征
clinicalDiagnosis: '', // 临床诊断
otherDiagnosis: '', // 其他诊断
relatedResult: '', // 相关结果
attention: '', // 注意事项
primaryDiagnosisList: [], //主诊断目录
otherDiagnosisList: [], //其他断目录
});
const rules = reactive({});
onBeforeMount(() => {});
onMounted(() => {
getList();
});
/**
* type(1watch监听类型 2:点击保存类型)
* selectProjectIds(选中项目的id数组)
* */
const projectWithDepartment = (selectProjectIds) => {
if (!selectProjectIds || selectProjectIds.length === 0) {
form.targetDepartment = '';
}
};
// 监听选择项目变化
watch(() => transferValue.value, (newValue) => {
projectWithDepartment(newValue);
});
const submit = () => {
if (transferValue.value.length == 0) {
return proxy.$message.error('请选择手术项目');
}
if (!form.targetDepartment) {
return proxy.$message.error('请选择发往科室');
}
let applicationListAllFilter = applicationListAll.value.filter((item) => {
return transferValue.value.includes(item.adviceDefinitionId);
});
applicationListAllFilter = applicationListAllFilter.map((item) => {
return {
adviceDefinitionId: item.adviceDefinitionId,
adviceDefinitionName: item.adviceName,
quantity: 1,
unitCode: item.unitCode,
unitPrice: item.price,
totalPrice: item.price,
positionId: form.targetDepartment || item.positionId, // 用户手动选择的发往科室优先于项目默认执行科室
definitionId: item.chargeItemDefinitionId,
accountId: patientInfo.value.accountId,
};
});
saveSurgery({
activityList: applicationListAllFilter,
patientId: patientInfo.value.patientId, //患者ID
encounterId: patientInfo.value.encounterId, // 就诊ID
organizationId: patientInfo.value.inHospitalOrgId, // 医疗机构ID
requestFormId: '', // 申请单ID
name: '手术申请单',
descJson: JSON.stringify(form),
categoryEnum: '24', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
}).then((res) => {
if (res.code === 200) {
proxy.$message.success(res.msg);
applicationList.value = [];
emits('submitOk');
} else {
proxy.$message.error(res.message);
}
});
};
/** 查询科室 */
const getLocationInfo = () => {
getDepartmentList().then((res) => {
orgOptions.value = res.data || [];
});
};
// 获取诊断目录
function getDiagnosisList() {
getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => {
console.log('诊断目录========>', JSON.stringify(res.data));
if (res.code == 200) {
const datas = (res.data || []).map((item) => {
let obj = {
...item,
};
if (obj.diagSrtNo == null) {
obj.diagSrtNo = '1';
}
return obj;
});
// 主诊断
form.primaryDiagnosisList = datas.filter((item) => {
return item?.maindiseFlag == 1;
});
console.log('@@@@@@========>', form.primaryDiagnosisList);
if (form.primaryDiagnosisList.length == 1) {
const obj = form.primaryDiagnosisList[0];
form.clinicalDiagnosis = obj.name;
}
//其他诊断
form.otherDiagnosisList = datas.filter((item) => {
return item?.maindiseFlag !== 1;
});
const otherDiagnosisNameList = form.otherDiagnosisList.map((item) => {
return item.name;
});
form.otherDiagnosis = otherDiagnosisNameList.join(',');
}
});
}
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
</script>
<style lang="scss" scoped>
.surgery-container {
height: 100%;
width: 100%;
.transfer-wrapper {
position: relative;
min-height: 300px;
}
.el-transfer {
--el-transfer-panel-width: 480px !important;
}
.bloodTransfusion-form {
padding: 8px 8px 0 8px;
height: 100%;
width: 100%;
display: flex;
}
}
</style>

View File

@@ -1,149 +0,0 @@
<template>
<el-dialog
v-model="dialogVisible"
destroy-on-close
width="600px"
:close-on-click-modal="false"
title="转科申请"
@close="closeDialog"
>
<div style="padding: 0 80px">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item label="转入科室" prop="targetOrganizationId">
<el-select
v-model="form.targetOrganizationId"
@change="fetchWardList"
placeholder="请选择转入科室"
>
<el-option
v-for="item in deptList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="转入病区" prop="targetLocationId">
<el-select v-model="form.targetLocationId" no-data-text="请先选择科室" placeholder="请选择转入病区">
<el-option
v-for="item in wardList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="转科时间" prop="startTime">
<el-radio-group v-model="form.startTime">
<el-radio :value="today">今日</el-radio>
<el-radio :value="tomorrow">明日</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="转科原因" prop="reasonText">
<el-input
v-model="form.reasonText"
type="textarea"
rows="5"
placeholder="请输入转科原因"
/>
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitApplicationForm"> 确认 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {transferOrganization} from './api.js';
import {getOrgList, getWardList} from '@/api/public.js';
import {patientInfo} from '../../../store/patient.js';
const { proxy } = getCurrentInstance();
const dialogVisible = ref(false);
const deptList = ref([]); // 科室列表
const wardList = ref([]); // 病区列表
const today = ref(proxy.formatDateStr(new Date(),'YYYY-MM-DD HH:mm:ss'))
const tomorrow = ref(proxy.formatDateStr(new Date().setDate(new Date + 1),'YYYY-MM-DD HH:mm:ss'))
const form = reactive({
targetOrganizationId: '',
targetLocationId: '',
startTime: '',
reasonText: '',
});
// 表单校验规则
const rules = ref({
targetOrganizationId: [{ required: true, message: '请选择转入科室', trigger: 'change' }],
targetLocationId: [{ required: true, message: '请选择转入病区', trigger: 'change' }],
startTime: [{ required: true, message: '请选择转科时间', trigger: 'change' }],
reasonText: [{ required: false }],
});
// 获取科室列表
function fetchDeptList() {
getOrgList().then((response) => {
deptList.value = response.data;
});
}
// 获取病区列表
function fetchWardList() {
getWardList({ orgId: form.targetOrganizationId }).then((response) => {
wardList.value = response.data;
});
}
function openDialog() {
dialogVisible.value = true;
// 打开对话框时获取数据
fetchDeptList();
}
// 提交表单
function submitApplicationForm() {
proxy.$refs.formRef.validate((valid) => {
console.log(patientInfo.value);
if (valid) {
form.encounterId = patientInfo.value.encounterId
form.patientId = patientInfo.value.patientId
// 表单校验通过,执行提交逻辑
transferOrganization(form).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('转科申请已提交');
dialogVisible.value = false;
}
});
} else {
// 表单校验不通过
proxy.$modal.msgWarning('请完善必填信息');
return false;
}
});
}
// 关闭对话框
function closeDialog() {
// 重置表单
proxy.$refs.formRef.resetFields();
// 重置数据
Object.assign(form, {
targetOrganizationId: '',
targetLocationId: '',
startTime: '',
reasonText: '',
});
}
defineExpose({
openDialog,
});
</script>

View File

@@ -1,188 +0,0 @@
<!--
* @Author: sjjh
* @Date: 2025-04-07 23:47:54
* @Description:
-->
<template>
<div class="advice-container">
<div class="operate-btns">
<el-space>
<el-button type="primary" @click="addNew">新增</el-button>
<el-button type="primary">签发</el-button>
<el-button type="danger">撤回</el-button>
<el-button type="danger">作废</el-button>
<el-button type="danger">停止</el-button>
<el-button>复制</el-button>
<el-button>粘贴</el-button>
</el-space>
</div>
<div class="operate-btns">
<el-space>
<el-radio-group v-model="searchForm.orderType">
<el-radio-button label="全部" value="New York" />
<el-radio-button label="长期" value="Washington" />
<el-radio-button label="临时" value="Los Angeles" />
</el-radio-group>
<el-select v-model="searchForm.orderClassCode" placeholder="医嘱类型" style="width: 240px">
<el-option
v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-select v-model="searchForm.orderStatus" placeholder="医嘱状态" style="width: 240px">
<el-option
v-for="item in statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-space>
</div>
<div class="orderTableData-container">
<el-table
:data="orderTableData"
border
row-key="id"
style="width: 100%; height: 100%"
highlight-current-row
:expand-row-keys="expandOrder"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="date" label="状态" width="180" sortable />
<el-table-column prop="orderTypeName" label="类型" width="180" />
<el-table-column prop="address" label="医嘱内容" />
<el-table-column prop="address" label="时间" />
<el-table-column prop="address" label="执行科室" />
<el-table-column prop="address" label="停止、作废人/时间" width="180" />
<el-table-column fixed="right" label="操作" width="120">
<template #default="props">
<el-button link type="primary" size="small" @click="editRow(props.row)">
查看
</el-button>
<el-button link type="primary" size="small">编辑</el-button>
</template>
</el-table-column>
<el-table-column type="expand" width="1" style="width: 0">
<template #default="props">
<div m="4">
<p m="t-0 b-2">State: {{ props.row.state }}</p>
<p m="t-0 b-2">City: {{ props.row.city }}</p>
<p m="t-0 b-2">Address: {{ props.row.address }}</p>
<p m="t-0 b-2">Zip: {{ props.row.zip }}</p>
<h3>Family</h3>
<el-button type="primary" @click="save">保存</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script setup>
import {onBeforeMount, onMounted, reactive, ref} from 'vue'
// const { proxy } = getCurrentInstance()
// const emits = defineEmits([])
// const props = defineProps({})
import {orderTableData} from './useOrder'
import {ElMessage} from 'element-plus'
const state = reactive({})
onBeforeMount(() => {})
onMounted(() => {})
defineExpose({ state })
const typeOptions = [
{
value: 'Option1',
label: '中药',
},
{
value: 'Option2',
label: '西药',
},
{
value: 'Option3',
label: '检查',
},
{
value: 'Option4',
label: '检验',
},
{
value: 'Option5',
label: '处置',
},
]
const statusOptions = [
{
value: 'Option1',
label: '暂存',
},
{
value: 'Option2',
label: '签署',
},
{
value: 'Option3',
label: '回退',
},
{
value: 'Option4',
label: '停止',
},
{
value: 'Option5',
label: '作废',
},
]
const searchForm = reactive({
orderType: '',
orderClassCode: '',
orderStatus: '',
})
const expandOrder = ref([]) //目前的展开行
const newId = ref('')
/* ==== 操作 */
// 新增
const addNew = () => {
if (!newId.value) {
newId.value = '1111112222'
orderTableData.unshift({ id: '1111112222' })
expandOrder.value = ['1111112222']
} else {
ElMessage({
message: '请先操作当前编辑的医嘱!',
type: 'warning',
})
}
}
// 新增
const editRow = (row) => {
expandOrder.value = [row.id]
}
const save = (row) => {
expandOrder.value = []
newId.value = undefined
}
</script>
<style lang="scss" scoped>
.advice-container {
height: 100%;
display: flex;
flex-direction: column;
.operate-btns {
height: 44px;
display: flex;
align-items: center;
flex: none;
}
.orderTableData-container {
flex: auto;
height: calc(100% - 88px);
}
}
</style>