From 743becb90e6f5e50d1331cad3b35253ed7fd693c Mon Sep 17 00:00:00 2001
From: chenhj <1263187585@qq.com>
Date: 星期四, 30 四月 2026 13:36:26 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' of http://114.132.189.42:9002/r/product-inventory-management into dev_NEW_pro
---
src/views/basicData/product/index.vue | 1132 ++++++++++++++++----------------
src/api/productionManagement/productionProcess.js | 6
src/views/productionManagement/productionOrder/index.vue | 15
src/views/productionManagement/productionTraceability/index.vue | 831 ++++++++++++++++++++++++
src/api/basicData/productProcess.js | 10
src/router/index.js | 13
6 files changed, 1,435 insertions(+), 572 deletions(-)
diff --git a/src/api/basicData/productProcess.js b/src/api/basicData/productProcess.js
index e0208fa..46356fd 100644
--- a/src/api/basicData/productProcess.js
+++ b/src/api/basicData/productProcess.js
@@ -1,10 +1,10 @@
-import request from '@/utils/request'
+import request from "@/utils/request";
// 宸ュ簭鍒楄〃鍒嗛〉鏌ヨ
export function productProcessListPage(query) {
return request({
- url: '/productProcess/listPage',
- method: 'get',
- params: query
- })
+ url: "/technologyOperation/listPage",
+ method: "get",
+ params: query,
+ });
}
diff --git a/src/api/productionManagement/productionProcess.js b/src/api/productionManagement/productionProcess.js
index 7001997..783f584 100644
--- a/src/api/productionManagement/productionProcess.js
+++ b/src/api/productionManagement/productionProcess.js
@@ -4,7 +4,7 @@
// 鍒嗛〉鏌ヨ
export function listPage(query) {
return request({
- url: "/productProcess/listPage",
+ url: "/technologyOperation/listPage",
method: "get",
params: query,
});
@@ -53,7 +53,7 @@
// 瀵煎叆鏁版嵁
export function importData(data) {
return request({
- url: "/productProcess/importData",
+ url: "/technologyOperation/importData",
method: "post",
data: data,
});
@@ -62,7 +62,7 @@
// 涓嬭浇妯℃澘
export function downloadTemplate() {
return request({
- url: "/productProcess/downloadTemplate",
+ url: "/technologyOperation/downloadTemplate",
method: "post",
responseType: "blob",
});
diff --git a/src/router/index.js b/src/router/index.js
index cc2b88c..6b89a17 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -106,6 +106,19 @@
name: "DeviceInfo",
meta: { title: "璁惧淇℃伅", icon: "monitor" },
},
+ {
+ path: "/productionTraceability",
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: "index",
+ component: () => import("@/views/productionManagement/productionTraceability/index.vue"),
+ name: "ProductionTraceability",
+ meta: { title: "鐢熶骇杩芥函", activeMenu: "/productionManagement/productionOrder" },
+ },
+ ],
+ },
// 娣诲姞椤圭洰璇︽儏椤甸潰璺敱閰嶇疆
{
path: "/oaSystem/projectManagement/projectDetail",
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index 10b51bf..99ab028 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -2,41 +2,34 @@
<div class="app-container product-view">
<div class="left">
<div>
- <el-input
- v-model="search"
- style="width: 210px"
- placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�"
- @change="searchFilter"
- @clear="searchFilter"
- clearable
- prefix-icon="Search"
- />
- <el-button
- v-if="false"
- type="primary"
- @click="openProDia('addOne')"
- style="margin-left: 10px"
- >鏂板浜у搧澶х被</el-button
- >
+ <el-input v-model="search"
+ style="width: 210px"
+ placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�"
+ @change="searchFilter"
+ @clear="searchFilter"
+ clearable
+ prefix-icon="Search" />
+ <el-button v-if="false"
+ type="primary"
+ @click="openProDia('addOne')"
+ style="margin-left: 10px">鏂板浜у搧澶х被</el-button>
</div>
<div ref="containerRef">
- <el-tree
- ref="tree"
- v-loading="treeLoad"
- :data="list"
- @node-click="handleNodeClick"
- :expand-on-click-node="false"
- @node-expand="handleNodeExpand"
- @node-collapse="handleNodeCollapse"
- :key="treeKey"
- :default-expanded-keys="expandedKeys"
- :filter-node-method="filterNode"
- :props="{ children: 'children', label: 'label' }"
- highlight-current
- node-key="id"
- class="product-tree-scroll"
- style="height: calc(100vh - 190px); overflow-y: auto"
- >
+ <el-tree ref="tree"
+ v-loading="treeLoad"
+ :data="list"
+ @node-click="handleNodeClick"
+ :expand-on-click-node="false"
+ @node-expand="handleNodeExpand"
+ @node-collapse="handleNodeCollapse"
+ :key="treeKey"
+ :default-expanded-keys="expandedKeys"
+ :filter-node-method="filterNode"
+ :props="{ children: 'children', label: 'label' }"
+ highlight-current
+ node-key="id"
+ class="product-tree-scroll"
+ style="height: calc(100vh - 190px); overflow-y: auto">
<template #default="{ node, data }">
<div class="custom-tree-node">
<span class="tree-node-content">
@@ -47,25 +40,23 @@
<span class="tree-node-label">{{ data.label }}</span>
</span>
<div>
- <el-button
- type="primary"
- link
- :disabled="isTopLevelNode(data, node)"
- @click="openProDia('edit', data)"
- >
+ <el-button type="primary"
+ link
+ :disabled="isTopLevelNode(data, node)"
+ @click="openProDia('edit', data)">
缂栬緫
</el-button>
- <el-button type="primary" link @click="openProDia('add', data)">
+ <el-button type="primary"
+ link
+ @click="openProDia('add', data)">
娣诲姞浜у搧
</el-button>
- <el-button
- v-if="!node.childNodes.length"
- style="margin-left: 4px"
- type="danger"
- link
- :disabled="isTopLevelNode(data, node)"
- @click="remove(node, data)"
- >
+ <el-button v-if="!node.childNodes.length"
+ style="margin-left: 4px"
+ type="danger"
+ link
+ :disabled="isTopLevelNode(data, node)"
+ @click="remove(node, data)">
鍒犻櫎
</el-button>
</div>
@@ -75,103 +66,109 @@
</div>
</div>
<div class="right">
- <div style="margin-bottom: 10px" v-if="isShowButton">
- <el-button type="primary" @click="openModelDia('add')">
+ <div style="margin-bottom: 10px"
+ v-if="isShowButton">
+ <el-button type="primary"
+ @click="openModelDia('add')">
鏂板瑙勬牸鍨嬪彿
</el-button>
- <ImportExcel :product-id="currentId" @uploadSuccess="getModelList" />
- <el-button
- type="danger"
- @click="handleDelete"
- style="margin-left: 10px"
- plain
- >
+ <ImportExcel :product-id="currentId"
+ @uploadSuccess="getModelList" />
+ <el-button type="danger"
+ @click="handleDelete"
+ style="margin-left: 10px"
+ plain>
鍒犻櫎
</el-button>
</div>
- <PIMTable
- rowKey="id"
- :column="tableColumn"
- :tableData="tableData"
- :page="page"
- :isSelection="true"
- @selection-change="handleSelectionChange"
- :tableLoading="tableLoading"
- @pagination="pagination"
- ></PIMTable>
+ <PIMTable rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"></PIMTable>
</div>
- <el-dialog v-model="productDia" title="浜у搧" width="400px" @keydown.enter.prevent>
- <el-form
- :model="form"
- label-width="140px"
- label-position="top"
- :rules="rules"
- ref="formRef"
- >
+ <el-dialog v-model="productDia"
+ title="浜у搧"
+ width="400px"
+ @keydown.enter.prevent>
+ <el-form :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef">
<el-row :gutter="30">
<el-col :span="24">
- <el-form-item label="浜у搧鍚嶇О锛�" prop="productName">
- <el-input
- v-model="form.productName"
- placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
- maxlength="20"
- show-word-limit
- clearable
- @keydown.enter.prevent
- />
+ <el-form-item label="浜у搧鍚嶇О锛�"
+ prop="productName">
+ <el-input v-model="form.productName"
+ placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+ maxlength="20"
+ show-word-limit
+ clearable
+ @keydown.enter.prevent />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
- <el-button type="primary" @click="submitForm">纭</el-button>
+ <el-button type="primary"
+ @click="submitForm">纭</el-button>
<el-button @click="closeProDia">鍙栨秷</el-button>
</div>
</template>
</el-dialog>
- <el-dialog
- v-model="modelDia"
- title="瑙勬牸鍨嬪彿"
- width="400px"
- @close="closeModelDia"
- @keydown.enter.prevent
- >
- <el-form
- :model="modelForm"
- label-width="140px"
- label-position="top"
- :rules="modelRules"
- ref="modelFormRef"
- >
+ <el-dialog v-model="modelDia"
+ title="瑙勬牸鍨嬪彿"
+ width="400px"
+ @close="closeModelDia"
+ @keydown.enter.prevent>
+ <el-form :model="modelForm"
+ label-width="140px"
+ label-position="top"
+ :rules="modelRules"
+ ref="modelFormRef">
<el-row>
+ <el-row>
+ <el-col :span="24">
+ <el-form-item label="浜у搧缂栧彿锛�"
+ prop="productCode">
+ <el-input v-model="modelForm.productCode"
+ placeholder="璇疯緭鍏ヤ骇鍝佺紪鍙�"
+ clearable
+ @keydown.enter.prevent />
+ </el-form-item>
+ </el-col>
+ </el-row>
<el-col :span="24">
- <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="model">
- <el-input
- v-model="modelForm.model"
- placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
- clearable
- @keydown.enter.prevent
- />
+ <el-form-item label="瑙勬牸鍨嬪彿锛�"
+ prop="model">
+ <el-input v-model="modelForm.model"
+ placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+ clearable
+ @keydown.enter.prevent />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
- <el-form-item label="鍗曚綅锛�" prop="unit">
- <el-input
- v-model="modelForm.unit"
- placeholder="璇疯緭鍏ュ崟浣�"
- clearable
- @keydown.enter.prevent
- />
+ <el-form-item label="鍗曚綅锛�"
+ prop="unit">
+ <el-input v-model="modelForm.unit"
+ placeholder="璇疯緭鍏ュ崟浣�"
+ clearable
+ @keydown.enter.prevent />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
- <el-button type="primary" @click="submitModelForm">纭</el-button>
+ <el-button type="primary"
+ @click="submitModelForm">纭</el-button>
<el-button @click="closeModelDia">鍙栨秷</el-button>
</div>
</template>
@@ -180,473 +177,480 @@
</template>
<script setup>
-import { nextTick, ref } from "vue";
-import { ElMessageBox } from "element-plus";
-import {
- addOrEditProduct,
- addOrEditProductModel,
- delProduct,
- delProductModel,
- modelListPage,
- productTreeList,
-} from "@/api/basicData/product.js";
-import ImportExcel from "./ImportExcel/index.vue";
+ import { nextTick, ref } from "vue";
+ import { ElMessageBox } from "element-plus";
+ import {
+ addOrEditProduct,
+ addOrEditProductModel,
+ delProduct,
+ delProductModel,
+ modelListPage,
+ productTreeList,
+ } from "@/api/basicData/product.js";
+ import ImportExcel from "./ImportExcel/index.vue";
-const { proxy } = getCurrentInstance();
-const tree = ref(null);
-const containerRef = ref(null);
-const treeKey = ref(0);
-const expandedKeySet = new Set();
-const EXPANDED_STORAGE_KEY = "basicData_product_tree_expanded_keys_v2";
+ const { proxy } = getCurrentInstance();
+ const tree = ref(null);
+ const containerRef = ref(null);
+ const treeKey = ref(0);
+ const expandedKeySet = new Set();
+ const EXPANDED_STORAGE_KEY = "basicData_product_tree_expanded_keys_v2";
-const loadExpandedKeys = () => {
- if (typeof window === "undefined") {
- return [];
- }
- try {
- const saved = localStorage.getItem(EXPANDED_STORAGE_KEY);
- return saved ? JSON.parse(saved) : [];
- } catch (error) {
- console.error(error);
- return [];
- }
-};
-
-const saveExpandedKeys = () => {
- if (typeof window === "undefined") {
- return;
- }
- localStorage.setItem(
- EXPANDED_STORAGE_KEY,
- JSON.stringify(Array.from(expandedKeySet))
- );
-};
-
-loadExpandedKeys().forEach((key) => expandedKeySet.add(key));
-
-const syncExpandedKeysFromTree = () => {
- const keys = [];
- const walk = (nodes) => {
- (nodes || []).forEach((item) => {
- if (item.expanded && item.data?.id !== undefined) {
- keys.push(item.data.id);
- }
- if (item.childNodes && item.childNodes.length) {
- walk(item.childNodes);
- }
- });
- };
-
- walk(tree.value?.root?.childNodes);
- expandedKeySet.clear();
- keys.forEach((key) => expandedKeySet.add(key));
- expandedKeys.value = keys;
- saveExpandedKeys();
-};
-
-const normalizeExpandedKeys = (treeData) => {
- const parentMap = new Map();
- const walk = (nodes, parentId = null) => {
- (nodes || []).forEach((item) => {
- parentMap.set(item.id, parentId);
- if (item.children && item.children.length) {
- walk(item.children, item.id);
- }
- });
- };
-
- walk(treeData);
-
- const normalizedKeys = Array.from(expandedKeySet).filter((key) => {
- if (!parentMap.has(key)) {
- return false;
+ const loadExpandedKeys = () => {
+ if (typeof window === "undefined") {
+ return [];
}
- let currentId = key;
- while (parentMap.has(currentId)) {
- const parentId = parentMap.get(currentId);
- if (!parentId) {
- return true;
- }
- if (!expandedKeySet.has(parentId)) {
+ try {
+ const saved = localStorage.getItem(EXPANDED_STORAGE_KEY);
+ return saved ? JSON.parse(saved) : [];
+ } catch (error) {
+ console.error(error);
+ return [];
+ }
+ };
+
+ const saveExpandedKeys = () => {
+ if (typeof window === "undefined") {
+ return;
+ }
+ localStorage.setItem(
+ EXPANDED_STORAGE_KEY,
+ JSON.stringify(Array.from(expandedKeySet))
+ );
+ };
+
+ loadExpandedKeys().forEach(key => expandedKeySet.add(key));
+
+ const syncExpandedKeysFromTree = () => {
+ const keys = [];
+ const walk = nodes => {
+ (nodes || []).forEach(item => {
+ if (item.expanded && item.data?.id !== undefined) {
+ keys.push(item.data.id);
+ }
+ if (item.childNodes && item.childNodes.length) {
+ walk(item.childNodes);
+ }
+ });
+ };
+
+ walk(tree.value?.root?.childNodes);
+ expandedKeySet.clear();
+ keys.forEach(key => expandedKeySet.add(key));
+ expandedKeys.value = keys;
+ saveExpandedKeys();
+ };
+
+ const normalizeExpandedKeys = treeData => {
+ const parentMap = new Map();
+ const walk = (nodes, parentId = null) => {
+ (nodes || []).forEach(item => {
+ parentMap.set(item.id, parentId);
+ if (item.children && item.children.length) {
+ walk(item.children, item.id);
+ }
+ });
+ };
+
+ walk(treeData);
+
+ const normalizedKeys = Array.from(expandedKeySet).filter(key => {
+ if (!parentMap.has(key)) {
return false;
}
- currentId = parentId;
- }
- return true;
- });
-
- if (normalizedKeys.length !== expandedKeySet.size) {
- expandedKeySet.clear();
- normalizedKeys.forEach((key) => expandedKeySet.add(key));
- saveExpandedKeys();
- }
-};
-
-const productDia = ref(false);
-const modelDia = ref(false);
-const modelOperationType = ref("");
-const search = ref("");
-const currentId = ref("");
-const currentParentId = ref("");
-const operationType = ref("");
-const treeLoad = ref(false);
-const list = ref([]);
-const expandedKeys = ref([]);
-const tableColumn = ref([
- {
- label: "瑙勬牸鍨嬪彿",
- prop: "model",
- },
- {
- label: "鍗曚綅",
- prop: "unit",
- },
- {
- dataType: "action",
- label: "鎿嶄綔",
- align: "center",
- operation: [
- {
- name: "缂栬緫",
- type: "text",
- clickFun: (row) => {
- openModelDia("edit", row);
- },
- },
- ],
- },
-]);
-const tableData = ref([]);
-const tableLoading = ref(false);
-const isShowButton = ref(false);
-const selectedRows = ref([]);
-const page = reactive({
- current: 1,
- size: 10,
- total: 0,
-});
-const data = reactive({
- form: {
- productName: "",
- },
- rules: {
- productName: [
- { required: true, message: "璇疯緭鍏�", trigger: "blur" },
- { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
- ],
- },
- modelForm: {
- model: "",
- unit: "",
- },
- modelRules: {
- model: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- },
-});
-const { form, rules, modelForm, modelRules } = toRefs(data);
-// 鏌ヨ浜у搧鏍�
-const getProductTreeList = () => {
- treeLoad.value = true;
- productTreeList()
- .then((res) => {
- list.value = res || [];
- normalizeExpandedKeys(list.value);
- expandedKeys.value = Array.from(expandedKeySet);
- treeKey.value += 1;
- nextTick(() => {
- tree.value?.setDefaultExpandedKeys?.(expandedKeys.value);
- });
- })
- .catch((err) => {
- console.error(err);
- })
- .finally(() => {
- treeLoad.value = false;
- });
-};
-const handleNodeExpand = (data) => {
- nextTick(syncExpandedKeysFromTree);
-};
-const handleNodeCollapse = (data, node) => {
- node?.eachNode?.((item) => {
- item.collapse();
- });
- nextTick(syncExpandedKeysFromTree);
-};
-// 杩囨护浜у搧鏍�
-const searchFilter = () => {
- proxy.$refs.tree.filter(search.value);
-};
-const isTopLevelNode = (data, node) => {
- if (node?.level !== undefined) {
- return node.level === 1;
- }
- return [null, undefined, "", 0, "0"].includes(data?.parentId);
-};
-// 鎵撳紑浜у搧寮规
-const openProDia = (type, data) => {
- if (data && type === "edit" && isTopLevelNode(data)) {
- proxy.$modal.msgWarning("涓�绾ц妭鐐逛笉鑳界紪杈戞垨鍒犻櫎");
- return;
- }
- operationType.value = type;
- productDia.value = true;
- form.value.productName = "";
- if (type === "edit") {
- form.value.productName = data.productName;
- }
-};
-// 鎵撳紑瑙勬牸鍨嬪彿寮规
-const openModelDia = (type, data) => {
- modelOperationType.value = type;
- modelDia.value = true;
- modelForm.value.model = "";
- modelForm.value.model = "";
- modelForm.value.id = "";
- if (type === "edit") {
- modelForm.value = { ...data };
- }
-};
-// 鎻愪氦浜у搧鍚嶇О淇敼
-const submitForm = () => {
- proxy.$refs.formRef.validate((valid) => {
- if (valid) {
- if (operationType.value === "add") {
- form.value.parentId = currentId.value;
- form.value.id = "";
- } else if (operationType.value === "addOne") {
- form.value.id = "";
- form.value.parentId = "";
- } else {
- form.value.id = currentId.value;
- form.value.parentId = "";
+ let currentId = key;
+ while (parentMap.has(currentId)) {
+ const parentId = parentMap.get(currentId);
+ if (!parentId) {
+ return true;
+ }
+ if (!expandedKeySet.has(parentId)) {
+ return false;
+ }
+ currentId = parentId;
}
- addOrEditProduct(form.value).then((res) => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeProDia();
- getProductTreeList();
- });
- }
- });
-};
-// 鍏抽棴浜у搧寮规
-const closeProDia = () => {
- proxy.$refs.formRef.resetFields();
- productDia.value = false;
-};
+ return true;
+ });
-// 鍒犻櫎浜у搧
-const remove = (node, data) => {
- if (isTopLevelNode(data, node)) {
- proxy.$modal.msgWarning("涓�绾ц妭鐐逛笉鑳界紪杈戞垨鍒犻櫎");
- return;
- }
- let ids = [];
- ids.push(data.id);
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- tableLoading.value = true;
- delProduct(ids)
- .then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ if (normalizedKeys.length !== expandedKeySet.size) {
+ expandedKeySet.clear();
+ normalizedKeys.forEach(key => expandedKeySet.add(key));
+ saveExpandedKeys();
+ }
+ };
+
+ const productDia = ref(false);
+ const modelDia = ref(false);
+ const modelOperationType = ref("");
+ const search = ref("");
+ const currentId = ref("");
+ const currentParentId = ref("");
+ const operationType = ref("");
+ const treeLoad = ref(false);
+ const list = ref([]);
+ const expandedKeys = ref([]);
+ const tableColumn = ref([
+ {
+ label: "浜у搧缂栧彿",
+ prop: "productCode",
+ },
+ {
+ label: "瑙勬牸鍨嬪彿",
+ prop: "model",
+ },
+ {
+ label: "鍗曚綅",
+ prop: "unit",
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: row => {
+ openModelDia("edit", row);
+ },
+ },
+ ],
+ },
+ ]);
+ const tableData = ref([]);
+ const tableLoading = ref(false);
+ const isShowButton = ref(false);
+ const selectedRows = ref([]);
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+ const data = reactive({
+ form: {
+ productName: "",
+ },
+ rules: {
+ productName: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
+ ],
+ },
+ modelForm: {
+ model: "",
+ unit: "",
+ productCode: "",
+ },
+ modelRules: {
+ model: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ productCode: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ },
+ });
+ const { form, rules, modelForm, modelRules } = toRefs(data);
+ // 鏌ヨ浜у搧鏍�
+ const getProductTreeList = () => {
+ treeLoad.value = true;
+ productTreeList()
+ .then(res => {
+ list.value = res || [];
+ normalizeExpandedKeys(list.value);
+ expandedKeys.value = Array.from(expandedKeySet);
+ treeKey.value += 1;
+ nextTick(() => {
+ tree.value?.setDefaultExpandedKeys?.(expandedKeys.value);
+ });
+ })
+ .catch(err => {
+ console.error(err);
+ })
+ .finally(() => {
+ treeLoad.value = false;
+ });
+ };
+ const handleNodeExpand = data => {
+ nextTick(syncExpandedKeysFromTree);
+ };
+ const handleNodeCollapse = (data, node) => {
+ node?.eachNode?.(item => {
+ item.collapse();
+ });
+ nextTick(syncExpandedKeysFromTree);
+ };
+ // 杩囨护浜у搧鏍�
+ const searchFilter = () => {
+ proxy.$refs.tree.filter(search.value);
+ };
+ const isTopLevelNode = (data, node) => {
+ if (node?.level !== undefined) {
+ return node.level === 1;
+ }
+ return [null, undefined, "", 0, "0"].includes(data?.parentId);
+ };
+ // 鎵撳紑浜у搧寮规
+ const openProDia = (type, data) => {
+ if (data && type === "edit" && isTopLevelNode(data)) {
+ proxy.$modal.msgWarning("涓�绾ц妭鐐逛笉鑳界紪杈戞垨鍒犻櫎");
+ return;
+ }
+ operationType.value = type;
+ productDia.value = true;
+ form.value.productName = "";
+ if (type === "edit") {
+ form.value.productName = data.productName;
+ }
+ };
+ // 鎵撳紑瑙勬牸鍨嬪彿寮规
+ const openModelDia = (type, data) => {
+ modelOperationType.value = type;
+ modelDia.value = true;
+ modelForm.value.model = "";
+ modelForm.value.unit = "";
+ modelForm.value.productCode = "";
+ modelForm.value.id = "";
+ if (type === "edit") {
+ modelForm.value = { ...data };
+ }
+ };
+ // 鎻愪氦浜у搧鍚嶇О淇敼
+ const submitForm = () => {
+ proxy.$refs.formRef.validate(valid => {
+ if (valid) {
+ if (operationType.value === "add") {
+ form.value.parentId = currentId.value;
+ form.value.id = "";
+ } else if (operationType.value === "addOne") {
+ form.value.id = "";
+ form.value.parentId = "";
+ } else {
+ form.value.id = currentId.value;
+ form.value.parentId = "";
+ }
+ addOrEditProduct(form.value).then(res => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeProDia();
getProductTreeList();
- })
- .finally(() => {
- tableLoading.value = false;
});
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
+ }
});
-};
-// 閫夋嫨浜у搧
-const handleNodeClick = (val, node, el) => {
- // 鍒ゆ柇鏄惁涓哄彾瀛愯妭鐐�
- isShowButton.value = !(val.children && val.children.length > 0);
- // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫
- currentId.value = val.id;
- currentParentId.value = val.parentId;
- getModelList();
-};
+ };
+ // 鍏抽棴浜у搧寮规
+ const closeProDia = () => {
+ proxy.$refs.formRef.resetFields();
+ productDia.value = false;
+ };
-// 鎻愪氦瑙勬牸鍨嬪彿淇敼
-const submitModelForm = () => {
- proxy.$refs.modelFormRef.validate((valid) => {
- if (valid) {
- modelForm.value.productId = currentId.value;
- addOrEditProductModel(modelForm.value).then((res) => {
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- closeModelDia();
- getModelList();
- });
+ // 鍒犻櫎浜у搧
+ const remove = (node, data) => {
+ if (isTopLevelNode(data, node)) {
+ proxy.$modal.msgWarning("涓�绾ц妭鐐逛笉鑳界紪杈戞垨鍒犻櫎");
+ return;
}
- });
-};
-// 鍏抽棴鍨嬪彿寮规
-const closeModelDia = () => {
- proxy.$refs.modelFormRef.resetFields();
- modelDia.value = false;
-};
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
-};
-
-// 鏌ヨ瑙勬牸鍨嬪彿
-const pagination = (obj) => {
- page.current = obj.page;
- page.size = obj.limit;
- getModelList();
-};
-const getModelList = () => {
- tableLoading.value = true;
- modelListPage({
- id: currentId.value,
- current: page.current,
- size: page.size,
- }).then((res) => {
- console.log("res", res);
- tableData.value = res.records;
- page.total = res.total;
- tableLoading.value = false;
- });
-};
-// 鍒犻櫎瑙勬牸鍨嬪彿
-const handleDelete = () => {
- let ids = [];
- if (selectedRows.value.length > 0) {
- ids = selectedRows.value.map((item) => item.id);
- } else {
- proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
- return;
- }
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- tableLoading.value = true;
- delProductModel(ids)
- .then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
- getModelList();
- })
- .finally(() => {
- tableLoading.value = false;
- });
+ let ids = [];
+ ids.push(data.id);
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
})
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
+ .then(() => {
+ tableLoading.value = true;
+ delProduct(ids)
+ .then(res => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getProductTreeList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+ // 閫夋嫨浜у搧
+ const handleNodeClick = (val, node, el) => {
+ // 鍒ゆ柇鏄惁涓哄彾瀛愯妭鐐�
+ isShowButton.value = !(val.children && val.children.length > 0);
+ // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫
+ currentId.value = val.id;
+ currentParentId.value = val.parentId;
+ getModelList();
+ };
+
+ // 鎻愪氦瑙勬牸鍨嬪彿淇敼
+ const submitModelForm = () => {
+ proxy.$refs.modelFormRef.validate(valid => {
+ if (valid) {
+ modelForm.value.productId = currentId.value;
+ addOrEditProductModel(modelForm.value).then(res => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeModelDia();
+ getModelList();
+ });
+ }
});
-};
-// 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
-const filterNode = (value, data, node) => {
- if (!value) {
- //濡傛灉鏁版嵁涓虹┖锛屽垯杩斿洖true,鏄剧ず鎵�鏈夌殑鏁版嵁椤�
- return true;
- }
- // 鏌ヨ鍒楄〃鏄惁鏈夊尮閰嶆暟鎹紝灏嗗�煎皬鍐欙紝鍖归厤鑻辨枃鏁版嵁
- let val = value.toLowerCase();
- return chooseNode(val, data, node); // 璋冪敤杩囨护浜屽眰鏂规硶
-};
-// 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� (濡傛灉杈撳叆鐨勫弬鏁版槸鐖惰妭鐐逛笖鑳藉尮閰嶏紝鍒欒繑鍥炶鑺傜偣浠ュ強鍏朵笅鐨勬墍鏈夊瓙鑺傜偣锛涘鏋滃弬鏁版槸瀛愯妭鐐癸紝鍒欒繑鍥炶鑺傜偣鐨勭埗鑺傜偣銆俷ame鏄腑鏂囧瓧绗︼紝enName鏄嫳鏂囧瓧绗�.
-const chooseNode = (value, data, node) => {
- if (data.label.indexOf(value) !== -1) {
- return true;
- }
- const level = node.level;
- // 濡傛灉浼犲叆鐨勮妭鐐规湰韬氨鏄竴绾ц妭鐐瑰氨涓嶇敤鏍¢獙浜�
- if (level === 1) {
- return false;
- }
- // 鍏堝彇褰撳墠鑺傜偣鐨勭埗鑺傜偣
- let parentData = node.parent;
- // 閬嶅巻褰撳墠鑺傜偣鐨勭埗鑺傜偣
- let index = 0;
- while (index < level - 1) {
- // 濡傛灉鍖归厤鍒扮洿鎺ヨ繑鍥烇紝姝ゅname鍊兼槸涓枃瀛楃锛宔nName鏄嫳鏂囧瓧绗︺�傚垽鏂尮閰嶄腑鑻辨枃杩囨护
- if (parentData.data.label.indexOf(value) !== -1) {
+ };
+ // 鍏抽棴鍨嬪彿寮规
+ const closeModelDia = () => {
+ proxy.$refs.modelFormRef.resetFields();
+ modelDia.value = false;
+ };
+ // 琛ㄦ牸閫夋嫨鏁版嵁
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ // 鏌ヨ瑙勬牸鍨嬪彿
+ const pagination = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getModelList();
+ };
+ const getModelList = () => {
+ tableLoading.value = true;
+ modelListPage({
+ id: currentId.value,
+ current: page.current,
+ size: page.size,
+ }).then(res => {
+ console.log("res", res);
+ tableData.value = res.records;
+ page.total = res.total;
+ tableLoading.value = false;
+ });
+ };
+ // 鍒犻櫎瑙勬牸鍨嬪彿
+ const handleDelete = () => {
+ let ids = [];
+ if (selectedRows.value.length > 0) {
+ ids = selectedRows.value.map(item => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ tableLoading.value = true;
+ delProductModel(ids)
+ .then(res => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getModelList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+ // 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
+ const filterNode = (value, data, node) => {
+ if (!value) {
+ //濡傛灉鏁版嵁涓虹┖锛屽垯杩斿洖true,鏄剧ず鎵�鏈夌殑鏁版嵁椤�
return true;
}
- // 鍚﹀垯鐨勮瘽鍐嶅線涓婁竴灞傚仛鍖归厤
- parentData = parentData.parent;
- index++;
- }
- // 娌″尮閰嶅埌杩斿洖false
- return false;
-};
-getProductTreeList();
+ // 鏌ヨ鍒楄〃鏄惁鏈夊尮閰嶆暟鎹紝灏嗗�煎皬鍐欙紝鍖归厤鑻辨枃鏁版嵁
+ let val = value.toLowerCase();
+ return chooseNode(val, data, node); // 璋冪敤杩囨护浜屽眰鏂规硶
+ };
+ // 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� (濡傛灉杈撳叆鐨勫弬鏁版槸鐖惰妭鐐逛笖鑳藉尮閰嶏紝鍒欒繑鍥炶鑺傜偣浠ュ強鍏朵笅鐨勬墍鏈夊瓙鑺傜偣锛涘鏋滃弬鏁版槸瀛愯妭鐐癸紝鍒欒繑鍥炶鑺傜偣鐨勭埗鑺傜偣銆俷ame鏄腑鏂囧瓧绗︼紝enName鏄嫳鏂囧瓧绗�.
+ const chooseNode = (value, data, node) => {
+ if (data.label.indexOf(value) !== -1) {
+ return true;
+ }
+ const level = node.level;
+ // 濡傛灉浼犲叆鐨勮妭鐐规湰韬氨鏄竴绾ц妭鐐瑰氨涓嶇敤鏍¢獙浜�
+ if (level === 1) {
+ return false;
+ }
+ // 鍏堝彇褰撳墠鑺傜偣鐨勭埗鑺傜偣
+ let parentData = node.parent;
+ // 閬嶅巻褰撳墠鑺傜偣鐨勭埗鑺傜偣
+ let index = 0;
+ while (index < level - 1) {
+ // 濡傛灉鍖归厤鍒扮洿鎺ヨ繑鍥烇紝姝ゅname鍊兼槸涓枃瀛楃锛宔nName鏄嫳鏂囧瓧绗︺�傚垽鏂尮閰嶄腑鑻辨枃杩囨护
+ if (parentData.data.label.indexOf(value) !== -1) {
+ return true;
+ }
+ // 鍚﹀垯鐨勮瘽鍐嶅線涓婁竴灞傚仛鍖归厤
+ parentData = parentData.parent;
+ index++;
+ }
+ // 娌″尮閰嶅埌杩斿洖false
+ return false;
+ };
+ getProductTreeList();
</script>
<style scoped>
-.product-view {
- display: flex;
-}
-.left {
- width: 450px;
- min-width: 450px;
- padding: 16px;
- background: #ffffff;
-}
-.right {
- flex: 1;
- min-width: 0;
- padding: 16px;
- margin-left: 20px;
- background: #ffffff;
-}
-.custom-tree-node {
- flex: 1;
- min-width: 0;
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-size: 14px;
- padding-right: 8px;
-}
-.tree-node-content {
- flex: 1;
- min-width: 0;
- display: flex;
- align-items: center;
- height: 100%;
- overflow: hidden;
-}
-.tree-node-content .orange-icon {
- flex-shrink: 0;
-}
-.tree-node-label {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-.orange-icon {
- color: orange;
- font-size: 18px;
- margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
-}
-.product-tree-scroll {
- scrollbar-width: thin;
- scrollbar-color: #c0c4cc #f5f7fa;
-}
-.product-tree-scroll::-webkit-scrollbar {
- width: 8px;
-}
-.product-tree-scroll::-webkit-scrollbar-track {
- background: #f5f7fa;
- border-radius: 4px;
-}
-.product-tree-scroll::-webkit-scrollbar-thumb {
- background: #c0c4cc;
- border-radius: 4px;
-}
-.product-tree-scroll::-webkit-scrollbar-thumb:hover {
- background: #909399;
-}
+ .product-view {
+ display: flex;
+ }
+ .left {
+ width: 450px;
+ min-width: 450px;
+ padding: 16px;
+ background: #ffffff;
+ }
+ .right {
+ flex: 1;
+ min-width: 0;
+ padding: 16px;
+ margin-left: 20px;
+ background: #ffffff;
+ }
+ .custom-tree-node {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 14px;
+ padding-right: 8px;
+ }
+ .tree-node-content {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ height: 100%;
+ overflow: hidden;
+ }
+ .tree-node-content .orange-icon {
+ flex-shrink: 0;
+ }
+ .tree-node-label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .orange-icon {
+ color: orange;
+ font-size: 18px;
+ margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
+ }
+ .product-tree-scroll {
+ scrollbar-width: thin;
+ scrollbar-color: #c0c4cc #f5f7fa;
+ }
+ .product-tree-scroll::-webkit-scrollbar {
+ width: 8px;
+ }
+ .product-tree-scroll::-webkit-scrollbar-track {
+ background: #f5f7fa;
+ border-radius: 4px;
+ }
+ .product-tree-scroll::-webkit-scrollbar-thumb {
+ background: #c0c4cc;
+ border-radius: 4px;
+ }
+ .product-tree-scroll::-webkit-scrollbar-thumb:hover {
+ background: #909399;
+ }
</style>
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 28356dd..4fd65f6 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -384,6 +384,21 @@
handlePrint(row);
},
},
+ {
+ name: "鐢熶骇杩芥函",
+ type: "text",
+ color: "#409eff",
+ clickFun: row => {
+ router.push({
+ path: "/productionTraceability/index",
+ query: {
+ npsNo: row.npsNo,
+ productName: row.productName,
+ model: row.model,
+ },
+ });
+ },
+ },
],
},
]);
diff --git a/src/views/productionManagement/productionTraceability/index.vue b/src/views/productionManagement/productionTraceability/index.vue
new file mode 100644
index 0000000..e2ac6df
--- /dev/null
+++ b/src/views/productionManagement/productionTraceability/index.vue
@@ -0,0 +1,831 @@
+<template>
+ <div class="app-container">
+ <el-card style="height:82vh;overflow:auto;">
+ <template #header>
+ <div class="card-header">
+ <el-form :inline="true"
+ :model="searchForm"
+ class="search-form">
+ <el-form-item label="鐢熶骇璁㈠崟鍙�">
+ <el-select v-model="selectedNpsNo"
+ filterable
+ remote
+ reserve-keyword
+ placeholder="璇疯緭鍏ョ敓浜ц鍗曞彿"
+ :loading="npsNoLoading"
+ :remote-method="handleNpsNoSearch"
+ @change="handleSearch"
+ style="width: 400px;">
+ <el-option v-for="option in npsNoOptions"
+ :key="option.id"
+ :label="option.npsNo + '-' + option.productName + '-' + option.model"
+ :value="option.id" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button @click="handleBack">杩斿洖</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ </template>
+ <!-- 鍩虹淇℃伅 -->
+ <div v-if="rowData.productionOrderDto"
+ class="detail-section">
+ <h3 class="section-title">鍩虹淇℃伅</h3>
+ <el-descriptions :column="3"
+ border>
+ <el-descriptions-item label="鐢熶骇璁㈠崟鍙�">{{ rowData.productionOrderDto?.npsNo || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="浜у搧鍚嶇О">{{ rowData.productionOrderDto?.productName || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="浜у搧瑙勬牸">{{ rowData.productionOrderDto?.model || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="鐗╂枡缂栫爜">{{ rowData.productionOrderDto?.materialCode || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="璁″垝鏁伴噺">{{ rowData.productionOrderDto?.quantity || 0 }} <span class="unit">{{ rowData.productionOrderDto?.unit || '-' }}</span></el-descriptions-item>
+ <el-descriptions-item label="褰撳墠鐘舵��">
+ <el-tag :type="getStatusType(rowData.productionOrderDto?.status)">
+ {{ getStatusText(rowData.productionOrderDto?.status) }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="瀹㈡埛鍚嶇О">{{ rowData.productionOrderDto?.customerName || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="寮�濮嬫棩鏈�">{{ rowData.productionOrderDto?.startTime || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="瀹屾垚杩涘害">
+ <el-progress :percentage="rowData.productionOrderDto?.completionStatus"
+ :color="customColors(rowData.productionOrderDto?.completionStatus)"
+ :status="rowData.productionOrderDto?.completionStatus === 100 ? 'success' : ''"
+ style="width: 120px;" />
+ </el-descriptions-item>
+ </el-descriptions>
+ </div>
+ <el-empty v-else
+ description="璇锋悳绱㈢敓浜ц鍗曞彿" />
+ <!-- 鐢熶骇鎶ュ伐璁板綍 -->
+ <div v-if="rowData.productionRecords && rowData.productionRecords.length > 0"
+ class="progress-container">
+ <div class="progress-section">
+ <h3 class="section-title">宸ュ崟淇℃伅</h3>
+ <div class="order-item">
+ <el-table :data="rowData.productionRecords"
+ border
+ style="width: 100%">
+ <el-table-column prop="productNo"
+ label="宸ュ崟缂栧彿"
+ align="center">
+ </el-table-column>
+ <el-table-column prop="productName"
+ label="浜у搧鍚嶇О"
+ align="center" />
+ <el-table-column prop="model"
+ label="瑙勬牸"
+ align="center" />
+ <el-table-column prop="processName"
+ label="宸ュ簭鍚嶇О"
+ align="center" />
+ <el-table-column prop="requiredQuantity"
+ label="闇�姹傛暟閲�"
+ align="center" />
+ <el-table-column prop="completedQuantity"
+ label="瀹屾垚鏁伴噺"
+ align="center" />
+ <el-table-column label="璇︽儏"
+ align="center"
+ width="200">
+ <template #default="{ row }">
+ <el-link @click="handleClickStep(row)"
+ type="primary">鎶ュ伐璁板綍</el-link>
+ <el-link @click="handleClickQuality(row)"
+ style="margin-left:20px"
+ type="primary">璐ㄦ淇℃伅</el-link>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </div>
+ </div>
+ <el-empty v-else-if="rowData.productionOrderDto"
+ description="鏆傛棤鎶ュ伐璁板綍" />
+ </el-card>
+ <!-- 鐢熶骇鎶ュ伐璇︽儏寮圭獥 -->
+ <el-dialog v-model="detailDialogVisible"
+ title="鐢熶骇鎶ュ伐璇︽儏"
+ width="1000px"
+ :close-on-click-modal="false"
+ custom-class="custom-dialog">
+ <div class="detail-container">
+ <!-- 鍩虹淇℃伅 -->
+ <div class="detail-section">
+ <h3 class="section-title">鍩虹淇℃伅</h3>
+ <el-descriptions :column="3"
+ border>
+ <el-descriptions-item label="鐢熶骇宸ュ崟鍙�">{{ detailData.npsNo || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="鐝粍">
+ <el-tag :type="detailData.schedule === '鐧界彮' ? 'primary' : 'warning'">{{ detailData.schedule || '-' }}</el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="浜у搧缂栫爜">{{ detailData.materialCode || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="浜у搧鍚嶇О">{{ detailData.productName || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="瑙勬牸">{{ detailData.model || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="鍚堟牸鏁伴噺"><span class="num2">{{ detailData.qualifiedQuantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item>
+ <el-descriptions-item label="涓嶅悎鏍兼暟閲�"><span class="num3">{{ detailData.unqualifiedQuantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item>
+ <el-descriptions-item label="鎬绘暟閲�"><span class="num1">{{ detailData.quantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item>
+ <el-descriptions-item label="寮�濮嬫椂闂�">{{ detailData.reportingTime || '-' }}</el-descriptions-item>
+ </el-descriptions>
+ </div>
+ <div class="detail-section">
+ <h3 class="section-title">鎶ュ伐鏄庣粏</h3>
+ <el-table :data="[detailData]"
+ border
+ style="width: 100%">
+ <el-table-column label="鎶ュ伐鍗曞彿"
+ prop="productNo"
+ align="center" />
+ <el-table-column label="浜у嚭鏁伴噺"
+ prop="qualifiedQuantity"
+ align="center" />
+ <el-table-column label="鎶ュ簾鏁伴噺"
+ prop="unqualifiedQuantity"
+ align="center" />
+ <el-table-column label="鍒涘缓鏃堕棿"
+ prop="reportingTime"
+ align="center" />
+ <el-table-column label="鎿嶄綔"
+ align="center"
+ width="200">
+ <template #default="{ row }">
+ <el-button type="primary"
+ link
+ @click="showInput(row.id)">鏌ョ湅鎶曞叆</el-button>
+ <el-button type="primary"
+ link
+ @click="showParamDetail(row.productionOperationParamList)">鍙傛暟璇︽儏</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ <!-- 鎶曞叆妯℃�佹 -->
+ <input-modal v-if="isShowInput"
+ v-model:visible="isShowInput"
+ :production-product-main-id="isShowingId" />
+ <!-- 鍙傛暟璇︽儏寮圭獥 -->
+ <el-dialog v-model="paramDetailVisible"
+ title="鍙傛暟璇︽儏"
+ width="600px">
+ <div v-if="currentParams && currentParams.length > 0"
+ class="param-detail-list">
+ <el-descriptions :column="1"
+ border>
+ <el-descriptions-item v-for="param in currentParams"
+ :key="param.id"
+ :label="param.paramName">
+ {{ param.inputValue }}
+ <span v-if="param.unit && param.unit !== '/'"
+ class="unit-text">({{ param.unit }})</span>
+ </el-descriptions-item>
+ </el-descriptions>
+ </div>
+ <el-empty v-else
+ description="鏆傛棤鍙傛暟鏁版嵁" />
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="paramDetailVisible = false">鍏抽棴</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <!-- 璐ㄦ淇℃伅寮圭獥 -->
+ <el-dialog v-model="qualityDialogVisible"
+ title="璐ㄦ璇︽儏"
+ width="1000px"
+ :close-on-click-modal="false"
+ custom-class="custom-dialog">
+ <div class="detail-container">
+ <div v-for="(record, index) in qualityRecords"
+ :key="record.id"
+ class="quality-record-block">
+ <div class="detail-section">
+ <h3 class="section-title">妫�娴嬭褰� {{ index + 1 }} - {{ record.checkTime }}</h3>
+ <el-descriptions :column="3"
+ border>
+ <el-descriptions-item label="妫�娴嬫棩鏈�">{{ record.checkTime || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="鐢熶骇宸ュ崟鍙�">{{ record.workOrderNo || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="宸ュ簭">{{ record.process || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="妫�楠屽憳">{{ record.checkName || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="浜у搧鍚嶇О">{{ record.productName || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="瑙勬牸鍨嬪彿">{{ record.model || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="鏁伴噺">{{ record.quantity || 0 }} {{ record.unit || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="妫�娴嬪崟浣�">{{ record.checkCompany || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="妫�娴嬬粨鏋�">
+ <el-tag :type="record.checkResult === '鍚堟牸' ? 'success' : 'danger'">
+ {{ record.checkResult || '-' }}
+ </el-tag>
+ </el-descriptions-item>
+ </el-descriptions>
+ <h4 class="sub-section-title">妫�楠屾寚鏍囧垪琛�</h4>
+ <el-table :data="record.inspectItems"
+ border
+ style="width: 100%">
+ <el-table-column label="搴忓彿"
+ type="index"
+ width="60"
+ align="center" />
+ <el-table-column label="鎸囨爣"
+ prop="itemName"
+ align="center" />
+ <el-table-column label="鍗曚綅"
+ prop="unit"
+ align="center" />
+ <el-table-column label="鏍囧噯鍊�"
+ prop="standardValue"
+ align="center" />
+ <el-table-column label="鍐呮帶鍊�"
+ prop="controlValue"
+ align="center" />
+ <el-table-column label="瀹為檯鍊�"
+ prop="actualValue"
+ align="center" />
+ </el-table>
+ </div>
+ <!-- <div class="detail-section">
+
+ </div> -->
+ <el-divider v-if="index < qualityRecords.length - 1" />
+ </div>
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="qualityDialogVisible = false">鍏抽棴</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+ import { ref, reactive, onMounted } from "vue";
+ import { useRoute, useRouter } from "vue-router";
+ import { ElMessage } from "element-plus";
+ import InputModal from "@/views/productionManagement/productionReporting/Input.vue";
+
+ const route = useRoute();
+ const router = useRouter();
+
+ // 鎼滅储鐩稿叧
+ const searchForm = reactive({
+ npsNo: "",
+ });
+ const selectedNpsNo = ref(null);
+ const npsNoLoading = ref(false);
+ const npsNoOptions = ref([
+ {
+ id: 1,
+ npsNo: "PO20240301001",
+ productName: "绮惧瘑娑插帇缂�",
+ model: "HG-100/50-500",
+ materialCode: "MAT-2024-001",
+ quantity: 500,
+ unit: "浠�",
+ status: 1,
+ customerName: "閲嶅伐鏈烘鏈夐檺鍏徃",
+ startTime: "2024-03-01",
+ completionStatus: 65,
+ },
+ {
+ id: 2,
+ npsNo: "PO20240301002",
+ productName: "宸ヤ笟浼烘湇鐢垫満",
+ model: "SV-400W-3000",
+ materialCode: "MAT-2024-002",
+ quantity: 200,
+ unit: "鍙�",
+ status: 2,
+ customerName: "鑷姩鍖栬澶囩鎶�鍏徃",
+ startTime: "2024-03-02",
+ completionStatus: 100,
+ },
+ {
+ id: 3,
+ npsNo: "PO20240301003",
+ productName: "楂樺帇瀵嗗皝鍦�",
+ model: "SR-80-5",
+ materialCode: "MAT-2024-003",
+ quantity: 5000,
+ unit: "涓�",
+ status: 0,
+ customerName: "瀵嗗皝绯荤粺閰嶄欢鍘�",
+ startTime: "2024-03-05",
+ completionStatus: 0,
+ },
+ ]);
+
+ // 璇︽儏鏁版嵁
+ const rowData = reactive({
+ productionOrderDto: null,
+ productionRecords: [],
+ });
+
+ // 鎶ュ伐璇︽儏寮圭獥
+ const detailDialogVisible = ref(false);
+ const detailData = ref({});
+
+ // 鎶曞叆妯℃�佹
+ const isShowInput = ref(false);
+ const isShowingId = ref(0);
+ const showInput = id => {
+ isShowInput.value = true;
+ isShowingId.value = id;
+ };
+
+ // 鍙傛暟璇︽儏寮圭獥
+ const paramDetailVisible = ref(false);
+ const currentParams = ref([]);
+ const showParamDetail = params => {
+ currentParams.value = params || [];
+ paramDetailVisible.value = true;
+ };
+
+ // 璐ㄦ淇℃伅寮圭獥
+ const qualityDialogVisible = ref(false);
+ const qualityRecords = ref([]);
+
+ // 鐘舵�佸鐞�
+ const getStatusType = status => {
+ const typeMap = { 0: "info", 1: "primary", 2: "success" };
+ return typeMap[status] || "info";
+ };
+ const getStatusText = status => {
+ const statusMap = { 0: "鏈紑濮�", 1: "鐢熶骇涓�", 2: "宸插畬鎴�" };
+ return statusMap[status] || "鏈煡";
+ };
+ const customColors = percentage => {
+ if (percentage < 30) return "#f56c6c";
+ if (percentage < 70) return "#e6a23c";
+ return "#67c23a";
+ };
+
+ // 妯℃嫙鎼滅储鏂规硶
+ const handleNpsNoSearch = query => {
+ if (query) {
+ npsNoLoading.value = true;
+ setTimeout(() => {
+ npsNoLoading.value = false;
+ }, 300);
+ }
+ };
+
+ const handleSearch = id => {
+ const selected = npsNoOptions.value.find(item => item.id === id);
+ if (selected) {
+ rowData.productionOrderDto = selected;
+ rowData.productionRecords = [
+ {
+ id: 1001,
+ productNo: "MO-2024-001-01",
+ productName: selected.productName,
+ model: selected.model,
+ processName: "姣涘澂鍔犲伐",
+ requiredQuantity: selected.quantity,
+ completedQuantity: Math.floor(selected.quantity * 0.4),
+ qualifiedQuantity: Math.floor(selected.quantity * 0.4) - 2,
+ unqualifiedQuantity: 2,
+ reportingTime: "2024-03-01 10:00:00",
+ schedule: "鐧界彮",
+ postName: "寮犱笁",
+ unit: selected.unit,
+ },
+ {
+ id: 1002,
+ productNo: "MO-2024-001-02",
+ productName: selected.productName,
+ model: selected.model,
+ processName: "绮惧姞宸�",
+ requiredQuantity: Math.floor(selected.quantity * 0.4),
+ completedQuantity: Math.floor(selected.quantity * 0.25),
+ qualifiedQuantity: Math.floor(selected.quantity * 0.25),
+ unqualifiedQuantity: 0,
+ reportingTime: "2024-03-01 16:00:00",
+ schedule: "鐧界彮",
+ postName: "鏉庡洓",
+ unit: selected.unit,
+ },
+ ];
+ }
+ };
+
+ const handleBack = () => {
+ router.back();
+ };
+
+ const handleClickStep = row => {
+ detailData.value = {
+ id: row.id || Math.floor(Math.random() * 1000),
+ productNo: row.productNo,
+ npsNo: rowData.productionOrderDto.npsNo,
+ schedule: row.schedule,
+ postName: row.postName,
+ materialCode: rowData.productionOrderDto.materialCode,
+ productName: row.productName,
+ model: row.model,
+ qualifiedQuantity: row.qualifiedQuantity,
+ unqualifiedQuantity: row.unqualifiedQuantity || 0,
+ quantity: row.completedQuantity,
+ unit: row.unit,
+ reportingTime: row.reportingTime,
+ productionOperationParamList: [
+ { id: 1, paramName: "涓昏酱杞��", inputValue: "2400", unit: "rpm" },
+ { id: 2, paramName: "杩涚粰閫熷害", inputValue: "120", unit: "mm/min" },
+ { id: 3, paramName: "鍒囧墛娣卞害", inputValue: "0.5", unit: "mm" },
+ { id: 4, paramName: "鍐峰嵈娑插帇鍔�", inputValue: "0.6", unit: "Mpa" },
+ ],
+ };
+ detailDialogVisible.value = true;
+ };
+
+ const handleClickQuality = row => {
+ qualityRecords.value = [
+ {
+ id: 2001,
+ checkTime: "2024-03-01 11:30:00",
+ workOrderNo: row.productNo,
+ process: row.processName,
+ checkName: "璐ㄩ噺閮�-鐜嬪缓鍥�",
+ productName: row.productName,
+ model: row.model,
+ unit: row.unit,
+ quantity: row.completedQuantity,
+ checkCompany: "鍐呴儴瀹為獙瀹�",
+ checkResult: "鍚堟牸",
+ inspectItems: [
+ {
+ id: 1,
+ itemName: "澶栧緞灏哄",
+ unit: "mm",
+ standardValue: "100.00卤0.05",
+ controlValue: "100.00卤0.03",
+ actualValue: "100.01",
+ result: "鍚堟牸",
+ },
+ {
+ id: 2,
+ itemName: "鍐呭緞灏哄",
+ unit: "mm",
+ standardValue: "50.00+0.02/-0",
+ controlValue: "50.00+0.01/-0",
+ actualValue: "50.01",
+ result: "鍚堟牸",
+ },
+ {
+ id: 3,
+ itemName: "琛ㄩ潰绮楃硻搴�",
+ unit: "Ra",
+ standardValue: "鈮�1.6",
+ controlValue: "鈮�1.2",
+ actualValue: "0.8",
+ result: "鍚堟牸",
+ },
+ ],
+ },
+ {
+ id: 2001,
+ checkTime: "2024-03-01 11:30:00",
+ workOrderNo: row.productNo,
+ process: row.processName,
+ checkName: "璐ㄩ噺閮�-鐜嬪缓鍥�",
+ productName: row.productName,
+ model: row.model,
+ unit: row.unit,
+ quantity: row.completedQuantity,
+ checkCompany: "鍐呴儴瀹為獙瀹�",
+ checkResult: "鍚堟牸",
+ inspectItems: [
+ {
+ id: 1,
+ itemName: "澶栧緞灏哄",
+ unit: "mm",
+ standardValue: "100.00卤0.05",
+ controlValue: "100.00卤0.03",
+ actualValue: "100.01",
+ result: "鍚堟牸",
+ },
+ {
+ id: 2,
+ itemName: "鍐呭緞灏哄",
+ unit: "mm",
+ standardValue: "50.00+0.02/-0",
+ controlValue: "50.00+0.01/-0",
+ actualValue: "50.01",
+ result: "鍚堟牸",
+ },
+ {
+ id: 3,
+ itemName: "琛ㄩ潰绮楃硻搴�",
+ unit: "Ra",
+ standardValue: "鈮�1.6",
+ controlValue: "鈮�1.2",
+ actualValue: "0.8",
+ result: "鍚堟牸",
+ },
+ ],
+ },
+ ];
+ qualityDialogVisible.value = true;
+ };
+
+ onMounted(() => {
+ if (route.query.npsNo) {
+ const npsNo = route.query.npsNo;
+ const found = npsNoOptions.value.find(item => item.npsNo === npsNo);
+ if (found) {
+ selectedNpsNo.value = found.id;
+ handleSearch(found.id);
+ } else {
+ // 濡傛灉娌℃壘鍒帮紝鍒涘缓涓�涓复鏃剁殑
+ const mockItem = {
+ id: Date.now(),
+ npsNo: npsNo,
+ productName: route.query.productName || "绮惧瘑娑插帇缂�",
+ model: route.query.model || "HG-100/50-500",
+ materialCode: "MAT-2024-MOCK",
+ quantity: 100,
+ unit: "浠�",
+ status: 1,
+ customerName: "妯℃嫙瀹㈡埛",
+ startTime: "2024-03-01",
+ completionStatus: 50,
+ };
+ npsNoOptions.value.push(mockItem);
+ selectedNpsNo.value = mockItem.id;
+ handleSearch(mockItem.id);
+ }
+ }
+ });
+</script>
+
+<style scoped>
+ .app-container {
+ padding: 20px;
+ background-color: #f5f7fa;
+ min-height: 100vh;
+ }
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 10px;
+ }
+
+ .search-form {
+ width: 100%;
+ }
+
+ .search-form .el-form-item {
+ margin-right: 10px;
+ }
+
+ .detail-section {
+ margin-bottom: 24px;
+ background-color: #ffffff;
+ border-radius: 10px;
+ padding: 24px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
+ transition: all 0.3s ease;
+ }
+
+ .detail-section:hover {
+ box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12);
+ }
+
+ .section-title {
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 20px;
+ color: #1a1a1a;
+ border-bottom: 2px solid #409eff;
+ padding-bottom: 10px;
+ display: flex;
+ align-items: center;
+ }
+
+ .section-title::before {
+ content: "";
+ display: inline-block;
+ width: 4px;
+ height: 16px;
+ background-color: #409eff;
+ margin-right: 8px;
+ border-radius: 2px;
+ }
+
+ .sub-section-title {
+ font-size: 14px;
+ font-weight: 600;
+ margin-bottom: 16px;
+ color: #303133;
+ display: flex;
+ align-items: center;
+ }
+
+ .sub-section-title::before {
+ content: "";
+ display: inline-block;
+ width: 3px;
+ height: 12px;
+ background-color: #67c23a;
+ margin-right: 8px;
+ border-radius: 2px;
+ }
+
+ .unit {
+ font-size: 12px;
+ color: #909399;
+ margin-left: 4px;
+ }
+
+ :deep(.el-descriptions) {
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+ }
+
+ :deep(.el-descriptions__row:nth-child(odd)) {
+ background-color: #f9f9f9;
+ }
+
+ :deep(.el-descriptions__label) {
+ font-weight: 500;
+ color: #606266;
+ background-color: #f5f7fa;
+ }
+
+ :deep(.el-descriptions__content) {
+ color: #303133;
+ font-weight: 500;
+ }
+
+ .progress-container {
+ display: flex;
+ gap: 24px;
+ }
+
+ .progress-section {
+ margin-bottom: 24px;
+ background-color: #ffffff;
+ border-radius: 10px;
+ padding: 24px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
+ flex: 1;
+ transition: all 0.3s ease;
+ width: 100%;
+ }
+
+ .progress-section:hover {
+ box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12);
+ }
+
+ .order-item {
+ margin-bottom: 20px;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+ }
+
+ :deep(.el-table) {
+ border-radius: 8px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+ }
+
+ :deep(.el-table th) {
+ background-color: #f5f7fa !important;
+ font-weight: 600;
+ color: #606266;
+ }
+
+ :deep(.el-progress-bar__inner) {
+ border-radius: 10px;
+ }
+
+ :deep(.el-tag) {
+ border-radius: 12px;
+ padding: 2px 10px;
+ }
+
+ /* 寮圭獥鏍峰紡 */
+ .detail-container {
+ max-height: 600px;
+ overflow-y: auto;
+ padding: 0 16px;
+ }
+
+ .process-item {
+ margin-bottom: 24px;
+ padding: 20px;
+ background-color: #ffffff;
+ border-radius: 8px;
+ border: 1px solid #ebeef5;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ }
+
+ .process-header {
+ margin-bottom: 20px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #f0f2f5;
+ }
+
+ .process-title {
+ font-size: 15px;
+ font-weight: 600;
+ margin-bottom: 12px;
+ color: #1a1a1a;
+ display: flex;
+ align-items: center;
+ }
+
+ .process-title::before {
+ content: "";
+ display: inline-block;
+ width: 4px;
+ height: 16px;
+ background-color: #409eff;
+ margin-right: 8px;
+ border-radius: 2px;
+ }
+
+ .process-info {
+ display: flex;
+ gap: 20px;
+ font-size: 13px;
+ color: #606266;
+ }
+
+ .process-label {
+ padding: 4px 12px;
+ background-color: #ecf5ff;
+ border-radius: 4px;
+ color: #409eff;
+ font-weight: 500;
+ }
+
+ .process-details {
+ margin-bottom: 20px;
+ }
+
+ .num1 {
+ color: #1107cc;
+ font-weight: 600;
+ }
+
+ .num2 {
+ color: #0fcf25;
+ font-weight: 600;
+ }
+
+ .num3 {
+ color: #d31818;
+ font-weight: 600;
+ }
+
+ .dialog-footer {
+ text-align: center;
+ padding: 20px;
+ border-top: 1px solid #ebeef5;
+ }
+
+ .dialog-footer .el-button {
+ min-width: 100px;
+ padding: 8px 20px;
+ }
+
+ /* 鑷畾涔夊璇濇鏍峰紡 */
+ :deep(.custom-dialog) {
+ border-radius: 12px;
+ overflow: hidden;
+ }
+
+ :deep(.custom-dialog .el-dialog__header) {
+ background-color: #f5f7fa;
+ padding: 20px;
+ border-bottom: 1px solid #ebeef5;
+ }
+
+ :deep(.custom-dialog .el-dialog__title) {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1a1a1a;
+ }
+
+ :deep(.custom-dialog .el-dialog__body) {
+ padding: 20px;
+ }
+
+ .unit-text {
+ margin-left: 5px;
+ color: #909399;
+ font-size: 12px;
+ }
+
+ .param-detail-list {
+ padding: 10px;
+ }
+</style>
--
Gitblit v1.9.3