yyb
15 小时以前 fd85c03902343231574fdbe7f3fcdd388593a5ff
src/views/qualityManagement/nonconformingManagement/components/formDia.vue
@@ -2,20 +2,12 @@
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增原材料检验' : '编辑原材料检验'"
        :title="operationType === 'add' ? '新增不合格管理' : '编辑不合格管理'"
        width="70%"
        @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="状态:" prop="inspectState">
              <el-select v-model="form.inspectState">
                <el-option label="待处理" :value="0" />
                <el-option label="已处理" :value="1" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="类别:" prop="inspectType">
              <el-select v-model="form.inspectType">
@@ -43,7 +35,10 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="model">
              <el-input v-model="form.model" placeholder="请输入" clearable/>
              <el-select v-model="form.model" placeholder="请选择" clearable :disabled="operationType === 'edit'"
                          filterable readonly @change="handleChangeModel">
              <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
            </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -55,14 +50,16 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:" prop="quantity">
              <el-input v-model="form.quantity" placeholder="请输入" clearable/>
              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请输入" clearable :precision="2"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-input v-model="form.checkName" placeholder="请输入" clearable/>
              <el-select v-model="form.checkName" placeholder="请选择" clearable style="width: 100%">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -87,14 +84,18 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="处理结果:" prop="dealResult">
              <el-input v-model="form.dealResult" placeholder="请输入" clearable/>
              <el-select v-model="form.dealResult" placeholder="请选择" clearable>
                <el-option :label="item.label" :value="item.value" v-for="item in rejection_handling" :key="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="处理人:" prop="dealName">
              <el-input v-model="form.dealName" placeholder="请输入" clearable/>
              <el-select v-model="form.dealName" placeholder="请选择" clearable style="width: 100%">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -111,6 +112,82 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="工时损失:" prop="lossWorking">
              <el-input-number
                  v-model="form.lossWorking"
                  :min="0"
                  :step="0.01"
                  :precision="2"
                  style="width: 100%"
                  placeholder="请输入"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="材料费损失:" prop="lossMaterial">
              <el-input-number
                  v-model="form.lossMaterial"
                  :min="0"
                  :step="0.01"
                  :precision="2"
                  style="width: 100%"
                  placeholder="请输入"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="原因分析:" prop="reasonAnalysis">
              <el-input
                  v-model="form.reasonAnalysis"
                  type="textarea"
                  :rows="4"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="预防与纠正措施:" prop="preventiveCorrective">
              <el-input
                  v-model="form.preventiveCorrective"
                  type="textarea"
                  :rows="4"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="不良品照片:" prop="defectivePhotos">
              <el-upload
                v-model:file-list="defectivePhotoFileList"
                :action="upload.url"
                multiple
                ref="defectivePhotoUploadRef"
                auto-upload
                accept="image/*"
                :headers="upload.headers"
                :before-upload="handleBeforeUpload"
                :on-error="handleUploadError"
                :on-success="handleDefectivePhotoUploadSuccess"
                :on-remove="handleDefectivePhotoRemove"
              >
                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                <template #tip v-if="operationType !== 'view'">
                  <div class="el-upload__tip">
                    文件格式支持 doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                  </div>
                </template>
              </el-upload>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
@@ -123,18 +200,29 @@
</template>
<script setup>
import {ref} from "vue";
import {productTreeList} from "@/api/basicData/product.js";
import { ref, reactive, toRefs, getCurrentInstance, watch } from "vue";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import {
  getQualityUnqualifiedInfo,
  qualityUnqualifiedAdd,
  qualityUnqualifiedUpdate
} from "@/api/qualityManagement/nonconformingManagement.js";
import {userListNoPage} from "@/api/system/user.js";
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth";
const { proxy } = getCurrentInstance()
const userStore = useUserStore()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('')
const { rejection_handling } = proxy.useDict("rejection_handling")
const defectivePhotoFileList = ref([]);
const defectivePhotoUploadRef = ref(null);
const upload = reactive({
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  headers: { Authorization: "Bearer " + getToken() },
});
const data = reactive({
  form: {
    checkTime: "",
@@ -147,35 +235,84 @@
    quantity: "",
    checkCompany: "",
    checkResult: "",
    inspectState: '',
    inspectType: '',
    defectivePhenomena: '',
    defectivePhotos: '',
    tempFileIds: [],
    dealResult: '',
    dealName: '',
    dealTime: '',
    reasonAnalysis: '',
    preventiveCorrective: '',
    lossWorking: 0,
    lossMaterial: 0,
  },
  rules: {
    checkTime: [{ required: false, message: "请输入", trigger: "blur" },],
    process: [{ required: true, message: "请输入", trigger: "blur" }],
    checkName: [{ required: false, message: "请输入", trigger: "blur" }],
    checkName: [{ required: true, message: "请选择检验员", trigger: "change" }],
    productId: [{ required: true, message: "请输入", trigger: "blur" }],
    model: [{ required: false, message: "请输入", trigger: "blur" }],
    model: [{ required: true, message: "请输入", trigger: "blur" }],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    dealName: [{ required: true, message: "请选择处理人", trigger: "change" }],
  },
});
const { form, rules } = toRefs(data);
const productOptions = ref([]);
const modelOptions = ref([]);
const userList = ref([]); // 检验员/处理人下拉列表
// 打开弹框
const openDialog = (type, row) => {
const openDialog = async (type, row) => {
  operationType.value = type;
  try {
    const userRes = await userListNoPage();
    userList.value = userRes.data || [];
  } catch (e) {
    console.error("加载用户列表失败", e);
    userList.value = [];
  }
  dialogFormVisible.value = true;
  form.value = {}
  if (operationType.value === 'add') {
    defectivePhotoFileList.value = [];
    form.value = {
      checkName: userStore.nickName || '',
      dealName: '',
      dealTime: '',
      dealResult: '',
      defectivePhenomena: '',
      defectivePhotos: '',
      tempFileIds: [],
      inspectType: '',
      checkTime: '',
      productId: '',
      model: '',
      unit: '',
      quantity: '',
      productName: '',
      reasonAnalysis: '',
      preventiveCorrective: '',
      lossWorking: 0,
      lossMaterial: 0,
    };
  } else {
    defectivePhotoFileList.value = [];
    form.value = {};
  }
  getProductOptions();
  if (operationType.value === 'edit') {
    getQualityUnqualifiedInfo(row.id).then(res => {
      form.value = {...res.data}
      const { inspectState, ...rest } = (res.data || {})
      form.value = {
        reasonAnalysis: '',
        preventiveCorrective: '',
        lossWorking: 0,
        lossMaterial: 0,
        ...rest
      }
    })
  }
}
@@ -186,6 +323,9 @@
};
const getModels = (value) => {
  form.value.productName = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  })
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
@@ -219,13 +359,15 @@
const submitForm = () => {
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      // 状态字段不在表单填写,也不传给后端
      const { inspectState, ...payload } = (form.value || {})
      if (operationType.value === "add") {
        qualityUnqualifiedAdd(form.value).then(res => {
        qualityUnqualifiedAdd(payload).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        qualityUnqualifiedUpdate(form.value).then(res => {
        qualityUnqualifiedUpdate(payload).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
@@ -233,8 +375,88 @@
    }
  })
}
// 上传前校检(参考协同审批附件上传)
function handleBeforeUpload() {
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
}
function handleUploadError() {
  proxy.$modal.msgError("上传文件失败");
  proxy.$modal.closeLoading();
}
// 不良品照片上传成功:保存 tempId 与 tempPath,并回写到表单
function handleDefectivePhotoUploadSuccess(res, file) {
  proxy.$modal.closeLoading();
  if (res?.code === 200) {
    const tempId = res?.data?.tempId;
    const tempPath = res?.data?.tempPath || res?.data?.url || "";
    const originalName = res?.data?.originalName || file?.name || "图片";
    if (!form.value.tempFileIds) form.value.tempFileIds = [];
    if (tempId) form.value.tempFileIds.push(tempId);
    // el-upload 列表回显需要 url/name
    file.id = tempId || file.id;
    file.name = originalName;
    file.url = tempPath ? (import.meta.env.VITE_APP_BASE_API + tempPath) : file.url;
    // 以“路径字符串”形式传给新增/编辑接口(后端若只认 tempFileIds 也不冲突)
    syncDefectivePhotosFromFileList();
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res?.msg || "上传失败");
    defectivePhotoUploadRef.value?.handleRemove(file);
  }
}
function handleDefectivePhotoRemove(file) {
  // 同步移除 tempFileIds
  const tempId = file?.id || file?.response?.data?.tempId;
  if (tempId && Array.isArray(form.value.tempFileIds)) {
    form.value.tempFileIds = form.value.tempFileIds.filter(id => id !== tempId);
  }
  syncDefectivePhotosFromFileList();
}
function syncDefectivePhotosFromFileList() {
  const base = import.meta.env.VITE_APP_BASE_API;
  const paths = (defectivePhotoFileList.value || [])
    .map(f => f?.url || f?.response?.data?.tempPath || f?.response?.data?.url)
    .filter(Boolean)
    .map(url => (typeof url === "string" ? url.replace(base, "") : ""))
    .filter(Boolean);
  form.value.defectivePhotos = paths.join(",");
}
// 编辑/详情时,把后端返回的 defectivePhotos 路径串转成 el-upload 可回显的 fileList
watch(
  () => form.value?.defectivePhotos,
  val => {
    if (!val) {
      defectivePhotoFileList.value = [];
      return;
    }
    const base = import.meta.env.VITE_APP_BASE_API;
    const list = String(val)
      .split(",")
      .map((p, idx) => {
        const path = p?.trim();
        if (!path) return null;
        return {
          name: `图片${idx + 1}`,
          url: path.startsWith("http") ? path : base + path,
          id: undefined,
        };
      })
      .filter(Boolean);
    defectivePhotoFileList.value = list;
  },
  { immediate: true }
);
// 关闭弹框
const closeDia = () => {
  defectivePhotoFileList.value = []
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  emit('close')