xwt
6 天以前 59dc3bbd1fdedab400b56d67a169bcda97dcf75e
pages/QC/RKJ/detail.vue
@@ -67,21 +67,6 @@
    <view class="section">
      <view class="section-header">检验结果</view>
      <view class="section-body">
        <view class="info-grid">
          <view class="info-item">
            <view class="info-label">AC数</view>
            <view class="info-value">{{formData.freQty ? formData.freQty - 1 : 0}}</view>
          </view>
          <view class="info-item">
            <view class="info-label">RE数</view>
            <view class="info-value">{{formData.freQty || 0}}</view>
          </view>
          <view class="info-item">
            <view class="info-label">不合格数</view>
            <view class="info-value">{{formData.unqualified || 0}}</view>
          </view>
        </view>
        <view v-if="formData.result != null" class="result-preview">
          <view class="info-label">预览结果</view>
          <view class="info-value">{{formData.result}}</view>
@@ -98,23 +83,19 @@
    <view class="section">
      <view class="section-header">检验结果录入</view>
      <view class="section-body">
        <view class="inspection-hint">
          <view class="hint-text">没有最大值和最小值时填写0(未通过检验)或1(通过检验)</view>
        </view>
        <!-- 不合格检验项目提示 -->
        <view class="validation-hint" v-if="isUnqualifiedResult()">
        <!-- 验证提示 -->
        <view v-if="isUnqualifiedResult()" class="validation-hint">
          <view class="validation-icon">⚠️</view>
          <view class="validation-text">
            <view class="validation-title">检验项目不合格,必须完成以下操作:</view>
            <view class="validation-requirements">
              <view class="requirement-item" :class="{ 'completed': hasImage(), 'required': !hasImage() }">
                <text class="requirement-icon">{{ hasImage() ? '✅' : '❌' }}</text>
                <text class="requirement-text">上传图片</text>
              <view class="requirement-item" :class="{ 'completed': hasImage() }">
                <view class="requirement-icon">{{ hasImage() ? '✅' : '❌' }}</view>
                <view class="requirement-text">上传图片</view>
              </view>
              <view class="requirement-item" :class="{ 'completed': hasRemarks(), 'required': !hasRemarks() }">
                <text class="requirement-icon">{{ hasRemarks() ? '✅' : '❌' }}</text>
                <text class="requirement-text">填写不良描述</text>
              <view class="requirement-item" :class="{ 'completed': hasRemarks() }">
                <view class="requirement-icon">{{ hasRemarks() ? '✅' : '❌' }}</view>
                <view class="requirement-text">填写不良描述</view>
              </view>
            </view>
          </view>
@@ -124,28 +105,58 @@
          <view class="input-wrapper">
            <input v-if="tableData.length < formData.levelNum"
              v-model="formData.fcheckResu"
              type="number"
              type="text"
              class="result-input"
              placeholder="请输入检验结果"
              placeholder="无上下限时填写OK(合格)或NG(不合格)"
              placeholder-class="placeholder" />
            <button v-if="(tableData.length < formData.levelNum)"
              class="btn primary-btn"
              style="margin: 0px;background-color: #3498db;color:#ffffff ;" class="btn primary-btn"
              @click="submit">保存结果</button>
          </view>
        </view>
      </view>
    </view>
    <!-- 图片预览 -->
    <view v-if="isShowImg" class="section">
      <view class="section-header">
        <view class="section-title">相关图片</view>
        <view class="section-actions">
          <button class="delete-image-btn" @click="deleteImage">删除图片</button>
    <!-- 操作按钮 -->
    <view class="action-buttons">
      <button class="btn upload-btn" @click="saveRemarks">
        <view class="btn-content">
          <uni-icons type="compose" size="16" color="#fff"></uni-icons>
          <view class="btn-text">添加不合格描述</view>
        </view>
      </button>
      <button class="btn attachment-btn" @click="viewAttachmentInfo">
        <view class="btn-content">
          <uni-icons type="folder" size="16" color="#fff"></uni-icons>
          <view class="btn-text">查看检验项目</view>
        </view>
      </button>
      <button class="action-btn success tablet-upload-btn" @click="uploadImage">
        <view class="btn-content">
          <view class="btn-icon">📷</view>
          <view class="btn-text">拍照/上传图片</view>
        </view>
      </button>
    </view>
    <!-- 图片预览 - 支持多图片 -->
    <view v-if="imageList && imageList.length > 0" class="section">
      <view class="section-header">
        <view class="section-title">相关图片 ({{ imageList.length }}张)</view>
      </view>
      <view class="section-body">
        <view class="image-preview" @click="previewImage">
          <image :src="base64Image" mode="aspectFit" class="preview-image"/>
        <view class="image-list">
          <view v-for="(img, index) in imageList" :key="img.id" class="image-item">
            <view class="image-preview" @click="previewMultiImage(index)">
              <image :src="'data:image/jpeg;base64,' + img.base64Data" mode="aspectFill" class="preview-image"/>
            </view>
            <view class="image-actions">
              <button class="delete-image-btn" @click="deleteSingleImage(img)">
                <text class="delete-icon">🗑️</text>
                <text>删除</text>
              </button>
            </view>
          </view>
        </view>
      </view>
    </view>
@@ -170,7 +181,7 @@
              </view>
            </view>
            <view class="td">
              <view class="result-badge" :class="item.fcheckResu === '1' ? 'OK' : 'NG'">
              <view class="result-badge" :class="(item.fcheckResu === 'OK' || item.fcheckResu === '1' || item.fcheckResu == 1) ? 'OK' : 'NG'">
                {{ item.fcheckResu }}
              </view>
            </view>
@@ -181,18 +192,6 @@
          </view>
        </view>
      </view>
    </view>
    <!-- 操作按钮 -->
    <view class="action-buttons">
      <button class="action-btn warning" @click="saveRemarks">添加不合格描述</button>
      <button class="action-btn primary" @click="viewAttachmentInfo">查看检验项目</button>
      <button class="action-btn success tablet-upload-btn" @click="uploadImage">
        <view class="btn-content">
          <view class="btn-icon">📷</view>
          <view class="btn-text">拍照/上传图片</view>
        </view>
      </button>
    </view>
    <!-- 修改检验结果弹出框 -->
@@ -408,6 +407,7 @@
      tableData: [],
      isShowImg: false,
      base64Image: "",
      imageList: [],  // 多图片列表
      remarks: "",
      remarksPopup: false,
     fcheckResu: null,
@@ -441,7 +441,7 @@
    
    // 判断是否已上传图片
    hasImage() {
      return this.formData.imageData && this.formData.imageData.length > 0;
      return this.imageList && this.imageList.length > 0;
    },
    
    // 判断是否已填写不良描述
@@ -456,7 +456,8 @@
    },
    editResult(fcheckResu) {
      if (fcheckResu == '1') {
      // 1 或 OK 都表示合格
      if (fcheckResu == '1' || fcheckResu == 'OK' || fcheckResu == 1) {
        return "改为不合格";
      } else {
        return "改为合格";
@@ -485,22 +486,27 @@
      } else {
        if (!this.formData.fcheckResu) {
          this.formData.fcheckResu = 1
          this.formData.fcheckResu = 'OK';  // 默认合格
        }
        if (this.formData.fcheckResu == 0 || this.formData.fcheckResu == 1) {
          this.formData.isPass = this.formData.fcheckResu
        // 统一使用 OK/NG
        const upperValue = String(this.formData.fcheckResu).toUpperCase();
        if (upperValue === 'OK' || upperValue === 'NG') {
          this.formData.fcheckResu = upperValue;  // 统一转为大写
          if (upperValue === 'NG') {
            fstand = "×";
          }
        } else {
          this.$showMessage("无标准值时,检验结果只能为0或1!");
          this.$showMessage("无上下限时,检验结果只能为OK或NG!");
          return;
        }
        count = count - this.tableData.length;
      }
      // 验证不合格检验项目必须上传图片并填写不良描述
      if (fstand === "×" || this.formData.fcheckResu == 0) {
      if (fstand === "×" || this.formData.fcheckResu === 'NG') {
        // 检查是否已上传图片
        if (!this.formData.imageData || this.formData.imageData.length === 0) {
        if (!this.imageList || this.imageList.length === 0) {
          this.$showMessage("检验项目不合格,必须上传图片!");
          return;
        }
@@ -542,12 +548,26 @@
        this.tableData = res.data.tbBillList.itemXj02s;
        // 处理图片显示状态
        if (this.formData.imageData && this.formData.imageData.length > 0) {
        // 处理多图片列表
        if (this.formData.imageList && this.formData.imageList.length > 0) {
          this.imageList = this.formData.imageList;
          this.isShowImg = true;
          if (this.imageList.length > 0) {
            this.base64Image = 'data:image/jpeg;base64,' + this.imageList[0].base64Data;
          }
        } else if (this.formData.imageData && this.formData.imageData.length > 0) {
          // 向后兼容:如果没有imageList但有imageData
          this.imageList = [{
            id: 0,
            base64Data: this.formData.imageData,
            fileName: '',
            createDate: null,
            createBy: ''
          }];
          this.isShowImg = true;
          this.base64Image = 'data:image/jpeg;base64,' + this.formData.imageData;
        } else {
          // 如果没有图片数据,隐藏图片显示区域
          this.imageList = [];
          this.isShowImg = false;
          this.base64Image = '';
        }
@@ -594,23 +614,26 @@
      } else {
        if (!this.editData.fcheckResu) {
          this.editData.fcheckResu = 1
          this.editData.fcheckResu = 'OK';  // 默认合格
        }
        if (this.editData.fcheckResu == 0 || this.editData.fcheckResu == 1) {
          if (this.editData.fcheckResu == 0) {
        // 统一使用 OK/NG
        const upperValue = String(this.editData.fcheckResu).toUpperCase();
        if (upperValue === 'OK' || upperValue === 'NG') {
          this.editData.fcheckResu = upperValue;  // 统一转为大写
          if (upperValue === 'NG') {
            fstand = "×";
          }
        } else {
          this.$showMessage("无标准值时,检验结果只能为0或1!");
          this.$showMessage("无上下限时,检验结果只能为OK或NG!");
          return;
        }
      }
      // 验证不合格检验项目必须上传图片并填写不良描述
      if (fstand === "×" || this.editData.fcheckResu == 0) {
      if (fstand === "×" || this.editData.fcheckResu === 'NG') {
        // 检查是否已上传图片
        if (!this.formData.imageData || this.formData.imageData.length === 0) {
        if (!this.imageList || this.imageList.length === 0) {
          this.$showMessage("检验项目不合格,必须上传图片!");
          return;
        }
@@ -650,17 +673,18 @@
    numberEdit(item) {
      let fstand = "√";
      let fcheckResu = 1;
      let fcheckResu = 'OK';  // 合格用OK
      if (item.fcheckResu == '1') {
      // 判断当前是否为合格状态(OK 或 1 都表示合格)
      if (item.fcheckResu == '1' || item.fcheckResu == 'OK' || item.fcheckResu == 1) {
        fstand = "×";
        fcheckResu = 0;
        fcheckResu = 'NG';  // 不合格用NG
      }
      // 验证不合格检验项目必须上传图片并填写不良描述
      if (fstand === "×" || fcheckResu == 0) {
      if (fstand === "×" || fcheckResu == 'NG') {
        // 检查是否已上传图片
        if (!this.formData.imageData || this.formData.imageData.length === 0) {
        if (!this.imageList || this.imageList.length === 0) {
          this.$showMessage("检验项目不合格,必须上传图片!");
          return;
        }
@@ -991,20 +1015,34 @@
      });
    },
    
    // 根据选择的来源选择图片
    // 根据选择的来源选择图片(支持多选)
    chooseImageWithSource(sourceType) {
      // 计算还可以上传多少张图片
      const currentCount = this.imageList ? this.imageList.length : 0;
      const maxCount = 9;
      const remainCount = maxCount - currentCount;
      if (remainCount <= 0) {
        uni.showToast({
          title: '最多只能上传' + maxCount + '张图片',
          icon: 'none'
        });
        return;
      }
      uni.chooseImage({
        count: 1, // 最多选择1张图片
        sizeType: ['compressed', 'original'], // 提供压缩和原图选项
        sourceType: sourceType, // 根据用户选择设置来源
        count: sourceType.includes('camera') ? 1 : remainCount, // 拍照只能一张,相册可多选
        sizeType: ['compressed', 'original'],
        sourceType: sourceType,
        success: (res) => {
          const tempFilePath = res.tempFilePaths[0];
          const tempFilePaths = res.tempFilePaths;
          
          // 如果是拍照,显示预览确认
          if (sourceType.includes('camera')) {
            this.showImagePreview(tempFilePath);
            this.showImagePreview(tempFilePaths[0]);
          } else {
            this.uploadImageToServer(tempFilePath);
            // 多图片批量上传
            this.uploadMultipleImages(tempFilePaths);
          }
        },
        fail: (error) => {
@@ -1015,6 +1053,77 @@
          }
          uni.showToast({
            title: errorMessage,
            icon: 'none'
          });
        }
      });
    },
    // 批量上传图片
    uploadMultipleImages(tempFilePaths) {
      if (!tempFilePaths || tempFilePaths.length === 0) {
        return;
      }
      uni.showLoading({
        title: '上传中... (0/' + tempFilePaths.length + ')'
      });
      const uploadUrl = this.$store.state.serverInfo.serverAPI + "/RKJ/UploadImageToPicture";
      let successCount = 0;
      let failCount = 0;
      const total = tempFilePaths.length;
      const uploadPromises = tempFilePaths.map((filePath, index) => {
        return new Promise((resolve) => {
          uni.uploadFile({
            url: uploadUrl,
            filePath: filePath,
            name: 'file',
            formData: {
              id: this.id,
              gid: this.gid,
              billNo: this.billNo,
              createBy: this.$loginInfo.account
            },
            success: (uploadRes) => {
              try {
                const result = JSON.parse(uploadRes.data);
                if (result.status === 0) {
                  successCount++;
                } else {
                  failCount++;
                }
              } catch (error) {
                failCount++;
              }
              uni.showLoading({
                title: '上传中... (' + (successCount + failCount) + '/' + total + ')'
              });
              resolve();
            },
            fail: () => {
              failCount++;
              uni.showLoading({
                title: '上传中... (' + (successCount + failCount) + '/' + total + ')'
              });
              resolve();
            }
          });
        });
      });
      Promise.all(uploadPromises).then(() => {
        uni.hideLoading();
        if (successCount > 0) {
          uni.showToast({
            title: '成功上传' + successCount + '张图片',
            icon: 'success'
          });
          this.refreshResult();
        } else {
          uni.showToast({
            title: '图片上传失败',
            icon: 'none'
          });
        }
@@ -1122,17 +1231,45 @@
      });
    },
    
    // 预览多图片
    previewMultiImage(index) {
      const urls = this.imageList.map(img => 'data:image/jpeg;base64,' + img.base64Data);
      uni.previewImage({
        urls: urls,
        current: index
      });
    },
    // 删除单张图片
    deleteSingleImage(img) {
      uni.showModal({
        title: '确认删除',
        content: '确定要删除这张图片吗?',
        confirmText: '删除',
        cancelText: '取消',
        confirmColor: '#e74c3c',
        success: (res) => {
          if (res.confirm) {
            this.deleteImageFromServer(img.id);
          }
        }
      });
    },
    // 从服务器删除图片
    deleteImageFromServer() {
    deleteImageFromServer(imageId = null) {
      uni.showLoading({
        title: '删除中...'
      });
      
      const data = { id: this.id };
      if (imageId !== null && imageId !== undefined && imageId !== 0) {
        data.imageId = imageId;
      }
      this.$post({
        url: "/RKJ/DeleteImageFromPicture",
        data: {
          id: this.id // 当前检验项目ID
        }
        data: data
      }).then(res => {
        uni.hideLoading();
        if (res.status === 0) {
@@ -1231,6 +1368,56 @@
  }
}
/* 多图片列表样式 */
.image-list {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}
.image-item {
  width: 150px;
  display: flex;
  flex-direction: column;
  align-items: center;
  .image-preview {
    width: 150px;
    height: 150px;
    border: 1px solid $border-color;
    border-radius: 8px;
    overflow: hidden;
    cursor: pointer;
    .preview-image {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  }
  .image-actions {
    margin-top: 8px;
    .delete-image-btn {
      display: flex;
      align-items: center;
      gap: 4px;
      padding: 6px 12px;
      background-color: $danger-color;
      color: #fff;
      border: none;
      border-radius: 4px;
      font-size: 12px;
      cursor: pointer;
      .delete-icon {
        font-size: 14px;
      }
    }
  }
}
.info-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
@@ -1250,18 +1437,6 @@
      font-weight: 500;
    }
  }
}
.inspection-hint {
  background-color: #fff3cd;
  padding: 10px;
  border-radius: 4px;
  margin-bottom: 15px;
}
.hint-text {
  font-size: 12px;
  color: #856404;
}
/* 验证提示样式 */
@@ -1305,19 +1480,16 @@
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  padding: 8px 12px;
  background: rgba(255, 255, 255, 0.7);
  border-radius: 6px;
  border: 1px solid rgba(255, 107, 107, 0.3);
  transition: all 0.3s ease;
}
.requirement-item.completed {
  background-color: #d4edda;
  border: 1px solid #c3e6cb;
}
.requirement-item.required {
  background-color: #f8d7da;
  border: 1px solid #f5c6cb;
  background: rgba(46, 204, 113, 0.1);
  border-color: rgba(46, 204, 113, 0.3);
}
.requirement-icon {
@@ -1327,26 +1499,30 @@
.requirement-text {
  font-size: 13px;
  font-weight: 500;
  color: #2c3e50;
  font-weight: 500;
}
.requirement-item.completed .requirement-text {
  color: #27ae60;
}
.input-group {
  margin: 16px 0;
}
  .input-wrapper {
    display: flex;
    gap: 12px;
.input-wrapper {
  display: flex;
  gap: 12px;
}
    .result-input {
      flex: 1;
      height: 45px;
      padding: 0 12px;
      border: 1px solid $border-color;
      border-radius: 4px;
      font-size: 14px;
    }
  }
.result-input {
  flex: 1;
  height: 45px;
  padding: 0 12px;
  border: 1px solid $border-color;
  border-radius: 4px;
  font-size: 14px;
}
.btn {
@@ -1490,39 +1666,91 @@
}
.action-buttons {
  margin-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  justify-content: flex-end;
  gap: 12px;
  margin-bottom: 20px;
}
.action-btn {
  padding: 12px 20px;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
  padding: 8px 20px;
  border-radius: 4px;
  &.primary {
  &.primary-btn {
    background-color: $primary-color;
    color: white;
    color: #fff;
  }
  &.secondary {
  &.cancel-btn {
    background-color: #95a5a6;
    color: white;
  }
  &.warning {
    background-color: $warning-color;
    color: white;
    color: #fff;
  }
  &.success {
    background-color: $success-color;
    color: white;
    color: #fff;
  }
}
.btn {
  padding: 8px 20px;
  border-radius: 4px;
  border: none;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s;
  &.upload-btn {
    background-color: #909399;
    color: #fff;
    border: none;
    border-radius: 8px;
    padding: 12px 20px;
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    min-height: 48px;
    white-space: nowrap;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    cursor: pointer;
    transition: all 0.3s ease;
    box-shadow: 0 4px 12px rgba(144, 147, 153, 0.3);
  }
  &.upload-btn:hover {
    background-color: #7a7d82;
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(144, 147, 153, 0.4);
  }
  &.attachment-btn {
    background-color: #17a2b8;
    color: #fff;
    border: none;
    border-radius: 8px;
    padding: 12px 20px;
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    min-height: 48px;
    white-space: nowrap;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    cursor: pointer;
    transition: all 0.3s ease;
    box-shadow: 0 4px 12px rgba(23, 162, 184, 0.3);
  }
  &.attachment-btn:hover {
    background-color: #138496;
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(23, 162, 184, 0.4);
  }
}
@@ -1550,42 +1778,43 @@
/* 平板上传按钮样式 */
.tablet-upload-btn {
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
  box-shadow: 0 4px 15px rgba(46, 204, 113, 0.3);
  border: none;
  border-radius: 8px;
  padding: 12px 20px;
  color: white;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.3s ease;
  box-shadow: 0 4px 12px rgba(46, 204, 113, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  margin: 0;
}
.tablet-upload-btn:hover {
  background: linear-gradient(135deg, #27ae60 0%, #229954 100%);
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(46, 204, 113, 0.4);
}
.tablet-upload-btn:active {
  transform: translateY(0);
  box-shadow: 0 2px 10px rgba(46, 204, 113, 0.3);
  box-shadow: 0 6px 16px rgba(46, 204, 113, 0.4);
}
.btn-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px;
}
.btn-icon {
  font-size: 24px;
  line-height: 1;
  font-size: 18px;
}
.btn-text {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
}
.overlay {
@@ -2103,88 +2332,8 @@
  }
}
/* 平板优化样式 */
@media (min-width: 768px) and (max-width: 1024px) {
  .action-buttons {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 15px;
    margin-bottom: 30px;
  }
  .action-btn {
    padding: 20px 16px;
    font-size: 16px;
    min-height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .tablet-upload-btn {
    min-height: 100px;
  }
  .btn-content {
    gap: 12px;
    padding: 12px;
  }
  .btn-icon {
    font-size: 32px;
  }
  .btn-text {
    font-size: 16px;
  }
  .info-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 20px;
  }
  .section-body {
    padding: 30px;
  }
}
/* 大屏平板优化 */
@media (min-width: 1024px) {
  .action-buttons {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    margin-bottom: 40px;
  }
  .action-btn {
    padding: 25px 20px;
    font-size: 18px;
    min-height: 100px;
  }
  .tablet-upload-btn {
    min-height: 120px;
  }
  .btn-content {
    gap: 15px;
    padding: 15px;
  }
  .btn-icon {
    font-size: 36px;
  }
  .btn-text {
    font-size: 18px;
  }
  .info-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 25px;
  }
/* 响应式设计 */
@media (min-width: 768px) {
  .section-body {
    padding: 40px;
  }