uni-app——uni-app 小程序 之 【数据返回空问题排查】

📅 发布时间:2026/7/8 9:52:56 👁️ 浏览次数:
uni-app——uni-app 小程序 之 【数据返回空问题排查】
一、问题背景通用场景在数据统计类页面中数据提交完成后进入「统计结果」页面出现数据显示为空或不完整的异常具体表现为评分类数据的「平均得分」显示为空选择类数据的「最终结果」和「占比百分比」显示为空有效数据量、无效数据量等统计信息显示错误。二、核心问题问题源于后端数据查询逻辑异常和前端数据处理不兼容适用于各类统计场景具体分为两部分2.1 后端原因后端查询统计数据时仅加载deleted_flag0未软删除的基础数据但数据提交前编辑基础数据时旧数据会被软删除而提交记录仍指向这些旧数据导致统计时无法匹配到对应基础数据最终返回空数据。2.2 前端原因类型判断不兼容前端仅支持字符串类型枚举值无法处理后端返回的数字类型枚举值字段名称不匹配前端仍使用旧字段命名而后端已更新为新字段命名导致数据无法正常渲染统计取值错误错误将「总数据量」当作「有效数据量」导致统计结果异常。三、通用解决方案Demo版可直接复用适配核心思路后端修正数据查询逻辑关联软删除数据前端优化数据处理逻辑解决类型兼容、字段匹配和取值错误问题以下Demo适配所有类似统计场景。3.1 后端修复通用核心调整统计数据查询逻辑查询提交记录时关联软删除deleted_flag1的基础数据确保提交记录能匹配到对应数据避免返回空结果Demo仅提供逻辑具体语法根据后端语言调整。3.2 前端修复4个通用核心点可直接复制使用3.2.1 枚举类型判断优化解决类型不兼容// 统一转换并多层次判断兼容后端数字/字符串两种枚举值返回类型// 可根据自身场景修改枚举值如1/2/3或对应字符串const parseDataType (dataType) {// 字符串数字先转换为数字统一判断标准const dataTypeValue typeof dataType ‘string’ ? Number(dataType) : dataType;// 兼容数字1/2/3和字符串两种类型可按需修改枚举映射const isScoreType dataTypeValue 3 ||dataType ‘SCORE’ ||dataType ‘RATING’;return {isScoreType,isChoiceType: !isScoreType // 非评分类即为选择类};};3.2.2 字段映射逻辑重构解决字段不匹配// 映射后端新字段替换前端旧字段适配评分类/选择类两种通用场景// 可按需修改字段名称和逻辑适配自身数据结构const mapDemoData (dataItem, index) {const options Array.isArray(dataItem.options) ? dataItem.options : [];// 计算总提交量使用后端新字段可替换为自身新字段名const totalCount options.reduce((sum, option) {return sum (option.newCountField || 0);}, 0);const { isScoreType } parseDataType(dataItem.dataType);if (isScoreType) {// 评分类数据从选项中获取后端新字段平均得分const firstOption options[0] || {};const averageScore formatDemoNum(firstOption.newScoreField ?? 0);return { title: dataItem.content || Demo数据${index 1}, type: score, averageScore, finalResult: totalCount 0 ? ${averageScore} : 无提交数据, };} else {// 选择类数据使用新字段计算占比优先使用后端返回的占比字段const topOption findTopOption(options);const percentage formatDemoNum(topOption?.newPercentField ??(totalCount 0 ? ((topOption?.newCountField || 0) * 100) / totalCount : 0));return { title: dataItem.content || Demo数据${index 1}, type: choice, finalResult: totalCount 0 ? ${topOption?.optionName || 选项}占比${percentage}% : 无提交数据, };}};3.2.3 数值格式化函数避免空值、NaN异常/**通用数值格式化函数保留1位小数避免NaN和空值异常可直接复用无需修改param {any} value - 需要格式化的值returns {number} 格式化后的数值*/const formatDemoNum (value) {const num Number(value);if (Number.isNaN(num)) {return 0;}return Number(num.toFixed(1));};3.2.4 有效数据量修正解决取值错误// 修正有效数据量取值区分总数据量和有效数据量// 可按需修改字段名称适配自身接口返回结构const calculateDemoStats (data, totalTotal) {const resultStats data.resultStats || {};// 正确取值优先使用有效数据量字段其次使用参与量字段拒绝使用总数据量字段const validCount resultStats.validCount ?? data.participateCount ?? 0;// 无效数据量总数据量 - 有效数据量避免负数const invalidCount resultStats.invalidCount ??(totalTotal 0 ? Math.max(0, totalTotal - validCount) : 0);return {valid: validCount,invalid: invalidCount,};};3.3 前端完整Demo通用版整合所有修复点可直接复用有效数据{{ demoStats.valid }}无效数据{{ demoStats.invalid }}!-- 数据列表修复字段和类型兼容 -- div classdata-list div v-for(item, index) in demoData :keyindex classdata-card div classdata-title{{ item.title }}/div !-- 评分类数据结果 -- div v-ifitem.type score classscore-result span classlabel平均得分/span template v-ifitem.finalResult ! 无提交数据 span classscore{{ item.averageScore }}/span div classstars span v-forstar in 5 :keystar :class[star, { filled: star Math.round(item.averageScore) }] ★/span /div /template span v-else classno-data{{ item.finalResult }}/span /div !-- 选择类数据结果 -- div v-else classchoice-result span classlabel最终结果/span span :class[result, { no-data: item.finalResult 无提交数据 }] {{ item.finalResult }} /span /div /div /div