From 21c1360819a78ab734046fe6e0aa91b4da9f510a Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期六, 18 四月 2026 10:02:55 +0800
Subject: [PATCH] 生产订单工艺路线
---
src/views/productionManagement/processRoute/processRouteItem/index.vue | 391 ++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 299 insertions(+), 92 deletions(-)
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index 18e21e8..c2c24b4 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -1,9 +1,9 @@
<template>
<div class="app-container">
- <PageHeader content="宸ヨ壓璺嚎椤圭洰" />
+ <PageHeader content="浜у搧閮ㄤ欢" />
<!-- 宸ヨ壓璺嚎淇℃伅灞曠ず -->
- <el-card v-if="routeInfo.processRouteCode" class="route-info-card" shadow="hover">
+ <!-- <el-card v-if="routeInfo.processRouteCode" class="route-info-card" shadow="hover">
<div class="route-info">
<div class="info-item">
<div class="info-label-wrapper">
@@ -46,11 +46,11 @@
</div>
</div>
</div>
- </el-card>
+ </el-card> -->
<!-- 琛ㄦ牸瑙嗗浘 -->
<div v-if="viewMode === 'table'" class="section-header">
- <div class="section-title">宸ヨ壓璺嚎椤圭洰鍒楄〃</div>
+ <div class="section-title">浜у搧閮ㄤ欢鍒楄〃</div>
<div class="section-actions">
<el-button
icon="Grid"
@@ -74,18 +74,55 @@
class="lims-table"
>
<el-table-column align="center" label="搴忓彿" width="60" type="index" />
- <el-table-column label="宸ュ簭鍚嶇О" prop="processId" width="200">
+ <el-table-column label="浜у搧鍚嶇О" prop="name" min-width="140" show-overflow-tooltip>
<template #default="scope">
- {{ getProcessName(scope.row.processId) || '-' }}
+ {{ getProcessField(scope.row, 'name') }}
</template>
</el-table-column>
- <el-table-column label="浜у搧鍚嶇О" prop="productName" min-width="160" />
- <el-table-column label="瑙勬牸鍚嶇О" prop="model" min-width="140" />
- <el-table-column label="鍗曚綅" prop="unit" width="100" />
+ <el-table-column label="浜у搧瑙勬牸" prop="productModel" min-width="120" show-overflow-tooltip>
+ <template #default="scope">
+ {{ getProcessField(scope.row, 'productModel') }}
+ </template>
+ </el-table-column>
+ <el-table-column label="閮ㄤ欢缂栧彿" prop="no" width="120" show-overflow-tooltip>
+ <template #default="scope">
+ {{ getProcessField(scope.row, 'no') }}
+ </template>
+ </el-table-column>
+ <el-table-column label="閮ㄤ欢绫诲瀷" prop="typeText" width="120" show-overflow-tooltip>
+ <template #default="scope">
+ {{ getProcessTypeText(getProcessRaw(scope.row)?.type) || '-' }}
+ </template>
+ </el-table-column>
+ <el-table-column label="璁″垝宸ユ椂(灏忔椂)" prop="salaryQuota" width="130" align="center">
+ <template #default="scope">
+ {{ getProcessField(scope.row, 'salaryQuota') }}
+ </template>
+ </el-table-column>
+ <el-table-column label="璁″垝浜哄憳" prop="plannerName" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ {{ getProcessField(scope.row, 'plannerName') }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鏄惁璐ㄦ" prop="isQuality" width="90" align="center">
+ <template #default="scope">
+ {{ scope.row.isQuality ? '鏄�' : '鍚�' }}
+ </template>
+ </el-table-column>
+ <el-table-column label="澶囨敞" prop="remark" min-width="100" show-overflow-tooltip>
+ <template #default="scope">
+ {{ getProcessField(scope.row, 'remark') }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鏇存柊鏃堕棿" prop="updateTime" width="160" align="center">
+ <template #default="scope">
+ {{ getProcessField(scope.row, 'updateTime') }}
+ </template>
+ </el-table-column>
<el-table-column label="鎿嶄綔" align="center" fixed="right" width="150">
<template #default="scope">
- <el-button type="primary" link size="small" @click="handleEdit(scope.row)">缂栬緫</el-button>
- <el-button type="danger" link size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ <el-button type="primary" link size="small" @click="handleEdit(scope.row)" :disabled="scope.row.isComplete">缂栬緫</el-button>
+ <el-button type="danger" link size="small" @click="handleDelete(scope.row)" :disabled="scope.row.isComplete">鍒犻櫎</el-button>
</template>
</el-table-column>
</el-table>
@@ -119,99 +156,153 @@
<!-- 搴忓彿鍦嗗湀 -->
<div class="card-header">
<div class="card-number">{{ index + 1 }}</div>
- <div class="card-process-name">{{ getProcessName(item.processId) || '-' }}</div>
+ <div class="card-process-name">{{ getProcessField(item, 'name') }}</div>
</div>
- <!-- 浜у搧淇℃伅 -->
+ <!-- 涓庡伐搴忎富琛ㄤ竴鑷寸殑绠�瑕佷俊鎭� -->
<div class="card-content">
- <div v-if="item.productName" class="product-info">
- <div class="product-name">{{ item.productName }}</div>
- <div v-if="item.model" class="product-model">
- {{ item.model }}
- <!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> -->
+ <div class="product-info">
+ <div class="product-name">{{ getProcessField(item, 'productModel') }}</div>
+ <div v-if="getProcessRaw(item)?.no" class="product-model">缂栧彿 {{ getProcessRaw(item)?.no }}</div>
+ <div v-if="getProcessTypeText(getProcessRaw(item)?.type)" class="product-model">
+ {{ getProcessTypeText(getProcessRaw(item)?.type) }}
</div>
+ <el-tag type="primary" class="product-tag" v-if="item.isQuality">璐ㄦ</el-tag>
</div>
- <div v-else class="product-info empty">鏆傛棤浜у搧淇℃伅</div>
</div>
<!-- 鎿嶄綔鎸夐挳 -->
<div class="card-footer">
- <el-button type="primary" link size="small" @click="handleEdit(item)">缂栬緫</el-button>
- <el-button type="danger" link size="small" @click="handleDelete(item)">鍒犻櫎</el-button>
+ <el-button type="primary" link size="small" @click="handleEdit(item)" :disabled="item.isComplete">缂栬緫</el-button>
+ <el-button type="danger" link size="small" @click="handleDelete(item)" :disabled="item.isComplete">鍒犻櫎</el-button>
</div>
</div>
</div>
</div>
</template>
- <!-- 鏂板/缂栬緫寮圭獥 -->
+ <!-- 鏂板/缂栬緫寮圭獥锛堝竷灞�銆佸瓧娈典笌宸ュ簭/閮ㄤ欢椤� New銆丒dit 涓�鑷达紱鎻愪氦鍙傛暟浠嶄负鍘熸帴鍙e瓧娈碉級 -->
<el-dialog
v-model="dialogVisible"
:title="operationType === 'add' ? '鏂板宸ヨ壓璺嚎椤圭洰' : '缂栬緫宸ヨ壓璺嚎椤圭洰'"
- width="500px"
+ width="760"
@close="closeDialog"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
- label-width="120px"
+ label-width="140px"
+ label-position="top"
>
- <el-form-item label="宸ュ簭" prop="processId">
- <el-select
- v-model="form.processId"
- placeholder="璇烽�夋嫨宸ュ簭"
- clearable
- style="width: 100%"
- >
- <el-option
- v-for="process in processOptions"
- :key="process.id"
- :label="process.name"
- :value="process.id"
- />
- </el-select>
- </el-form-item>
-
- <el-form-item label="浜у搧鍚嶇О" prop="productModelId">
- <el-button type="primary" @click="showProductSelectDialog = true">
- {{ form.productName && form.model
- ? `${form.productName} - ${form.model}`
- : '閫夋嫨浜у搧' }}
- </el-button>
- </el-form-item>
-
- <el-form-item label="鍗曚綅" prop="unit">
- <el-input
- v-model="form.unit"
- :placeholder="form.productModelId ? '鏍规嵁閫夋嫨鐨勪骇鍝佽嚜鍔ㄥ甫鍑�' : '璇峰厛閫夋嫨浜у搧'"
- clearable
- :disabled="true"
- />
- </el-form-item>
+ <el-row :gutter="16">
+ <el-col :span="24">
+ <el-form-item label="閮ㄤ欢" prop="processId">
+ <el-select
+ v-model="form.processId"
+ placeholder="璇烽�夋嫨閮ㄤ欢"
+ clearable
+ filterable
+ style="width: 100%"
+ >
+ <el-option
+ v-for="process in processOptions"
+ :key="process.id"
+ :label="formatProcessOptionLabel(process)"
+ :value="process.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item
+ label="浜у搧鍚嶇О锛�"
+ prop="productId"
+ >
+ <el-tree-select
+ v-model="form.productId"
+ placeholder="璇烽�夋嫨浜у搧鍚嶇О"
+ clearable
+ filterable
+ check-strictly
+ :data="productCategoryOptions"
+ :render-after-expand="false"
+ style="width: 100%"
+ @change="handleProductChange"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item
+ label="浜у搧瑙勬牸锛�"
+ prop="productModelId"
+ >
+ <el-select
+ v-model="form.productModelId"
+ placeholder="璇烽�夋嫨浜у搧瑙勬牸"
+ clearable
+ filterable
+ :disabled="!form.productId"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in modelOptions"
+ :key="item.id"
+ :label="item.model"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="閮ㄤ欢缂栧彿">
+ <el-input :model-value="selectedProcess?.no ?? ''" readonly />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="閮ㄤ欢绫诲瀷">
+ <el-input :model-value="getProcessTypeText(selectedProcess?.type) || '-'" readonly />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璁″垝宸ユ椂(灏忔椂)">
+ <el-input :model-value="selectedProcess?.salaryQuota != null && selectedProcess?.salaryQuota !== '' ? String(selectedProcess.salaryQuota) : ''" readonly>
+ <template #append>灏忔椂</template>
+ </el-input>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璁″垝浜哄憳">
+ <el-input :model-value="selectedProcess?.plannerName ?? ''" readonly />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
+ <el-switch v-model="form.isQuality" :active-value="true" inactive-value="false"/>
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="澶囨敞">
+ <el-input :model-value="selectedProcess?.remark ?? ''" type="textarea" readonly />
+ </el-form-item>
+ </el-col>
+ </el-row>
</el-form>
<template #footer>
- <el-button @click="closeDialog">鍙栨秷</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">纭畾</el-button>
+ <el-button @click="closeDialog">鍙栨秷</el-button>
</template>
</el-dialog>
-
- <!-- 浜у搧閫夋嫨瀵硅瘽妗� -->
- <ProductSelectDialog
- v-model="showProductSelectDialog"
- @confirm="handleProductSelect"
- single
- />
</div>
</template>
<script setup>
import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import { findProcessRouteItemList, addOrUpdateProcessRouteItem, sortProcessRouteItem, batchDeleteProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
import { findProductProcessRouteItemList, deleteRouteItem, addRouteItem, addOrUpdateProductProcessRouteItem, sortRouteItem } from "@/api/productionManagement/productProcessRoute.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
+import { modelListPage, productTreeList } from "@/api/basicData/product";
import { useRoute } from 'vue-router'
import { ElMessageBox } from 'element-plus'
import Sortable from 'sortablejs'
@@ -241,7 +332,8 @@
});
const processOptions = ref([]);
-const showProductSelectDialog = ref(false);
+const productCategoryOptions = ref([]);
+const modelOptions = ref([]);
let tableSortable = null;
let cardSortable = null;
@@ -258,22 +350,133 @@
id: undefined,
routeId: routeId.value,
processId: undefined,
+ productId: undefined,
productModelId: undefined,
productName: "",
model: "",
- unit: "",
+ isQuality: false,
});
const rules = {
- processId: [{ required: true, message: '璇烽�夋嫨宸ュ簭', trigger: 'change' }],
- productModelId: [{ required: true, message: '璇烽�夋嫨浜у搧', trigger: 'change' }],
+ processId: [{ required: true, message: '璇烽�夋嫨閮ㄤ欢', trigger: 'change' }],
+ productId: [{ required: true, message: '璇烽�夋嫨浜у搧鍚嶇О', trigger: 'change' }],
+ productModelId: [{ required: true, message: '璇烽�夋嫨浜у搧瑙勬牸', trigger: 'change' }],
};
-// 鏍规嵁宸ュ簭ID鑾峰彇宸ュ簭鍚嶇О
-const getProcessName = (processId) => {
- if (!processId) return '';
- const process = processOptions.value.find(p => p.id === processId);
- return process ? process.name : '';
+const selectedProcess = computed(() => {
+ if (form.value.processId === undefined || form.value.processId === null || form.value.processId === '') return null;
+ return processOptions.value.find(p => String(p.id) === String(form.value.processId)) || null;
+});
+
+const getProcessTypeText = (type) => {
+ if (type === undefined || type === null) return '';
+ const map = {
+ 1: '鍔犲伐',
+ 2: '鍒澘鍐疯姱鍒朵綔',
+ 3: '绠¤矾缁勫',
+ 4: '缃愪綋杩炴帴鍙婅皟璇�',
+ 5: '娴嬭瘯鎵撳帇',
+ 6: '鍏朵粬',
+ };
+ return map[type] || '';
+};
+
+const getProcessRaw = (row) => {
+ if (!row?.processId) return null;
+ return processOptions.value.find(p => String(p.id) === String(row.processId)) || null;
+};
+
+const getProcessField = (row, key) => {
+ const p = getProcessRaw(row);
+ const fromProcess = p ? p[key] : undefined;
+ if (fromProcess !== undefined && fromProcess !== null && fromProcess !== '') return fromProcess;
+ if (key === 'name' && row.productName) return row.productName;
+ if (key === 'productModel' && row.model) return row.model;
+ const fromRow = row[key];
+ if (fromRow !== undefined && fromRow !== null && fromRow !== '') return fromRow;
+ return '-';
+};
+
+const formatProcessOptionLabel = (process) => {
+ if (!process) return '';
+ const no = process.no ? String(process.no).trim() : '';
+ const name = process.name || '';
+ if (no && name) return `${no} ${name}`;
+ return name || no || '';
+};
+
+const convertProductTree = (list) => {
+ return (list || []).map(item => {
+ const children = convertProductTree(item.children || item.childList || []);
+ return {
+ ...item,
+ value: item.id,
+ label: item.name || item.label,
+ children,
+ disabled: children.length > 0,
+ };
+ });
+};
+
+const findNodeById = (nodes, targetId) => {
+ for (const node of nodes || []) {
+ if (String(node.value) === String(targetId)) {
+ return node;
+ }
+ if (node.children && node.children.length > 0) {
+ const found = findNodeById(node.children, targetId);
+ if (found) return found;
+ }
+ }
+ return null;
+};
+
+const findNodeIdByLabel = (nodes, targetLabel) => {
+ for (const node of nodes || []) {
+ if (node.label === targetLabel) {
+ return node.value;
+ }
+ if (node.children && node.children.length > 0) {
+ const found = findNodeIdByLabel(node.children, targetLabel);
+ if (found !== null && found !== undefined) return found;
+ }
+ }
+ return undefined;
+};
+
+const getProductCategoryOptions = async () => {
+ try {
+ const res = await productTreeList();
+ const list = Array.isArray(res) ? res : res?.data || [];
+ productCategoryOptions.value = convertProductTree(list);
+ } catch (e) {
+ productCategoryOptions.value = [];
+ }
+};
+
+const getModelOptions = async (productId) => {
+ if (!productId) {
+ modelOptions.value = [];
+ return;
+ }
+ try {
+ const res = await modelListPage({
+ id: productId,
+ current: 1,
+ size: 999,
+ });
+ const records = res?.records || res?.data?.records || [];
+ modelOptions.value = records;
+ } catch (e) {
+ modelOptions.value = [];
+ }
+};
+
+const handleProductChange = async (value) => {
+ const selectedNode = findNodeById(productCategoryOptions.value, value);
+ form.value.productName = selectedNode?.label || '';
+ form.value.productModelId = undefined;
+ await getModelOptions(value);
};
// 鑾峰彇鍒楄〃
@@ -327,21 +530,28 @@
operationType.value = 'add';
resetForm();
dialogVisible.value = true;
+ getProductCategoryOptions();
};
// 缂栬緫
-const handleEdit = (row) => {
+const handleEdit = async (row) => {
operationType.value = 'edit';
form.value = {
id: row.id,
routeId: routeId.value,
processId: row.processId,
+ productId: row.productId,
productModelId: row.productModelId,
productName: row.productName || "",
model: row.model || "",
- unit: row.unit || "",
+ isQuality: row.isQuality,
};
dialogVisible.value = true;
+ await getProductCategoryOptions();
+ if (!form.value.productId && form.value.productName) {
+ form.value.productId = findNodeIdByLabel(productCategoryOptions.value, form.value.productName);
+ }
+ await getModelOptions(form.value.productId);
};
// 鍒犻櫎
@@ -370,20 +580,6 @@
.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.model = product.model;
- form.value.unit = product.unit || "";
- showProductSelectDialog.value = false;
- // 瑙﹀彂琛ㄥ崟楠岃瘉
- formRef.value?.validateField('productModelId');
- }
-};
-
// 鎻愪氦
const handleSubmit = () => {
formRef.value.validate((valid) => {
@@ -402,12 +598,14 @@
productRouteId: routeId.value,
processId: form.value.processId,
productModelId: form.value.productModelId,
+ isQuality: form.value.isQuality,
dragSort,
})
: addOrUpdateProcessRouteItem({
routeId: routeId.value,
processId: form.value.processId,
productModelId: form.value.productModelId,
+ isQuality: form.value.isQuality,
dragSort,
});
@@ -432,12 +630,14 @@
id: form.value.id,
processId: form.value.processId,
productModelId: form.value.productModelId,
+ isQuality: form.value.isQuality,
})
: addOrUpdateProcessRouteItem({
routeId: routeId.value,
processId: form.value.processId,
productModelId: form.value.productModelId,
id: form.value.id,
+ isQuality: form.value.isQuality,
});
updatePromise
@@ -463,12 +663,14 @@
id: undefined,
routeId: routeId.value,
processId: undefined,
+ productId: undefined,
productModelId: undefined,
productName: "",
model: "",
- unit: "",
+ isQuality: false,
};
- formRef.value?.resetFields();
+ modelOptions.value = [];
+ nextTick(() => formRef.value?.clearValidate());
};
// 鍏抽棴寮圭獥
@@ -611,6 +813,7 @@
getRouteInfo();
getList();
getProcessList();
+ getProductCategoryOptions();
});
onUnmounted(() => {
@@ -733,6 +936,10 @@
color: #409eff;
}
+.product-tag {
+ margin: 10px 0;
+}
+
.card-footer {
display: flex;
justify-content: space-around;
--
Gitblit v1.9.3