zhangwencui
6 小时以前 4be9b07e00bea78ae394aec67062b4db9895b565
src/views/productionManagement/productStructure/index.vue
@@ -1,96 +1,156 @@
<template>
  <div class="app-container">
    <div style="text-align: right; margin-bottom: 10px;">
      <el-button type="info" plain icon="Upload" @click="handleImport"
      <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"
      <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>
      <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">
    <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 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"
    <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">
      <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 || '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item label="版本号" prop="version">
          <el-input v-model="form.version" placeholder="请输入版本号" clearable />
        <el-form-item label="版本号"
                      prop="version">
          <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-form-item label="备注"
                      prop="remark">
          <el-input v-model="form.remark"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入备注"
                    clearable />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="closeDialog">取消</el-button>
        <el-button type="primary" @click="handleSubmit">确定</el-button>
        <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" />
    <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 {
    ref,
    reactive,
    toRefs,
    onMounted,
    getCurrentInstance,
    defineAsyncComponent,
  } from "vue";
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 {
    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()
const StructureEdit = defineAsyncComponent(() => import('@/views/productionManagement/productStructure/StructureEdit.vue'))
  const router = useRouter();
  const { proxy } = getCurrentInstance();
  const StructureEdit = defineAsyncComponent(() =>
    import("@/views/productionManagement/productStructure/StructureEdit.vue")
  );
const tableColumn = ref([
  {
    label: "BOM编号",
    prop: "bomNo",
    dataType: 'slot',
      dataType: "slot",
    slot: "detail",
    minWidth: 140
      minWidth: 140,
  },
  {
    label: "产品名称",
    prop: "productName",
    minWidth: 160
      minWidth: 160,
  },
  {
    label: "规格型号",
    prop: "productModelName",
    minWidth: 140
      minWidth: 140,
  },
  {
    label: "版本号",
    prop: "version",
    width: 100
      width: 100,
  },
  {
    label: "备注",
    prop: "remark",
    minWidth: 160
      minWidth: 160,
  },
  {
    dataType: "action",
@@ -102,20 +162,20 @@
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          handleEdit(row)
        }
          clickFun: row => {
            handleEdit(row);
          },
      },
      {
        name: "删除",
        type: "danger",
        link: true,
        clickFun: (row) => {
          handleDelete(row)
        }
      }
    ]
  }
          clickFun: row => {
            handleDelete(row);
          },
        },
      ],
    },
]);
const tableData = ref([]);
@@ -124,7 +184,7 @@
const selectedRows = ref([]);
const currentRow = ref({});
const dialogVisible = ref(false);
const operationType = ref('add'); // add | edit
  const operationType = ref("add"); // add | edit
const formRef = ref(null);
const showProductSelectDialog = ref(false);
@@ -139,7 +199,7 @@
  // 设置上传的请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/productBom/uploadBom"
    url: import.meta.env.VITE_APP_BASE_API + "/productBom/uploadBom",
});
const page = reactive({
@@ -155,23 +215,25 @@
    productModelName: "",
    productModelId: "",
    remark: "",
    version: ""
      version: "",
  },
  rules: {
    productModelId: [{ required: true, message: "请选择产品", trigger: "change" }],
    version: [{ required: true, message: "请输入版本号", trigger: "blur" }]
  }
      productModelId: [
        { required: true, message: "请选择产品", trigger: "change" },
      ],
      version: [{ required: true, message: "请输入版本号", trigger: "blur" }],
    },
});
const { form, rules } = toRefs(data);
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
// 分页
const pagination = (obj) => {
  const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
@@ -184,12 +246,12 @@
    current: page.current,
    size: page.size,
  })
    .then((res) => {
      .then(res => {
      const records = res?.data?.records || [];
      tableData.value = records;
      page.total = res?.data?.total || 0;
    })
    .catch((err) => {
      .catch(err => {
      console.error("获取列表失败:", err);
    })
    .finally(() => {
@@ -199,47 +261,47 @@
// 新增
const handleAdd = () => {
  operationType.value = 'add';
    operationType.value = "add";
  Object.assign(form.value, {
    id: undefined,
    productName: "",
    productModelName: "",
    productModelId: "",
    remark: "",
    version: ""
      version: "",
  });
  dialogVisible.value = true;
};
// 编辑
const handleEdit = (row) => {
  operationType.value = 'edit';
  const handleEdit = row => {
    operationType.value = "edit";
  Object.assign(form.value, {
    id: row.id,
    productName: row.productName || "",
    productModelName: row.productModelName || "",
    productModelId: row.productModelId || "",
    remark: row.remark || "",
    version: row.version || ""
      version: row.version || "",
  });
  dialogVisible.value = true;
};
// 删除(单条)
const handleDelete = (row) => {
  ElMessageBox.confirm('确认删除该BOM?', '提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
  const handleDelete = row => {
    ElMessageBox.confirm("确认删除该BOM?", "提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
  })
    .then(() => {
      batchDelete([row.id])
        .then(() => {
          proxy.$modal.msgSuccess('删除成功');
            proxy.$modal.msgSuccess("删除成功");
          getList();
        })
        .catch(() => {
          proxy.$modal.msgError('删除失败');
            proxy.$modal.msgError("删除失败");
        });
    })
    .catch(() => { });
@@ -248,63 +310,63 @@
// 批量删除
const handleBatchDelete = () => {
  if (!selectedRows.value.length) {
    proxy.$modal.msgWarning('请选择数据');
      proxy.$modal.msgWarning("请选择数据");
    return;
  }
  const ids = selectedRows.value.map(item => item.id);
  ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
  })
    .then(() => {
      batchDelete(ids)
        .then(() => {
          proxy.$modal.msgSuccess('删除成功');
            proxy.$modal.msgSuccess("删除成功");
          getList();
        })
        .catch(() => {
          proxy.$modal.msgError('删除失败');
            proxy.$modal.msgError("删除失败");
        });
    })
    .catch(() => { });
};
// 产品选择
const handleProductSelect = (products) => {
  const handleProductSelect = products => {
  if (products && products.length > 0) {
    const product = products[0];
    form.value.productModelId = product.id;
    form.value.productName = product.productName;
    form.value.productModelName = product.model;
      form.value.productModelId = product.skuId;
      form.value.productName = product.materialName;
      form.value.productModelName = product.specification;
  }
  showProductSelectDialog.value = false;
};
// 提交表单
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    formRef.value.validate(valid => {
    if (valid) {
      const payload = { ...form.value };
      if (operationType.value === 'add') {
        if (operationType.value === "add") {
        add(payload)
          .then(() => {
            proxy.$modal.msgSuccess('新增成功');
              proxy.$modal.msgSuccess("新增成功");
            closeDialog();
            getList();
          })
          .catch(() => {
            proxy.$modal.msgError('新增失败');
              proxy.$modal.msgError("新增失败");
          });
      } else {
        update(payload)
          .then(() => {
            proxy.$modal.msgSuccess('修改成功');
              proxy.$modal.msgSuccess("修改成功");
            closeDialog();
            getList();
          })
          .catch(() => {
            proxy.$modal.msgError('修改失败');
              proxy.$modal.msgError("修改失败");
          });
      }
    }
@@ -342,7 +404,13 @@
    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 });
      proxy.$alert(
        "<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
          response.msg +
          "</div>",
        "导入结果",
        { dangerouslyUseHTMLString: true }
      );
  }
};
@@ -361,18 +429,21 @@
  const bomId = selectedRows.value[0].id;
  const fileName = `BOM_${selectedRows.value[0].bomNo || bomId}.xlsx`;
  exportBom(bomId).then(res => {
    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 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.style.display = "none";
    downloadElement.href = href;
    downloadElement.download = fileName;
@@ -383,7 +454,8 @@
    window.URL.revokeObjectURL(href);
    proxy.$modal.msgSuccess("导出成功");
  }).catch(err => {
      })
      .catch(err => {
    console.error("导出异常:", err);
    proxy.$modal.msgError("系统异常,导出失败");
  });
@@ -398,8 +470,10 @@
    return;
  }
  const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  const downloadElement = document.createElement('a');
    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;
@@ -415,15 +489,15 @@
};
// 查看详情
const showDetail = (row) => {
  const showDetail = row => {
  router.push({
    path: '/productionManagement/productStructureDetail',
      path: "/productionManagement/productStructureDetail",
    query: {
      id: row.id,
      bomNo: row.bomNo || '',
      productName: row.productName || '',
      productModelName: row.productModelName || ''
    }
        bomNo: row.bomNo || "",
        productName: row.productName || "",
        productModelName: row.productModelName || "",
      },
  });
};