xwt
2025-09-24 816ab1967ee766e3e3f3631c08371f2e7704408e
pages/QC/LLJ/detail.vue
@@ -7,12 +7,14 @@
      </view>
      <!-- 标签栏 -->
      <view class="tabs">
         <view v-for="(tab, index) in tabs" :key="index" class="tab" :class="{active: currentTab === index}"
            @tap="switchTab(index,tab.id)">
            {{ tab.fcheckItem }}
      <scroll-view class="tabs" scroll-x="true" :show-scrollbar="false">
         <view class="tabs-container">
            <view v-for="(tab, index) in tabs" :key="index" class="tab" :class="{active: currentTab === index}"
               @tap="switchTab(index,tab.id)">
               {{ tab.fcheckItem }}
            </view>
         </view>
      </view>
      </scroll-view>
      <view class="tab-content">
         <!-- 基本信息 -->
         <view class="section">
@@ -22,6 +24,10 @@
                  <view class="info-item">
                     <view class="info-label">项目名称</view>
                     <view class="info-value">{{ formData.fcheckItem }}</view>
                  </view>
                  <view v-if="parseHoleCount(formData.fcheckItem)" class="info-item">
                     <view class="info-label">穴数</view>
                     <view class="info-value hole-count">{{ parseHoleCount(formData.fcheckItem) }}穴</view>
                  </view>
                  <view class="info-item">
                     <view class="info-label">检验工具</view>
@@ -112,7 +118,7 @@
         <view class="section">
            <view class="section-header">检验结果录入</view>
            <view class="section-body">
               <view class="input-group" v-if="tableData.length >= formData.checkQyt">
               <view class="input-group" v-if="tableData.length >= formData.checkQyt && formData.fstand == null">
                  <view class="input-wrapper">
                     <input v-model="batchInput" type="text" class="result-input"
                        placeholder="格式:OK-3 或 NG-3(请勿修改结果为数字的值)" placeholder-class="placeholder"
@@ -138,8 +144,27 @@
                        <uni-icons type="compose" size="16" color="#fff"></uni-icons>
                        备注
                     </button>
                     <input v-if="(tableData.length < formData.checkQyt)" @input="search($event)"
                        v-model="inputTxt" type="text" class="result-input" placeholder="请输入检验结果..."
                     <input v-if="(tableData.length < formData.checkQyt) && formData.fupAllow && formData.fdownAllow"
                        ref="numberInput"
                        :focus="isFocus"
                        @input="onNumberInput"
                        @confirm="onEnterSave"
                        @blur="isFocus = false"
                        v-model="formData.fcheckResu"
                        type="text"
                        class="result-input"
                        placeholder="请输入检验结果..."
                        placeholder-class="placeholder" />
                     <input v-else
                        ref="textInput"
                        :focus="isFocus"
                        @input="search($event)"
                        @confirm="onEnterSave"
                        @blur="isFocus = false"
                        v-model="inputTxt"
                        type="text"
                        class="result-input"
                        placeholder="请输入检验结果..."
                        placeholder-class="placeholder" />
                     <button v-if="(tableData.length < formData.checkQyt)"
                        style="margin: 0px;background-color: #3498db;color:#ffffff ;" class="btn primary-btn"
@@ -167,7 +192,10 @@
                   
                  <view class="input-wrapper" style="margin-top: 15px;">
                     <input v-if="(tableData.length < formData.checkQyt)" @input="search($event)"
                     <input v-if="(tableData.length < formData.checkQyt)"
                        ref="textInput2"
                        @input="search($event)"
                        @confirm="onEnterSave"
                        v-model="inputTxt" type="text" class="result-input" placeholder="请输入检验结果..."
                        placeholder-class="placeholder" />
                     <button v-if="(tableData.length < formData.checkQyt)"
@@ -180,29 +208,62 @@
         <!-- 结果表格 -->
         <view v-if="tableData.length>0" class="table-container">
            <view class="table-header">
            <!-- 有穴数时的表格头部 -->
            <view v-if="parseHoleCount(formData.fcheckItem)" class="table-header">
               <view class="th">编号</view>
               <view class="th">穴号</view>
               <view class="th">记录值</view>
               <view class="th">检验结果<i style="color: rgb(0 212 68);"
                     v-if="!(tableData.length < formData.checkQyt)">(输入已完成)</i></view>
               <view class="th" v-if="current">操作</view>
            </view>
            <!-- 无穴数时的表格头部 -->
            <view v-else class="table-header">
               <view class="th">编号</view>
               <view class="th">检验结果<i style="color: rgb(0 212 68);"
                     v-if="!(tableData.length < formData.checkQyt)">(输入已完成)</i></view>
               <view class="th" v-if="current">操作</view>
            </view>
            <view v-for="(item, index) in tableData" :key="index" class="table-row">
               <view class="td">{{ index + 1 }}</view>
               <view class="td">
                  <view :class="['result-badge', item.fcheckResu]">
                     {{ item.fcheckResu }}
            <!-- 有穴数时的表格行 -->
            <template v-if="parseHoleCount(formData.fcheckItem)">
               <view v-for="(item, index) in completeHoleList" :key="index" class="table-row">
                  <view class="td">{{ index + 1 }}</view>
                  <view class="td">{{ item.holeNumber }}穴</view>
                  <view class="td">{{ item.recordValue }}</view>
                  <view class="td">
                     <view :class="['result-badge', getResultBadgeClass(item.resultStatus)]">
                        {{ item.resultStatus }}
                     </view>
                  </view>
                  <view class="td" v-if="current">
                     <button v-if="!item.isDefault && isNumber" class="btn danger-btn" @tap="toDetail(item)">
                        修改
                     </button>
                  </view>
               </view>
               <view class="td" v-if="current">
                  <button v-if="!isNumber" class="btn danger-btn" @tap="toggleResult(item)">
                     {{ editResult(item.fcheckResu) }}
                  </button>
                  <button v-if="isNumber" class="btn danger-btn" @tap="toDetail(item)">
                     修改
                  </button>
            </template>
            <!-- 无穴数时的表格行 -->
            <template v-else>
               <view v-for="(item, index) in tableData" :key="index" class="table-row">
                  <view class="td">{{ index + 1 }}</view>
                  <view class="td">
                     <view :class="['result-badge', item.fcheckResu]">
                        {{ item.fcheckResu }}
                     </view>
                  </view>
                  <view class="td" v-if="current">
                     <button v-if="!isNumber" class="btn danger-btn" @tap="toggleResult(item)">
                        {{ editResult(item.fcheckResu) }}
                     </button>
                     <button v-if="isNumber" class="btn danger-btn" @tap="toDetail(item)">
                        修改
                     </button>
                  </view>
               </view>
            </view>
            </template>
         </view>
         <view v-if="remarksPopup" class="overlay">
            <view class="popup">
@@ -277,9 +338,169 @@
            meomPopup: false,
            showMeom:false,
            meom: '',
            autoSaveTimer: null, // 自动保存定时器
            isFocus: false, // 新增,控制输入框聚焦
         }
      },
      computed: {
         // 生成完整的穴位列表(根据检验数量生成,穴号循环显示)
         completeHoleList() {
            const holeCount = this.parseHoleCount(this.formData.fcheckItem);
            if (!holeCount) return this.tableData;
            const checkQyt = this.formData.checkQyt || 0; // 检验数量
            const completeList = [];
            // 根据检验数量生成记录,穴号循环显示
            for (let i = 1; i <= checkQyt; i++) {
               // 计算当前记录的穴号(循环显示)
               const holeNumber = ((i - 1) % holeCount) + 1;
               // 查找是否已有该位置的记录
               const existingRecord = this.tableData.find((item, index) => {
                  return index === i - 1; // 按顺序匹配
               });
               if (existingRecord) {
                  // 如果已有记录,使用现有数据
                  completeList.push({
                     ...existingRecord,
                     holeNumber: holeNumber,
                     recordValue: existingRecord.fcheckResu || 'N/A',
                     resultStatus: this.getResultStatus(existingRecord.fcheckResu) || '未填写'
                  });
               } else {
                  // 如果没有记录,创建默认记录
                  completeList.push({
                     id: null,
                     holeNumber: holeNumber,
                     recordValue: 'N/A',
                     resultStatus: '未填写',
                     fcheckResu: null,
                     isDefault: true // 标记为默认记录
                  });
               }
            }
            return completeList;
         }
      },
      methods: {
         // 解析检验项目名称中的穴数
         parseHoleCount(checkItemName) {
            if (!checkItemName) return null;
            // 匹配格式:尺寸检查(5穴)或 尺寸检查(5穴)
            const match = checkItemName.match(/[((](\d+)穴[))]/);
            return match ? parseInt(match[1]) : null;
         },
         // 根据记录值判断检验结果状态
         getResultStatus(recordValue) {
            if (!recordValue) return '';
            // 如果有上下限,根据数值判断
            if (this.formData.fupAllow && this.formData.fdownAllow) {
               const numValue = parseFloat(recordValue);
               if (isNaN(numValue)) return recordValue;
               if (numValue >= parseFloat(this.formData.fdownAllow) &&
                  numValue <= parseFloat(this.formData.fupAllow)) {
                  return 'OK';
               } else {
                  return 'NG';
               }
            }
            // 无上下限时,直接返回记录值
            return recordValue;
         },
         // 获取检验结果徽章的样式类
         getResultBadgeClass(resultStatus) {
            switch(resultStatus) {
               case 'OK':
                  return 'OK';
               case 'NG':
                  return 'NG';
               case '未填写':
                  return 'pending';
               default:
                  return 'default';
            }
         },
         // 添加新记录(用于未填写的穴位)
         addNewRecord(item) {
            // 这里可以触发填写逻辑,比如弹出输入框或跳转到填写页面
            // 暂时先显示提示
            this.$showMessage(`请填写第${item.holeNumber}穴的检验结果`);
         },
         // 防抖自动保存方法
         autoSaveResult() {
            // 清除之前的定时器
            if (this.autoSaveTimer) {
               clearTimeout(this.autoSaveTimer);
            }
            // 设置新的定时器,1秒后自动保存
            this.autoSaveTimer = setTimeout(() => {
               // 检查是否已经达到检验数量上限
               if (this.tableData.length >= this.formData.checkQyt) {
                  return;
               }
               // 验证输入并保存
               if (this.validateAndSave()) {
                  this.saveResult();
               }
            }, 2000);
         },
         // 验证输入是否有效
         validateAndSave() {
            // 有上下限的情况
            if (this.formData.fupAllow && this.formData.fdownAllow) {
               const value = this.formData.fcheckResu;
               if (!value || value.trim() === '') {
                  return false;
               }
               // 验证是否为有效数字
               if (isNaN(parseFloat(value)) || !/^-?\d+(\.\d+)?$/.test(value)) {
                  return false;
               }
               return true;
            } else {
               // 无上下限的情况,检验结果存储在 formData.fcheckResu 中
               const value = this.formData.fcheckResu;
               if (!value || value.trim() === '') {
                  return false;
               }
               return true;
            }
         },
         // 处理回车键保存事件
         onEnterSave() {
            // 检查是否已经达到检验数量上限
            if (this.tableData.length >= this.formData.checkQyt) {
               this.$showMessage("已达到检验数量上限");
               return;
            }
            // 清除自动保存定时器
            if (this.autoSaveTimer) {
               clearTimeout(this.autoSaveTimer);
               this.autoSaveTimer = null;
            }
            // 直接保存结果
            this.saveResult();
         },
         switchTab(index, mainIds) {
            this.currentTab = index
            this.mainId = mainIds;
@@ -364,6 +585,21 @@
         //检测输入框的输入,并给变量赋值
         search(event) {
            this.formData.fcheckResu = event.detail.value;
            this.inputTxt = event.detail.value;
            // 触发自动保存
            this.autoSaveResult();
         },
         onNumberInput(e) {
            // 只允许输入数字和小数点
            let val = e.detail.value.replace(/[^\d.]/g, '');
            // 只允许一个小数点
            val = val.replace(/\.{2,}/g, '.');
            val = val.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.');
            this.formData.fcheckResu = val;
            // 触发自动保存
            this.autoSaveResult();
         },
         toggleResult(item) {
            let fstand = "√";
@@ -398,7 +634,6 @@
            this.remarksPopup = true;
         },
         saveResult() {
            let count = this.formData.checkQyt;
            let fstand = "√";
@@ -414,7 +649,11 @@
                  this.$showMessage("请输入正确的检验值");
                  return;
               }
               if (!/^-?\d+(\.\d+)?$/.test(this.formData.fcheckResu)) {
                  this.$showMessage("请输入正确的数值!");
                  return;
               }
               if (parseFloat(this.formData.fcheckResu) >= parseFloat(this.formData.fdownAllow) && parseFloat(this
                     .formData
                     .fcheckResu) <= parseFloat(this.formData.fupAllow)) {
@@ -455,9 +694,15 @@
               this.$showMessage("保存成功");
               this.refreshResult();
               this.inputTxt = '';
               // 使用 isFocus 控制聚焦,兼容多端
               this.isFocus = false;
               this.$nextTick(() => {
                  this.isFocus = true;
               });
            })
         },
         goBack() {
            uni.navigateBack()
         },
@@ -488,11 +733,8 @@
            })
         },
         editResult(fcheckResu) {
            if (fcheckResu == 'OK') {
               return "改为不合格";
            } else {
               return "改为合格";
            }
            // 统一显示"改为不合格"
            return "改为不合格";
         },
         toDetail(item) {
            this.showPopup = !this.showPopup;
@@ -674,6 +916,14 @@
         this.$nextTick(() => {
            this.validateBatchInput();
         });
      },
      // 页面卸载时清除定时器
      onUnload() {
         if (this.autoSaveTimer) {
            clearTimeout(this.autoSaveTimer);
            this.autoSaveTimer = null;
         }
      }
   }
</script>
@@ -708,17 +958,25 @@
   }
   .tabs {
      display: flex;
      background-color: $bg-color;
      border-bottom: 1px solid $border-color;
      width: 100%;
      white-space: nowrap;
      .tabs-container {
         display: flex;
         min-width: 100%;
      }
      .tab {
         flex: 1;
         flex: none;
         min-width: 120px;
         text-align: center;
         padding: 12px 0;
         padding: 12px 16px;
         border-right: 1px solid $border-color;
         color: #666;
         transition: all 0.3s;
         white-space: nowrap;
         &:last-child {
            border-right: none;
@@ -821,6 +1079,7 @@
            flex: 1;
            padding: 12px;
            font-weight: bold;
            text-align: center;
         }
      }
@@ -837,6 +1096,8 @@
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
         }
      }
   }
@@ -856,6 +1117,16 @@
      &.NG {
         background-color: rgba($danger-color, 0.1);
         color: $danger-color;
      }
      &.pending {
         background-color: rgba(#f39c12, 0.1);
         color: #f39c12;
      }
      &.default {
         background-color: rgba(#909399, 0.1);
         color: #909399;
      }
   }
@@ -950,4 +1221,13 @@
      opacity: 0.6;
      cursor: not-allowed;
   }
   .hole-count {
      color: #409EFF;
      font-weight: bold;
      background-color: rgba(64, 158, 255, 0.1);
      padding: 2px 8px;
      border-radius: 4px;
      display: inline-block;
   }
</style>