spring
9 天以前 d991871847391f150dd9c5aa9c871a96b75b7880
fix: 质量管理 可编辑
已修改7个文件
491 ■■■■■ 文件已修改
src/api/consumablesLogistics/consumablesInRecord.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/consumablesLogistics/consumablesOutRecord.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/dispatchLog/Record.vue 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/receiptManagement/Record.vue 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/components/formDia.vue 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/index.vue 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterial/index.vue 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/consumablesLogistics/consumablesInRecord.js
@@ -17,6 +17,15 @@
    });
};
// 编辑耗材入库(仅用于台账编辑)
export const editStockInStock = (data) => {
    return request({
        url: "/consumablesInRecord/editStockInStock",
        method: "post",
        data,
    });
};
export const batchDeleteConsumablesInRecords = (ids) => {
    return request({
        url: "/consumablesInRecord",
src/api/consumablesLogistics/consumablesOutRecord.js
@@ -17,3 +17,12 @@
        data: ids,
    });
};
// 编辑耗材出库(仅用于台账编辑)
export const editStockOut = (data) => {
    return request({
        url: "/consumablesOutRecord/editStockOut",
        method: "post",
        data,
    });
};
src/views/consumablesLogistics/dispatchLog/Record.vue
@@ -28,7 +28,12 @@
            </div>
            <div>
                <el-button @click="handleOut">导出</el-button>
                <el-button type="danger" plain @click="handleDelete">删除</el-button>
                <el-button
                    type="danger"
                    plain
                    @click="handleDelete"
                    v-if="hasCDispatchCancel"
                >删除</el-button>
                <!-- <el-button type="primary" plain @click="handlePrint">打印</el-button> -->
            </div>
        </div>
@@ -86,6 +91,16 @@
                    prop="createBy"
                    show-overflow-tooltip
                />
                <el-table-column label="操作" width="160" align="center">
                    <template #default="scope">
                        <el-button
                            v-if="hasCDispatchEdit"
                            type="primary"
                            size="mini"
                            @click="handleEdit(scope.row)"
                        >编辑</el-button>
                    </template>
                </el-table-column>
        <!-- <el-table-column label="来源"
                         prop="recordType"
                         show-overflow-tooltip>
@@ -105,21 +120,66 @@
            />
        </div>
    </div>
    <el-dialog
        title="编辑出库信息"
        v-model="isShowEditModal"
        width="600px"
    >
        <el-form
            ref="editFormRef"
            :model="editForm"
            label-width="90px"
        >
            <el-form-item
                label="数量"
                prop="qualitity"
                :rules="[{ required: true, message: '请输入数量', trigger: ['blur', 'change'] }]"
            >
                <el-input-number
                    v-model="editForm.qualitity"
                    :min="0"
                    :step="1"
                    :precision="0"
                    controls-position="right"
                    style="width: 100%"
                    placeholder="请输入数量"
                />
            </el-form-item>
            <el-form-item
                label="采购员"
                prop="purchaser"
                :rules="[{ required: true, message: '请输入采购员', trigger: ['blur', 'change'] }]"
            >
                <el-input
                    v-model="editForm.purchaser"
                    placeholder="请输入采购员"
                />
            </el-form-item>
        </el-form>
        <template #footer>
            <el-button @click="closeEditModal">取消</el-button>
            <el-button type="primary" @click="handleEditSubmit">确定</el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ref, reactive, toRefs, getCurrentInstance, computed } from "vue";
import { ElMessageBox } from "element-plus";
import useUserStore from "@/store/modules/user";
import { getCurrentDate } from "@/utils/index.js";
import {
    getConsumablesOutRecordPage,
    delConsumablesOutRecord,
    editStockOut,
} from "@/api/consumablesLogistics/consumablesOutRecord.js";
import {
  findAllQualifiedStockOutRecordTypeOptions, findAllUnQualifiedStockOutRecordTypeOptions,
} from "@/api/basicData/enum.js";
import { checkPermi } from "@/utils/permission.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
@@ -142,6 +202,46 @@
  }
})
const hasCDispatchEdit = computed(() => checkPermi(['c_dispatch_edit']));
const hasCDispatchCancel = computed(() => checkPermi(['c_dispatch_cancel']));
// 编辑弹框数据
const isShowEditModal = ref(false);
const editFormRef = ref();
const editForm = reactive({
    id: undefined,
    qualitity: undefined,
    purchaser: "",
});
const handleEdit = (row) => {
    editForm.id = row.id;
    editForm.qualitity = row.qualitity;
    editForm.purchaser = row.purchaser ?? "";
    isShowEditModal.value = true;
};
const closeEditModal = () => {
    isShowEditModal.value = false;
    editForm.id = undefined;
    editForm.qualitity = undefined;
    editForm.purchaser = "";
    editFormRef.value?.clearValidate?.();
};
const handleEditSubmit = () => {
    editFormRef.value?.validate?.((valid) => {
        if (!valid) return;
        editStockOut({ ...editForm }).then(() => {
            closeEditModal();
            proxy.$modal.msgSuccess("编辑成功");
            getList();
        });
    });
};
// 打印相关
const printPreviewVisible = ref(false);
const printData = ref([]);
src/views/consumablesLogistics/receiptManagement/Record.vue
@@ -34,7 +34,8 @@
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除
                   @click="handleDelete"
                   v-if="hasCReceiptCancel">删除
        </el-button>
      </div>
    </div>
@@ -95,6 +96,18 @@
                       @click="handlePreview(scope.row)">导出过磅单</el-button>
          </template>
        </el-table-column> -->
        <el-table-column label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button
              v-if="hasCReceiptEdit"
              type="primary"
              size="mini"
              @click="handleEdit(scope.row)"
            >编辑</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div style="margin-top: 12px; display: flex; justify-content: flex-end;">
        <pagination v-show="total > 0"
@@ -106,6 +119,51 @@
      </div>
    </div>
  </div>
  <el-dialog
    v-model="isShowEditModal"
    title="编辑入库"
    width="600"
    @close="closeEditModal"
  >
    <el-form
      label-width="100px"
      :model="editForm"
      label-position="top"
      ref="editFormRef"
    >
      <el-form-item
        label="数量"
        prop="qualitity"
        :rules="[{ required: true, message: '请输入数量', trigger: ['blur', 'change'] }]"
      >
        <el-input-number
          v-model="editForm.qualitity"
          :min="0"
          :step="1"
          :precision="0"
          controls-position="right"
          style="width: 100%"
          placeholder="请输入数量"
        />
      </el-form-item>
      <el-form-item
        label="采购员"
        prop="purchaser"
        :rules="[{ required: true, message: '请输入采购员', trigger: ['blur', 'change'] }]"
      >
        <el-input v-model="editForm.purchaser" placeholder="请输入采购员" />
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="handleEditSubmit">确认</el-button>
        <el-button @click="closeEditModal">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
@@ -116,15 +174,18 @@
  toRefs,
  onMounted,
  getCurrentInstance,
  computed,
} from "vue";
import {ElMessageBox} from "element-plus";
import {
  getConsumablesInRecordListPage,
  batchDeleteConsumablesInRecords,
  editStockInStock,
} from "@/api/consumablesLogistics/consumablesInRecord.js";
import {
  findAllQualifiedStockInRecordTypeOptions, findAllUnQualifiedStockInRecordTypeOptions,
} from "@/api/basicData/enum.js";
import { checkPermi } from "@/utils/permission.js";
const {proxy} = getCurrentInstance();
@@ -135,6 +196,9 @@
    default: '0'
  }
})
const hasCReceiptEdit = computed(() => checkPermi(['c_receipt_edit']));
const hasCReceiptCancel = computed(() => checkPermi(['c_receipt_cancel']));
const tableData = ref([]);
const selectedRows = ref([]);
@@ -253,6 +317,37 @@
      });
};
// 编辑耗材入库
const isShowEditModal = ref(false);
const editFormRef = ref(null);
const editForm = ref({});
const handleEdit = (row) => {
  editForm.value = {
    id: row.id,
    qualitity: row.qualitity,
    purchaser: row.purchaser,
  };
  isShowEditModal.value = true;
};
const closeEditModal = () => {
  isShowEditModal.value = false;
  editForm.value = {};
  editFormRef.value?.clearValidate?.();
};
const handleEditSubmit = () => {
  editFormRef.value?.validate?.((valid) => {
    if (!valid) return;
    editStockInStock(editForm.value).then(() => {
      closeEditModal();
      proxy.$modal.msgSuccess("编辑成功");
      getList();
    });
  });
};
onMounted(() => {
  getList();
  fetchStockRecordTypeOptions();
src/views/qualityManagement/nonconformingManagement/components/formDia.vue
@@ -11,6 +11,7 @@
          <el-col :span="12">
            <el-form-item label="产品名称:" prop="productId">
              <el-tree-select
                  v-if="operationType !== 'edit'"
                  v-model="form.productId"
                  placeholder="请选择"
                  clearable
@@ -18,7 +19,13 @@
                  @change="getModels"
                  :data="productOptions"
                  :render-after-expand="false"
                  :disabled="operationType === 'edit'"
                  style="width: 100%"
              />
              <!-- 编辑态:不依赖下拉选项回显,直接展示文本 -->
              <el-input
                  v-else
                  v-model="form.productName"
                  disabled
                  style="width: 100%"
              />
            </el-form-item>
@@ -26,16 +33,28 @@
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select
                  v-if="operationType !== 'edit'"
                  v-model="form.productModelId"
                  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-option
                    v-for="item in modelOptions"
                    :key="item.id"
                    :label="item.model"
                    :value="item.id"
                />
              </el-select>
              <!-- 编辑态:不展示规格型号列表,直接展示文本 -->
              <el-input
                  v-else
                  v-model="form.model"
                  disabled
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
@@ -187,7 +206,7 @@
  },
  rules: {
    checkTime: [{ required: true, message: "请选择检测日期", trigger: "change" }],
    checkUserName: [{ required: true, message: "请选择检验员", trigger: "change" }],
    checkName: [{ required: true, message: "请选择检验员", trigger: "change" }],
    productId: [{ required: true, message: "请选择产品名称", trigger: "change" }],
    productModelId: [{ required: true, message: "请选择规格型号", trigger: "change" }],
    batchNo: [{ required: true, message: "请输入批号", trigger: "blur" }],
@@ -203,27 +222,114 @@
const modelOptions = ref([])
// 打开弹框
const openDialog = (type, row) => {
const openDialog = async (type, row) => {
  operationType.value = type;
  userListNoPage().then(res => {
    userList.value = res.data || [];
  })
  dialogFormVisible.value = true;
  form.value = {}
  getProductOptions();
  // 编辑态不校验规格型号(prop 仍绑定 productModelId,但编辑态改为文本展示)
  data.rules.productModelId = [
    {
      required: type !== "edit",
      message: "请选择规格型号",
      trigger: "change",
    },
  ];
  // 先加载下拉选项,确保编辑数据可以正确匹配回显
  const userRes = await userListNoPage();
  userList.value = userRes.data || [];
  await getProductOptions();
  // 处理结果默认“报废”,且不可选择其它项
  form.value.dealResult = getScrapDealResultValue();
  if (operationType.value === 'edit') {
    getQualityUnqualifiedInfo(row.id).then(res => {
      const { inspectState, ...rest } = (res.data || {})
      form.value = { ...rest, dealResult: getScrapDealResultValue() }
    })
    const fallback = row || {};
    const res = await getQualityUnqualifiedInfo(fallback.id);
    const { inspectState, ...rest } = res.data || {};
    // 先用列表行数据把“必回显字段”直接填上,避免详情接口字段名不一致导致全空。
    const productName = rest?.productName ?? fallback?.productName;
    const modelName = rest?.model ?? fallback?.model;
    const checkTypeValue = rest?.checkType ?? fallback?.checkType;
    const checkNameValue =
      rest?.checkName ??
      rest?.checkUserName ??
      fallback?.checkName ??
      fallback?.checkUserName;
    const productId =
      rest?.productId ??
      findProductIdByLabel(productOptions.value, productName);
    // 先回填字段(productModelId 需要依赖 modelOptions,稍后再补)
    const normalizedProductId = normalizeProductIdByOptions(productId);
    // 编辑态产品名称展示只展示 label,避免树组件回显依赖 value 匹配
    const productNameLabel =
      rest?.productName ??
      fallback?.productName ??
      findNodeById(productOptions.value, normalizedProductId) ??
      productName;
    form.value = {
      ...rest,
      productName: productNameLabel,
      productId: normalizedProductId,
      productModelId: rest?.productModelId ?? undefined,
      model: rest?.model ?? fallback?.model,
      unit: rest?.unit ?? fallback?.unit,
      batchNo: rest?.batchNo ?? fallback?.batchNo ?? "",
      checkType:
        checkTypeValue === undefined || checkTypeValue === null
          ? undefined
          : Number(checkTypeValue),
      checkName: checkNameValue ?? "",
      checkTime: rest?.checkTime ?? fallback?.checkTime ?? "",
      defectivePhenomena:
        rest?.defectivePhenomena ?? fallback?.defectivePhenomena ?? "",
      dealName: rest?.dealName ?? fallback?.dealName ?? "",
      dealTime: rest?.dealTime ?? fallback?.dealTime ?? "",
      dealResult: getScrapDealResultValue(),
    };
    // 规格型号下拉需要依赖 productId
    await loadModelsForProductId(form.value.productId);
    // 规格型号回显(如详情没给 productModelId,就用 model 名称反查)
    if (!form.value.productModelId) {
      form.value.productModelId = findModelIdByModel(
        modelOptions.value,
        modelName
      );
    }
    // 根据 productModelId 回填 model/unit
    if (form.value.productModelId) {
      form.value.productModelId = normalizeModelIdByOptions(form.value.productModelId);
      handleChangeModel(form.value.productModelId);
    } else if (modelName) {
      // productModelId 仍然拿不到时,至少保证 model/unit 文本回显
      const matched =
        (modelOptions.value || []).find((m) => {
          const model = String(m?.model ?? "");
          const id = String(m?.id ?? "");
          const target = String(modelName ?? "");
          return model === target || id === target;
        }) ?? null;
      if (matched) {
        form.value.model = matched.model ?? form.value.model;
        form.value.unit = matched.unit ?? form.value.unit;
      } else {
        form.value.model = modelName;
  }
}
const getProductOptions = () => {
  productTreeList().then((res) => {
  }
}
const getProductOptions = async () => {
  const res = await productTreeList();
    productOptions.value = convertIdToValue(res);
  });
};
const getModels = (value) => {
  form.value.productName = findNodeById(productOptions.value, value);
@@ -236,9 +342,41 @@
  })
};
// 编辑模式/或任意需要时:只拉取规格型号列表,不清空已回填的字段
const loadModelsForProductId = async (productId) => {
  if (!productId) return;
  const res = await modelList({ id: productId });
  modelOptions.value = res || [];
  // 让单位/型号等字段保持与当前 productModelId 一致
  if (form.value.productModelId) {
    form.value.productModelId = normalizeModelIdByOptions(form.value.productModelId);
    handleChangeModel(form.value.productModelId);
  }
};
const handleChangeModel = (value) => {
  form.value.model = modelOptions.value.find(item => item.id == value)?.model || '';
  form.value.unit = modelOptions.value.find(item => item.id == value)?.unit || '';
};
// 解决回显时类型不一致导致 el-tree-select / el-select 只显示 value(id)
const normalizeProductIdByOptions = (productId) => {
  if (productId === undefined || productId === null) return productId;
  const target = String(productId);
  const stack = Array.isArray(productOptions.value) ? [...productOptions.value] : [];
  while (stack.length) {
    const node = stack.shift();
    if (node && String(node?.value ?? "") === target) return node?.value;
    if (node?.children?.length) stack.push(...node.children);
  }
  return productId;
};
const normalizeModelIdByOptions = (modelId) => {
  if (modelId === undefined || modelId === null) return modelId;
  const target = String(modelId);
  return (modelOptions.value || []).find((m) => String(m?.id ?? "") === target)?.id ?? modelId;
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
@@ -254,6 +392,33 @@
  }
  return null; // 没有找到节点,返回null
};
// 根据树节点 label 回填 value(编辑回显兜底用)
const findProductIdByLabel = (nodes, label) => {
  const target = String(label ?? "");
  if (!target) return undefined;
  const stack = Array.isArray(nodes) ? [...nodes] : [];
  while (stack.length) {
    const node = stack.shift();
    if (node && String(node?.label ?? "") === target) return node?.value;
    if (node?.children?.length) stack.push(...node.children);
  }
  return undefined;
};
// 根据规格型号名称反查 id(编辑回显兜底用)
const findModelIdByModel = (models, model) => {
  const target = String(model ?? "");
  if (!target) return undefined;
  return (
    (models || []).find((m) => {
      const mModel = String(m?.model ?? "");
      const mId = String(m?.id ?? "");
      return mModel === target || mId === target;
    })?.id
  );
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
src/views/qualityManagement/nonconformingManagement/index.vue
@@ -37,7 +37,12 @@
      <div>
        <el-button type="primary" @click="openForm('add')">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button
            type="danger"
            plain
            @click="handleDelete"
            v-if="hasNonconformingCancel"
        >删除</el-button>
      </div>
    </div>
    <div class="table_list">
@@ -60,12 +65,13 @@
<script setup>
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import { onMounted, ref, computed } from "vue";
import FormDia from "@/views/qualityManagement/nonconformingManagement/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {qualityUnqualifiedDel, qualityUnqualifiedListPage} from "@/api/qualityManagement/nonconformingManagement.js";
import InspectionFormDia from "@/views/qualityManagement/nonconformingManagement/components/inspectionFormDia.vue";
import dayjs from "dayjs";
import { checkPermi } from "@/utils/permission.js";
const data = reactive({
  searchForm: {
@@ -78,6 +84,10 @@
  },
});
const { searchForm } = toRefs(data);
const hasNonconformingEdit = computed(() => checkPermi(["nonconforming_edit"]));
const hasNonconformingCancel = computed(() => checkPermi(["nonconforming_cancel"]));
const tableColumn = ref([
  {
    label: "检测日期",
@@ -154,6 +164,23 @@
    prop: "dealTime",
    width: 120
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 180,
    operation: [
      {
        name: "编辑",
        type: "text",
        showHide: (row) => hasNonconformingEdit.value,
        clickFun: (row) => {
          openForm("edit", row);
        },
      },
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
@@ -206,10 +233,6 @@
// 打开弹框
const openForm = (type, row) => {
  if (type !== 'add' && row?.inspectState === 1) {
    proxy.$modal.msgWarning("已处理的数据不能再编辑");
    return;
  }
  nextTick(() => {
    formDia.value?.openDialog(type, row)
  })
src/views/qualityManagement/rawMaterial/index.vue
@@ -45,7 +45,7 @@
      <div>
        <el-button type="primary" @click="openForm('add')">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button type="danger" plain @click="handleDelete" v-if="hasRawCancel">删除</el-button>
      </div>
    </div>
    <div class="table_list">
@@ -86,7 +86,7 @@
</template>
<script setup>
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick, computed} from "vue";
import InspectionFormDia from "@/views/qualityManagement/rawMaterial/components/inspectionFormDia.vue";
import FormDia from "@/views/qualityManagement/rawMaterial/components/formDia.vue";
import {ElMessageBox} from "element-plus";
@@ -99,6 +99,7 @@
  findRawMaterialListPage,
  submitRawMaterial, updateCheckUserName,downloadRawMaterial
} from "@/api/qualityManagement/rawMaterial.js";
import { checkPermi } from "@/utils/permission.js";
const data = reactive({
  searchForm: {
@@ -114,6 +115,8 @@
  },
});
const {searchForm, rules} = toRefs(data);
const hasRawCancel = computed(() => checkPermi(["raw_cancel"]));
const tableColumn = ref([
  {
    label: "检测日期",
@@ -190,11 +193,6 @@
        disabled: (row) => {
          // 已提交则禁用
          if (row.inspectState == 1) return true;
          // 如果检验员有值,只有当前登录用户能编辑
          if (row.checkUserName) {
            return row.checkUserName !== userStore.nickName;
          }
          return false;
        }
      },
      {
@@ -216,20 +214,20 @@
          return false;
        }
      },
      {
        name: "分配检验员",
        type: "text",
        clickFun: (row) => {
          if (!row.checkUserName) {
            open(row)
          } else {
            proxy.$modal.msgError("检验员已存在");
          }
        },
        disabled: (row) => {
          return row.inspectState === 1 || row.checkUserName || row.checkUserName !== '';
        }
      },
      // {
      //   name: "分配检验员",
      //   type: "text",
      //   clickFun: (row) => {
      //     if (!row.checkUserName) {
      //       open(row)
      //     } else {
      //       proxy.$modal.msgError("检验员已存在");
      //     }
      //   },
      //   disabled: (row) => {
      //     return row.inspectState === 1 || row.checkUserName || row.checkUserName !== '';
      //   }
      // },
      {
        name: "下载",
        type: "text",