已修改7个文件
480 ■■■■ 文件已修改
src/api/inventoryManagement/stockManage.js 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productBom.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/workOrder.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/issueManagement/index.vue 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/index.vue 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productStructure/index.vue 185 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/index.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockManage.js
@@ -80,4 +80,38 @@
    })
}
//
// 采购入库-库存管理-冻结不合格产品
export function frozenQuality(ids) {
    return request({
        url: '/stockin/frozenStorageQuality',
        method: 'post',
        data: ids
    })
}
// 采购入库-库存管理-解冻不合格产品
export function thawQuality(ids) {
    return request({
        url: '/stockin/thawStorageQuality',
        method: 'post',
        data: ids
    })
}
// 成品入库-库存管理-冻结不合格产品
export function frozenFinishedQuality(ids) {
    return request({
        url: '/stockin/frozenFinishedQuality',
        method: 'post',
        data: ids
    })
}
// 成品入库-库存管理-解冻不合格产品
export function thawFinishedQuality(ids) {
    return request({
        url: '/stockin/thawFinishedQuality',
        method: 'post',
        data: ids
    })
}
src/api/productionManagement/productBom.js
@@ -45,3 +45,22 @@
    params: { productModelId },
  });
}
// 导出BOM
export function exportBom(bomId) {
  return request({
    url: "/productBom/exportBom",
    method: "post",
    params: { bomId },
    responseType: "blob",
  });
}
//  下载模板
export function downloadTemplate() {
  return request({
    url: "/productBom/downloadTemplate",
    method: "get",
    responseType: "blob",
  });
}
src/api/productionManagement/workOrder.js
@@ -23,3 +23,13 @@
    data: data,
  });
}
// 下载工单流转卡(返回文件流)
export function downProductWorkOrder(id) {
  return request({
    url: "/productWorkOrder/down",
    method: "post",
    data: { id },
    responseType: "blob",
  });
}
src/views/inventoryManagement/issueManagement/index.vue
@@ -51,12 +51,22 @@
                             prop="inboundNum0"
                             width="90"
                             show-overflow-tooltip />
            <el-table-column label="状态"
                             align="center"
                             prop="isFrozen"
                             width="100">
              <template #default="scope">
                <el-tag :type="scope.row.isFrozen ? 'danger' : 'success'">
                  {{ scope.row.isFrozen ? '已冻结' : '正常' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column fixed="right"
                             label="操作"
                             min-width="60"
                             align="center">
              <template #default="scope">
                <el-button :disabled="scope.row.inboundNum0 <= 0"
                <el-button :disabled="scope.row.inboundNum0 <= 0 || scope.row.isFrozen"
                           link
                           type="primary"
                           size="small"
@@ -131,13 +141,23 @@
            <el-table-column label="含税总价(元)"
                             prop="taxInclusiveTotalPrice"
                             width="150"></el-table-column>
            <el-table-column label="状态"
                             align="center"
                             prop="isFrozen"
                             width="100">
              <template #default="scope">
                <el-tag :type="scope.row.isFrozen ? 'danger' : 'success'">
                  {{ scope.row.isFrozen ? '已冻结' : '正常' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column fixed="right"
                             label="操作"
                             min-width="60"
                             align="center">
              <template #default="scope">
                <el-button link
                           :disabled="scope.row.inboundNum0 <= 0"
                           :disabled="scope.row.inboundNum0 <= 0 || scope.row.isFrozen"
                           type="primary"
                           size="small"
                           @click="openForm(scope.row);">领用</el-button>
@@ -201,13 +221,23 @@
                             prop="inboundNum0"
                             width="90"
                             show-overflow-tooltip />
            <el-table-column label="状态"
                             align="center"
                             prop="isFrozen"
                             width="100">
              <template #default="scope">
                <el-tag :type="scope.row.isFrozen ? 'danger' : 'success'">
                  {{ scope.row.isFrozen ? '已冻结' : '正常' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column fixed="right"
                             label="操作"
                             min-width="60"
                             align="center">
              <template #default="scope">
                <el-button link
                           :disabled="scope.row.inboundNum0 <= 0"
                           :disabled="scope.row.inboundNum0 <= 0 || scope.row.isFrozen"
                           type="primary"
                           size="small"
                           @click="openForm(scope.row);">领用</el-button>
@@ -421,6 +451,9 @@
const productModelId = ref(null);
  // 打开弹框
  const openForm = async row => {
    if (row.isFrozen) {
      return proxy.$modal.msgError("该产品已冻结,无法领用");
    }
    dialogFormVisible.value = true;
    currentRowId.value = row.id;
    currentRowNum.value = row.inboundNum0;
src/views/inventoryManagement/stockManagement/index.vue
@@ -25,6 +25,12 @@
          </div>
          <div>
            <el-button @click="handleOut">导出</el-button>
            <el-button type="danger"
                       plain
                       @click="handleFrozenFinished">冻结</el-button>
            <el-button type="success"
                       plain
                       @click="handleThawFinished">解冻</el-button>
            <!--            <el-button type="danger" plain @click="handleDelete">删除</el-button>-->
          </div>
        </div>
@@ -71,6 +77,16 @@
            <el-table-column label="剩余库存"
                             prop="stockQuantity"
                             show-overflow-tooltip />
            <el-table-column label="状态"
                             align="center"
                             prop="isFrozen"
                             width="100">
              <template #default="scope">
                <el-tag :type="scope.row.isFrozen ? 'danger' : 'success'">
                  {{ scope.row.isFrozen ? '已冻结' : '正常' }}
                </el-tag>
              </template>
            </el-table-column>
            <!-- <el-table-column fixed="right" label="操作" min-width="60" align="center">
              <template #default="scope">
                <el-button link type="primary" size="small" @click="openForm('edit', scope.row);">编辑</el-button>
@@ -108,6 +124,12 @@
          </div>
          <div>
            <el-button @click="handleOut">导出</el-button>
            <el-button type="danger"
                       plain
                       @click="handleFrozen">冻结</el-button>
            <el-button type="success"
                       plain
                       @click="handleThaw">解冻</el-button>
            <!--           <el-button type="danger" plain @click="handleDelete">删除</el-button>-->
          </div>
        </div>
@@ -164,6 +186,16 @@
            <el-table-column label="含税总价(元)"
                             prop="taxInclusiveTotalPrice"
                             width="150"></el-table-column>
            <el-table-column label="状态"
                             align="center"
                             prop="isFrozen"
                             width="100">
              <template #default="scope">
                <el-tag :type="scope.row.isFrozen ? 'danger' : 'success'">
                  {{ scope.row.isFrozen ? '已冻结' : '正常' }}
                </el-tag>
              </template>
            </el-table-column>
          </el-table>
          <pagination v-show="total > 0"
                      :total="total"
@@ -200,6 +232,10 @@
    getStockManagePage,
    getStockManagePageByProduction,
    delStockManage, getStockManageProduction,
    frozenQuality,
    thawQuality,
    frozenFinishedQuality,
    thawFinishedQuality,
  } from "@/api/inventoryManagement/stockManage.js";
  import {
    updateManagement,
@@ -534,6 +570,108 @@
        proxy.$modal.msg("已取消");
      });
  };
  // 冻结
  const handleFrozen = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.salesLedgerProductId);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被标记为不合格产品并冻结,是否确认?", "警告", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        frozenQuality(ids).then(res => {
          proxy.$modal.msgSuccess("操作成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // 原料解冻
  const handleThaw = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被解冻,是否确认?", "提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "success",
    })
      .then(() => {
        thawQuality(ids).then(res => {
          proxy.$modal.msgSuccess("操作成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // 成品冻结
  const handleFrozenFinished = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.productId);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
        console.log(ids);
    ElMessageBox.confirm("选中的内容将被标记为不合格产品并冻结,是否确认?", "警告", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        frozenFinishedQuality(ids).then(res => {
          proxy.$modal.msgSuccess("操作成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // 成品解冻
  const handleThawFinished = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.productId);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    console.log(ids);
    ElMessageBox.confirm("选中的内容将被解冻,是否确认?", "提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "success",
    })
      .then(() => {
        thawFinishedQuality(ids).then(res => {
          proxy.$modal.msgSuccess("操作成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // 删除
  const handleDelete = () => {
    let ids = [];
src/views/productionManagement/productStructure/index.vue
@@ -1,42 +1,26 @@
<template>
  <div class="app-container">
    <div style="text-align: right; margin-bottom: 10px;">
      <el-button type="info" plain icon="Upload" @click="handleImport"
        v-hasPermi="['product:bom:import']">导入</el-button>
      <el-button type="warning" plain icon="Download" @click="handleExport" :disabled="selectedRows.length !== 1"
        v-hasPermi="['product:bom:export']">导出</el-button>
      <el-button type="primary" @click="handleAdd">新增</el-button>
      <el-button type="danger" plain @click="handleBatchDelete" :disabled="selectedRows.length === 0">删除</el-button>
    </div>
    <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
    >
      <template #detail="{row}">
        <el-button
            type="primary"
            text
            @click="showDetail(row)">{{ row.bomNo }}
    <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true"
      @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination">
      <template #detail="{ row }">
        <el-button type="primary" text @click="showDetail(row)">{{ row.bomNo }}
        </el-button>
      </template>
    </PIMTable>
    <StructureEdit v-if="showEdit" v-model:show-model="showEdit" :record="currentRow"/>
    <StructureEdit v-if="showEdit" v-model:show-model="showEdit" :record="currentRow" />
    <!-- 新增/编辑弹窗 -->
    <el-dialog
        v-model="dialogVisible"
        :title="operationType === 'add' ? '新增BOM' : '编辑BOM'"
        width="600px"
        @close="closeDialog"
    >
      <el-form
          ref="formRef"
          :model="form"
          :rules="rules"
          label-width="120px"
      >
    <el-dialog v-model="dialogVisible" :title="operationType === 'add' ? '新增BOM' : '编辑BOM'" width="600px"
      @close="closeDialog">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="产品名称" prop="productModelId">
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ form.productName || '选择产品' }}
@@ -46,13 +30,7 @@
          <el-input v-model="form.version" placeholder="请输入版本号" clearable />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input
              v-model="form.remark"
              type="textarea"
              :rows="3"
              placeholder="请输入备注"
              clearable
          />
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" clearable />
        </el-form-item>
      </el-form>
      <template #footer>
@@ -60,22 +38,26 @@
        <el-button type="primary" @click="handleSubmit">确定</el-button>
      </template>
    </el-dialog>
    <!-- 产品选择弹窗 -->
    <ProductSelectDialog
        v-model="showProductSelectDialog"
        @confirm="handleProductSelect"
        single
    />
    <ProductSelectDialog v-model="showProductSelectDialog" @confirm="handleProductSelect" single />
    <!-- BOM导入对话框 -->
    <ImportDialog ref="uploadRef" v-model="upload.open" :title="upload.title" :action="upload.url"
      :headers="upload.headers" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress"
      :on-success="handleFileSuccess" :show-download-template="true" @confirm="submitFileForm"
      @download-template="handleDownloadTemplate" @close="handleImportClose" />
  </div>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted, getCurrentInstance, defineAsyncComponent } from "vue";
import { listPage, add, update, batchDelete } from "@/api/productionManagement/productBom.js";
import { getToken } from "@/utils/auth";
import { listPage, add, update, batchDelete, exportBom, downloadTemplate } from "@/api/productionManagement/productBom.js";
import { useRouter } from 'vue-router'
import { ElMessageBox } from 'element-plus'
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import ImportDialog from "@/components/Dialog/ImportDialog.vue";
const router = useRouter()
const { proxy } = getCurrentInstance()
@@ -92,7 +74,7 @@
  {
    label: "产品名称",
    prop: "productName",
    minWidth: 160
  },
  {
@@ -145,6 +127,20 @@
const operationType = ref('add'); // add | edit
const formRef = ref(null);
const showProductSelectDialog = ref(false);
//  BOM导入参数
const upload = reactive({
  // 是否显示弹出层(BOM导入)
  open: false,
  // 弹出层标题(BOM导入)
  title: "",
  // 是否禁用上传
  isUploading: false,
  // 设置上传的请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/productBom/uploadBom"
});
const page = reactive({
  current: 1,
@@ -246,7 +242,7 @@
          proxy.$modal.msgError('删除失败');
        });
    })
    .catch(() => {});
    .catch(() => { });
};
// 批量删除
@@ -271,7 +267,7 @@
          proxy.$modal.msgError('删除失败');
        });
    })
    .catch(() => {});
    .catch(() => { });
};
// 产品选择
@@ -321,6 +317,103 @@
  formRef.value?.resetFields();
};
//  导入按钮操作
const handleImport = () => {
  upload.title = "BOM导入";
  upload.open = true;
};
// 关闭导入对话框时清除文件
const handleImportClose = () => {
  proxy.$refs["uploadRef"].clearFiles();
};
//  文件上传中处理
const handleFileUploadProgress = (event, file, fileList) => {
  upload.isUploading = true;
};
//  文件上传成功处理
const handleFileSuccess = (response, file, fileList) => {
  upload.open = false;
  upload.isUploading = false;
  proxy.$refs["uploadRef"].clearFiles();
  if (response.code === 200) {
    proxy.$modal.msgSuccess(response.msg || "导入成功");
    getList();
  } else {
    proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
  }
};
// 提交上传文件
const submitFileForm = () => {
  proxy.$refs["uploadRef"].submit();
};
//  导出按钮操作
const handleExport = () => {
  if (selectedRows.value.length !== 1) {
    proxy.$modal.msgWarning("请选择一条数据进行导出");
    return;
  }
  const bomId = selectedRows.value[0].id;
  const fileName = `BOM_${selectedRows.value[0].bomNo || bomId}.xlsx`;
  exportBom(bomId).then(res => {
    // 返回的数据是否为空
    if (!res) {
      proxy.$modal.msgError("导出失败,返回数据为空");
      return;
    }
    const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const downloadElement = document.createElement('a');
    const href = window.URL.createObjectURL(blob);
    downloadElement.style.display = 'none';
    downloadElement.href = href;
    downloadElement.download = fileName;
    document.body.appendChild(downloadElement);
    downloadElement.click();
    document.body.removeChild(downloadElement);
    window.URL.revokeObjectURL(href);
    proxy.$modal.msgSuccess("导出成功");
  }).catch(err => {
    console.error("导出异常:", err);
    proxy.$modal.msgError("系统异常,导出失败");
  });
};
//  下载模板
const handleDownloadTemplate = async () => {
  const res = await downloadTemplate();
  // 返回的数据是否为空
  if (!res) {
    proxy.$modal.msgError("下载失败,返回数据为空");
    return;
  }
  const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  const downloadElement = document.createElement('a');
  const href = window.URL.createObjectURL(blob);
  downloadElement.href = href;
  downloadElement.download = "BOM模板.xlsx";
  document.body.appendChild(downloadElement);
  downloadElement.click();
  document.body.removeChild(downloadElement);
  window.URL.revokeObjectURL(href);
  proxy.$modal.msgSuccess("下载成功");
};
// 查看详情
const showDetail = (row) => {
  router.push({
src/views/productionManagement/workOrder/index.vue
@@ -221,6 +221,7 @@
    productWorkOrderPage,
    updateProductWorkOrder,
    addProductMain,
    downProductWorkOrder,
  } from "@/api/productionManagement/workOrder.js";
  import { getUserProfile, userListNoPageByTenantId } from "@/api/system/user.js";
  import QRCode from "qrcode";
@@ -309,7 +310,7 @@
        {
          name: "流转卡",
          clickFun: row => {
            showTransferCard(row);
            downloadAndPrintWorkOrder(row);
          },
        },
        {
@@ -404,6 +405,56 @@
      });
  };
  // 下载并打印工单流转卡(文件流)
  const downloadAndPrintWorkOrder = async row => {
    if (!row || !row.id) {
      proxy.$modal.msgError("缺少工单ID,无法下载流转卡");
      return;
    }
    const fileName = row.workOrderNo
      ? `工单流转卡_${row.workOrderNo}.xlsx`
      : "工单流转卡.xlsx";
    try {
      // 调用接口,以 responseType: 'blob' 获取文件流
      const blob = await downProductWorkOrder(row.id);
      if (!blob) {
        proxy.$modal.msgError("未获取到流转卡文件");
        return;
      }
      // 创建 Blob URL
      const fileBlob =
        blob instanceof Blob ? blob : new Blob([blob], { type: blob.type || "application/octet-stream" });
      const url = window.URL.createObjectURL(fileBlob);
      // 创建隐藏 iframe,用于触发浏览器打印
      const iframe = document.createElement("iframe");
      iframe.style.position = "fixed";
      iframe.style.right = "0";
      iframe.style.bottom = "0";
      iframe.style.width = "0";
      iframe.style.height = "0";
      iframe.style.border = "0";
      iframe.src = url;
      document.body.appendChild(iframe);
      iframe.onload = () => {
        try {
          iframe.contentWindow?.focus();
          iframe.contentWindow?.print();
        } catch (e) {
          console.error("自动调用打印失败", e);
          // 退而求其次,打开新窗口由用户手动打印
          window.open(url);
        }
      };
    } catch (e) {
      console.error("下载工单流转卡失败", e);
      proxy.$modal.msgError("下载工单流转卡失败");
    }
  };
  const showTransferCard = async row => {
    transferCardRowData.value = row;
    const qrContent = String(row.id);