生成时间: 2026-08-03 · 数据来源: xnybw平台需求及工作计划.xlsx(南网在线绿能云缺陷跟踪表)· 仅针对 backup730/730backup 分支代码

Row 42:项目基本信息保存后,切到实际并网容量信息页面会清空已填写但未保存的数据

一般 2.1.待开发 全部类型 模块:项目信息填报 / 项目基本信息 处理人:待指派

1基本信息

问题描述项目基本信息页面保存之后,进入实际并网容量信息页面不要清空已填写但是未保存的信息。
所属模块项目信息填报 / 项目基本信息
严重程度一般
涉及类型全部类型
提出人文日明
处理人 / 状态待指派 / 2.1.待开发

2问题截图

实际并网容量信息表单被清空
图1:"实际并网容量信息"Tab 下场站基本信息与发电单元信息表单均为空白——用户此前在该Tab填写但尚未保存的内容,在"项目基本信息"Tab保存后被清空

3旧代码定位(backup730 分支)

容器组件:xnybw5f · src/views/projectNew/ProjectTabs.vue

第 165-207 行,refreshAll(options = {}) 同时兼容"新版命名参数对象"与"旧版位置参数"两种调用方式,用 typeof options === 'object' 做判断:

async refreshAll(options = {}) {
  let processedParams;
  if (typeof options === 'object' && options !== null) {
    processedParams = options;                 // 新版:一个 { planId, status, refs, activeTab... } 对象
  } else {
    processedParams = {                        // 旧版:refreshAll(planId, status, refs, activeTab)
      planId: arguments[0], status: arguments[1], refs: arguments[2], activeTab: arguments[3]
    };
  }
  const {
    planId = this.planId, status = this.status,
    refs = ['basicTab', 'planTab', 'bwzlDetail', 'actualTab'],   // 默认值:四个 Tab 全部刷新
    activeTab, showLoading = true, forceRender = true            // 默认强制重新渲染
  } = processedParams;
  ...
  if (forceRender) { this.componentKey = new Date().getTime(); } // 触发四个 Tab 组件重建
  const safeRefs = Array.isArray(refs) ? refs : ['basicTab','planTab','bwzlDetail','actualTab'];
  const refreshPromises = safeRefs.map(ref =>
    this.$refs[ref] && this.$refs[ref].refreshData(planId, status)   // 对包括 actualTab 在内的全部Tab重新拉取数据
  );
  await Promise.all(refreshPromises);
},

触发方:src/views/projectNew/components/baseInfo/projectDetail.vue 第 497、500 行,"项目基本信息"保存成功后按旧版位置参数风格调用:

this.$emit("refreshAll", res.data, "edit", null, "plan");     // 第497行:保存后跳转"计划并网"
this.$emit("refreshAll", this.currentPlanId, "edit", null, "basic"); // 第500行:仅刷新基本信息自身
根因说明(关键 Bug):$emit("refreshAll", res.data, "edit", null, "plan") 通过 Vue 事件以多参数形式派发,@refreshAll="refreshAll" 绑定下会被原样透传为 refreshAll(res.data, "edit", null, "plan")——即函数的第一个形参 options 拿到的是 res.data(保存后返回的项目对象,本身也是 object 类型),而不是调用方真正想传的"参数对象"。由于 typeof options === 'object' 判断为真,代码错误地走进了"新版命名参数"分支,把 res.data 整体当作 {planId, status, refs, activeTab} 解构;res.data 里没有 refs 字段,于是解构默认值生效,refs 被兜底成 ['basicTab', 'planTab', 'bwzlDetail', 'actualTab'] —— 包括原本调用方想通过第三个位置参数 null 表示"不刷新"的那些 Tab。同时 forceRender 也因为解构不到而默认 true,触发 componentKey 变化,强制重建全部四个 Tab 组件。最终结果:用户仅仅保存了"项目基本信息",却连带把"实际并网容量信息"Tab(actualTab)也重新拉取服务端数据并重建组件,导致该 Tab 里任何尚未保存的手工填写内容被整体覆盖清空。

4修复方案

修复文件:src/views/projectNew/components/baseInfo/projectDetail.vue + src/views/projectNew/ProjectTabs.vue

第一步(统一调用方式,消除歧义):把所有 $emit('refreshAll', ...) 的调用方改为统一传"单个命名参数对象",不再使用位置参数风格:
// projectDetail.vue 第497行 改为:
this.$emit("refreshAll", {
  planId: res.data.id, status: "edit",
  refs: ["planTab"],       // 只刷新真正需要联动更新的Tab(跳转到"计划并网"前,只需它自己的数据是最新的)
  activeTab: "plan",
})

// 第500行 改为:
this.$emit("refreshAll", {
  planId: this.currentPlanId, status: "edit",
  refs: ["basicTab"],      // 仅刷新已保存的基本信息自身,不动其它未保存的Tab
  activeTab: "basic",
})
第二步(收紧 ProjectTabs.vue 的兼容判断,避免同类问题在其它调用点复发):去掉基于 typeof 的模糊双签名兼容,只保留对象签名,倒逼所有调用方显式指定 refs
async refreshAll(options = {}) {
  if (typeof options !== 'object' || options === null || Array.isArray(options)) {
    console.error('refreshAll 已不再支持位置参数调用,请改用 { planId, status, refs, activeTab } 对象形式')
    return
  }
  const {
    planId = this.planId, status = this.status,
    refs = [],                 // 默认空数组:不显式指定就什么都不强制刷新,而不是"刷新全部"
    activeTab, showLoading = true, forceRender = refs.length > 0,
  } = options
  ...
}
效果:保存"项目基本信息"后只刷新确实需要联动更新的 Tab(自身 + 目标跳转 Tab),"实际并网容量信息"等其它未被操作的 Tab 不再被强制重新拉取数据/重建组件,用户已填写但未保存的内容得以保留;同时把默认行为从"全量刷新"改为"按需刷新",从源头上避免同类隐式覆盖问题在其它触发点复现。