@@ -174,6 +174,11 @@
:disabled = "!isCategoryLoaded"
@change ="
(value) => {
// 切换类型时强制收起编辑区,防止残留字段(如药品→诊疗时 dose/rateCode 未清理)
// 注意:模板内联表达式中 ref 自动解包, expandOrder 即数组,不要加 .value
if (expandOrder.length > 0) {
collapseAllExpanded();
}
filterPrescriptionList[scope.rowIndex].adviceName = undefined;
// 根据选中的医嘱类型,设置对应的 categoryCode
const selectedItem = adviceTypeList.find(item => item.value === value);
@@ -196,7 +201,7 @@
searchKey: adviceQueryParams.value?.searchKey || '',
};
// 直接调用子组件 refresh 方法,立即刷新列表(用 scope.rowIndex 找到当前行的子组件)
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[scope.rowIndex] : adviceTableRef.value ;
const tableRef = getAdviceTableRef() ;
if (tableRef && tableRef.refresh) {
tableRef.refresh(newAdviceType, newCategoryCode, '');
}
@@ -221,7 +226,7 @@
popper -class = " order -advice -popper "
:offset = "0"
:visible = "scope.row.showPopover"
:width = "1200 "
:width = "advicePopperWidth "
:teleported = "true"
:popper-options = "advicePopperOptions"
>
@@ -246,7 +251,7 @@
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
e.preventDefault();
// 传递事件到弹窗容器
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[scope.rowIndex] : adviceTableRef.value ;
const tableRef = getAdviceTableRef() ;
if (tableRef && tableRef.handleKeyDown) tableRef.handleKeyDown(e);
}
}
@@ -461,7 +466,7 @@ import {
} from '../api' ;
import adviceBaseList from '../adviceBaseList' ;
import { calculateQuantityByDays } from '@/utils/his' ;
import { localPatientInfo as p atientInfo } from '../../store/localPatient.js' ;
import { localPatientInfo as localP atient} from '../../store/localPatient.js' ;
import OrderGroupDrawer from '@/views/doctorstation/components/prescription/orderGroupDrawer.vue' ;
import PrescriptionHistory from '@/views/doctorstation/components/prescription/prescriptionHistory.vue' ;
import Decimal from 'decimal.js' ;
@@ -492,7 +497,7 @@ const groupIndexList = ref([]);
const diagnosisList = ref ( [ ] ) ;
const nextId = ref ( 1 ) ;
const unitCodeList = ref ( [ ] ) ;
const adviceTableRef = ref ( [ ] ) ;
const adviceTableRef = ref ( null ) ;
const organization = ref ( [ ] ) ;
const conditionDefinitionId = ref ( '' ) ;
const encounterDiagnosisId = ref ( '' ) ;
@@ -505,6 +510,11 @@ const popoverJustClosedByKey = ref(null);
// 医嘱检索下拉浮框对齐:跟踪表格水平滚动偏移与主体区域边界限制
const tableScrollLeft = ref ( 0 ) ;
const mainBoundary = ref ( null ) ;
const advicePopperWidth = computed ( ( ) => {
// 取主体区域宽度与 900px 中较小值,避免小屏幕溢出;下限 500px
const mainWidth = mainBoundary . value ? . clientWidth || window . innerWidth - 300 ;
return Math . max ( 500 , Math . min ( mainWidth - 24 , 900 ) ) ;
} ) ;
const advicePopperStyle = computed ( ( ) => ( {
padding : '0' ,
marginLeft : ` - ${ tableScrollLeft . value } px ` ,
@@ -549,10 +559,10 @@ const unitMap = ref({
unit : 'unit' ,
} ) ;
const buttonDisabled = computed ( ( ) => {
return ! p atientInfo . value ;
return ! localP atient. value ;
} ) ;
const isSaveDisabled = computed ( ( ) => {
return ! p atientInfo . value || prescriptionList . value . length === 0 ;
return ! localP atient. value || prescriptionList . value . length === 0 ;
} ) ;
const props = defineProps ( {
patientInfo : {
@@ -763,7 +773,7 @@ function getListInfo(addNewRow) {
const orgTreePromise = getOrgTree ( ) . then ( ( res ) => {
organization . value = res ? . data ? . records ? ? res ? . data ? ? [ ] ;
} ) ;
getPrescriptionList ( p atientInfo . value . encounterId ) . then ( ( res ) => {
getPrescriptionList ( localP atient. value . encounterId ) . then ( ( res ) => {
// 等待科室树加载完成后再处理处方数据,确保 resolveOrgId 能正确匹配
orgTreePromise . then ( ( ) => {
loading . value = false ;
@@ -830,10 +840,10 @@ function getListInfo(addNewRow) {
}
} ) ;
} ) . catch ( ( ) => { loading . value = false ; } ) ;
getContract ( { encounterId : p atientInfo . value . encounterId } ) . then ( ( res ) => {
getContract ( { encounterId : localP atient. value . encounterId } ) . then ( ( res ) => {
contractList . value = res . data ;
} ) ;
accountId . value = p atientInfo . value . accountId ;
accountId . value = localP atient. value . accountId ;
// 加载已配置的药品类别
loadConfiguredCategories ( ) ;
@@ -843,7 +853,7 @@ function getListInfo(addNewRow) {
* 加载已配置的药品类别
*/
function loadConfiguredCategories ( ) {
const orgId = p atientInfo . value ? . inHospitalOrgId ;
const orgId = localP atient. value ? . inHospitalOrgId ;
if ( ! orgId ) {
isCategoryLoaded . value = true ; // 标记已加载( 即使没有科室ID)
return ;
@@ -917,7 +927,7 @@ const filterPrescriptionList = computed(() => {
} ) ;
function getDiagnosisInfo ( ) {
getEncounterDiagnosis ( p atientInfo . value . encounterId ) . then ( ( res ) => {
getEncounterDiagnosis ( localP atient. value . encounterId ) . then ( ( res ) => {
diagnosisList . value = res . data ;
let diagnosisInfo = diagnosisList . value . filter ( ( item ) => {
return item . maindiseFlag == 1 ;
@@ -937,6 +947,7 @@ function getRowDisabled(row) {
/**
* 将行的 adviceType + categoryCode 映射为 el-select 的选中值
* 药品子分类使用复合值如 '1-2'( adviceType-categoryCode) , 诊疗/手术/全部使用原始值
* 注意: el-select 使用严格匹配,返回值类型需与 adviceTypeList option value 一致
*/
function getRowSelectValue ( row ) {
if ( row . adviceType == 1 && row . categoryCode ) {
@@ -945,13 +956,17 @@ function getRowSelectValue(row) {
if ( row . adviceType == 7 ) {
return 7 ;
}
// adviceType == 1 但没有 categoryCode 时无匹配项,返回 undefined 让 select 显示空
if ( row . adviceType == 1 ) {
return undefined ;
}
return row . adviceType ;
}
// 新增医嘱
function handleAddPrescription ( ) {
// 校验是否选中患者
if ( ! p atientInfo . value || ! p atientInfo . value . encounterId ) {
if ( ! localP atient. value || ! localP atient. value . encounterId ) {
proxy . $modal . msgWarning ( '请先选择患者' ) ;
return ;
}
@@ -1002,9 +1017,9 @@ function checkUnit(item, row) {
* @returns {boolean} true=通过, false=不通过
*/
function validateStartTime ( startTime ) {
if ( ! startTime || ! p atientInfo . value ? . inHospitalTime ) return true ;
if ( ! startTime || ! localP atient. value ? . inHospitalTime ) return true ;
const startDate = new Date ( startTime ) ;
const inHospitalDate = new Date ( p atientInfo . value . inHospitalTime ) ;
const inHospitalDate = new Date ( localP atient. value . inHospitalTime ) ;
if ( startDate < inHospitalDate ) {
const pad = ( n ) => String ( n ) . padStart ( 2 , '0' ) ;
const d = inHospitalDate ;
@@ -1168,25 +1183,40 @@ function handleFocus(row, index) {
return ;
}
row . showPopover = true ;
// Bug #555: handleFocus 初始化查询参数并加载初始数据( searchKey 为空,无竞态风险 )
let categoryCod e = '' ;
if ( row . adviceType !== undefined ) {
const selectValue = ( a dviceType == 1 && row . categoryCode ) ? '1-' + row . categoryCode : adviceType ;
const selectedItem = adviceTypeList . value . find ( item => item . value === selectValue ) || adviceTypeList . value . find ( item => item . adviceType === adviceType ) ;
categoryCode = selectedItem ? selectedItem . categoryCode : ( row . categoryCode || '' ) ;
}
adviceQueryParams . value = { adviceType , categoryCode , searchKey : '' } ;
// 弹窗首次打开时加载初始数据
const tableRef = Array . isArray ( adviceTableRef . value ) ? adviceTableRef . value [ index ] : adviceTableRef . value ;
// 初始化查询参数( searchKey 使用当前输入框已有文本,避免 @input 二次触发导致竞态 )
const searchKey = row . adviceNam e || '' ;
adviceQueryParams . value = { adviceType , categoryCode : resolveCategoryCode ( row , adviceType ) , searchKey } ;
const tableRef = getA dviceTableRef ( ) ;
if ( tableRef && tableRef . refresh ) {
tableRef . refresh ( adviceType , categoryCode , '' ) ;
tableRef . refresh ( adviceType , adviceQueryParams . value . categoryCode, searchKey ) ;
}
}
/** 从行数据和 adviceType 解析 categoryCode */
function resolveCategoryCode ( row , adviceType ) {
if ( row . adviceType !== undefined ) {
const selectValue = ( adviceType == 1 && row . categoryCode ) ? '1-' + row . categoryCode : adviceType ;
const selectedItem = adviceTypeList . value . find ( item => item . value === selectValue ) || adviceTypeList . value . find ( item => item . adviceType === adviceType ) ;
return selectedItem ? selectedItem . categoryCode : ( row . categoryCode || '' ) ;
}
return '' ;
}
/** 获取 adviceTableRef( 单实例, 不在 v-for 中) */
function getAdviceTableRef ( ) {
return adviceTableRef . value ;
}
function handleBlur ( row ) {
row . showPopover = false ;
// Bug #587: 标记弹窗刚关闭,防止点击空白处时触发行展开
popoverJustClosedByKey . value = row . uniqueKey ;
// 延迟关闭弹窗,等待 click 事件在弹出层内容上正常触发
// 原因:浏览器 blur 事件先于 click 触发;同步关闭会移除 DOM, 导致 click 丢失
// 如果用户在弹出层内点击药品行, selectAdviceBase 会在 150ms 内设 showPopover=false
// 此处的赋值变为 no-op( false→false) , 弹窗正常关闭且数据正确填充
setTimeout ( ( ) => {
row . showPopover = false ;
// Bug #587: 标记弹窗刚关闭,防止点击空白处时触发行展开
popoverJustClosedByKey . value = row . uniqueKey ;
} , 150 ) ;
}
function handleChange ( value , row , index ) {
@@ -1197,21 +1227,13 @@ function handleChange(value, row, index) {
return ;
}
adviceQueryParams . value . searchKey = value ;
// @focus 已先于 @input 执行, rowIndex 必定有效
// popover 被 blur 关闭后,用户继续输入时自行打开
if ( ! row . showPopover ) {
row . showPopover = true ;
}
const tableRef = Array . isArray ( adviceTableRef . value ) ? adviceTableRef . value [ index ] : adviceTableRef . value ;
const tableRef = getAdviceTableRef ( ) ;
if ( tableRef && tableRef . refresh ) {
const adviceType = row ? . adviceType !== undefined ? row . adviceType : adviceQueryParams . value . adviceType ;
let categoryCode = '' ;
if ( row ? . adviceType !== undefined ) {
const selectValue = ( adviceType == 1 && row ? . categoryCode ) ? '1-' + row . categoryCode : adviceType ;
const selectedItem = adviceTypeList . value . find ( item => item . value === selectValue ) || adviceTypeList . value . find ( item => item . adviceType === adviceType ) ;
categoryCode = selectedItem ? selectedItem . categoryCode : ( adviceQueryParams . value . categoryCode || '' ) ;
}
tableRef . refresh ( adviceType , categoryCode , value ) ;
tableRef . refresh ( adviceType , resolveCategoryCode ( row , adviceType ) , value ) ;
}
}
@@ -1279,9 +1301,10 @@ function selectAdviceBase(key, row) {
prescriptionList . value [ rowIndex . value ] . skinTestFlag = 1 ;
prescriptionList . value [ rowIndex . value ] . skinTestFlag _enumText = '是' ;
expandOrder . value = [ currentUniqueKey ] ;
const expandRow = filterPrescriptionList . value . find ( item => item . uniqueKey === currentUniqueKey ) ;
if ( expandRow && prescriptionRef . value ? . setRowExpand ) {
prescriptionRef . value . setRowExpand ( [ expandRow ] , true ) ;
// 使用 prescriptionList 而非 filterPrescriptionList, 避免过滤后找不到行导致展开失败
const rowObj = prescriptionList . value . find ( item => item . uniqueKey === currentUniqueKey ) ;
if ( rowObj && prescriptionRef . value ? . setRowExpand ) {
prescriptionRef . value . setRowExpand ( [ rowObj ] , true ) ;
}
expandOrderAndFocus ( currentUniqueKey , row ) ;
} )
@@ -1290,17 +1313,18 @@ function selectAdviceBase(key, row) {
prescriptionList . value [ rowIndex . value ] . skinTestFlag = 0 ;
prescriptionList . value [ rowIndex . value ] . skinTestFlag _enumText = '否' ;
expandOrder . value = [ currentUniqueKey ] ;
const expandRow = filterPrescriptionList . value . find ( item => item . uniqueKey === currentUniqueKey ) ;
if ( expandRow && prescriptionRef . value ? . setRowExpand ) {
prescriptionRef . value . setRowExpand ( [ expandRow ] , true ) ;
// 使用 prescriptionList 而非 filterPrescriptionList, 避免过滤后找不到行导致展开失败
const rowObj = prescriptionList . value . find ( item => item . uniqueKey === currentUniqueKey ) ;
if ( rowObj && prescriptionRef . value ? . setRowExpand ) {
prescriptionRef . value . setRowExpand ( [ rowObj ] , true ) ;
}
expandOrderAndFocus ( currentUniqueKey , row ) ;
} ) ;
} else {
// 选完药品后展开编辑区域,供医生编辑剂量等字段
expandOrder . value = [ currentUniqueKey ] ;
// 直接调 vxe-table 实例方法展开( expandRowKeys 仅在初始化时生效)
const rowObj = filterP rescriptionList. value . find ( item => item . uniqueKey === currentUniqueKey ) ;
// 使用 prescriptionList 而非 filterPrescriptionList, 避免过滤后找不到行导致展开失败
const rowObj = p rescriptionList. value . find ( item => item . uniqueKey === currentUniqueKey ) ;
if ( rowObj && prescriptionRef . value ? . setRowExpand ) {
prescriptionRef . value . setRowExpand ( [ rowObj ] , true ) ;
}
@@ -1539,9 +1563,9 @@ function handleSave() {
// 签发处理逻辑
function executeSaveLogic ( ) {
saveList . forEach ( ( item ) => {
item . patientId = p atientInfo . value . patientId ;
item . encounterId = p atientInfo . value . encounterId ;
item . accountId = p atientInfo . value . accountId ;
item . patientId = localP atient. value . patientId ;
item . encounterId = localP atient. value . encounterId ;
item . accountId = localP atient. value . accountId ;
item . dbOpType = '1' ;
// Bug #589: 出院带药保存时转为药品类型
if ( item . adviceType == 7 ) {
@@ -1570,11 +1594,11 @@ function handleSave() {
// 保存签发按钮
isSaving . value = true ;
console . log ( '签发处方参数:' , {
organizationId : p atientInfo . value . inHospitalOrgId ,
organizationId : localP atient. value . inHospitalOrgId ,
adviceSaveList : list ,
} ) ;
savePrescriptionSign ( {
organizationId : p atientInfo . value . inHospitalOrgId ,
organizationId : localP atient. value . inHospitalOrgId ,
regAdviceSaveList : list ,
} )
. then ( ( res ) => {
@@ -1636,8 +1660,8 @@ function handleOrderBindInfo(bindIdInfo) {
const newRow = {
... prescriptionList . value [ rowIndex . value ] ,
uniqueKey : nextId . value ++ ,
patientId : p atientInfo . value . patientId ,
encounterId : p atientInfo . value . encounterId ,
patientId : localP atient. value . patientId ,
encounterId : localP atient. value . encounterId ,
accountId : accountId . value ,
quantity : item . quantity ,
methodCode : item . methodCode ,
@@ -1742,8 +1766,8 @@ function handleSaveSign(row, index) {
// 执行保存
row . contentJson = undefined ;
row . patientId = p atientInfo . value . patientId ;
row . encounterId = p atientInfo . value . encounterId ;
row . patientId = localP atient. value . patientId ;
row . encounterId = localP atient. value . encounterId ;
row . accountId = accountId . value ;
// 🔧 文字医嘱(type=8): 跳过计费逻辑, 总金额为0
@@ -1867,6 +1891,11 @@ function handleSaveBatch() {
const therapyEnum = item . therapyEnum || '1' ;
const result = {
... item ,
// 确保 patientId/encounterId/accountId 始终存在,防止新增行未保存时这些字段为 null
// (新增行由 handleAddPrescription 创建时不携带患者信息,与其他保存路径保持一致)
patientId : item . patientId || localPatient . value ? . patientId ,
encounterId : item . encounterId || localPatient . value ? . encounterId ,
accountId : item . accountId || accountId . value ,
therapyEnum : therapyEnum ,
dbOpType : item . requestId ? '2' : '1' ,
// 确保 skinTestFlag 是数字类型( 1 或 0)
@@ -1992,15 +2021,17 @@ function setValue(row) {
// 2. 诊疗类型优先使用项目维护的所属科室(row.orgId), 其次positionId
// 3. 如果都为空,回退到患者当前所在科室(patientInfo.orgId)
// 4. 使用 resolveOrgId 从组织树中匹配正确的 String id, 解决大 Long 精度丢失问题
orgId : row . adviceType != 3 ? undefined : ( resolveOrgId ( row . orgId || row . positionId || p atientInfo . value ? . inHospitalOrgId ) || '' ) ,
orgId : row . adviceType != 3 ? undefined : ( resolveOrgId ( row . orgId || row . positionId || localP atient. value ? . inHospitalOrgId ) || '' ) ,
// 🔧 修复:同时保存 orgName, 当 orgId 在科室树中匹配不到时作为兜底显示
orgName : row . adviceType != 3 ? undefined : ( findOrgName ( row . orgId || row . positionId || p atientInfo . value ? . inHospitalOrgId ) || row . orgName || p atientInfo . value ? . inHospitalOrgName || '' ) ,
orgName : row . adviceType != 3 ? undefined : ( findOrgName ( row . orgId || row . positionId || localP atient. value ? . inHospitalOrgId ) || row . orgName || localP atient. value ? . inHospitalOrgName || '' ) ,
// dose: undefined, Removed to preserve dose value from group package
unitCodeList : unitCodeList . value ,
doseUnitCode : row . doseUnitCode ,
minUnitCode : String ( row . minUnitCode ) ,
unitCode : row . partAttributeEnum == 1 ? String ( row . minUnitCode ) : String ( row . unitCode ) ,
categoryEnum : row . categoryCode ,
// 显式保留 categoryCode, 防止 API 未返回时类型下拉框显示空白
categoryCode : row . categoryCode || baseRow . categoryCode || prevRow . categoryCode || '' ,
categoryEnum : row . categoryCode || baseRow . categoryCode || prevRow . categoryEnum || '' ,
// 确保 skinTestFlag 是数字类型( 1 或 0) , 如果未定义则默认为 0
skinTestFlag : row . skinTestFlag !== undefined && row . skinTestFlag !== null
? ( typeof row . skinTestFlag === 'number' ? row . skinTestFlag : ( row . skinTestFlag ? 1 : 0 ) )
@@ -2100,8 +2131,8 @@ function handleSaveGroup(orderGroupList) {
... prescriptionList . value [ tempIndex ] ,
requesterId _dictText : userStore . nickName ,
requesterId : userStore . id ,
patientId : p atientInfo . value . patientId ,
encounterId : p atientInfo . value . encounterId ,
patientId : localP atient. value . patientId ,
encounterId : localP atient. value . encounterId ,
accountId : accountId . value ,
// 🔧 修复 Bug #403: 从 mergedDetail 读取明细字段,而非直接从 item 取
// item.dose 等字段可能为 null, mergedDetail 已做 ?? 兜底
@@ -2115,9 +2146,9 @@ function handleSaveGroup(orderGroupList) {
unitCode : mergedDetail . unitCode ? ? item . unitCode ,
unitCode _dictText : item . unitCodeName || mergedDetail . unitCodeName || '' ,
statusEnum : 1 ,
orgId : resolveOrgId ( mergedDetail . orgId || p atientInfo . value ? . inHospitalOrgId ) || '' ,
orgId : resolveOrgId ( mergedDetail . orgId || localP atient. value ? . inHospitalOrgId ) || '' ,
// 🔧 修复:同时存储 orgName, 确保树匹配不到时仍有中文名称可显示
orgName : findOrgName ( mergedDetail . orgId || p atientInfo . value ? . inHospitalOrgId ) || mergedDetail . orgName || p atientInfo . value ? . inHospitalOrgName || '' ,
orgName : findOrgName ( mergedDetail . orgId || localP atient. value ? . inHospitalOrgId ) || mergedDetail . orgName || localP atient. value ? . inHospitalOrgName || '' ,
startTime : mergedDetail . startTime || defaultStartTimeFn ( ) ,
dbOpType : prescriptionList . value [ tempIndex ] . requestId ? '2' : '1' ,
conditionId : conditionId . value ,
@@ -2161,8 +2192,8 @@ function handleSaveHistory(value) {
... value ,
requesterId _dictText : userStore . nickName ,
requesterId : userStore . id ,
patientId : p atientInfo . value . patientId ,
encounterId : p atientInfo . value . encounterId ,
patientId : localP atient. value . patientId ,
encounterId : localP atient. value . encounterId ,
accountId : accountId . value ,
uniqueKey : undefined ,
startTime : defaultStartTimeFn ( ) ,
@@ -2386,9 +2417,9 @@ function confirmStopAdvice() {
return ;
}
// 校验:停嘱时间不能早于患者入院时间
if ( p atientInfo . value ? . inHospitalTime ) {
if ( localP atient. value ? . inHospitalTime ) {
const stopDate = new Date ( stopForm . stopTime ) ;
const inHospitalDate = new Date ( p atientInfo . value . inHospitalTime ) ;
const inHospitalDate = new Date ( localP atient. value . inHospitalTime ) ;
if ( stopDate < inHospitalDate ) {
const pad = ( n ) => String ( n ) . padStart ( 2 , '0' ) ;
const d = inHospitalDate ;
@@ -2903,7 +2934,7 @@ function sortPrescriptionList() {
}
function handleLeaveHospital ( ) {
if ( ! p atientInfo . value ) {
if ( ! localP atient. value ) {
proxy . $modal . msgWarning ( '请先选择患者' ) ;
return ;
}
@@ -3053,7 +3084,7 @@ defineExpose({ getListInfo, getDiagnosisInfo });
. inpatientDoctor - order - table {
flex : auto ;
width : 100 % ;
min - height : 0 ;
min - height : 300 px ;
overflow : auto ;
}
. applicationForm - bottom - btn {