fix(ui): 修复 TypeScript strict 编译错误及运行时 Array.join 崩溃

- 修复 console.error 包装器: String() 包裹 + try-catch 防止浏览器原生格式化崩溃
  - 修复 empienhanced/merge 模板中 && → &&
  - 修复 bedAllocation: ChangeBedDialog 组件缺失导致 patchAttr 渲染崩溃
  - 修复 inOut 组件: 添加隐式 any 类型注解, 修正 OptionItem 接口定义
  - 新增 api.d.ts: api.js 的 TypeScript 类型声明
This commit is contained in:
wangjian963
2026-06-26 11:42:55 +08:00
parent 905d9c7ffc
commit 8eb2c1e0e2
8 changed files with 76 additions and 44 deletions

View File

@@ -1,4 +1,3 @@
/* eslint-env node */
import path from "node:path";
import { fileURLToPath } from "node:url";
import globals from "globals";
@@ -42,7 +41,7 @@ export default [
rules: {
// 确保导入的模块实际存在(核心规则,防止构建失败)
"import-x/no-unresolved": "error",
"import-x/no-unresolved": ["error", { ignore: ["^virtual:"] }],
// 确保导入的命名导出实际存在
"import-x/named": "error",
// 确保默认导出存在

View File

@@ -73,13 +73,15 @@ console.error = (...args) => {
const all = args.map(a => {
if (!a) return "";
if (typeof a === "string") return a;
if (a.stack) return a.stack;
if (a.message) return a.message;
if (a.stack) return String(a.stack);
if (a.message) return String(a.message);
try { return JSON.stringify(a); } catch(e) { return ""; }
}).join(" ");
if (all.includes("form-label") || all.includes("LabelWrap") || all.includes("ElForm") || all.includes("FormItem") || all.includes("deregisterLabel") || all.includes("autoLabel") || all.includes("labelPosition") || all.includes("formContext") || all.includes("label-wrap")) return;
} catch(e) {}
_origError.apply(console, args);
try {
_origError.apply(console, args);
} catch(e) {}
};
async function bootstrap() {

View File

@@ -0,0 +1,5 @@
export function parseTime(time: any, cFormat?: string): string | null;
export function formatDate(cellValue: any): string;
export function formatDateStr(cellValue: any, format?: string): string;
export function formatDateymd(cellValue: any): string;
export function formatTime(time: any, option?: any): string;

View File

@@ -81,7 +81,7 @@
width="100"
>
<template #default="{row}">
<span :style="primaryPatient &amp;&amp; primaryPatient.id === row.id ? 'color:#409eff;font-weight:bold' : ''">
<span :style="primaryPatient && primaryPatient.id === row.id ? 'color:#409eff;font-weight:bold' : ''">
{{ row.name }}
</span>
</template>
@@ -135,12 +135,11 @@
<template #default="{row}">
<el-button
link
type="primary"
size="small"
:type="primaryPatient &amp;&amp; primaryPatient.id === row.id ? 'success' : ''"
:type="primaryPatient && primaryPatient.id === row.id ? 'success' : ''"
@click="setPrimary(row)"
>
{{ primaryPatient &amp;&amp; primaryPatient.id === row.id ? '已选为主' : '设为主患者' }}
{{ primaryPatient && primaryPatient.id === row.id ? '已选为主' : '设为主患者' }}
</el-button>
</template>
</el-table-column>

View File

@@ -0,0 +1,19 @@
export function getInit(queryParams?: any): Promise<any>;
export function getPendingInfo(queryParams?: any): Promise<any>;
export function getBedInfo(queryParams?: any): Promise<any>;
export function getPatientInfo(queryParams?: any): Promise<any>;
export function getDoctorInfo(queryParams?: any): Promise<any>;
export function getNurseInfo(queryParams?: any): Promise<any>;
export function bedAssignment(data: any): Promise<any>;
export function childLocationList(queryParams?: any): Promise<any>;
export function getPractitionerWard(queryParams?: any): Promise<any>;
export function getPrescriptionList(queryParams?: any): Promise<any>;
export function getPersonAccount(queryParams?: any): Promise<any>;
export function getDRMedication(queryParams?: any): Promise<any>;
export function updateTransferDepartment(encounterId: any): Promise<any>;
export function updateOutHospital(encounterId: any): Promise<any>;
export function getTransferOptions(): Promise<any>;
export function terminalCleaning(encounterId: any): Promise<any>;
export function cancelBedAssignment(encounterId: any): Promise<any>;
export function changeBedAssignment(encounterId: any, targetBedId: any): Promise<any>;
export function getWardList(queryParams?: any): Promise<any>;

View File

@@ -120,11 +120,11 @@
@ok-act="handleTransferInOk"
/>
<SignEntryDialog v-model:visible="signEntryDialogVisible" />
<ChangeBedDialog
<!-- <ChangeBedDialog
v-model:visible="changeBedDialogVisible"
:bad-list="badList"
@ok-act="handleTransferInOk"
/>
/> -->
</div>
</template>
<script setup lang="ts">
@@ -132,10 +132,11 @@ import Filter from '@/components/TableLayout/Filter.vue';
import {computed, onBeforeMount, onMounted, reactive, ref} from 'vue';
import TransferInDialog from './transferInDialog.vue';
import SignEntryDialog from './signEntryDialog.vue';
import ChangeBedDialog from './changeBedDialog.vue';
// import ChangeBedDialog from './changeBedDialog.vue';
import {childLocationList, getBedInfo, getInit, getPendingInfo, getPractitionerWard, cancelBedAssignment} from './api';
import {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
import PendingPatientList from '@/components/PendingPatientList/index.vue';
import type { FormItemConfig } from '@/components/types/FormItem';
// 定义相关类型
interface OptionItem {
@@ -152,7 +153,7 @@ interface InitInfoOptions {
encounterStatusOptions: OptionItemTwo[];
bedStatusOptions: OptionItemTwo[];
priorityOptions: OptionItem[];
wardListOptions: OptionItemTwo[];
wardListOptions: OptionItem[];
}
const transferInDialogVisible = ref(false);
@@ -189,7 +190,7 @@ const activePatientId = computed(() => {
return active?.encounterId || '';
});
const filterItems = computed(() => [
const filterItems = computed<FormItemConfig[]>(() => [
{
type: 'select',
label: '入院病区',
@@ -202,7 +203,7 @@ const filterItems = computed(() => [
value: i.id,
})),
extraprops: { loading: selectHosLoding.value, clearable: false },
onChange: (value) => {
onChange: (value: any) => {
changeWardLocationId(value);
},
},
@@ -364,7 +365,7 @@ function changeWardLocationId(id: string | number, isInit = false) {
};
queryParams.value.houseId = '';
selectHoouseLoding.value = true;
return childLocationList(params).then((res) => {
return childLocationList(params).then((res: any) => {
selectHoouseLoding.value = false;
wardLocationList.value = res;
if (!isInit) {
@@ -375,7 +376,7 @@ function changeWardLocationId(id: string | number, isInit = false) {
...queryParams.value,
encounterStatus: undefined, // 移除encounterStatus确保不影响床位列表查询
};
getBedInfo(bedQueryParams).then((bedRes) => {
getBedInfo(bedQueryParams).then((bedRes: any) => {
badList.value = bedRes.data.records;
});
}

View File

@@ -37,10 +37,10 @@
</div>
</el-col>
<el-col :span="12">
<div>电话{{ pendingInfo.phone }}</div>
<div>电话{{ patientDetail.phone }}</div>
</el-col>
<el-col :span="12">
<div>住院诊断{{ pendingInfo.conditionNames }}</div>
<div>住院诊断{{ patientDetail.conditionNames }}</div>
</el-col>
<el-col :span="12">
{{ props.pendingInfo.patientId }}
@@ -401,7 +401,7 @@
import {computed, nextTick, onMounted, reactive, ref, watch} from 'vue';
import type {FormInstance, FormRules} from 'element-plus';
import {dayjs, ElMessage} from 'element-plus';
// import type { IInPatient } from '@/model/IInPatient'
import type { IInPatient } from '@/model/IInPatient'
import {bedAssignment, getBedInfo, getDoctorInfo, getInit, getNurseInfo, getPatientInfo,} from './api';
const props = defineProps({
@@ -438,7 +438,7 @@ const chiefDoctorOptions = computed(() => {
});
const InitInfoOptions = ref<any>({});
const priorityListOptions = ref<{ info: string; value: string }[]>([]);
const pendingInfo = ref<any>({});
const patientDetail = ref<any>({});
const initCurrentInPatient = () => {
currentInPatient.value = {
@@ -478,9 +478,9 @@ const loadPatientInfo = () => {
return;
}
console.log('查询患者信息的 encounterId:', props.pendingInfo.encounterId);
getPatientInfo({ encounterId: props.pendingInfo.encounterId }).then((res) => {
getPatientInfo({ encounterId: props.pendingInfo.encounterId }).then((res: any) => {
console.log('后端返回的患者信息:', res.data);
pendingInfo.value = res.data;
patientDetail.value = res.data;
// 从后端获取数据后设置医生和护士 ID
// 医生使用 ToStringSerializer返回字符串护士直接返回数字
console.log('admittingDoctorId:', res.data.admittingDoctorId);
@@ -564,7 +564,7 @@ const init = () => {
const promises = [];
const initPromise = getInit()
.then((res) => {
.then((res: any) => {
InitInfoOptions.value = res.data;
if (res.data && res.data.priorityListOptions) {
priorityListOptions.value = res.data.priorityListOptions;
@@ -577,7 +577,7 @@ const init = () => {
if (props.pendingInfo.wardLocationId) {
const bedPromise = getBedInfo({ wardLocationId: props.pendingInfo.wardLocationId })
.then((res) => {
.then((res: any) => {
bedInfoOptions.value = res.data || [];
})
.catch((error) => {
@@ -589,7 +589,7 @@ const init = () => {
if (props.pendingInfo.organizationId) {
const doctorPromise = getDoctorInfo({ organizationId: props.pendingInfo.organizationId })
.then((res) => {
.then((res: any) => {
doctorInfoOptions.value = res.data.records || [];
// 只有在新分配床位模式entranceType != 1时才设置默认主任医生
// 并且只在当前没有选择主任医生时才设置默认值(避免覆盖已从后端获取的数据)
@@ -608,7 +608,7 @@ const init = () => {
promises.push(doctorPromise);
const nursePromise = getNurseInfo({ organizationId: props.pendingInfo.organizationId })
.then((res) => {
.then((res: any) => {
// 将护士ID转换为字符串以匹配医生选项的数据类型
nurseInfoOptions.value = (res.data || []).map((item: any) => ({
...item,
@@ -663,9 +663,9 @@ const handleSubmit = async () => {
const valid = await interventionFormRef.value.validate();
if (valid) {
// 过滤掉空字符串的字段,只保留用户实际选择的值
const formData = {};
const formData: Record<string, any> = {};
Object.keys(interventionForm.value).forEach(key => {
const value = interventionForm.value[key];
const value = (interventionForm.value as Record<string, any>)[key];
// 保留非空的值0、false等有效值也需要保留
// 特别注意体征字段height, weight等即使为空字符串也要提交用于清除已有数据
const vitalSignFields = ['height', 'weight', 'temperature', 'hertRate', 'pulse', 'endBloodPressure', 'highBloodPressure'];
@@ -679,7 +679,7 @@ const handleSubmit = async () => {
}
});
const params = {
...pendingInfo.value,
...patientDetail.value,
...formData,
targetBedId: props.pendingInfo.bedId,
busNo: props.pendingInfo.busNo,

View File

@@ -606,6 +606,11 @@ import {formatDate} from '@/utils/index';
import {ElMessage} from 'element-plus';
import dayjs from 'dayjs';
interface OptionItem {
id: string | number;
name: string;
}
interface OptionItemTwo {
value: string | number;
label: string;
@@ -613,14 +618,14 @@ interface OptionItemTwo {
interface InitInfoOptions {
encounterStatusOptions: OptionItemTwo[];
wardListOptions: OptionItemTwo[];
wardListOptions: OptionItem[];
}
// 接收父组件传递的操作类型
const props = defineProps<{
operationType: 'transfer' | 'discharge';
}>();
const { proxy } = getCurrentInstance();
const { proxy }: any = getCurrentInstance() ?? {};
const { med_category_code, activity_category_code, unit_code } = proxy.useDict(
'med_category_code',
'activity_category_code',
@@ -717,7 +722,7 @@ const changeHouseId = () => {
};
// 病区切换事件
const changeWardLocationId = (id) => {
const changeWardLocationId = (id: any) => {
if (currentOperationType.value === 'transfer') {
// 转科模式:入院病房独立数据源,选中后刷新患者列表
queryParams.value.houseId = '';
@@ -731,7 +736,7 @@ const changeWardLocationId = (id) => {
};
queryParams.value.houseId = '';
childLocationList(params)
.then((res) => {
.then((res: any) => {
wardLocationList.value = res;
})
.catch((err) => {
@@ -752,14 +757,16 @@ const resetSearchForm = () => {
searchKey: '',
wardId: '',
houseId: '',
transferTargetWardId: undefined,
transferTargetOrgId: undefined,
};
wardLocationList.value = [];
getPractitionerWard().then((res) => {
getPractitionerWard().then((res: any) => {
initInfoOptions.value.wardListOptions = res.data || [];
});
// 转科页面:额外获取转科申请数据覆盖下拉框
if (currentOperationType.value === 'transfer') {
getTransferOptions().then((res) => {
getTransferOptions().then((res: any) => {
if (res.data && res.data.wardListOptions) {
initInfoOptions.value.wardListOptions = res.data.wardListOptions || [];
wardLocationList.value = res.data.departmentListOptions || [];
@@ -772,7 +779,7 @@ const resetSearchForm = () => {
// 获取患者列表
function getPatientList() {
// 根据操作类型构建请求参数:转科模式使用 transferTargetWardId/transferTargetOrgId
let params = { ...queryParams.value };
let params: Record<string, any> = { ...queryParams.value };
if (currentOperationType.value === 'transfer') {
params.transferTargetWardId = queryParams.value.wardId || undefined;
params.transferTargetOrgId = queryParams.value.houseId || undefined;
@@ -781,7 +788,7 @@ function getPatientList() {
}
console.log('queryParams==========>', JSON.stringify(params));
// 根据操作类型筛选不同状态的患者
getPendingInfo(params).then((res) => {
getPendingInfo(params).then((res: any) => {
console.log('res==========>', JSON.stringify(res.data));
let filteredPatients = [];
if (currentOperationType.value === 'transfer') {
@@ -789,11 +796,11 @@ function getPatientList() {
// filteredPatients = res.data.records.filter(
// (item) => item.encounterStatus === 6 || item.encounterStatus === 2
// );
filteredPatients = res.data.records.filter((item) => item.encounterStatus === 6);
filteredPatients = res.data.records.filter((item: any) => item.encounterStatus === 6);
} else {
// 出院患者筛选条件状态3
filteredPatients = res.data.records.filter(
(item) =>
(item: any) =>
item.encounterStatus === 3 || item.encounterStatus === 4 || item.encounterStatus === 7
);
}
@@ -830,9 +837,9 @@ function getPatientDetail(item: any): Promise<void> {
try {
detailLoading.value = true;
getPatientInfo(item)
.then((res) => {
.then((res: any) => {
const patientDetail = res.data;
return getPersonAccount(item).then((accountRes) => {
return getPersonAccount(item).then((accountRes: any) => {
selectedPatient.value = patientDetail;
selectedAccount.value = accountRes.data;
return;
@@ -865,7 +872,7 @@ function handleGetPrescription() {
encounterId: selectedPatient.value.encounterId,
therapyEnum: therapyEnum.value,
})
.then((response) => {
.then((response: any) => {
prescriptionList.value = response.data.records || [];
})
.catch((error) => {
@@ -990,7 +997,7 @@ function handleGetDRMedication() {
getDRMedication({
encounterId: selectedPatient.value.encounterId,
})
.then((response) => {
.then((response: any) => {
medicationList.value = response.data || [];
})
.catch((error) => {