表单多文件上传和其他参数处理

javascript 复制代码
 rejectQualityInspection: async (data: FormData) => {
    return await request.put({
      url: `/mission/quality-inspection/reject`,
      data,
      headers: { 'Content-Type': 'multipart/form-data' }
    })
  },

上传文件和其他参数接口

下面是表单和多个文件上传页面

javascript 复制代码
<template>
  <ContentWrap>
    <el-row :gutter="20">
      <el-col class="section-header">
        <div class="section-title">基础信息</div>
        <el-button @click="goBack" plain>返回</el-button>
      </el-col>
    </el-row>
    <div class="underline"></div>
    <el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px" v-loading="formLoading"
      :disabled="disabled">
      <div>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="客户名称" prop="customerName">
              <el-input readonly v-model="formData.customerName" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="产品名称" prop="productName">
              <el-input readonly v-model="formData.productName" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="产品图号" prop="productDrawNo">
              <el-input readonly v-model="formData.productDrawNo" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="合格数量" prop="passQuantity">
              <el-input-number v-model="formData.passQuantity" :min="0" :max="planQty - 1" style="width: 100%"
                @change="updateFailQuantity()" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="不合格数量" prop="failQuantity">
              <el-input-number v-model="formData.failQuantity" :min="1" :max="planQty" style="width: 100%"
                @change="updatePassQuantity()" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="不合格类型" prop="failType">
              <el-select multiple v-model="formData.failType" placeholder="请选择不合格类型">
                <el-option label="尺寸不合格" :value="1" />
                <el-option label="外观不合格" :value="2" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="责任部门" prop="responsibleDeptName">
              <el-input readonly v-model="formData.responsibleDeptName" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item v-if="formData.responsibleDeptName === '外协部'" label="责任人" prop="responsiblePerson">
              <el-select disabled="true" v-model="formData.responsiblePerson" placeholder="请选择外协单位" filterable
                clearable>
                <el-option v-for="item in outSourceList" :key="item.id" :label="item.companyName" :value="item.id" />
              </el-select>
            </el-form-item>

            <el-form-item v-else label="责任人" prop="responsiblePerson">
              <el-select disabled="true" v-model="formData.responsiblePerson" placeholder="请选择责任人" filterable clearable>
                <el-option v-for="person in userList" :key="person.id" :label="person.nickname" :value="person.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="任务单编号" prop="mainTaskOrder">
              <el-input readonly v-model="formData.mainTaskOrder" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col>
            <el-form-item label="不合格内容" prop="failDescription">
              <el-input v-model="formData.failDescription" type="textarea" :rows="3" maxlength="1000" show-word-limit />
            </el-form-item>
          </el-col>
        </el-row>
      </div>
    </el-form>

    <div class="file-box">
      <div class="file-item" v-for="(item, index) in fileList" :key="index">
        <span class="file-name"> {{ item.name }}</span>

        <span class="iconfont icon-shanchu" @click="deleteFile(index)"> </span>
      </div>

    </div>
    <input ref="fileInput" type="file" class="file-input" accept=".png,.jpg,.jpeg" @change="handleFileChange"
      multiple />

    <el-form-item>
      <div style="
          width: calc(100% - 120px);
          margin-left: 120px;
          display: flex;
          justify-content: center;
          align-items: center;
          gap: 12px;
        ">
        <el-button @click="triggerFileInput">上传附件</el-button>
        <el-button type="primary" @click="submitForm" :loading="formLoading">保存</el-button>
        <el-button @click="goBack">取消</el-button>
      </div>
    </el-form-item>
  </ContentWrap>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import type { FormRules } from 'element-plus'
import { FailReviewApi, FailReview } from '@/api/mission/failreview'
import { DeptVO, getSimpleDeptList } from '@/api/system/dept'
import { QualityInspectionApi } from '@/api/mission/qualityinspection'
import { UserVO, getSimpleUserList } from '@/api/system/user'
import { OutsourcedUnitApi, OutsourcedUnit } from '@/api/foundation/outsourcedunit'
import axios from "axios";
defineOptions({ name: 'FailReviewDetail' })
const route = useRoute()
const router = useRouter()
const message = useMessage()
const formLoading = ref(false)
const disabled = ref(false)
const editable = ref(false)
const formData = reactive<Partial<FailReview>>({})
const formRules = reactive<FormRules>({
  customerName: [{ required: true, message: '', trigger: 'blur' }],
  productName: [{ required: true, message: '', trigger: 'blur' }],
  productDrawNo: [{ required: true, message: '', trigger: 'blur' }],
  passQuantity: [{ type: 'number' as any, required: true, message: '', trigger: 'blur' }],
  failQuantity: [{ type: 'number' as any, required: true, message: '', trigger: 'blur' }],
  failType: [{ required: true, message: '', trigger: 'change' }],
  responsibleDept: [{ required: true, message: '', trigger: 'blur' }],
  responsiblePerson: [{ required: true, message: '', trigger: 'blur' }],
  mainTaskOrder: [{ required: true, message: '', trigger: 'blur' }],
  failDescription: [{ required: true, min: 0, message: '', trigger: 'blur' }]
})
const deptList = ref<DeptVO[]>([])
const userList = ref<UserVO[]>([])
const outSourceList = ref<OutsourcedUnit[]>([])
const fileInput = ref(null);

const triggerFileInput = (code) => {
  fileInput.value?.click();
};

const fileList = ref([]);


const deleteFile = (index) => {
  fileList.value.splice(index, 1);
};
// 选择文件
const handleFileChange = async (e) => {
  const files = e.target.files;
  // 没有选择文件则直接返回
  if (!files || files.length === 0) return;
  // 追加多个文件
  for (let i = 0; i < files.length; i++) {
    fileList.value.push(files[i]);
  }
  e.target.value = "";
};

const planQty = ref(1)
const updatePassQuantity = () => {
  formData.passQuantity = planQty.value - (formData.failQuantity || 1)
}
const updateFailQuantity = () => {
  formData.failQuantity = planQty.value - formData.passQuantity
}
// 获取 详情
const loadDetail = async (type: number) => {
  console.log('type', type)
  const id = Number(route.query.id)
  planQty.value = Number(route.query.planQty)


  if (!id) {
    message.error('缺少详情ID')
    return
  }
  formLoading.value = true
  try {
    let resp = {}
    if (type == 1) {
      resp = await FailReviewApi.getFillFailReview(id)
    } else {
      resp = await FailReviewApi.getFailReviewById(id)
    }
    deptList.value = await getSimpleDeptList()
    Object.assign(formData, resp)
    formData.responsibleDeptName = deptList.value.find(s => s.id == formData.responsibleDept)?.name
    updatePassQuantity()
    outSourceList.value = await OutsourcedUnitApi.getAllOutsourcedUnit(0)
    userList.value = await getSimpleUserList()
    if (formData.failType == undefined) {
      return
    }
    if (formData.failType == 3) {
      formData.failType = [1, 2]
    } else {
      formData.failType = [formData.failType]
    }


  } catch (e) {
    message.error('加载详情失败')
  } finally {
    formLoading.value = false
  }
}

const formRef = ref()
const init = () => {
  console.log('init')
  var type = String(route.query.type)
  if (type == 'detail') {
    disabled.value = true
    editable.value = false
    loadDetail(0)
  } else if (type == 'create') {
    disabled.value = false
    editable.value = true
    loadDetail(1)
  } else if (type == 'edit') {
    disabled.value = false
    editable.value = true
    loadDetail(0)
  }
}
const goBack = () => router.back()
const submitForm = async () => {
  formLoading.value = true;
  formRef.value.validate(async (valid: boolean) => {
    if (valid) {
      if (Array.isArray(formData.failType)) {
        if (formData.failType.includes(1) && formData.failType.includes(2)) {
          formData.failType = 3
        } else {
          formData.failType = formData.failType[0] || formData.failType
        }
      }
      try {
        // 上传文件
        const fileFromData = new FormData();
        fileList.value.forEach(file => {
          fileFromData.append('files', file);
        });
        // 其他参数追加到fileFromData处理null和undefined
        for (let key in formData) {
          if (formData[key] !== null && formData[key] !== undefined) {
            fileFromData.append(key, formData[key])
          }
        }
        await QualityInspectionApi.rejectQualityInspection(fileFromData)
        message.success('保存成功')
        goBack()
      } catch (e) {
        message.error('保存失败')
      }
      formLoading.value = false;
    }
  })
}

const handleCheckedCitiesChange = async () => { }
onMounted(() => {
  init()
})
</script>

<style scoped lang="scss">
.file-box {
  padding: 0 120px;

  .file-item {
    font-size: 14px;
    line-height: 30px;
    display: flex;

    .file-name {
      width: 200px;
      // 超出显示省略号
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      color: #707070;
    }

    .icon-shanchu {
      color: red;
      cursor: pointer;
      font-size: 12px;
      font-weight: 100;
    }
  }
}

.file-input {
  display: none;
}

.el-form .el-form-item {
  margin-bottom: 12px;
}

.section-header {
  border-radius: 4px;
  background: #fff;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.section-title {
  padding: 12px 16px;
  font-size: 16px;
  font-weight: 600;
  color: #333;
}

.underline {
  width: 100%;
  border-bottom: 1px solid #ccc;
  margin-bottom: 12px;
}

.signArea {
  width: 100%;
  height: 70%;
  text-align: center;
  display: block;
}
</style>
相关推荐
why技术2 小时前
我拿到了腾讯QClaw的内测码,然后沉默了。
前端·后端
小一梦2 小时前
宝塔面板单域名部署多个 Vue 项目:从路径冲突到完美共存
服务器·javascript·vue.js
谪星·阿凯3 小时前
XSS漏洞解析博客
前端·web安全·xss
ole ' ola3 小时前
lambda表达式
java·前端·jvm
wefly20173 小时前
无需安装、开箱即用!m3u8live.cn 在线 HLS 播放器,调试直播流效率翻倍
前端·后端·python·前端开发工具·后端开发工具
UXbot3 小时前
为什么 AI 正在重新定义 UI 设计工具的入门门槛
前端·人工智能·低代码·ui·交互·ai编程·ux
柳杉3 小时前
两款惊艳的 WebGL 开源项目推荐
前端·javascript·数据可视化
给算法爸爸上香3 小时前
web网页显示点云
前端·3d·web·点云
IT_陈寒3 小时前
React组件性能翻倍的5个冷门技巧,90%的开发者不知道!
前端·人工智能·后端