2026-08-07.md 9.9 KB

2026-08-07 工作日志

data-board/nine-safety-measures/index.vue — 修复 getMonthList 报错与红名单过滤

问题:

  1. 控制台报错 echartsApi.getMonthList is not a function,阻塞页面后续加载。
  2. 红名单 highRiskItems 中后端仍返回「简易精神状态」,选择「防误食」后过滤为空。

改动:

  • 月份筛选相关代码已整体移除:filter.monthmonthOptions ref、onMounted 中的 echartsApi.getMonthList() 调用、handleReset 中的 filter.month = ''。月份下拉在模板中已不存在,属于残留代码。
  • 增强 normalizeRiskName:对 trim 后的名称做精确或包含匹配,把「简易精神状态」统一映射为「防误食」。
  • 增强 filteredRedList:过滤时对每个 risk 再次调用 normalizeRiskName,并同时支持带「防」前缀、去前缀、互相包含等多种匹配方式,避免后端字段不统一导致过滤失败。

类型检查:NO_NINE_SAFETY_ERRORS。

data-board/nine-safety-measures/index.vue — 红名单数据源头替换「简易精神状态」

用户要求:不要复杂的过滤兼容逻辑,直接把 multiHighRiskElders[i].highRiskItems 字符串里的「简易精神状态」替换成「防误食」。

改动:

  • redList 映射中,highRiskItems 先整体 replace(/简易精神状态/g, '防误食'),再按逗号拆分,不再依赖 normalizeRiskName 逐元素映射。
  • filteredRedList 恢复为简单的 r.risks.includes(redRiskFilter.value),因为数据源头已经统一为「防误食」。
  • normalizeRiskName 仍保留,继续用于图表等其他接口返回的九防名称映射。

类型检查:NO_NINE_SAFETY_ERRORS。

data-board/nine-safety-measures/index.vue — 红名单筛选"只有全部有数据"根因

现象:选「全部」有数据,选任何具体九防风险都 0 条。

根因:后端 highRiskItems 字符串里风险项的分隔符不是 ASCII 逗号,而是顿号「、」或全角逗号「,」。原 .split(',') 拆不开,整串变成一个数组元素,导致数组 includes('防误食') 恒为 false(数组里只有一个长串 ≠ '防误食')。这也解释了上一版 normalize 逐元素版本同样失败。

改动:

  • risks 解析 split 改为正则 /[,,、\s]+/,支持中英文逗号、顿号、空白统一拆分。
  • filteredRedList 改为宽松匹配:精确 / 去「防」前缀 / 子串包含,进一步兜底命名差异。

类型检查:NO_NINE_SAFETY_ERRORS。

elderly/contracts — 合同主体增加合同类型下拉与湖北合同组件

需求:

  • 合同主体分隔栏左侧文字、右侧增加「合同类型」下拉:广州地区合同 / 湖北地区合同 / 天河老人院合同(默认广州地区合同)。
  • 选中类型随首次保存写入 contractFormUpdate.value.contractInfo(JSON)。
  • 下次打开详情时解析该字段,据此渲染不同合同组件。
  • 复制一份合同组件作为「湖北合同」。

改动:

  • components/ContractBody.vue
    • contractForm reactive 新增 contractType: '广州地区合同'(首字段)。
    • 新增 prop contractType?: string 和 emit 'update:contractType'
    • 新增 watch:父级下拉变化时同步到 contractForm.contractType
    • fetchContractData 末尾 emit update:contractType(用 contractForm.contractType || '广州地区合同')。
    • 保存(saveContract 与无 contractInfo 的兜底分支)均 JSON.stringify({ ...contractForm }),自动包含 contractType。
  • 新建 components/ContractBodyHubei.vue = ContractBody.vue 的副本(含上述 contractType 支持)。
  • ContractManageForm.vue
    • 新增 contractType ref(默认'广州地区合同')与 contractTypeOptions
    • 合同主体 分隔栏改为 flex:左标题、右 el-select,新增 .contract-type-bar 等样式。
    • ContractBody / ContractBodyHubeiv-if="contractType !== '湖北地区合同'" 条件渲染,二者共用 ref="contractBodyRef",都接收 :contract-type 并监听 @update:contract-type 回写。
    • 打印 Teleport 容器内同样条件渲染两个组件。
    • open() 中重置 contractType 为默认,避免上一次选择残留;实际类型由详情加载后子组件回传再切换。

注意:ContractBody.vue 中 contractForm.attachmentscontractFormUpdate.value.bedName/contractInfo 等为既有类型告警(非本次引入),ContractBodyHubei.vue 因复制同样存在;本次 contractType 改动未引入任何新类型错误。

elderly/contracts — 修复合同组件运行时初始化报错

报错:

  1. ReferenceError: Cannot access 'contractForm' before initialization(ContractBody.vue:91)
  2. TypeError: Cannot read properties of null (reading 'emitsOptions')(ContractManageForm.vue:428)

根因:上一版在 ContractBody.vue 顶部添加了 watch(() => props.contractType, ..., { immediate: true }),回调里访问了 contractForm.contractType,但 contractFormreactive(...) 声明在文件更下方。<script setup> 中变量存在暂时性死区(TDZ),immediate: true 的 watch 在 setup 执行期间立即触发,导致 contractForm 还未初始化就被访问。

第二个 emitsOptions 错误是 Vue 在子组件 setup 失败后内部更新逻辑抛出的连锁错误。

改动:

  • ContractBody.vueContractBodyHubei.vue 中,把该 watch 块从 contractForm 声明之前移到 contractForm reactive 声明之后(即 stageExpenses 字段下方)。
  • 保持 immediate: true 逻辑不变:父组件下拉切换合同类型时仍同步写入本地表单。

类型检查:本次修复未引入新类型错误;剩余告警均为原文件既有问题。

elderly/contracts — 修复合同类型切换回弹 + 二次修改确认

现象:

  1. 默认广州合同 → 切湖北并保存 → 重开显示湖北 → 再选广州,会自动跳回湖北。
  2. 用户希望二次修改(已有保存记录)时弹确认框,确认后再保存。

根因:子组件 fetchContractData 末尾 emit('update:contractType', 存储的类型),且父组件 @update:contract-type="contractType = $event" 无差别覆盖。手动切换后 v-if 重挂载子组件,又会回传存储的旧类型,把下拉框拽回旧值(回弹)。

改动:

  • ContractManageForm.vue
    • 新增 savedContractType(初次解析出的已保存类型)、hasSavedContract(是否已存在保存记录)、contractTypeResolved(是否已解析)三个 ref。
    • 新增 handleContractTypeEmit:仅当 !contractTypeResolved 时接受子组件回传类型并解析,其余回传忽略(消除手动切换重挂载导致的回弹)。
    • 新增 handleContractExists(exists):记录合同是否已存在。
    • 新增 handleContractTypeChange(async):下拉 @change 触发;若 hasSavedContract 且类型与 savedContractType 不同,弹 ElMessageBox.confirm;确认则把 savedContractType 更新为新选择,取消则把 contractType 回退到 savedContractType。首次创建(无保存)不弹框。
    • open() 重置三个状态;两个 ContractBody/ContractBodyHubei(含打印 Teleport 内)均绑定 :lock-contract-type="contractTypeResolved" 并改 @update:contract-type/@update:contract-exists 到新 handler。
    • import ElMessageBox from 'element-plus'。
  • components/ContractBody.vueContractBodyHubei.vue
    • defineEmits 增加 update:contractExists;props 增加 lockContractType?: boolean
    • fetchContractData 末尾:若 props.lockContractType && props.contractTypecontractForm.contractType = props.contractType(手动切换时用父组件选中值,保证保存值正确);emit update:contractExists(依据 contractFormUpdate.value.contractInfo 是否存在);再 emit update:contractType

注意:初次加载(lock=false)用存储类型驱动下拉;手动切换(lock=true)用父组件选中值并忽略旧回传,避免回弹且保证保存为新类型。类型检查无新增错误。

elderly/fee/comprehensive-setting — 合同区域配置改为 value2 字段并放到合同模块配置内

需求:合同默认区域配置项放在「合同模块配置」卡片内部,而不是独立成新的卡片;字段使用 value2 而不是 orgType

改动:

  • src/views/elderly/fee/comprehensive-setting/index.vue
    • 将「合同区域设置」作为第二个 child 放在「合同过期通知设置」同一个 children 数组内。
    • 把标识字段从 orgTypeField 改为 value2Field
    • handleEdit 中:保存时 value2Field 项传 value: ''value2: c.value
    • getList 中:回显时 value2Field 项读取 r.value2
  • src/views/elderly/contracts/ContractManageForm.vue
    • loadContractTypeConfig 读取 config.value2 作为 defaultContractType

类型检查:comprehensive-setting/index.vueContractManageForm.vue 无新增错误;子组件剩余告警均为既有问题。

elderly/contracts — 修复保存时 "[object Object] is not valid JSON" 控制台报错

现象:点击保存合同,控制台报 SyntaxError: "[object Object]" is not valid JSON(ContractBody.vue:820),但保存成功。

根因:saveContract 中遍历 props.attachmentIds 时对每个 row 直接 JSON.parse(row)。当 row 已经是对象而非字符串时,JSON.parse 会把它隐式转成 "[object Object]" 再解析,导致 SyntaxError。该错误被内层 try-catch 捕获并 console.log,不影响外层保存流程,所以保存仍能成功。

改动:

  • src/views/elderly/contracts/components/ContractBody.vue
    • 第 811 行改为 const data = typeof row === 'string' ? JSON.parse(row) : row,对象直接复用。
  • src/views/elderly/contracts/components/ContractBodyHubei.vue
    • 同步上述修复。

类型检查:未引入新类型错误;剩余告警均为原文件既有问题。