zhangwencui
2026-06-30 7a0790d8224db45a039bf33d0ef4e24ae879a243
src/pages/inspectionUpload/components/formDia.vue
@@ -1,256 +1,398 @@
<template>
  <u-popup
    v-model="dialogVisitable"
    mode="center"
    :round="10"
    :closeable="true"
    @close="cancel"
  >
  <u-popup v-model="dialogVisitable"
           mode="center"
           :round="10"
           :closeable="true"
           @close="cancel">
    <view class="popup-content">
      <view class="popup-header">
        <text class="popup-title">上传</text>
        <text class="popup-title">巡检记录上传</text>
      </view>
      <view class="upload-container">
        <!-- 异常状态选择 -->
        <view class="form-container">
          <view class="title">生产前</view>
          <u-upload
            :fileList="beforeModelValue"
            @afterRead="afterRead"
            @delete="deleteFile"
            name="before"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :previewFullImage="true"
          ></u-upload>
          <view class="title">巡检状态</view>
          <view class="exception-section">
            <view class="exception-options">
              <view class="exception-option"
                    :class="{ active: hasException === false }"
                    @click="setExceptionStatus(false)">
                <u-icon name="checkmark-circle"
                        size="20"
                        color="#52c41a"></u-icon>
                <text class="option-text">正常</text>
              </view>
              <view class="exception-option"
                    :class="{ active: hasException === true }"
                    @click="setExceptionStatus(true)">
                <u-icon name="close-circle"
                        size="20"
                        color="#ff4d4f"></u-icon>
                <text class="option-text">存在异常</text>
              </view>
            </view>
          </view>
        </view>
        <view class="form-container">
          <view class="title">生产后</view>
          <u-upload
            :fileList="afterModelValue"
            @afterRead="afterRead"
            @delete="deleteFile"
            name="after"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :previewFullImage="true"
          ></u-upload>
        <!-- 异常描述(仅在异常时显示) -->
        <view class="form-container"
              v-if="hasException === true">
          <view class="title">异常描述</view>
          <u-input v-model="exceptionDescription"
                   type="textarea"
                   :maxlength="500"
                   placeholder="请描述异常情况..."
                   :customStyle="{ padding: '10px', backgroundColor: '#f5f5f5' }" />
        </view>
        <view class="form-container">
          <view class="title">生产问题</view>
          <u-upload
            :fileList="issueModelValue"
            @afterRead="afterRead"
            @delete="deleteFile"
            name="issue"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :previewFullImage="true"
          ></u-upload>
        <!-- 上传区域(仅在异常时显示) -->
        <template v-if="hasException === true">
          <view class="form-container">
            <view class="title">巡检照片</view>
            <u-upload :fileList="beforeModelValue"
                      @afterRead="afterRead"
                      @delete="deleteFile"
                      name="before"
                      multiple
                      :maxCount="10"
                      :maxSize="5 * 1024 * 1024"
                      accept="image/*"
                      :previewFullImage="true"></u-upload>
          </view>
        </template>
        <!-- 正常状态提示 -->
        <view class="form-container normal-tip"
              v-if="hasException === false">
          <u-icon name="info-circle"
                  size="40"
                  color="#52c41a"></u-icon>
          <text class="tip-text">设备运行正常,无需上传照片</text>
        </view>
      </view>
      <view class="popup-footer">
        <u-button @click="cancel" :customStyle="{ marginRight: '10px' }">取消</u-button>
        <u-button type="primary" @click="submitForm">保存</u-button>
        <u-button @click="cancel"
                  :customStyle="{ marginRight: '10px' }">取消</u-button>
        <u-button type="primary"
                  @click="submitForm">保存</u-button>
      </view>
    </view>
  </u-popup>
</template>
<script setup>
import { ref } from 'vue'
import { submitInspectionRecord } from '@/api/equipmentManagement/inspection.js'
  import { ref, computed } from "vue";
  import { submitInspectionRecord } from "@/api/equipmentManagement/inspection.js";
  import { getToken } from "@/utils/auth";
  import config from "@/config";
const emit = defineEmits(['closeDia'])
  const emit = defineEmits(["closeDia"]);
const dialogVisitable = ref(false)
const beforeModelValue = ref([])
const afterModelValue = ref([])
const issueModelValue = ref([])
const infoData = ref(null)
  const dialogVisitable = ref(false);
  const beforeModelValue = ref([]);
  const infoData = ref(null);
// 文件上传处理
const afterRead = (event) => {
  const { name, file } = event
  // 上传文件到服务器
  uni.uploadFile({
    url: '/api/upload', // 替换为实际的上传接口
    filePath: file.url,
    name: 'file',
    success: (res) => {
      const data = JSON.parse(res.data)
      if (data.code === 200) {
        const fileItem = {
          url: data.data.url,
          name: file.name,
          status: 'success'
  // 异常状态:null=未选择, false=正常, true=异常
  const hasException = ref(null);
  // 异常描述
  const exceptionDescription = ref("");
  // 计算上传URL
  const uploadFileUrl = computed(() => {
    let baseUrl = "";
    if (process.env.VUE_APP_BASE_API) {
      baseUrl = process.env.VUE_APP_BASE_API;
    } else {
      baseUrl = config.baseUrl;
    }
    return baseUrl + "/file/upload";
  });
  const uploadSingleFile = async (fileItem, typeValue) => {
    const token = getToken();
    if (!token) throw new Error("用户未登录");
    // H5: u-upload 可能给原生 File(fileItem.file)
    const rawFile = fileItem?.file;
    if (rawFile) {
      const formData = new FormData();
      formData.append("file", rawFile, rawFile.name || "image.jpg");
      formData.append("type", String(typeValue));
      const res = await fetch(uploadFileUrl.value, {
        method: "POST",
        headers: { Authorization: "Bearer " + token },
        body: formData,
      });
      const data = await res.json();
      if (data?.code !== 200) throw new Error(data?.msg || "上传失败");
      return {
        url: data?.data?.url,
        name: rawFile.name || "image.jpg",
        status: "success",
      };
    }
    // 非 H5 / 兼容:走 uni.uploadFile
    return await new Promise((resolve, reject) => {
      uni.uploadFile({
        url: uploadFileUrl.value,
        filePath: fileItem.url,
        name: "file",
        header: {
          Authorization: `Bearer ${token}`,
        },
        formData: {
          type: typeValue,
        },
        success: res => {
          try {
            const data = JSON.parse(res.data);
            if (data.code === 200) {
              resolve({
                url: data.data.url,
                name: fileItem.name,
                status: "success",
              });
            } else {
              reject(new Error(data.msg || "上传失败"));
            }
          } catch (e) {
            reject(e);
          }
        },
        fail: err => reject(err),
      });
    });
  };
  // 文件上传处理
  const afterRead = event => {
    const { file } = event;
    // 仅保留生产前(typeValue=10)
    let typeValue = 10;
    const files = Array.isArray(file) ? file : [file];
    Promise.resolve()
      .then(async () => {
        for (const f of files) {
          const uploaded = await uploadSingleFile(f, typeValue);
          beforeModelValue.value.push(uploaded);
        }
        // 根据name添加到对应的数组
        if (name === 'before') {
          beforeModelValue.value.push(fileItem)
        } else if (name === 'after') {
          afterModelValue.value.push(fileItem)
        } else if (name === 'issue') {
          issueModelValue.value.push(fileItem)
        }
        uni.showToast({
          title: '上传成功',
          icon: 'success'
        })
      } else {
        uni.showToast({
          title: '上传失败',
          icon: 'error'
        })
      }
    },
    fail: () => {
      uni.showToast({
        title: '上传失败',
        icon: 'error'
        uni.showToast({ title: "上传成功", icon: "success" });
      })
      .catch(err => {
        console.error("上传失败:", err);
        uni.showToast({ title: "上传失败", icon: "error" });
      });
  };
  // 删除文件
  const deleteFile = event => {
    const { index } = event;
    beforeModelValue.value.splice(index, 1);
  };
  // 设置异常状态
  const setExceptionStatus = status => {
    hasException.value = status;
  };
  // 提交表单
  const submitForm = async () => {
    try {
      // 检查是否选择了巡检状态
      if (hasException.value === null) {
        uni.showToast({
          title: "请选择巡检状态",
          icon: "none",
        });
        return;
      }
      // 如果是异常状态,检查是否有上传文件
      if (hasException.value === true) {
        if (beforeModelValue.value.length === 0) {
          uni.showToast({
            title: "请上传异常照片",
            icon: "none",
          });
          return;
        }
        // 检查是否填写了异常描述
        if (!exceptionDescription.value.trim()) {
          uni.showToast({
            title: "请填写异常描述",
            icon: "none",
          });
          return;
        }
      }
      let arr = [];
      if (beforeModelValue.value.length > 0) {
        arr.push(
          ...beforeModelValue.value.map(item => ({ ...item, statusType: 0 }))
        );
      }
      // 提交数据
      infoData.value.storageBlobDTO = arr;
      infoData.value.hasException = hasException.value;
      infoData.value.exceptionDescription = exceptionDescription.value;
      await submitInspectionRecord({ ...infoData.value });
      uni.showToast({
        title: "提交成功",
        icon: "success",
      });
      cancel();
    } catch (error) {
      console.error("提交失败:", error);
      uni.showToast({
        title: "提交失败",
        icon: "error",
      });
    }
  })
}
  };
// 删除文件
const deleteFile = (event) => {
  const { name, index } = event
  if (name === 'before') {
    beforeModelValue.value.splice(index, 1)
  } else if (name === 'after') {
    afterModelValue.value.splice(index, 1)
  } else if (name === 'issue') {
    issueModelValue.value.splice(index, 1)
  }
}
  // 打开弹框
  const openDialog = async row => {
    infoData.value = row;
    dialogVisitable.value = true;
// 提交表单
const submitForm = async () => {
  try {
    let arr = []
    if (beforeModelValue.value.length > 0) {
      arr.push(...beforeModelValue.value.map(item => ({ ...item, statusType: 0 })))
    }
    if (afterModelValue.value.length > 0) {
      arr.push(...afterModelValue.value.map(item => ({ ...item, statusType: 1 })))
    }
    if (issueModelValue.value.length > 0) {
      arr.push(...issueModelValue.value.map(item => ({ ...item, statusType: 2 })))
    }
    // 提交数据
    infoData.value.storageBlobDTO = arr
    await submitInspectionRecord({ ...infoData.value })
    uni.showToast({
      title: '提交成功',
      icon: 'success'
    })
    cancel()
  } catch (error) {
    console.error('提交失败:', error)
    uni.showToast({
      title: '提交失败',
      icon: 'error'
    })
  }
}
    // 清空之前的数据
    beforeModelValue.value = [];
    hasException.value = null;
    exceptionDescription.value = "";
  };
// 打开弹框
const openDialog = async (row) => {
  infoData.value = row
  dialogVisitable.value = true
  // 清空之前的数据
  beforeModelValue.value = []
  afterModelValue.value = []
  issueModelValue.value = []
}
  // 关闭弹框
  const cancel = () => {
    dialogVisitable.value = false;
    emit("closeDia");
  };
// 关闭弹框
const cancel = () => {
  dialogVisitable.value = false
  emit('closeDia')
}
defineExpose({ openDialog })
  defineExpose({ openDialog });
</script>
<style scoped lang="scss">
.popup-content {
  width: 90vw;
  max-width: 400px;
  background-color: #fff;
  border-radius: 10px;
  overflow: hidden;
}
.popup-header {
  padding: 20px 20px 10px;
  text-align: center;
  border-bottom: 1px solid #f0f0f0;
}
.popup-title {
  font-size: 18px;
  font-weight: 600;
  color: #333;
}
.upload-container {
  padding: 20px;
  max-height: 60vh;
  overflow-y: auto;
}
.form-container {
  margin-bottom: 20px;
  &:last-child {
    margin-bottom: 0;
  .popup-content {
    width: 90vw;
    max-width: 400px;
    background-color: #fff;
    border-radius: 10px;
    overflow: hidden;
  }
}
.title {
  font-size: 14px;
  color: #1890ff;
  line-height: 20px;
  font-weight: 600;
  padding-left: 10px;
  position: relative;
  margin: 6px 0 10px;
  &::before {
    content: "";
    position: absolute;
    left: 0;
    top: 3px;
    width: 4px;
    height: 14px;
    background-color: #1890ff;
  .popup-header {
    padding: 20px 20px 10px;
    text-align: center;
    border-bottom: 1px solid #f0f0f0;
  }
}
.popup-footer {
  display: flex;
  justify-content: center;
  padding: 15px 20px;
  border-top: 1px solid #f0f0f0;
  background-color: #fafafa;
}
  .popup-title {
    font-size: 18px;
    font-weight: 600;
    color: #333;
  }
  .upload-container {
    padding: 20px;
    max-height: 60vh;
    overflow-y: auto;
  }
  .form-container {
    margin-bottom: 20px;
    &:last-child {
      margin-bottom: 0;
    }
  }
  .title {
    font-size: 14px;
    color: #1890ff;
    line-height: 20px;
    font-weight: 600;
    padding-left: 10px;
    position: relative;
    margin: 6px 0 10px;
    &::before {
      content: "";
      position: absolute;
      left: 0;
      top: 3px;
      width: 4px;
      height: 14px;
      background-color: #1890ff;
    }
  }
  .popup-footer {
    display: flex;
    justify-content: center;
    padding: 15px 20px;
    border-top: 1px solid #f0f0f0;
    background-color: #fafafa;
  }
  // 异常状态选择样式
  .exception-section {
    padding: 10px 0;
  }
  .exception-options {
    display: flex;
    gap: 15px;
  }
  .exception-option {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 15px 20px;
    border: 2px solid #e0e0e0;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.3s;
    background-color: #fff;
    &.active {
      border-color: #1890ff;
      background-color: #e6f7ff;
    }
    &:active {
      opacity: 0.8;
    }
  }
  .option-text {
    font-size: 14px;
    color: #333;
    font-weight: 500;
  }
  // 正常状态提示样式
  .normal-tip {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 40px 20px;
    background-color: #f6ffed;
    border: 1px dashed #b7eb8f;
    border-radius: 8px;
    .tip-text {
      margin-top: 15px;
      font-size: 14px;
      color: #52c41a;
    }
  }
</style>