From d1733a6f090c0caf96c30c4b2f4f08c7d43c8d3c Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期三, 18 三月 2026 14:35:34 +0800
Subject: [PATCH] Merge branch 'dev_银川_中盛建材' of http://114.132.189.42:9002/r/product-inventory-management into dev_银川_中盛建材
---
src/views/productionManagement/productStructure/index.vue | 670 +++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 572 insertions(+), 98 deletions(-)
diff --git a/src/views/productionManagement/productStructure/index.vue b/src/views/productionManagement/productStructure/index.vue
index e32ff8d..7032c09 100644
--- a/src/views/productionManagement/productStructure/index.vue
+++ b/src/views/productionManagement/productStructure/index.vue
@@ -1,113 +1,587 @@
<template>
<div class="app-container">
- <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.id)">{{ row.productName }}
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
+ <!-- 鎼滅储鍖哄煙 -->
+ <div class="search-form">
+ <el-form :inline="true"
+ :model="searchForm">
+ <el-form-item label="浜у搧绫诲瀷"
+ style="margin-bottom: 0px !important;">
+ <el-select v-model="searchForm.dictCode"
+ style="width: 200px;"
+ placeholder="璇烽�夋嫨浜у搧绫诲瀷"
+ clearable>
+ <el-option v-for="option in productTypeOptions"
+ :key="option.dictCode"
+ :label="option.dictLabel"
+ :value="option.dictCode" />
+ </el-select>
+ </el-form-item>
+ <el-form-item style="margin-bottom: 0px !important;">
+ <el-button type="primary"
+ @click="handleSearch">鎼滅储</el-button>
+ <el-button @click="handleReset">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ <!-- 鎸夐挳鍖哄煙 -->
+ <div>
+ <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>
+ </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 }}
</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-form-item label="浜у搧绫诲瀷"
+ prop="dictCode">
+ <el-select v-model="form.dictCode"
+ placeholder="璇烽�夋嫨浜у搧绫诲瀷"
+ clearable>
+ <el-option v-for="option in productTypeOptions"
+ :key="option.dictCode"
+ :label="option.dictLabel"
+ :value="option.dictCode" />
+ </el-select>
+ </el-form-item>
+ <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>
+ </el-form>
+ <template #footer>
+ <el-button @click="closeDialog">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="handleSubmit">纭畾</el-button>
+ </template>
+ </el-dialog>
+ <!-- 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"
+ :show-product-type-select="true"
+ @confirm="submitFileForm"
+ @download-template="handleDownloadTemplate"
+ @close="handleImportClose" />
</div>
</template>
<script setup>
-import {ref} from "vue";
-import {productModelList} from "@/api/basicData/productModel.js";
-import { useRouter } from 'vue-router'
+ 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 ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+ import ImportDialog from "./ImportDialog.vue";
+ import { getDicts } from "@/api/system/dict/data";
-const router = useRouter()
-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: "浜у搧缂栫爜",
- prop: "productCode",
- slot: "detail"
- },
- {
- label: "浜у搧鍚嶇О",
- prop: "productName",
- dataType: 'slot',
- slot: "detail"
- },
- {
- label: "瑙勬牸鍨嬪彿",
- prop: "model",
- },
- {
- label: "鍗曚綅",
- prop: "unit",
- }
-]);
-const tableData = ref([]);
-const tableLoading = ref(false);
-const showEdit = ref(false);
-const selectedRows = ref([]);
-const currentRow = ref({});
-const page = reactive({
- current: 1,
- size: 10,
- total: 0,
-});
-const data = reactive({
- form: {
- productName: "",
- },
- rules: {
- productName: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
- },
- modelForm: {
- otherModel: '',
- model: "",
- unit: "",
- speculativeTradingName: [],
- },
-});
-const {form, rules} = toRefs(data);
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
-};
+ const tableColumn = ref([
+ {
+ label: "BOM缂栧彿",
+ prop: "bomNo",
+ dataType: "slot",
+ slot: "detail",
+ minWidth: 140,
+ },
+ {
+ label: "浜у搧绫诲瀷",
+ prop: "dictLabel",
+ dataType: "tag",
+ formatType: params => {
+ if (params == "3.5鐮屽潡") {
+ return "warning";
+ } else if (params == "5.0鐮屽潡") {
+ return "primary";
+ } else if (params == "鏉挎潗") {
+ return "success";
+ } else {
+ return "info";
+ }
+ },
+ },
+ // {
+ // label: "浜у搧缂栫爜",
+ // prop: "productCode",
-// 鏌ヨ瑙勬牸鍨嬪彿
-const pagination = (obj) => {
- page.current = obj.page;
- page.size = obj.limit;
- getModelList();
-};
+ // minWidth: 160,
+ // },
+ // {
+ // label: "浜у搧鍚嶇О",
+ // prop: "productName",
-const showDetail = (id) => {
- router.push({
- path: '/productionManagement/productStructureDetail',
- query: {
- id: id
- }
- })
-}
-const getModelList = () => {
- tableLoading.value = true;
- productModelList({
- current: page.current,
- size: page.size,
- }).then((res) => {
- tableData.value = res.records;
- page.total = res.total;
- tableLoading.value = false;
+ // minWidth: 160,
+ // },
+ // {
+ // label: "瑙勬牸鍨嬪彿",
+ // prop: "productModelName",
+ // minWidth: 140,
+ // },
+ {
+ label: "鐗堟湰鍙�",
+ prop: "version",
+ },
+ {
+ label: "澶囨敞",
+ prop: "remark",
+ minWidth: 160,
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 150,
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: row => {
+ handleEdit(row);
+ },
+ },
+ {
+ name: "鍒犻櫎",
+ type: "danger",
+ link: true,
+ clickFun: row => {
+ handleDelete(row);
+ },
+ },
+ ],
+ },
+ ]);
+
+ const tableData = ref([]);
+ const tableLoading = ref(false);
+ const showEdit = ref(false);
+ const selectedRows = ref([]);
+ const currentRow = ref({});
+ const dialogVisible = ref(false);
+ 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",
});
-};
-onMounted(() => {
- getModelList();
-})
+
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+
+ const data = reactive({
+ form: {
+ id: undefined,
+ dictCode: "",
+ remark: "",
+ version: "",
+ },
+ rules: {
+ dictCode: [
+ { required: true, message: "璇烽�夋嫨浜у搧绫诲瀷", trigger: "change" },
+ ],
+ version: [{ required: true, message: "璇疯緭鍏ョ増鏈彿", trigger: "blur" }],
+ },
+ searchForm: {
+ dictCode: "",
+ },
+ productTypeOptions: [],
+ });
+
+ const { form, rules, searchForm, productTypeOptions } = toRefs(data);
+
+ // 琛ㄦ牸閫夋嫨鏁版嵁
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ // 鍒嗛〉
+ const pagination = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+
+ // 鑾峰彇浜у搧绫诲瀷瀛楀吀
+ const getProductTypeOptions = () => {
+ getDicts("product_type")
+ .then(res => {
+ if (res.code === 200) {
+ productTypeOptions.value = res.data;
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇浜у搧绫诲瀷瀛楀吀澶辫触锛�", err);
+ });
+ };
+
+ // 鎼滅储
+ const handleSearch = () => {
+ getList();
+ };
+
+ // 閲嶇疆
+ const handleReset = () => {
+ searchForm.value.dictCode = "";
+ getList();
+ };
+
+ // 鏌ヨ鍒楄〃
+ const getList = () => {
+ tableLoading.value = true;
+ listPage({
+ current: page.current,
+ size: page.size,
+ dictCode: searchForm.value.dictCode,
+ })
+ .then(res => {
+ const records = res?.data?.records || [];
+ tableData.value = records;
+ page.total = res?.data?.total || 0;
+ })
+ .catch(err => {
+ console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ // 鏂板
+ const handleAdd = () => {
+ operationType.value = "add";
+ Object.assign(form.value, {
+ id: undefined,
+ dictCode: "",
+ remark: "",
+ version: "",
+ });
+ dialogVisible.value = true;
+ };
+
+ // 缂栬緫
+ const handleEdit = row => {
+ operationType.value = "edit";
+ Object.assign(form.value, {
+ id: row.id,
+ dictCode: row.dictCode || "",
+ remark: row.remark || "",
+ version: row.version || "",
+ });
+ dialogVisible.value = true;
+ };
+
+ // 鍒犻櫎锛堝崟鏉★級
+ const handleDelete = row => {
+ ElMessageBox.confirm("纭鍒犻櫎璇OM锛�", "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ batchDelete([row.id])
+ .then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("鍒犻櫎澶辫触");
+ });
+ })
+ .catch(() => {});
+ };
+
+ // 鎵归噺鍒犻櫎
+ const handleBatchDelete = () => {
+ if (!selectedRows.value.length) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ const ids = selectedRows.value.map(item => item.id);
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ batchDelete(ids)
+ .then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("鍒犻櫎澶辫触");
+ });
+ })
+ .catch(() => {});
+ };
+
+ // 浜у搧閫夋嫨
+ 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;
+ }
+ showProductSelectDialog.value = false;
+ };
+
+ // 鎻愪氦琛ㄥ崟
+ const handleSubmit = () => {
+ formRef.value.validate(valid => {
+ if (valid) {
+ const payload = { ...form.value };
+ if (operationType.value === "add") {
+ add(payload)
+ .then(() => {
+ proxy.$modal.msgSuccess("鏂板鎴愬姛");
+ closeDialog();
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("鏂板澶辫触");
+ });
+ } else {
+ update(payload)
+ .then(() => {
+ proxy.$modal.msgSuccess("淇敼鎴愬姛");
+ closeDialog();
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("淇敼澶辫触");
+ });
+ }
+ }
+ });
+ };
+
+ // 鍏抽棴寮圭獥
+ const closeDialog = () => {
+ dialogVisible.value = false;
+ 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({
+ path: "/productionManagement/productStructureDetail",
+ query: {
+ id: row.id,
+ bomNo: row.bomNo || "",
+ productName: row.productName || "",
+ productModelName: row.productModelName || "",
+ },
+ });
+ };
+
+ onMounted(() => {
+ getProductTypeOptions();
+ getList();
+ });
</script>
+<style scoped lang="scss">
+ .search-form {
+ :deep(.search-form) {
+ margin-bottom: 0px !important;
+ }
+ }
+</style>
--
Gitblit v1.9.3