适用范围:前端 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起本地验证。
弹窗子组件 alertComponents / alertComponents3 / alertComponents4 目前页面一打开就被实例化,各自 mounted 里发 5~7 个请求。改成「第一次打开弹窗时才创建组件」。
文件:src/views/standardManagement/components/testProjectConfiguration/testProjectConfiguration.vue
位置:第 320~323 行
改前:
<div is="alertComponets" :showFlag="showFlag" :Params="Params" @refresh='refresh'></div>
<!-- <div is="alertComponets2" :showFlag="showFlag2" :Params="Params2" @refresh='refresh2'></div> -->
<div is="alertComponets3" :showFlag="showFlag3" :Params="Params3" @refresh='refresh2'></div>
<div is="alertComponets4" :showFlag="showFlag4" :Params="Params5" :Params2="Params4" @refresh='refresh3'></div>
改后:
<div v-if="showFlag" is="alertComponets" :showFlag="showFlag" :Params="Params" @refresh='refresh'></div>
<!-- <div v-if="showFlag2" is="alertComponets2" :showFlag="showFlag2" :Params="Params2" @refresh='refresh2'></div> -->
<div v-if="showFlag3" is="alertComponets3" :showFlag="showFlag3" :Params="Params3" @refresh='refresh2'></div>
<div v-if="showFlag4" is="alertComponets4" :showFlag="showFlag4" :Params="Params5" :Params2="Params4" @refresh='refresh3'></div>
原因:子组件通过 watch: { showFlag } 打开弹窗。加 v-if 后,组件首次创建时 showFlag 已经是 true,watch 默认不触发首次值,弹窗会打不开。必须加 immediate: true。
文件 1:src/views/standardManagement/components/testProjectConfiguration/alertComponents.vue
位置:第 456~459 行
改前:
watch: {
showFlag (arg) {
this.dialogTableVisible = true;
}
},
改后:
watch: {
showFlag: {
handler (arg) {
this.dialogTableVisible = true;
},
immediate: true
}
},
文件 2:alertComponents3.vue,第 412~416 行 —— 同样改法(结构一致,照上面抄)。
文件 3:alertComponents4.vue,第 171~189 行 —— 同样改法(注意它 watch 里可能还有别的逻辑,只把 showFlag 这个 handler 包成对象 + immediate: true)。
npm run dev
该请求拉取全部测试项目,结果存入 testItemNameType,但模板里从未使用(页面下拉用的是 itemTypeNameType,来自另一个接口)。纯浪费。
文件:testProjectConfiguration.vue
改动 2.1:删掉第 422 行的调用
this.gettestItemNameTypeList(); // ← 删除这一行
改动 2.2:删掉第 430~435 行的方法定义
gettestItemNameTypeList () {
this.axios.get('pass/baseManagement/v1/limsstdtestitems/'+ '?validFlag=1')
.then(res => {
this.testItemNameType = res.data.list;
})
},
(可一并删掉 data 里第 395 行的 testItemNameType: [],不删也无害)
getList() 查询分析项目(pageSize=500),数据只在「新增分析项目」弹窗里用,但目前页面一打开就拉。
文件:testProjectConfiguration.vue
改动 3.1:删掉第 418 行的 mounted 调用
this.getList() // ← 删除这一行
改动 3.2:在 addData2()(第 626~633 行)里补上调用
改前:
addData2 () {
let _this = this;
if (_this.multipleSelection.length !== 1) {
this.$message.warning('请选择一条测试项目数据!');
return
}
this.dialogTableVisible = true
},
改后:
addData2 () {
let _this = this;
if (_this.multipleSelection.length !== 1) {
this.$message.warning('请选择一条测试项目数据!');
return
}
this.getList(); // ← 新增:弹窗打开时才加载分析项目列表
this.dialogTableVisible = true
},
当前点击/勾选任意一行,立刻触发 2 个 pageSize=990 的子表查询;多选时连续触发。
文件:testProjectConfiguration.vue
位置:第 799~807 行(handleSelectionChange)、第 846~858 行(handleCurrentChange)
改法:把两个方法里的 getDataList2/getDataList3 调用替换为防抖调用,并新增一个防抖方法。
handleSelectionChange 改后:
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 时的报错):
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 前面即可):
debounceLoadSub (testItemNo) {
clearTimeout(this._subTimer);
this._subTimer = setTimeout(() => {
this.getDataList2(testItemNo);
this.getDataList3(testItemNo);
}, 300);
},
文件:testProjectConfiguration.vue
位置:第 370~379 行
改前:
page2: {
pageSize: 990,
pageNum: 1,
totalPage: 0
},
page3: {
pageSize: 990,
pageNum: 1,
totalPage: 0
},
改后:
page2: {
pageSize: 50,
pageNum: 1,
totalPage: 0
},
page3: {
pageSize: 50,
pageNum: 1,
totalPage: 0
},
建议同时恢复被注释的分页组件:第 136~141 行有被注释的 <el-pagination>(数据是 page2),恢复它并复制一份给 page3 的表格,这样子表才能翻页查看完整数据(因为 pageSize 变小了)。
三个表格共约 69 列,每列都带 sortable + show-overflow-tooltip,且都开 show-summary 合计行 + :icore-filter-flag。show-overflow-tooltip 会给每个单元格注册 tooltip 实例,50 行 × 25 列 = 1250 个,是渲染大头。
主表(第 41~65 行)、分析项目表(第 98~133 行)、方法标准表(约第 163 行起):批量把每列开头的 sortable 删掉。
示例:
<!-- 改前 -->
<el-table-column sortable :show-overflow-tooltip="true" min-width="120px" prop="testItemNo" label="测试项目编号" align="center"></el-table-column>
<!-- 改后(保留 testItemNo / testItemName 两列的 sortable,其余删除) -->
<el-table-column sortable :show-overflow-tooltip="true" min-width="120px" prop="testItemNo" label="测试项目编号" align="center"></el-table-column>
<el-table-column :show-overflow-tooltip="true" min-width="120px" prop="testItemName" label="测试项目名称" align="center"></el-table-column>
getSummaries 只统计了「合计 + 行数」,价值低。三处表格的 :summary-method="getSummaries" show-summary(第 38、94、160 行)直接删除,getSummaries 方法(第 436~448 行)也可删。
第 76 行 <el-tabs v-model="activeName" type="card" @tab-click="handleClick"> 改为 <el-tabs v-model="activeName" type="card" @tab-click="handleClick" lazy>,让非激活 tab 延迟渲染。
文件:后端库执行 DDL(需 DBA / 维护窗口评估执行,仅 SELECT 查询无风险)
-- 子表按 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);
子组件里的全量字典查询(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(分析项目 / 单位 / 修约规则 / 公式)文件:beiHai-lims/lims-base/src/main/resources/application.properties
改前:
logging.level.com.steerinfo=DEBUG
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
改后:
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 条请求 |
验证方法:
cp testProjectConfiguration.vue testProjectConfiguration.vue.bak(三个子组件同理)src/views/standardManagement/components/testProjectConfiguration/ 目录内,后端改动在 beiHai-lims,互不冲突