# testProjectConfiguration 页面性能改造操作手册 **适用范围**:前端 beiHai-ui / 后端 beiHai-lims **改造目标**:页面打开 20 秒 → 2~3 秒以内 **说明**:本手册只写「怎么改」,每个改动都给出文件路径、行号、改前/改后代码。按编号顺序执行即可,做完 1~3 步就能砍掉绝大部分耗时。 --- ## 📋 改动清单总览 | 步骤 | 改动 | 收益 | 风险 | |---|---|---|---| | 1 | 3 个弹窗子组件懒加载(v-if) | 砍掉约 14 个首屏请求(最大收益) | 中(需同步改子组件 watch) | | 2 | 删除死代码 `gettestItemNameTypeList` | 少 1 个全量请求 | 极低 | | 3 | `getList()` 改为打开弹窗时才调用 | 首屏少 1 个 500 条请求 | 极低 | | 4 | 勾选/点击行加防抖 | 避免子表请求风暴 | 低 | | 5 | 子表 pageSize 990 → 50 | 减少每次交互的数据量 | 低 | | 6 | 表格瘦身(去 sortable/show-summary) | 减少渲染开销 | 低(视觉略有变化) | | 7 | 后端补索引(必须做) | 生产库数据量大时兜底 | 低(上线前 DBA 评估) | | 8 | 后端字典查询 pageSize 调小 | 减少全量传输 | 低 | | 9 | 后端关闭 SQL 日志输出 | 减少日志 IO | 极低 | > ⚠️ **先备份再动手**:每个文件修改前先复制一份,例如 `testProjectConfiguration.vue.bak`。改完用 `npm run dev` 起本地验证。 --- ## 第 1 步:3 个弹窗子组件懒加载(核心改动,必须做) 弹窗子组件 `alertComponents / alertComponents3 / alertComponents4` 目前**页面一打开就被实例化**,各自 `mounted` 里发 5~7 个请求。改成「第一次打开弹窗时才创建组件」。 ### 1.1 父组件加 v-if **文件**:`src/views/standardManagement/components/testProjectConfiguration/testProjectConfiguration.vue` **位置**:第 320~323 行 **改前**: ```html
``` **改后**: ```html
``` ### 1.2 三个子组件 watch 加 immediate(⚠️ 必须同步改,否则弹窗打不开) **原因**:子组件通过 `watch: { showFlag }` 打开弹窗。加 v-if 后,组件首次创建时 `showFlag` 已经是 `true`,**watch 默认不触发首次值**,弹窗会打不开。必须加 `immediate: true`。 **文件 1**:`src/views/standardManagement/components/testProjectConfiguration/alertComponents.vue` **位置**:第 456~459 行 **改前**: ```js watch: { showFlag (arg) { this.dialogTableVisible = true; } }, ``` **改后**: ```js watch: { showFlag: { handler (arg) { this.dialogTableVisible = true; }, immediate: true } }, ``` **文件 2**:`alertComponents3.vue`,第 412~416 行 —— 同样改法(结构一致,照上面抄)。 **文件 3**:`alertComponents4.vue`,第 171~189 行 —— 同样改法(注意它 watch 里可能还有别的逻辑,只把 `showFlag` 这个 handler 包成对象 + `immediate: true`)。 ### 1.3 验证 ``` npm run dev ``` 1. 打开测试项目配置页 → 打开浏览器 DevTools Network,刷新页面 2. 观察首屏请求数量:应从 ~19 个降到 ~4 个(只剩父组件的字典 + 主表分页 + 3 个 addScreen) 3. 点击「新增」按钮 → 弹窗应正常打开,此时才出现弹窗的字典请求(正常现象) 4. 连续开/关弹窗 2~3 次,确认不报错 --- ## 第 2 步:删除死代码 gettestItemNameTypeList 该请求拉取全部测试项目,结果存入 `testItemNameType`,但**模板里从未使用**(页面下拉用的是 `itemTypeNameType`,来自另一个接口)。纯浪费。 **文件**:`testProjectConfiguration.vue` **改动 2.1**:删掉第 422 行的调用 ```js this.gettestItemNameTypeList(); // ← 删除这一行 ``` **改动 2.2**:删掉第 430~435 行的方法定义 ```js gettestItemNameTypeList () { this.axios.get('pass/baseManagement/v1/limsstdtestitems/'+ '?validFlag=1') .then(res => { this.testItemNameType = res.data.list; }) }, ``` (可一并删掉 data 里第 395 行的 `testItemNameType: []`,不删也无害) --- ## 第 3 步:getList() 改为弹窗打开时才调用 `getList()` 查询分析项目(pageSize=500),数据只在「新增分析项目」弹窗里用,但目前页面一打开就拉。 **文件**:`testProjectConfiguration.vue` **改动 3.1**:删掉第 418 行的 mounted 调用 ```js this.getList() // ← 删除这一行 ``` **改动 3.2**:在 `addData2()`(第 626~633 行)里补上调用 **改前**: ```js addData2 () { let _this = this; if (_this.multipleSelection.length !== 1) { this.$message.warning('请选择一条测试项目数据!'); return } this.dialogTableVisible = true }, ``` **改后**: ```js addData2 () { let _this = this; if (_this.multipleSelection.length !== 1) { this.$message.warning('请选择一条测试项目数据!'); return } this.getList(); // ← 新增:弹窗打开时才加载分析项目列表 this.dialogTableVisible = true }, ``` --- ## 第 4 步:勾选/点击行加防抖(可选但建议) 当前点击/勾选任意一行,立刻触发 2 个 pageSize=990 的子表查询;多选时连续触发。 **文件**:`testProjectConfiguration.vue` **位置**:第 799~807 行(handleSelectionChange)、第 846~858 行(handleCurrentChange) **改法**:把两个方法里的 `getDataList2/getDataList3` 调用替换为防抖调用,并新增一个防抖方法。 `handleSelectionChange` 改后: ```js handleSelectionChange (val) { this.multipleSelection = val; if (val.length > 0) { this.testItemNo = val[val.length-1].testItemNo this.curItemTypeCode = val[val.length-1].itemTypeCode; this.debounceLoadSub(this.testItemNo); } }, ``` `handleCurrentChange` 改后(顺手修复 val 为 null 时的报错): ```js handleCurrentChange (val) { this.curItemTypeCode = '' if (val){ this.curItemTypeCode = val.itemTypeCode; } if (!val || val.testItemNo === null) { this.testItemNo = ''; } else { this.testItemNo = val.testItemNo; } this.debounceLoadSub(this.testItemNo); }, ``` 在 methods 里新增防抖方法(放在 getDataList2 前面即可): ```js debounceLoadSub (testItemNo) { clearTimeout(this._subTimer); this._subTimer = setTimeout(() => { this.getDataList2(testItemNo); this.getDataList3(testItemNo); }, 300); }, ``` --- ## 第 5 步:子表 pageSize 990 → 50,恢复分页 **文件**:`testProjectConfiguration.vue` **位置**:第 370~379 行 **改前**: ```js page2: { pageSize: 990, pageNum: 1, totalPage: 0 }, page3: { pageSize: 990, pageNum: 1, totalPage: 0 }, ``` **改后**: ```js page2: { pageSize: 50, pageNum: 1, totalPage: 0 }, page3: { pageSize: 50, pageNum: 1, totalPage: 0 }, ``` **建议同时恢复被注释的分页组件**:第 136~141 行有被注释的 ``(数据是 page2),恢复它并复制一份给 page3 的表格,这样子表才能翻页查看完整数据(因为 pageSize 变小了)。 --- ## 第 6 步:表格瘦身(降低渲染开销,工作量中等) 三个表格共约 69 列,**每列都带 `sortable` + `show-overflow-tooltip`**,且都开 `show-summary` 合计行 + `:icore-filter-flag`。`show-overflow-tooltip` 会给每个单元格注册 tooltip 实例,50 行 × 25 列 = 1250 个,是渲染大头。 ### 6.1 去掉每列 sortable(只保留 1~2 个关键列) 主表(第 41~65 行)、分析项目表(第 98~133 行)、方法标准表(约第 163 行起):批量把每列开头的 `sortable` 删掉。 示例: ```html ``` ### 6.2 去掉 show-summary 合计行 `getSummaries` 只统计了「合计 + 行数」,价值低。三处表格的 `:summary-method="getSummaries" show-summary`(第 38、94、160 行)直接删除,`getSummaries` 方法(第 436~448 行)也可删。 ### 6.3 (可选)el-tabs 加 lazy 第 76 行 `` 改为 ``,让非激活 tab 延迟渲染。 --- ## 第 7 步:后端补索引(生产环境必须做) **文件**:后端库执行 DDL(需 DBA / 维护窗口评估执行,仅 SELECT 查询无风险) ```sql -- 子表按 TEST_ITEM_NO 查询是高频路径,当前全表扫描 CREATE INDEX IDX_TID_TESTITEMNO ON LIMS_STD_TEST_ITEM_D (TEST_ITEM_NO); CREATE INDEX IDX_TMATCH_TESTITEMNO ON LIMS_STD_TEST_MATCH (TEST_ITEM_NO); -- 两个主表 ORDER BY CREATE_TIME DESC 无索引,每次全表排序 CREATE INDEX IDX_TITEM_CREATETIME ON LIMS_STD_TEST_ITEM (CREATE_TIME); CREATE INDEX IDX_ANLY_CREATETIME ON LIMS_STD_ANLY_ITEM (CREATE_TIME); -- 列表页常见过滤条件 CREATE INDEX IDX_TITEM_VALIDFLAG ON LIMS_STD_TEST_ITEM (VALID_FLAG); ``` --- ## 第 8 步:字典查询 pageSize 调小 子组件里的全量字典查询(`pageSize: 9999 / 99999`)虽然做了第 1 步后只在打开弹窗时发,但值仍过大,建议调小: - `alertComponents.vue` 第 312 行:`pageSize:9999` → `pageSize:500`(规则下拉,271 条数据) - `alertComponents.vue` 第 340 行:`pageSize:99999` → `pageSize:100`(岗位下拉,仅 12 条) - `alertComponents3.vue` 第 315 / 340 / 355 / 367 行:`pageSize:9999` → `pageSize:500`(分析项目 / 单位 / 修约规则 / 公式) --- ## 第 9 步:后端关闭 SQL 日志输出 **文件**:`beiHai-lims/lims-base/src/main/resources/application.properties` **改前**: ```properties logging.level.com.steerinfo=DEBUG mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl ``` **改后**: ```properties logging.level.com.steerinfo=INFO mybatis.configuration.log-impl=org.apache.ibatis.logging.nologging.NoLoggingImpl ``` > 注意:三个模块(lims-base / lims-api / lims-test)可能都有各自的 application.properties,生产环境每个都改。 --- ## ✅ 验收标准 改完 1~3 步后: | 指标 | 改前 | 改后预期 | |---|---|---| | 首屏请求数 | ~19 个 | ~4 个 | | 页面渲染完成 | ~20 秒 | 2~3 秒 | | 勾选/点击行 | 2 个 990 条请求 | 防抖后 2 个 50 条请求 | **验证方法**: 1. DevTools Network 面板看首屏请求数量和耗时分布(改前先录一份对比) 2. 若首屏仍有慢请求,看「Waiting (TTFB)」高还是「Content Download」高,分别对应后端慢 / 响应体大 3. 生产库验证前,先确认第 7 步索引已执行 --- ## ⚠️ 注意事项汇总 1. **第 1 步的 1.2 是强制配套**:只加 v-if 不改 watch,弹窗会打不开(watch 不触发首次值) 2. **先备份**:`cp testProjectConfiguration.vue testProjectConfiguration.vue.bak`(三个子组件同理) 3. 第 6 步表格瘦身是「锦上添花」,如果 1~5 步做完首屏已经达标,可暂缓 4. 索引 DDL 建议在低峰期由 DBA 执行,先 EXPLAIN 验证子表查询从 FULL SCAN 变为 INDEX RANGE SCAN 5. 所有前端改动都在 `src/views/standardManagement/components/testProjectConfiguration/` 目录内,后端改动在 `beiHai-lims`,互不冲突