From ec5bd9a7abba71e9234fa0d327e8261f89723603 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期二, 17 三月 2026 16:22:20 +0800
Subject: [PATCH] 工艺路线替换回去,参数功能添加、dist.js修改字典增加id
---
src/views/productionManagement/processRoute/ItemsForm.vue | 531 ++++
src/utils/dict.js | 2
src/views/productionManagement/processRoute/index.vue | 2567 +------------------
src/views/productionManagement/processRoute/New.vue | 194 +
src/views/productionManagement/processRoute/processRouteItem/index.vue | 1024 +++++++
src/components/ProcessParamListDialog.vue | 631 ++++
src/views/productionPlan/productionPlan/index.vue | 104
src/views/productionManagement/processRoute/Edit.vue | 257 +
src/views/productionManagement/processRoute/index2.vue | 2417 ++++++++++++++++++
9 files changed, 5,362 insertions(+), 2,365 deletions(-)
diff --git a/src/components/ProcessParamListDialog.vue b/src/components/ProcessParamListDialog.vue
new file mode 100644
index 0000000..3b9f158
--- /dev/null
+++ b/src/components/ProcessParamListDialog.vue
@@ -0,0 +1,631 @@
+<template>
+ <el-dialog v-model="visible"
+ :title="title"
+ width="800px"
+ destroy-on-close>
+ <div class="param-list-container">
+ <div class="params-header">
+ <span>鍙傛暟鍒楄〃</span>
+ <el-button v-if="editable"
+ type="primary"
+ link
+ size="small"
+ @click="handleAddParam">
+ <el-icon>
+ <Plus />
+ </el-icon>鏂板
+ </el-button>
+ </div>
+ <div class="params-list">
+ <div v-for="param in paramList"
+ :key="param.id"
+ class="param-item">
+ <div class="param-info">
+ <span class="param-code">{{ param.paramName }}</span>
+ <span v-if="param.valueMode == 1"
+ class="param-value">
+ 鏍囧噯鍊硷細{{ param.standardValue || "-" }} {{ param.unit }}
+ </span>
+ <span v-else
+ class="param-value">
+ 鏍囧噯鍊硷細{{ param.minValue || "-" }}-{{ param.maxValue || "-" }} {{ param.unit }}
+ </span>
+ </div>
+ <div class="param-actions">
+ <el-button v-if="editable"
+ link
+ type="primary"
+ size="small"
+ @click="handleEditParam(param)">
+ 缂栬緫
+ </el-button>
+ <el-button v-if="editable"
+ link
+ type="danger"
+ size="small"
+ @click="handleDeleteParam(param)">
+ 鍒犻櫎
+ </el-button>
+ </div>
+ </div>
+ <el-empty v-if="!paramList || paramList.length === 0"
+ description="鏆傛棤鍙傛暟"
+ :image-size="50" />
+ </div>
+ </div>
+ <!-- 閫夋嫨鍙傛暟瀵硅瘽妗� -->
+ <el-dialog v-model="selectParamDialogVisible"
+ title="閫夋嫨鍙傛暟"
+ width="1000px">
+ <div class="param-select-container">
+ <!-- 宸︿晶鍙傛暟鍒楄〃 -->
+ <div class="param-list-area">
+ <div class="area-title">鍙�夊弬鏁�</div>
+ <div class="search-box">
+ <el-input v-model="paramSearchKeyword"
+ placeholder="璇疯緭鍏ュ弬鏁板悕绉版悳绱�"
+ clearable
+ size="small"
+ @input="getBaseParamListData">
+ <template #prefix>
+ <el-icon>
+ <Search />
+ </el-icon>
+ </template>
+ </el-input>
+ </div>
+ <el-table :data="filteredParamList"
+ height="400"
+ border
+ highlight-current-row
+ @current-change="handleSelectParam">
+ <el-table-column prop="paramName"
+ label="鍙傛暟鍚嶇О" />
+ <el-table-column prop="paramType"
+ label="鍙傛暟绫诲瀷">
+ <template #default="scope">
+ <el-tag size="small"
+ :type="getParamTypeTag(scope.row.paramType)">{{ getParamTypeText(scope.row.paramType) }}</el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+ <!-- 鍒嗛〉鎺т欢 -->
+ <div class="pagination-container"
+ style="margin-top: 10px;">
+ <el-pagination v-model:current-page="paramPage.current"
+ v-model:page-size="paramPage.size"
+ :page-sizes="[10, 20, 50, 100]"
+ layout="total, sizes, prev, pager, next, jumper"
+ :total="paramPage.total"
+ @size-change="getBaseParamListData"
+ @current-change="getBaseParamListData"
+ size="small" />
+ </div>
+ </div>
+ <!-- 鍙充晶鍙傛暟璇︽儏 -->
+ <div class="param-detail-area">
+ <div class="area-title">鍙傛暟璇︽儏</div>
+ <el-form v-if="selectedParam"
+ :model="selectedParam"
+ label-width="100px"
+ class="param-detail-form">
+ <el-form-item label="鍙傛暟鍚嶇О">
+ <span class="detail-text">{{ selectedParam.paramName }}</span>
+ </el-form-item>
+ <el-form-item label="鍙傛暟妯″紡">
+ <el-tag size="small"
+ :type="selectedParam.valueMode == '1' ? 'success' : 'warning'">
+ {{ selectedParam.valueMode == '1' ? '鍗曞��' : '鍖洪棿' }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鍙傛暟绫诲瀷">
+ <el-tag size="small"
+ :type="getParamTypeTag(selectedParam.paramType)">{{ getParamTypeText(selectedParam.paramType) }}</el-tag>
+ </el-form-item>
+ <el-form-item label="鍙傛暟鏍煎紡">
+ <span class="detail-text">{{ selectedParam.paramFormat || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鍗曚綅">
+ <span class="detail-text">{{ selectedParam.unit || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鏍囧噯鍊�"
+ v-if="selectedParam.valueMode == '1' && selectedParam.paramType == '1'">
+ <el-input v-model="selectedParam.standardValue"
+ type="number"
+ placeholder="璇疯緭鍏ラ粯璁ゅ��" />
+ </el-form-item>
+ <el-form-item label="鏈�灏忓��"
+ v-if="selectedParam.valueMode == '2' && selectedParam.paramType == '1'">
+ <el-input v-model="selectedParam.minValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ渶灏忓��" />
+ </el-form-item>
+ <el-form-item label="鏈�澶у��"
+ v-if="selectedParam.valueMode == '2' && selectedParam.paramType == '1'">
+ <el-input v-model="selectedParam.maxValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ渶澶у��" />
+ </el-form-item>
+ <el-form-item label="鎺掑簭">
+ <el-input v-model="selectedParam.sort"
+ type="number"
+ placeholder="璇疯緭鍏ユ帓搴�" />
+ </el-form-item>
+ <el-form-item label="鏄惁蹇呭~">
+ <el-switch v-model="selectedParam.isRequired" />
+ </el-form-item>
+ </el-form>
+ <el-empty v-else
+ description="璇蜂粠宸︿晶閫夋嫨鍙傛暟"
+ :image-size="100" />
+ </div>
+ </div>
+ <template #footer>
+ <el-button @click="selectParamDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="handleParamSelectSubmit">纭畾</el-button>
+ </template>
+ </el-dialog>
+ <!-- 缂栬緫鍙傛暟瀵硅瘽妗� -->
+ <el-dialog v-model="editParamDialogVisible"
+ title="缂栬緫鍙傛暟"
+ width="600px">
+ <el-form :model="editParamForm"
+ :rules="editParamRules"
+ ref="editParamFormRef"
+ label-width="120px">
+ <el-form-item label="鍙傛暟鍚嶇О">
+ <span class="detail-text">{{ editParamForm.paramName }}</span>
+ </el-form-item>
+ <el-form-item label="鍙傛暟妯″紡">
+ <el-tag size="small"
+ :type="editParamForm.valueMode == '1' ? 'success' : 'warning'">
+ {{ editParamForm.valueMode == '1' ? '鍗曞��' : '鍖洪棿' }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鍙傛暟绫诲瀷">
+ <el-tag size="small"
+ :type="getParamTypeTag(editParamForm.paramType)">
+ {{ getParamTypeText(editParamForm.paramType) }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鍙傛暟鏍煎紡">
+ <span class="detail-text">{{ editParamForm.paramFormat || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鍗曚綅">
+ <span class="detail-text">{{ editParamForm.unit || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鏍囧噯鍊�"
+ v-if="editParamForm.valueMode == '1' && editParamForm.paramType == '1'"
+ prop="standardValue">
+ <el-input v-model="editParamForm.standardValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ爣鍑嗗��" />
+ </el-form-item>
+ <el-form-item label="鏈�灏忓��"
+ v-if="editParamForm.valueMode == '2' && editParamForm.paramType == '1'"
+ prop="minValue">
+ <el-input v-model="editParamForm.minValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ渶灏忓��" />
+ </el-form-item>
+ <el-form-item label="鏈�澶у��"
+ v-if="editParamForm.valueMode == '2' && editParamForm.paramType == '1'"
+ prop="maxValue">
+ <el-input v-model="editParamForm.maxValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ渶澶у��" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="editParamDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="handleEditParamSubmit">纭畾</el-button>
+ </template>
+ </el-dialog>
+ </el-dialog>
+</template>
+
+<script setup>
+ import { ref, computed, watch } from "vue";
+ import { ElMessage, ElMessageBox } from "element-plus";
+ import { Plus, Search } from "@element-plus/icons-vue";
+ import {
+ delProcessRouteItemParam,
+ editProcessRouteItemParam,
+ addProcessRouteItemParam,
+ } from "@/api/productionManagement/processRouteItem.js";
+ import { getBaseParamList } from "@/api/basicData/parameterMaintenance.js";
+
+ const props = defineProps({
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
+ title: {
+ type: String,
+ default: "鍙傛暟鍒楄〃",
+ },
+ routeId: {
+ type: Number,
+ default: 0,
+ },
+ process: {
+ type: Object,
+ default: () => ({}),
+ },
+ paramList: {
+ type: Array,
+ default: () => [],
+ },
+ editable: {
+ type: Boolean,
+ default: true,
+ },
+ });
+
+ const emit = defineEmits(["update:modelValue", "refresh"]);
+
+ const visible = computed({
+ get: () => props.modelValue,
+ set: value => emit("update:modelValue", value),
+ });
+
+ // 鍝嶅簲寮忔暟鎹�
+ const selectParamDialogVisible = ref(false);
+ const editParamDialogVisible = ref(false);
+ const paramSearchKeyword = ref("");
+ const selectedParam = ref(null);
+ const filteredParamList = ref([]);
+ const paramPage = ref({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+ const editParamForm = ref({
+ id: null,
+ processId: null,
+ paramId: null,
+ paramName: "",
+ valueMode: "1",
+ standardValue: null,
+ minValue: null,
+ maxValue: null,
+ sort: 1,
+ isRequired: 0,
+ paramType: null,
+ paramFormat: "",
+ unit: "",
+ });
+ const editParamRules = ref({
+ standardValue: [{ required: true, message: "璇疯緭鍏ユ爣鍑嗗��", trigger: "blur" }],
+ minValue: [{ required: true, message: "璇疯緭鍏ユ渶灏忓��", trigger: "blur" }],
+ maxValue: [{ required: true, message: "璇疯緭鍏ユ渶澶у��", trigger: "blur" }],
+ });
+ const editParamFormRef = ref(null);
+
+ // 鏂板鍙傛暟
+ const handleAddParam = () => {
+ selectedParam.value = null;
+ paramSearchKeyword.value = "";
+ paramPage.current = 1;
+ // 鑾峰彇鍙�夊弬鏁板垪琛�
+ getBaseParamListData();
+ selectParamDialogVisible.value = true;
+ };
+
+ // 缂栬緫鍙傛暟
+ const handleEditParam = param => {
+ editParamForm.value = {
+ id: param.id,
+ processId: props.process.id,
+ paramId: param.paramId,
+ paramName: param.parameterName || param.paramName,
+ valueMode: param.parameterType2 || param.valueMode || "1",
+ standardValue: param.standardValue,
+ minValue: param.minValue,
+ maxValue: param.maxValue,
+ sort: param.sort || 1,
+ isRequired: param.isRequired || 0,
+ paramType: param.parameterType || param.paramType,
+ paramFormat: param.parameterFormat || param.paramFormat,
+ unit: param.unit || param.unit,
+ };
+ editParamDialogVisible.value = true;
+ };
+
+ // 鍒犻櫎鍙傛暟
+ const handleDeleteParam = param => {
+ ElMessageBox.confirm("纭畾瑕佸垹闄よ鍙傛暟鍚楋紵", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ // 璋冪敤API鍒犻櫎鍙傛暟
+ delProcessRouteItemParam(param.id)
+ .then(res => {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ emit("refresh");
+ })
+ .catch(err => {
+ ElMessage.error("鍒犻櫎鍙傛暟澶辫触");
+ console.error("鍒犻櫎鍙傛暟澶辫触锛�", err);
+ });
+ })
+ .catch(() => {});
+ };
+
+ // 鑾峰彇鍙�夊弬鏁板垪琛�
+ const getBaseParamListData = () => {
+ getBaseParamList({
+ paramName: paramSearchKeyword.value,
+ current: paramPage.current,
+ size: paramPage.size,
+ }).then(res => {
+ if (res.code === 200) {
+ filteredParamList.value = res.data?.records || [];
+ paramPage.total = res.data?.total || 0;
+ } else {
+ ElMessage.error(res.msg || "鏌ヨ澶辫触");
+ }
+ });
+ };
+
+ // 閫夋嫨鍙傛暟
+ const handleSelectParam = param => {
+ selectedParam.value = param;
+ };
+
+ // 鎻愪氦閫夋嫨鍙傛暟
+ const handleParamSelectSubmit = () => {
+ if (!selectedParam.value) {
+ ElMessage.warning("璇峰厛閫夋嫨涓�涓弬鏁�");
+ return;
+ }
+
+ if (!props.process || !props.process.id) {
+ ElMessage.error("宸ヨ壓璺嚎椤圭洰淇℃伅涓嶅畬鏁�");
+ return;
+ }
+
+ // 鍒ゆ柇鍙傛暟绫诲瀷锛屽彧鏈夋暟鍊肩被鍨嬫墠浼犳爣鍑嗗�笺�佹渶澶у�煎拰鏈�灏忓��
+ const isNumericMode = selectedParam.value.valueMode === 1;
+
+ // 璋冪敤API鏂板鍙傛暟
+ addProcessRouteItemParam({
+ routeItemId: props.process.id,
+ paramId: selectedParam.value.id,
+ standardValue: isNumericMode ? selectedParam.value.standardValue || "" : "",
+ minValue: isNumericMode ? selectedParam.value.minValue || 0 : null,
+ maxValue: isNumericMode ? selectedParam.value.maxValue || 0 : null,
+ isRequired: selectedParam.value.isRequired || 0,
+ sort: selectedParam.value.sort || 1,
+ })
+ .then(res => {
+ if (res.code === 200) {
+ ElMessage.success("娣诲姞鍙傛暟鎴愬姛");
+ selectParamDialogVisible.value = false;
+ emit("refresh");
+ } else {
+ ElMessage.error(res.msg || "娣诲姞鍙傛暟澶辫触");
+ }
+ })
+ .catch(err => {
+ ElMessage.error("娣诲姞鍙傛暟澶辫触");
+ console.error("娣诲姞鍙傛暟澶辫触锛�", err);
+ });
+ };
+
+ // 鎻愪氦缂栬緫鍙傛暟
+ const handleEditParamSubmit = () => {
+ if (!editParamFormRef.value) return;
+ editParamFormRef.value.validate(valid => {
+ if (valid) {
+ // 鍒ゆ柇鍙傛暟绫诲瀷锛屽彧鏈夋暟鍊肩被鍨嬫墠浼犳爣鍑嗗�笺�佹渶澶у�煎拰鏈�灏忓��
+ const isNumericMode = editParamForm.value.valueMode == 1;
+
+ // 璋冪敤API淇敼鍙傛暟
+ editProcessRouteItemParam({
+ id: editParamForm.value.id,
+ routeItemId: props.process.id,
+ paramId: editParamForm.value.paramId,
+ standardValue: isNumericMode
+ ? editParamForm.value.standardValue || ""
+ : "",
+ minValue: isNumericMode ? editParamForm.value.minValue || 0 : null,
+ maxValue: isNumericMode ? editParamForm.value.maxValue || 0 : null,
+ isRequired: editParamForm.value.isRequired || 0,
+ })
+ .then(res => {
+ if (res.code === 200) {
+ ElMessage.success("缂栬緫鎴愬姛");
+ editParamDialogVisible.value = false;
+ emit("refresh");
+ } else {
+ ElMessage.error(res.msg || "缂栬緫澶辫触");
+ }
+ })
+ .catch(err => {
+ ElMessage.error("缂栬緫鍙傛暟澶辫触");
+ console.error("缂栬緫鍙傛暟澶辫触锛�", err);
+ });
+ }
+ });
+ };
+
+ // 鑾峰彇鍙傛暟绫诲瀷鏍囩
+ const getParamTypeTag = type => {
+ const typeMap = {
+ 1: "primary",
+ 2: "info",
+ 3: "warning",
+ 4: "success",
+ };
+ return typeMap[type] || "default";
+ };
+
+ // 鑾峰彇鍙傛暟绫诲瀷鏂囨湰
+ const getParamTypeText = type => {
+ const typeMap = {
+ 1: "鏁板�兼牸寮�",
+ 2: "鏂囨湰鏍煎紡",
+ 3: "涓嬫媺閫夐」",
+ 4: "鏃堕棿鏍煎紡",
+ };
+ return typeMap[type] || type;
+ };
+
+ watch(
+ () => props.modelValue,
+ newVal => {
+ if (!newVal) {
+ // 寮圭獥鍏抽棴鏃堕噸缃暟鎹�
+ selectParamDialogVisible.value = false;
+ editParamDialogVisible.value = false;
+ selectedParam.value = null;
+ paramSearchKeyword.value = "";
+ paramPage.current = 1;
+ filteredParamList.value = [];
+ editParamForm.value = {
+ id: null,
+ processId: null,
+ paramId: null,
+ paramName: "",
+ valueMode: "1",
+ standardValue: null,
+ minValue: null,
+ maxValue: null,
+ sort: 1,
+ isRequired: 0,
+ paramType: null,
+ paramFormat: "",
+ unit: "",
+ };
+ }
+ }
+ );
+</script>
+
+<style scoped>
+ .param-list-container {
+ padding: 10px 0;
+ }
+
+ .params-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #e4e7ed;
+ }
+
+ .params-header span {
+ font-size: 16px;
+ font-weight: 500;
+ color: #303133;
+ }
+
+ .params-list {
+ max-height: 400px;
+ overflow-y: auto;
+ }
+
+ .param-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px 16px;
+ margin-bottom: 8px;
+ background-color: #f9f9f9;
+ border-radius: 4px;
+ transition: all 0.3s ease;
+ }
+
+ .param-item:hover {
+ background-color: #ecf5ff;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ }
+
+ .param-info {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ flex: 1;
+ }
+
+ .param-code {
+ font-weight: 500;
+ color: #303133;
+ min-width: 120px;
+ }
+
+ .param-value {
+ color: #606266;
+ font-size: 14px;
+ }
+
+ .param-actions {
+ display: flex;
+ gap: 10px;
+ }
+
+ /* 婊氬姩鏉℃牱寮� */
+ .params-list::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ .params-list::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 3px;
+ }
+
+ .params-list::-webkit-scrollbar-thumb {
+ background: #c1c1c1;
+ border-radius: 3px;
+ }
+
+ .params-list::-webkit-scrollbar-thumb:hover {
+ background: #a8a8a8;
+ }
+
+ /* 閫夋嫨鍙傛暟瀵硅瘽妗嗘牱寮� */
+ .param-select-container {
+ display: flex;
+ gap: 20px;
+ }
+
+ .param-list-area {
+ flex: 1;
+ min-width: 400px;
+ }
+
+ .param-detail-area {
+ flex: 1;
+ min-width: 300px;
+ }
+
+ .area-title {
+ font-size: 14px;
+ font-weight: 500;
+ margin-bottom: 10px;
+ color: #303133;
+ }
+
+ .search-box {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 10px;
+ }
+
+ .param-detail-form {
+ background: #f9f9f9;
+ padding: 15px;
+ border-radius: 4px;
+ }
+
+ .detail-text {
+ font-weight: 500;
+ }
+</style>
\ No newline at end of file
diff --git a/src/utils/dict.js b/src/utils/dict.js
index a6db068..e457c19 100644
--- a/src/utils/dict.js
+++ b/src/utils/dict.js
@@ -14,7 +14,7 @@
res.value[dictType] = dicts
} else {
getDicts(dictType).then(resp => {
- res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
+ res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue,id: p.dictCode, elTagType: p.listClass, elTagClass: p.cssClass }))
useDictStore().setDict(dictType, res.value[dictType])
})
}
diff --git a/src/views/productionManagement/processRoute/Edit.vue b/src/views/productionManagement/processRoute/Edit.vue
new file mode 100644
index 0000000..53d8395
--- /dev/null
+++ b/src/views/productionManagement/processRoute/Edit.vue
@@ -0,0 +1,257 @@
+<template>
+ <div>
+ <el-dialog v-model="isShow"
+ title="缂栬緫宸ヨ壓璺嚎"
+ width="400"
+ @close="closeModal">
+ <el-form label-width="140px"
+ :model="formState"
+ label-position="top"
+ ref="formRef">
+ <el-form-item label="浜у搧鍚嶇О"
+ prop="productModelId"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨浜у搧',
+ trigger: 'change',
+ }
+ ]">
+ <el-button type="primary"
+ @click="showProductSelectDialog = true">
+ {{ formState.productName && formState.productModelName
+ ? `${formState.productName} - ${formState.productModelName}`
+ : '閫夋嫨浜у搧' }}
+ </el-button>
+ </el-form-item>
+ <el-form-item label="BOM"
+ prop="bomId"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨BOM',
+ trigger: 'change',
+ }
+ ]">
+ <el-select v-model="formState.bomId"
+ placeholder="璇烽�夋嫨BOM"
+ clearable
+ :disabled="!formState.productModelId || bomOptions.length === 0"
+ style="width: 100%">
+ <el-option v-for="item in bomOptions"
+ :key="item.id"
+ :label="item.bomNo || `BOM-${item.id}`"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="澶囨敞"
+ prop="description">
+ <el-input v-model="formState.description"
+ type="textarea" />
+ </el-form-item>
+ </el-form>
+ <!-- 浜у搧閫夋嫨寮圭獥 -->
+ <ProductSelectDialog v-model="showProductSelectDialog"
+ @confirm="handleProductSelect"
+ single />
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary"
+ @click="handleSubmit">纭</el-button>
+ <el-button @click="closeModal">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+ import {
+ ref,
+ computed,
+ getCurrentInstance,
+ onMounted,
+ nextTick,
+ watch,
+ } from "vue";
+ import { update } from "@/api/productionManagement/processRoute.js";
+ import { getByModel } from "@/api/productionManagement/productBom.js";
+ import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+
+ const props = defineProps({
+ visible: {
+ type: Boolean,
+ required: true,
+ },
+
+ record: {
+ type: Object,
+ required: true,
+ },
+ });
+
+ const emit = defineEmits(["update:visible", "completed"]);
+
+ // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+ const formState = ref({
+ productId: undefined,
+ productModelId: undefined,
+ productName: "",
+ productModelName: "",
+ bomId: undefined,
+ description: "",
+ });
+
+ const isShow = computed({
+ get() {
+ return props.visible;
+ },
+ set(val) {
+ emit("update:visible", val);
+ },
+ });
+
+ const showProductSelectDialog = ref(false);
+ const bomOptions = ref([]);
+
+ let { proxy } = getCurrentInstance();
+
+ const closeModal = () => {
+ isShow.value = false;
+ };
+
+ // 璁剧疆琛ㄥ崟鏁版嵁
+ const setFormData = () => {
+ if (props.record) {
+ formState.value = {
+ ...props.record,
+ productId: props.record.productId,
+ productModelId: props.record.productModelId,
+ productName: props.record.productName || "",
+ // 娉ㄦ剰锛歳ecord涓殑瀛楁鏄痬odel锛岄渶瑕佹槧灏勫埌productModelName
+ productModelName:
+ props.record.model || props.record.productModelName || "",
+ bomId: props.record.bomId,
+ description: props.record.description || "",
+ };
+ // 濡傛灉鏈変骇鍝佸瀷鍙稩D锛屽姞杞紹OM鍒楄〃
+ if (props.record.productModelId) {
+ loadBomList(props.record.productModelId);
+ }
+ }
+ };
+
+ // 鍔犺浇BOM鍒楄〃
+ const loadBomList = async productModelId => {
+ if (!productModelId) {
+ bomOptions.value = [];
+ return;
+ }
+ try {
+ const res = await getByModel(productModelId);
+ // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈data瀛楁
+ let bomList = [];
+ if (Array.isArray(res)) {
+ bomList = res;
+ } else if (res && res.data) {
+ bomList = Array.isArray(res.data) ? res.data : [res.data];
+ } else if (res && typeof res === "object") {
+ bomList = [res];
+ }
+ bomOptions.value = bomList;
+ } catch (error) {
+ console.error("鍔犺浇BOM鍒楄〃澶辫触锛�", error);
+ bomOptions.value = [];
+ }
+ };
+
+ // 浜у搧閫夋嫨澶勭悊
+ const handleProductSelect = async products => {
+ if (products && products.length > 0) {
+ const product = products[0];
+ // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
+ try {
+ const res = await getByModel(product.id);
+ // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈data瀛楁
+ let bomList = [];
+ if (Array.isArray(res)) {
+ bomList = res;
+ } else if (res && res.data) {
+ bomList = Array.isArray(res.data) ? res.data : [res.data];
+ } else if (res && typeof res === "object") {
+ bomList = [res];
+ }
+
+ if (bomList.length > 0) {
+ formState.value.productModelId = product.id;
+ formState.value.productName = product.productName;
+ formState.value.productModelName = product.model;
+ // 濡傛灉褰撳墠閫夋嫨鐨凚OM涓嶅湪鏂板垪琛ㄤ腑锛屽垯閲嶇疆BOM閫夋嫨
+ const currentBomExists = bomList.some(
+ bom => bom.id === formState.value.bomId
+ );
+ if (!currentBomExists) {
+ formState.value.bomId = undefined;
+ }
+ bomOptions.value = bomList;
+ showProductSelectDialog.value = false;
+ // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+ proxy.$refs["formRef"]?.validateField("productModelId");
+ } else {
+ proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+ }
+ } catch (error) {
+ // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
+ proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+ }
+ }
+ };
+
+ const handleSubmit = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰BOM
+ if (!formState.value.productModelId) {
+ proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+ return;
+ }
+ if (!formState.value.bomId) {
+ proxy.$modal.msgError("璇烽�夋嫨BOM");
+ return;
+ }
+ update(formState.value).then(res => {
+ // 鍏抽棴妯℃�佹
+ isShow.value = false;
+ // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+ emit("completed");
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ });
+ }
+ });
+ };
+
+ defineExpose({
+ closeModal,
+ handleSubmit,
+ isShow,
+ });
+
+ // 鐩戝惉寮圭獥鎵撳紑锛屽垵濮嬪寲琛ㄥ崟鏁版嵁
+ watch(
+ () => props.visible,
+ visible => {
+ if (visible && props.record) {
+ nextTick(() => {
+ setFormData();
+ });
+ }
+ },
+ { immediate: true }
+ );
+
+ onMounted(() => {
+ if (props.visible && props.record) {
+ setFormData();
+ }
+ });
+</script>
diff --git a/src/views/productionManagement/processRoute/ItemsForm.vue b/src/views/productionManagement/processRoute/ItemsForm.vue
new file mode 100644
index 0000000..ed6e499
--- /dev/null
+++ b/src/views/productionManagement/processRoute/ItemsForm.vue
@@ -0,0 +1,531 @@
+<template>
+ <div>
+ <el-dialog
+ v-model="isShow"
+ title="宸ヨ壓璺嚎椤圭洰"
+ width="800px"
+ @close="closeModal"
+ >
+ <div class="operate-button">
+ <el-button
+ type="primary"
+ @click="isShowProductSelectDialog = true"
+ class="mb5"
+ style="margin-bottom: 10px;"
+ >
+ 閫夋嫨浜у搧
+ </el-button>
+
+ <el-switch
+ v-model="isTable"
+ inline-prompt
+ active-text="琛ㄦ牸"
+ inactive-text="鍒楄〃"
+ @change="handleViewChange"
+ />
+ </div>
+
+ <el-table
+ v-if="isTable"
+ ref="multipleTable"
+ v-loading="tableLoading"
+ border
+ :data="routeItems"
+ :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+ row-key="id"
+ tooltip-effect="dark"
+ class="lims-table"
+ style="cursor: move;"
+ >
+ <el-table-column align="center" label="搴忓彿" width="60">
+ <template #default="scope">
+ {{ scope.$index + 1 }}
+ </template>
+ </el-table-column>
+
+ <el-table-column
+ v-for="(item, index) in tableColumn"
+ :key="index"
+ :label="item.label"
+ :width="item.width"
+ show-overflow-tooltip
+ >
+ <template #default="scope" v-if="item.dataType === 'action'">
+ <el-button
+ v-for="(op, opIndex) in item.operation"
+ :key="opIndex"
+ :type="op.type"
+ :link="op.link"
+ size="small"
+ @click.stop="op.clickFun(scope.row)"
+ >
+ {{ op.name }}
+ </el-button>
+ </template>
+
+ <template #default="scope" v-else>
+ <template v-if="item.prop === 'processId'">
+ <el-select
+ v-model="scope.row[item.prop]"
+ style="width: 100%;"
+ @mousedown.stop
+ >
+ <el-option
+ v-for="process in processOptions"
+ :key="process.id"
+ :label="process.name"
+ :value="process.id"
+ />
+ </el-select>
+ </template>
+ <template v-else>
+ {{ scope.row[item.prop] || '-' }}
+ </template>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 浣跨敤鏅�歞iv鏇夸唬el-steps -->
+ <div
+ v-else
+ ref="stepsContainer"
+ class="mb5 custom-steps"
+ style="padding: 10px 0; display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-start;"
+ >
+ <div
+ v-for="(item, index) in routeItems"
+ :key="item.id"
+ class="custom-step draggable-step"
+ :data-id="item.id"
+ style="cursor: move; flex: 0 0 auto; min-width: 220px;"
+ >
+ <div class="step-content">
+ <div class="step-number">{{ index + 1 }}</div>
+ <el-card
+ :header="item.productName"
+ class="step-card"
+ style="cursor: move;"
+ >
+ <div class="step-card-content">
+ <p>{{ item.model }}</p>
+ <p>{{ item.unit }}</p>
+ <el-select
+ v-model="item.processId"
+ style="width: 100%;"
+ @mousedown.stop
+ >
+ <el-option
+ v-for="process in processOptions"
+ :key="process.id"
+ :label="process.name"
+ :value="process.id"
+ />
+ </el-select>
+ </div>
+ <template #footer>
+ <div class="step-card-footer">
+ <el-button type="danger" link size="small" @click.stop="removeItemByID(item.id)">鍒犻櫎</el-button>
+ </div>
+ </template>
+ </el-card>
+ </div>
+ </div>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleSubmit">纭</el-button>
+ <el-button @click="closeModal">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <ProductSelectDialog
+ v-model="isShowProductSelectDialog"
+ @confirm="handelSelectProducts"
+ />
+ </div>
+</template>
+
+<script setup>
+import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
+import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+import { findProcessRouteItemList, addOrUpdateProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
+import { processList } from "@/api/productionManagement/productionProcess.js";
+import Sortable from 'sortablejs';
+
+const props = defineProps({
+ visible: {
+ type: Boolean,
+ required: true,
+ default: false
+ },
+ record: {
+ type: Object,
+ required: true,
+ default: () => ({})
+ }
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+const processOptions = ref([]);
+const tableLoading = ref(false);
+const isShowProductSelectDialog = ref(false);
+const routeItems = ref([]);
+let tableSortable = null;
+let stepsSortable = null;
+const multipleTable = ref(null);
+const stepsContainer = ref(null);
+const isTable = ref(true);
+
+const isShow = computed({
+ get() {
+ return props.visible;
+ },
+ set(val) {
+ emit('update:visible', val);
+ }
+});
+
+const tableColumn = ref([
+ { label: "浜у搧鍚嶇О", prop: "productName", width: 180 },
+ { label: "瑙勬牸鍚嶇О", prop: "model", width: 150 },
+ { label: "鍗曚綅", prop: "unit", width: 80 },
+ { label: "宸ュ簭鍚嶇О", prop: "processId", width: 180 },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 100,
+ operation: [
+ {
+ name: "鍒犻櫎",
+ type: "danger",
+ link: true,
+ clickFun: (row) => {
+ const idx = routeItems.value.findIndex(item => item.id === row.id);
+ if (idx > -1) {
+ removeItem(idx)
+ }
+ }
+ }
+ ]
+ }
+]);
+
+const removeItem = (index) => {
+ routeItems.value.splice(index, 1);
+ nextTick(() => initSortable());
+};
+
+const removeItemByID = (id) => {
+ const idx = routeItems.value.findIndex(item => item.id === id);
+ if (idx > -1) {
+ routeItems.value.splice(idx, 1);
+ nextTick(() => initSortable());
+ }
+};
+
+const closeModal = () => {
+ isShow.value = false;
+};
+
+const handelSelectProducts = (products) => {
+ destroySortable();
+
+ const newData = products.map(({ id, ...product }) => ({
+ ...product,
+ productModelId: id,
+ routeId: props.record.id,
+ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
+ processId: undefined
+ }));
+
+ console.log('閫夋嫨浜у搧鍓嶆暟缁�:', routeItems.value);
+ routeItems.value.push(...newData);
+ routeItems.value = [...routeItems.value];
+ console.log('閫夋嫨浜у搧鍚庢暟缁�:', routeItems.value);
+
+ // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+ nextTick(() => {
+ // 寮哄埗閲嶆柊娓叉煋缁勪欢
+ if (proxy?.$forceUpdate) {
+ proxy.$forceUpdate();
+ }
+
+ const temp = [...routeItems.value];
+ routeItems.value = [];
+ nextTick(() => {
+ routeItems.value = temp;
+ initSortable();
+ });
+ });
+};
+
+const findProcessRouteItems = () => {
+ tableLoading.value = true;
+ findProcessRouteItemList({ routeId: props.record.id })
+ .then(res => {
+ tableLoading.value = false;
+ routeItems.value = res.data.map(item => ({
+ ...item,
+ processId: item.processId === 0 ? undefined : item.processId
+ }));
+ // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+ nextTick(() => {
+ setTimeout(() => initSortable(), 100);
+ });
+ })
+ .catch(err => {
+ tableLoading.value = false;
+ console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+ });
+};
+
+const findProcessList = () => {
+ processList({})
+ .then(res => {
+ processOptions.value = res.data;
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
+ });
+};
+
+const { proxy } = getCurrentInstance() || {};
+
+const handleSubmit = () => {
+ const hasEmptyProcess = routeItems.value.some(item => !item.processId);
+ if (hasEmptyProcess) {
+ proxy?.$modal?.msgError("璇蜂负鎵�鏈夐」鐩�夋嫨宸ュ簭");
+ return;
+ }
+
+ addOrUpdateProcessRouteItem({
+ routeId: props.record.id,
+ processRouteItem: routeItems.value.map(({ id, ...item }) => item)
+ })
+ .then(res => {
+ isShow.value = false;
+ emit('completed');
+ proxy?.$modal?.msgSuccess("鎻愪氦鎴愬姛");
+ })
+ .catch(err => {
+ proxy?.$modal?.msgError(`鎻愪氦澶辫触锛�${err.msg || "缃戠粶寮傚父"}`);
+ });
+};
+
+const destroySortable = () => {
+ if (tableSortable) {
+ tableSortable.destroy();
+ tableSortable = null;
+ }
+ if (stepsSortable) {
+ stepsSortable.destroy();
+ stepsSortable = null;
+ }
+};
+
+const initSortable = () => {
+ destroySortable();
+
+ if (isTable.value) {
+ if (!multipleTable.value) return;
+ const tbody = multipleTable.value.$el.querySelector('.el-table__body tbody') ||
+ multipleTable.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
+ if (!tbody) return;
+
+ tableSortable = new Sortable(tbody, {
+ animation: 150,
+ ghostClass: 'sortable-ghost',
+ handle: '.el-table__row',
+ filter: '.el-button, .el-select',
+ onEnd: (evt) => {
+ if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
+
+ // 浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭锛屼笌琛ㄦ牸妯″紡淇濇寔涓�鑷�
+ const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
+ routeItems.value.splice(evt.newIndex, 0, moveItem);
+ routeItems.value = [...routeItems.value];
+ console.log('鎺掑簭鍚庢暟缁�:', routeItems.value);
+ }
+ });
+ } else {
+ if (!stepsContainer.value) return;
+
+ // 淇敼锛氱洿鎺ヤ娇鐢╯tepsContainer.value浣滀负鎷栨嫿瀹瑰櫒
+ const stepsList = stepsContainer.value;
+ if (!stepsList) {
+ console.warn('鏈壘鍒版楠ゆ潯鎷栨嫿瀹瑰櫒');
+ return;
+ }
+
+ // 淇敼锛氱畝鍖栨嫋鎷介厤缃�
+ stepsSortable = new Sortable(stepsList, {
+ animation: 150,
+ ghostClass: 'sortable-ghost',
+ draggable: '.draggable-step', // 鍙嫋鎷藉厓绱�
+ handle: '.draggable-step, .step-card', // 鎷栨嫿鎵嬫焺
+ filter: '.el-button, .el-select, .el-input', // 杩囨护鎸夐挳/閫夋嫨鍣�
+ forceFallback: true,
+ fallbackClass: 'sortable-fallback',
+ preventOnFilter: true,
+ scroll: true,
+ scrollSensitivity: 30,
+ scrollSpeed: 10,
+ bubbleScroll: true,
+ onEnd: (evt) => {
+ if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
+
+ // 浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭
+ const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
+ routeItems.value.splice(evt.newIndex, 0, moveItem);
+ routeItems.value = [...routeItems.value];
+ }
+ });
+
+ // 璋冭瘯锛氭墦鍗板鍣ㄥ拰瀹炰緥锛岀‘璁ょ粦瀹氭垚鍔�
+ console.log('姝ラ鏉℃嫋鎷藉鍣�:', stepsList);
+ console.log('Sortable瀹炰緥:', stepsSortable);
+ }
+};
+
+const handleViewChange = () => {
+ destroySortable();
+ // 寤惰繜鍒濆鍖栵紝纭繚瑙嗗浘鍒囨崲鍚嶥OM瀹屽叏娓叉煋
+ nextTick(() => {
+ setTimeout(() => initSortable(), 100);
+ });
+};
+
+onMounted(() => {
+ findProcessRouteItems();
+ findProcessList();
+});
+
+onUnmounted(() => {
+ destroySortable();
+});
+
+defineExpose({
+ closeModal,
+ handleSubmit,
+ isShow
+});
+</script>
+
+<style scoped>
+:deep(.sortable-ghost) {
+ opacity: 0.6;
+ background-color: #f5f7fa !important;
+}
+
+:deep(.el-table__row) {
+ transition: background-color 0.2s;
+}
+
+:deep(.el-table__row:hover) {
+ background-color: #f9fafc !important;
+}
+
+:deep(.el-card__footer){
+ padding: 0 !important;
+}
+
+.operate-button {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+/* 淇敼锛氳嚜瀹氫箟姝ラ鏉″鍣ㄦ牱寮� */
+.custom-steps {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: flex-start;
+ gap: 20px;
+ min-height: 100px;
+}
+
+/* 淇敼锛氳嚜瀹氫箟姝ラ椤规牱寮� */
+.custom-step {
+ cursor: move !important;
+ padding: 8px;
+ position: relative;
+ transition: all 0.2s ease;
+ flex: 0 0 auto;
+ min-width: 220px;
+ touch-action: none;
+}
+
+/* 鎷栨嫿鎮诞鏍峰紡锛屾彁绀哄彲鎷栨嫿 */
+.custom-step:hover {
+ background-color: rgba(64, 158, 255, 0.05);
+ transform: translateY(-2px);
+}
+
+.sortable-ghost {
+ opacity: 0.4;
+ background-color: #f5f7fa !important;
+ border: 2px dashed #409eff;
+ margin: 10px;
+ transform: scale(1.02);
+}
+
+.sortable-fallback {
+ opacity: 0.9;
+ background-color: #f5f7fa;
+ border: 1px solid #409eff;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ transform: rotate(2deg);
+ margin: 10px;
+}
+
+.step-card {
+ cursor: move !important;
+ transition: box-shadow 0.2s ease;
+ user-select: none;
+ -webkit-user-select: none;
+ pointer-events: auto;
+ margin: 10px;
+ height: 240px;
+}
+
+.step-card:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.step-content {
+ width: 220px;
+ user-select: none;
+}
+
+.step-card-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.step-card-footer {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ padding: 10px;
+}
+
+/* 鑷畾涔夊簭鍙锋牱寮忎紭鍖� */
+.step-number {
+ font-weight: bold;
+ text-align: center;
+ width: 36px;
+ height: 36px;
+ line-height: 36px;
+ margin: 0 auto 10px;
+ background: #409eff;
+ color: #fff;
+ border-radius: 50%;
+ font-size: 14px;
+}
+</style>
diff --git a/src/views/productionManagement/processRoute/New.vue b/src/views/productionManagement/processRoute/New.vue
new file mode 100644
index 0000000..62c6873
--- /dev/null
+++ b/src/views/productionManagement/processRoute/New.vue
@@ -0,0 +1,194 @@
+<template>
+ <div>
+ <el-dialog
+ v-model="isShow"
+ title="鏂板宸ヨ壓璺嚎"
+ width="400"
+ @close="closeModal"
+ >
+ <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
+ <el-form-item
+ label="浜у搧鍚嶇О"
+ prop="productModelId"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨浜у搧',
+ trigger: 'change',
+ }
+ ]"
+ >
+ <el-button type="primary" @click="showProductSelectDialog = true">
+ {{ formState.productName && formState.productModelName
+ ? `${formState.productName} - ${formState.productModelName}`
+ : '閫夋嫨浜у搧' }}
+ </el-button>
+ </el-form-item>
+
+ <el-form-item
+ label="BOM"
+ prop="bomId"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨BOM',
+ trigger: 'change',
+ }
+ ]"
+ >
+ <el-select
+ v-model="formState.bomId"
+ placeholder="璇烽�夋嫨BOM"
+ clearable
+ :disabled="!formState.productModelId || bomOptions.length === 0"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in bomOptions"
+ :key="item.id"
+ :label="item.bomNo || `BOM-${item.id}`"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="澶囨敞" prop="description">
+ <el-input v-model="formState.description" type="textarea" />
+ </el-form-item>
+ </el-form>
+
+ <!-- 浜у搧閫夋嫨寮圭獥 -->
+ <ProductSelectDialog
+ v-model="showProductSelectDialog"
+ @confirm="handleProductSelect"
+ single
+ />
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleSubmit">纭</el-button>
+ <el-button @click="closeModal">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import {ref, computed, getCurrentInstance} from "vue";
+import {add} from "@/api/productionManagement/processRoute.js";
+import {getByModel} from "@/api/productionManagement/productBom.js";
+import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+
+const props = defineProps({
+ visible: {
+ type: Boolean,
+ required: true,
+ },
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+const formState = ref({
+ productId: undefined,
+ productModelId: undefined,
+ productName: "",
+ productModelName: "",
+ bomId: undefined,
+ description: '',
+});
+
+const isShow = computed({
+ get() {
+ return props.visible;
+ },
+ set(val) {
+ emit('update:visible', val);
+ },
+});
+
+const showProductSelectDialog = ref(false);
+const bomOptions = ref([]);
+
+let { proxy } = getCurrentInstance()
+
+const closeModal = () => {
+ // 閲嶇疆琛ㄥ崟鏁版嵁
+ formState.value = {
+ productId: undefined,
+ productModelId: undefined,
+ productName: "",
+ productModelName: "",
+ bomId: undefined,
+ description: '',
+ };
+ bomOptions.value = [];
+ isShow.value = false;
+};
+
+// 浜у搧閫夋嫨澶勭悊
+const handleProductSelect = async (products) => {
+ if (products && products.length > 0) {
+ const product = products[0];
+ // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
+ try {
+ const res = await getByModel(product.id);
+ // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈data瀛楁
+ let bomList = [];
+ if (Array.isArray(res)) {
+ bomList = res;
+ } else if (res && res.data) {
+ bomList = Array.isArray(res.data) ? res.data : [res.data];
+ } else if (res && typeof res === 'object') {
+ bomList = [res];
+ }
+
+ if (bomList.length > 0) {
+ formState.value.productModelId = product.id;
+ formState.value.productName = product.productName;
+ formState.value.productModelName = product.model;
+ formState.value.bomId = undefined; // 閲嶇疆BOM閫夋嫨
+ bomOptions.value = bomList;
+ showProductSelectDialog.value = false;
+ // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+ proxy.$refs["formRef"]?.validateField('productModelId');
+ } else {
+ proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+ }
+ } catch (error) {
+ // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
+ proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+ }
+ }
+};
+
+const handleSubmit = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰BOM
+ if (!formState.value.productModelId) {
+ proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+ return;
+ }
+ if (!formState.value.bomId) {
+ proxy.$modal.msgError("璇烽�夋嫨BOM");
+ return;
+ }
+ add(formState.value).then(res => {
+ // 鍏抽棴妯℃�佹
+ isShow.value = false;
+ // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+ emit('completed');
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ })
+ }
+ })
+};
+
+
+defineExpose({
+ closeModal,
+ handleSubmit,
+ isShow,
+});
+</script>
diff --git a/src/views/productionManagement/processRoute/index.vue b/src/views/productionManagement/processRoute/index.vue
index 40ab129..83d789e 100644
--- a/src/views/productionManagement/processRoute/index.vue
+++ b/src/views/productionManagement/processRoute/index.vue
@@ -1,1201 +1,232 @@
<template>
<div class="app-container">
- <div class="route-header">
- <div class="add-route-btn"
- @click="handleAddRoute">
- <el-icon>
- <Plus />
- </el-icon>
- <span>鏂板宸ヨ壓璺嚎</span>
- </div>
+ <div class="search_form">
+ <el-form :model="searchForm"
+ :inline="true">
+ <el-form-item label="瑙勬牸鍚嶇О:">
+ <el-input v-model="searchForm.model"
+ placeholder="璇疯緭鍏�"
+ clearable
+ prefix-icon="Search"
+ style="width: 200px;"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary"
+ @click="handleQuery">鎼滅储</el-button>
+ </el-form-item>
+ </el-form>
</div>
- <div class="route-card-list">
- <div v-for="route in routeList"
- :key="route.id"
- class="route-card">
- <div class="card-header">
- <div class="route-info">
- <span class="route-name"><el-icon style="margin-right: 8px;line-height: 30px;">
- <ScaleToOriginal />
- </el-icon>{{route.routeCode }}<el-tag style="margin-left: 8px"
- :type="!route.status ? 'warning' : 'success'">{{ !route.status ? '鑽夌' : '鎵瑰噯' }}</el-tag></span>
- <!-- <span class="route-code">{{ route.routeCode }}</span> -->
- </div>
- <div class="route-actions">
- <el-button v-if="!route.status"
- link
- type="success"
- @click="handleApproveRoute(route)">
- <el-icon>
- <Check />
- </el-icon>
- 鎵瑰噯
- </el-button>
- <el-button v-if="route.status"
- link
- type="warning"
- @click="handleRevokeApproveRoute(route)">
- <el-icon>
- <Close />
- </el-icon>
- 鎾ら攢鎵瑰噯
- </el-button>
- <el-button link
- type="primary"
- @click="handleEditRoute(route)">
- <el-icon>
- <Edit />
- </el-icon>
- 缂栬緫
- </el-button>
- <el-button link
- type="danger"
- @click="handleDeleteRoute(route)">
- <el-icon>
- <Delete />
- </el-icon>
- 鍒犻櫎
- </el-button>
- </div>
- </div>
- <div class="card-body">
- <div class="route-meta">
- <span class="meta-item">
- <el-icon>
- <Box />
- </el-icon>
- <span class="meta-label">浜у搧:</span>
- <span class="meta-value">{{ route.productName }} - {{ route.productModelName }}</span>
- </span>
- <span class="meta-item">
- <el-icon>
- <Document />
- </el-icon>
- <span class="meta-label">BOM:</span>
- <span class="meta-value">{{ route.bomNo || '-' }}</span>
- </span>
- <span class="meta-item">
- <el-icon>
- <Document />
- </el-icon>
- <span class="meta-label">澶囨敞:</span>
- <span class="meta-value">{{ route.description || '鏆傛棤鎻忚堪' }}</span>
- </span>
- </div>
- <div class="expand-btn-wrapper">
- <el-button class="expand-btn"
- :class="{ expanded: route.expanded }"
- type="primary"
- text
- @click="toggleExpand(route)">
- <span class="btn-text">{{ route.expanded ? '鏀惰捣宸ュ簭璺嚎' : '灞曞紑宸ュ簭璺嚎' }}</span>
- <el-icon class="expand-icon">
- <component :is="route.expanded ? 'ArrowUp' : 'ArrowDown'" />
- </el-icon>
- </el-button>
- </div>
- </div>
- <div v-if="route.expanded"
- class="process-route">
- <div class="process-flow">
- <div v-for="(process, index) in route.processList"
- :key="process.id"
- class="process-flow-item"
- draggable="true"
- @dragstart="handleDragStart($event, index, route.id)"
- @dragover="handleDragOver($event)"
- @drop="handleDrop($event, index, route.id)"
- @dragend="handleDragEnd">
- <div class="process-node"
- :class="{ expanded: process.expanded }">
- <div class="process-node-header">
- <div class="process-number">{{ index + 1 }}</div>
- <div class="process-actions">
- <el-button link
- type="primary"
- @click="handleEditProcessSelect(route, index, process)">
- <el-icon>
- <Edit />
- </el-icon>
- </el-button>
- <el-button link
- type="danger"
- @click="handleDeleteProcess(route.id, process)">
- <el-icon>
- <Delete />
- </el-icon>
- </el-button>
- </div>
- </div>
- <div class="process-node-body">
- <!-- <div class="process-code">{{ process.processId }}</div> -->
- <div class="process-name">{{ process.processName }}</div>
- <!-- <div class="process-desc">{{ process.remark || '鏆傛棤鎻忚堪' }}</div> -->
- </div>
- <div class="process-node-footer">
- <!-- <el-tag size="small"
- :type="process.status === '1' ? 'success' : 'info'">
- {{ process.status === '1' ? '鍚敤' : '鍋滅敤' }}
- </el-tag> -->
- <el-button type="primary"
- link
- size="small"
- @click="toggleProcessParams(process)">
- {{ process.expanded ? '鏀惰捣鍙傛暟' : '灞曞紑鍙傛暟' }}
- ({{ process.paramCount }})
- </el-button>
- </div>
- <div v-if="process.expanded"
- class="process-params-section">
- <div class="params-header">
- <span>鍙傛暟鍒楄〃</span>
- <el-button type="primary"
- link
- size="small"
- @click="handleAddParam(route.id, process)">
- <el-icon>
- <Plus />
- </el-icon>鏂板
- </el-button>
- </div>
- <div class="params-list">
- <div v-for="param in process.paramList"
- :key="param.id"
- class="param-item">
- <div class="param-info">
- <span class="param-code">{{ param.paramName }}</span>
- <!-- <span class="param-name">{{ param.paramName }}</span> -->
- <!-- <el-tag size="small"
- style="margin-right: 20px;"
- :type="getParamTypeTag(param.parameterType)">
- {{ param.parameterType }}
- </el-tag> -->
- <span v-if="param.valueMode==1"
- class="param-value">鏍囧噯鍊硷細{{ param.standardValue || "-" }} {{ param.unit }}</span>
- <span v-else
- class="param-value">鏍囧噯鍊硷細{{ param.minValue || "-" }}-{{ param.maxValue || "-" }} {{ param.unit }}</span>
- </div>
- <div class="param-actions">
- <el-button link
- type="primary"
- size="small"
- @click="handleEditParam(route.id, process, param)">
- 缂栬緫
- </el-button>
- <el-button link
- type="danger"
- size="small"
- @click="handleDeleteParam(route.id, process, param)">
- 鍒犻櫎
- </el-button>
- </div>
- </div>
- <el-empty v-if="!process.paramList || process.paramList.length === 0"
- description="鏆傛棤鍙傛暟"
- :image-size="50" />
- </div>
- </div>
- </div>
- <div v-if="index < route.processList.length - 1"
- class="flow-arrow">
- <el-icon>
- <Right />
- </el-icon>
- </div>
- </div>
- <div class="add-process-node"
- @click="handleSelectProcess(route, index)">
- <el-icon>
- <Plus />
- </el-icon>
- <span>鏂板宸ュ簭</span>
- </div>
- </div>
- </div>
+ <div class="table_list">
+ <div style="text-align: right"
+ class="mb10">
+ <el-button type="primary"
+ @click="showNewModal">鏂板宸ヨ壓璺嚎</el-button>
+ <el-button type="danger"
+ @click="handleDelete"
+ :disabled="selectedRows.length === 0"
+ plain>鍒犻櫎宸ヨ壓璺嚎</el-button>
</div>
+ <PIMTable rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ :total="page.total" />
</div>
- <!-- 鍒嗛〉鎺т欢 -->
- <div class="pagination-container">
- <el-pagination v-model:current-page="routePage.current"
- v-model:page-size="routePage.size"
- :page-sizes="[10, 20, 50, 100]"
- layout="total, sizes, prev, pager, next, jumper"
- :total="routePage.total"
- @size-change="handleRouteSizeChange"
- @current-change="handleRouteCurrentChange" />
- </div>
- <!-- 宸ヨ壓璺嚎鏂板/缂栬緫瀵硅瘽妗� -->
- <el-dialog v-model="routeDialogVisible"
- :title="isRouteEdit ? '缂栬緫宸ヨ壓璺嚎' : '鏂板宸ヨ壓璺嚎'"
- width="500px">
- <el-form :model="routeForm"
- :rules="routeRules"
- ref="routeFormRef"
- label-width="120px">
- <el-form-item label="浜у搧鍚嶇О"
- prop="productModelId">
- <el-button type="primary"
- @click="handleProcessProductSelectClick2">
- {{ routeForm.productName && routeForm.productModelName
- ? `${routeForm.productName} - ${routeForm.productModelName}`
- : '閫夋嫨浜у搧' }}
- </el-button>
- </el-form-item>
- <el-form-item label="BOM"
- prop="bomId">
- <el-select v-model="routeForm.bomId"
- placeholder="璇烽�夋嫨BOM"
- clearable
- :disabled="!routeForm.productModelId || bomOptions.length === 0"
- style="width: 100%">
- <el-option v-for="item in bomOptions"
- :key="item.id"
- :label="item.bomNo || `BOM-${item.id}`"
- :value="item.id" />
- </el-select>
- </el-form-item>
- <el-form-item label="璺嚎缂栫爜"
- prop="routeCode">
- <el-input v-model="routeForm.routeCode"
- disabled
- placeholder="鑷姩鐢熸垚" />
- </el-form-item>
- <el-form-item label="澶囨敞"
- prop="description">
- <el-input v-model="routeForm.description"
- type="textarea"
- :rows="3"
- placeholder="璇疯緭鍏ヨ矾绾挎弿杩�" />
- </el-form-item>
- <!-- <el-form-item label="鐘舵��"
- prop="status">
- <el-radio-group v-model="routeForm.status">
- <el-radio label="1">鍚敤</el-radio>
- <el-radio label="0">鍋滅敤</el-radio>
- </el-radio-group>
- </el-form-item> -->
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="routeDialogVisible = false">鍙栨秷</el-button>
- <el-button type="primary"
- @click="handleRouteSubmit">纭畾</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 浜у搧閫夋嫨寮圭獥 -->
- <ProductSelectDialog v-model="showProductSelectDialog"
- @confirm="handleProductSelect"
- single />
- <!-- 宸ュ簭鏂板/缂栬緫瀵硅瘽妗� -->
- <el-dialog v-model="processDialogVisible"
- :title="isProcessEdit ? '缂栬緫宸ュ簭' : '鏂板宸ュ簭'"
- width="500px">
- <el-form :model="processForm"
- :rules="processRules"
- ref="processFormRef"
- label-width="120px">
- <el-form-item label="宸ュ簭缂栫爜"
- prop="no">
- <el-input v-model="processForm.no"
- placeholder="璇疯緭鍏ュ伐搴忕紪鐮�" />
- </el-form-item>
- <el-form-item label="宸ュ簭鍚嶇О"
- prop="name">
- <el-input v-model="processForm.name"
- placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
- </el-form-item>
- <el-form-item label="宸ュ簭鎻忚堪"
- prop="remark">
- <el-input v-model="processForm.remark"
- type="textarea"
- :rows="3"
- placeholder="璇疯緭鍏ュ伐搴忔弿杩�" />
- </el-form-item>
- <el-form-item label="鐘舵��"
- prop="status">
- <el-radio-group v-model="processForm.status">
- <el-radio :label="true">鍚敤</el-radio>
- <el-radio :label="false">鍋滅敤</el-radio>
- </el-radio-group>
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="processDialogVisible = false">鍙栨秷</el-button>
- <el-button type="primary"
- @click="handleProcessSubmit">纭畾</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 閫夋嫨宸ュ簭瀵硅瘽妗� -->
- <el-dialog v-model="selectProcessDialogVisible"
- title="閫夋嫨宸ュ簭"
- width="1000px">
- <div class="process-select-container">
- <!-- 宸︿晶宸ュ簭鍒楄〃 -->
- <div class="process-list-area">
- <div class="area-title">鍙�夊伐搴�</div>
- <div class="search-box">
- <el-input v-model="processSearchKeyword"
- placeholder="璇疯緭鍏ュ伐搴忓悕绉版悳绱�"
- clearable
- size="small"
- @input="handleProcessSearch">
- <template #prefix>
- <el-icon>
- <Search />
- </el-icon>
- </template>
- </el-input>
- </div>
- <el-table :data="filteredProcessList"
- height="360"
- border
- highlight-current-row
- @current-change="handleProcessSelect">
- <el-table-column prop="no"
- label="宸ュ簭缂栧彿"
- width="100" />
- <el-table-column prop="name"
- label="宸ュ簭鍚嶇О" />
- <el-table-column prop="remark"
- label="宸ュ簭鎻忚堪" />
- <el-table-column prop="status"
- label="鐘舵��"
- width="80">
- <template #default="scope">
- <el-tag size="small"
- :type="scope.row.status ? 'success' : 'info'">
- {{ scope.row.status ? '鍚敤' : '鍋滅敤' }}
- </el-tag>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <!-- 鍙充晶宸ュ簭璇︽儏 -->
- <div class="process-detail-area">
- <div class="area-title">宸ュ簭璇︽儏</div>
- <el-form v-if="selectedProcessItem"
- :model="processForm"
- label-width="100px"
- class="process-detail-form">
- <el-form-item label="宸ュ簭缂栧彿">
- <span class="detail-text">{{ selectedProcessItem.no }}</span>
- </el-form-item>
- <el-form-item label="宸ュ簭鍚嶇О">
- <span class="detail-text">{{ selectedProcessItem.name }}</span>
- </el-form-item>
- <el-form-item label="宸ュ簭鎻忚堪">
- <span class="detail-text">{{ selectedProcessItem.remark || '-' }}</span>
- </el-form-item>
- <el-form-item label="鐘舵��">
- <el-tag size="small"
- :type="selectedProcessItem.status ? 'success' : 'info'">
- {{ selectedProcessItem.status ? '鍚敤' : '鍋滅敤' }}
- </el-tag>
- </el-form-item>
- <el-form-item label="鏄惁璐ㄦ">
- <el-tag size="small"
- :type="selectedProcessItem.isQuality ? 'success' : 'info'">
- {{ selectedProcessItem.isQuality ? '璐ㄦ' : '闈炶川妫�' }}
- </el-tag>
- </el-form-item>
- <el-form-item label="浜у搧鍚嶇О"
- prop="productModelId">
- <el-button type="primary"
- @click="handleProcessProductSelectClick">
- {{ processForm.productName && processForm.model
- ? `${processForm.productName} - ${processForm.model}`
- : '閫夋嫨浜у搧' }}
- </el-button>
- </el-form-item>
- <el-form-item label="鍗曚綅"
- prop="unit">
- <el-input v-model="processForm.unit"
- :placeholder="processForm.productModelId ? '鏍规嵁閫夋嫨鐨勪骇鍝佽嚜鍔ㄥ甫鍑�' : '璇峰厛閫夋嫨浜у搧' "
- clearable
- :disabled="true" />
- </el-form-item>
- <el-form-item label="鏄惁璐ㄦ"
- prop="isQuality">
- <el-switch v-model="processForm.isQuality"
- :active-value="true"
- inactive-value="false" />
- </el-form-item>
- </el-form>
- <el-empty v-else
- description="璇蜂粠宸︿晶閫夋嫨宸ュ簭" />
- </div>
- </div>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="selectProcessDialogVisible = false">鍙栨秷</el-button>
- <el-button type="primary"
- :disabled="!selectedProcessItem || !processForm.productModelId"
- @click="handleProcessSelectSubmit">纭畾</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 鍙傛暟鏂板/缂栬緫瀵硅瘽妗� -->
- <el-dialog v-model="paramDialogVisible"
- :title="isParamEdit ? '缂栬緫鍙傛暟' : '鏂板鍙傛暟'"
- width="500px">
- <el-form :model="paramForm"
- :rules="paramRules"
- ref="paramFormRef"
- label-width="120px">
- <el-form-item label="鍙傛暟缂栧彿"
- prop="parameterCode">
- <el-input v-model="paramForm.parameterCode"
- placeholder="璇疯緭鍏ュ弬鏁扮紪鍙�" />
- </el-form-item>
- <el-form-item label="鍙傛暟鍚嶇О"
- prop="parameterName">
- <el-input v-model="paramForm.parameterName"
- placeholder="璇疯緭鍏ュ弬鏁板悕绉�" />
- </el-form-item>
- <el-form-item label="鍙傛暟妯″紡"
- prop="parameterType2">
- <el-select v-model="paramForm.parameterType2"
- placeholder="璇烽�夋嫨鍙傛暟妯″紡">
- <el-option label="鍗曞��"
- value="1" />
- <el-option label="鍖洪棿"
- value="2" />
- </el-select>
- </el-form-item>
- <el-form-item label="鍙傛暟绫诲瀷"
- prop="parameterType">
- <el-select v-model="paramForm.parameterType"
- @change="handleParamTypeChange"
- placeholder="璇烽�夋嫨鍙傛暟绫诲瀷">
- <el-option label="鏁板�兼牸寮�"
- value="鏁板�兼牸寮�" />
- <el-option label="鏂囨湰鏍煎紡"
- value="鏂囨湰鏍煎紡" />
- <el-option label="涓嬫媺閫夐」"
- value="涓嬫媺閫夐」" />
- <el-option label="鏃堕棿鏍煎紡"
- value="鏃堕棿鏍煎紡" />
- </el-select>
- </el-form-item>
- <el-form-item v-if="paramForm.parameterType === '涓嬫媺閫夐」'"
- label="鏁版嵁瀛楀吀"
- prop="parameterFormat">
- <el-select v-model="paramForm.parameterFormat"
- placeholder="璇烽�夋嫨鏁版嵁瀛楀吀">
- <el-option v-for="item in dictTypes"
- :key="item.dictType"
- :label="item.dictName"
- :value="item.dictType" />
- </el-select>
- </el-form-item>
- <el-form-item v-else-if="paramForm.parameterType === '鏃堕棿鏍煎紡'"
- label="鏃堕棿鏍煎紡"
- prop="parameterFormat">
- <el-select v-model="paramForm.parameterFormat"
- placeholder="璇烽�夋嫨鏃堕棿鏍煎紡">
- <el-option label="YYYY-MM-DD HH:mm:ss"
- value="YYYY-MM-DD HH:mm:ss" />
- <el-option label="YYYY-MM-DD"
- value="YYYY-MM-DD" />
- </el-select>
- </el-form-item>
- <el-form-item v-else
- label="鍙傛暟鏍煎紡"
- prop="parameterFormat">
- <el-input v-model="paramForm.parameterFormat"
- placeholder="璇疯緭鍏ュ弬鏁版牸寮�" />
- </el-form-item>
- <el-form-item label="鏍囧噯鍊�"
- prop="standardValue">
- <el-input v-model="paramForm.standardValue"
- placeholder="璇疯緭鍏ユ爣鍑嗗��" />
- </el-form-item>
- <el-form-item label="鍗曚綅"
- prop="unit">
- <el-input v-model="paramForm.unit"
- placeholder="璇疯緭鍏ュ崟浣�" />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="paramDialogVisible = false">鍙栨秷</el-button>
- <el-button type="primary"
- @click="handleParamSubmit">纭畾</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 閫夋嫨鍙傛暟瀵硅瘽妗� -->
- <el-dialog v-model="selectParamDialogVisible"
- title="閫夋嫨鍙傛暟"
- width="1000px">
- <div class="param-select-container">
- <!-- 宸︿晶鍙傛暟鍒楄〃 -->
- <div class="param-list-area">
- <div class="area-title">鍙�夊弬鏁�</div>
- <div class="search-box">
- <el-input v-model="paramSearchKeyword"
- placeholder="璇疯緭鍏ュ弬鏁板悕绉版悳绱�"
- clearable
- size="small"
- @input="handleParamSearch">
- <template #prefix>
- <el-icon>
- <Search />
- </el-icon>
- </template>
- </el-input>
- </div>
- <el-table :data="filteredParamList"
- height="300"
- border
- highlight-current-row
- @current-change="handleParamSelect">
- <el-table-column prop="paramName"
- label="鍙傛暟鍚嶇О" />
- <el-table-column prop="paramType"
- label="鍙傛暟绫诲瀷">
- <template #default="scope">
- <el-tag size="small"
- :type="getParamTypeTag(scope.row.paramType)">
- {{ getParamTypeText(scope.row.paramType) }}
- </el-tag>
- </template>
- </el-table-column>
- </el-table>
- <!-- 鍒嗛〉鎺т欢 -->
- <div class="pagination-container"
- style="margin-top: 10px;">
- <el-pagination v-model:current-page="paramPage.current"
- v-model:page-size="paramPage.size"
- :page-sizes="[10, 20, 50, 100]"
- layout="total, sizes, prev, pager, next, jumper"
- :total="paramPage.total"
- @size-change="handleParamSizeChange"
- @current-change="handleParamCurrentChange"
- size="small" />
- </div>
- </div>
- <!-- 鍙充晶鍙傛暟璇︽儏 -->
- <div class="param-detail-area">
- <div class="area-title">鍙傛暟璇︽儏</div>
- <el-form v-if="selectedParam"
- :model="selectedParam"
- label-width="100px"
- class="param-detail-form">
- <el-form-item label="鍙傛暟鍚嶇О">
- <span class="detail-text">{{ selectedParam.paramName }}</span>
- </el-form-item>
- <el-form-item label="鍙傛暟妯″紡">
- <el-tag size="small"
- :type="selectedParam.valueMode == '1' ? 'success' : 'warning'">
- {{ selectedParam.valueMode == '1' ? '鍗曞��' : '鍖洪棿' }}
- </el-tag>
- </el-form-item>
- <el-form-item label="鍙傛暟绫诲瀷">
- <el-tag size="small"
- :type="getParamTypeTag(selectedParam.paramType)">
- {{ getParamTypeText(selectedParam.paramType) }}
- </el-tag>
- </el-form-item>
- <el-form-item label="鍙傛暟鏍煎紡">
- <span class="detail-text">{{ selectedParam.paramFormat || '-' }}</span>
- </el-form-item>
- <el-form-item label="鍗曚綅">
- <span class="detail-text">{{ selectedParam.unit || '-' }}</span>
- </el-form-item>
- <el-form-item label="鏍囧噯鍊�"
- v-if="selectedParam.valueMode == '1' && selectedParam.paramType == '1'">
- <el-input v-model="selectedParam.standardValue"
- type="number"
- placeholder="璇疯緭鍏ラ粯璁ゅ��" />
- </el-form-item>
- <el-form-item label="鏈�灏忓��"
- v-if="selectedParam.valueMode == '2' && selectedParam.paramType == '1'">
- <el-input v-model="selectedParam.minValue"
- type="number"
- placeholder="璇疯緭鍏ユ渶灏忓��" />
- </el-form-item>
- <el-form-item label="鏈�澶у��"
- v-if="selectedParam.valueMode == '2' && selectedParam.paramType == '1'">
- <el-input v-model="selectedParam.maxValue"
- type="number"
- placeholder="璇疯緭鍏ユ渶澶у��" />
- </el-form-item>
- <el-form-item label="鎺掑簭">
- <el-input v-model="selectedParam.sort"
- type="number"
- placeholder="璇疯緭鍏ユ帓搴�" />
- </el-form-item>
- <el-form-item label="鏄惁蹇呭~">
- <el-switch v-model="selectedParam.isRequired"
- :active-value="1"
- :inactive-value="0" />
- </el-form-item>
- </el-form>
- <el-empty v-else
- description="璇蜂粠宸︿晶閫夋嫨鍙傛暟" />
- </div>
- </div>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="selectParamDialogVisible = false">鍙栨秷</el-button>
- <el-button type="primary"
- :disabled="!selectedParam"
- @click="handleParamSelectSubmit">纭畾</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 缂栬緫鍙傛暟瀵硅瘽妗� -->
- <el-dialog v-model="editParamDialogVisible"
- title="缂栬緫鍙傛暟"
- width="600px">
- <el-form :model="editParamForm"
- :rules="editParamRules"
- ref="editParamFormRef"
- label-width="120px">
- <el-form-item label="鍙傛暟鍚嶇О">
- <span class="detail-text">{{ editParamForm.paramName }}</span>
- </el-form-item>
- <el-form-item label="鍙傛暟妯″紡">
- <el-tag size="small"
- :type="editParamForm.valueMode == '1' ? 'success' : 'warning'">
- {{ editParamForm.valueMode == '1' ? '鍗曞��' : '鍖洪棿' }}
- </el-tag>
- </el-form-item>
- <el-form-item label="鍙傛暟绫诲瀷">
- <el-tag size="small"
- :type="getParamTypeTag(editParamForm.paramType)">
- {{ getParamTypeText(editParamForm.paramType) }}
- </el-tag>
- </el-form-item>
- <el-form-item label="鍙傛暟鏍煎紡">
- <span class="detail-text">{{ editParamForm.paramFormat || '-' }}</span>
- </el-form-item>
- <el-form-item label="鍗曚綅">
- <span class="detail-text">{{ editParamForm.unit || '-' }}</span>
- </el-form-item>
- <el-form-item label="鏍囧噯鍊�"
- v-if="editParamForm.valueMode == '1' && editParamForm.paramType == '1'"
- prop="standardValue">
- <el-input v-model="editParamForm.standardValue"
- type="number"
- placeholder="璇疯緭鍏ユ爣鍑嗗��" />
- </el-form-item>
- <el-form-item label="鏈�灏忓��"
- v-if="editParamForm.valueMode == '2' && editParamForm.paramType == '1'"
- prop="minValue">
- <el-input v-model="editParamForm.minValue"
- type="number"
- placeholder="璇疯緭鍏ユ渶灏忓��" />
- </el-form-item>
- <el-form-item label="鏈�澶у��"
- v-if="editParamForm.valueMode == '2' && editParamForm.paramType == '1'"
- prop="maxValue">
- <el-input v-model="editParamForm.maxValue"
- type="number"
- placeholder="璇疯緭鍏ユ渶澶у��" />
- </el-form-item>
- <el-form-item label="鎺掑簭"
- prop="sort">
- <el-input v-model="editParamForm.sort"
- type="number"
- placeholder="璇疯緭鍏ユ帓搴�" />
- </el-form-item>
- <el-form-item label="鏄惁蹇呭~"
- prop="isRequired">
- <el-switch v-model="editParamForm.isRequired"
- :active-value="1"
- :inactive-value="0" />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="editParamDialogVisible = false">鍙栨秷</el-button>
- <el-button type="primary"
- @click="handleEditParamSubmit">纭畾</el-button>
- </span>
- </template>
- </el-dialog>
+ <new-process v-if="isShowNewModal"
+ v-model:visible="isShowNewModal"
+ @completed="getList" />
+ <edit-process v-if="isShowEditModal"
+ v-model:visible="isShowEditModal"
+ :record="record"
+ @completed="getList" />
+ <route-item-form v-if="isShowItemModal"
+ v-model:visible="isShowItemModal"
+ :record="record"
+ @completed="getList" />
</div>
</template>
<script setup>
- import { ref, reactive, getCurrentInstance, onMounted } from "vue";
- import { ElMessage, ElMessageBox } from "element-plus";
+ import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
+ import NewProcess from "@/views/productionManagement/processRoute/New.vue";
+ import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
+ import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
import {
- Plus,
- Edit,
- Delete,
- ArrowUp,
- ArrowDown,
- Right,
- Search,
- Check,
- Close,
- Box,
- Document,
- } from "@element-plus/icons-vue";
- import { listType } from "@/api/system/dict/type";
- import { getByModel } from "@/api/productionManagement/productBom.js";
- import { add, update, del } from "@/api/productionManagement/processRoute.js";
- import {
- addOrUpdateProcessRouteItem,
- batchDeleteProcessRouteItem,
- sortProcessRouteItem,
- findProcessRouteItemList,
- getProcessParamList,
- addProcessRouteItemParam,
- editProcessRouteItemParam,
- delProcessRouteItemParam,
- } from "@/api/productionManagement/processRouteItem.js";
- import { list as getProcessListApi } from "@/api/productionManagement/productionProcess.js";
- import { getBaseParamList } from "@/api/basicData/parameterMaintenance.js";
- import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+ listPage,
+ del,
+ update,
+ } from "@/api/productionManagement/processRoute.js";
+ import { useRouter } from "vue-router";
+ import { ElMessageBox, ElMessage } from "element-plus";
- // 宸ヨ壓璺嚎鍒楄〃
- const routeList = ref([]);
- const dictTypes = ref([]);
-
- // 宸ヨ壓璺嚎鍒嗛〉
- const routePage = reactive({
+ const router = useRouter();
+ const data = reactive({
+ searchForm: {
+ model: "",
+ },
+ });
+ const { searchForm } = toRefs(data);
+ const tableColumn = ref([
+ {
+ label: "宸ヨ壓璺嚎缂栧彿",
+ prop: "processRouteCode",
+ },
+ {
+ label: "浜у搧鍚嶇О",
+ prop: "productName",
+ },
+ {
+ label: "瑙勬牸鍚嶇О",
+ prop: "model",
+ },
+ {
+ label: "BOM缂栧彿",
+ prop: "bomNo",
+ },
+ {
+ label: "鎻忚堪",
+ prop: "description",
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 280,
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: row => {
+ showEditModal(row);
+ },
+ },
+ {
+ name: "璺嚎椤圭洰",
+ type: "text",
+ clickFun: row => {
+ showItemModal(row);
+ },
+ },
+ {
+ name: "鎵瑰噯",
+ type: "primary",
+ text: true,
+ showHide: row => {
+ return !row.status;
+ },
+ clickFun: row => {
+ handleApproveRoute(row);
+ },
+ },
+ {
+ name: "鍙栨秷鎵瑰噯",
+ type: "warning",
+ text: true,
+ showHide: row => {
+ return row.status;
+ },
+ clickFun: row => {
+ handleRevokeApproveRoute(row);
+ },
+ },
+ ],
+ },
+ ]);
+ const tableData = ref([]);
+ const selectedRows = ref([]);
+ const tableLoading = ref(false);
+ const isShowNewModal = ref(false);
+ const isShowEditModal = ref(false);
+ const isShowItemModal = ref(false);
+ const record = ref({});
+ const page = reactive({
current: 1,
- size: 10,
+ size: 100,
total: 0,
});
-
- // 鑾峰彇鍏ㄥ眬瀹炰緥
const { proxy } = getCurrentInstance();
- // 浜у搧閫夋嫨鍜孊OM鐩稿叧
- const showProductSelectDialog = ref(false);
- const bomOptions = ref([]);
-
- // 宸ヨ壓璺嚎瀵硅瘽妗�
- const routeDialogVisible = ref(false);
- const isRouteEdit = ref(false);
- const routeFormRef = ref(null);
- const routeForm = reactive({
- id: null,
- productModelId: null,
- productName: "",
- productModelName: "",
- bomId: null,
- routeCode: "",
- description: "",
- status: true,
- });
- const routeRules = {
- productModelId: [
- { required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" },
- ],
- bomId: [{ required: true, message: "璇烽�夋嫨BOM", trigger: "change" }],
+ // 鏌ヨ鍒楄〃
+ /** 鎼滅储鎸夐挳鎿嶄綔 */
+ const handleQuery = () => {
+ page.current = 1;
+ getList();
};
- // 宸ュ簭瀵硅瘽妗�
- const processDialogVisible = ref(false);
- const isProcessEdit = ref(false);
- const processFormRef = ref(null);
- const currentRouteId = ref(null);
- const processForm = reactive({
- id: null,
- no: "",
- name: "",
- remark: "",
- status: true,
- });
- const processRules = {
- no: [{ required: true, message: "璇疯緭鍏ュ伐搴忕紪鐮�", trigger: "blur" }],
- name: [{ required: true, message: "璇疯緭鍏ュ伐搴忓悕绉�", trigger: "blur" }],
+ const pagination = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
};
-
- // 閫夋嫨宸ュ簭瀵硅瘽妗�
- const selectProcessDialogVisible = ref(false);
- const availableProcessList = ref([]);
- const filteredProcessList = ref([]);
- const selectedProcessItem = ref(null);
- const processSearchKeyword = ref("");
- const currentRouteIndex = ref(null);
-
- // 鍙傛暟瀵硅瘽妗�
- const paramDialogVisible = ref(false);
- const isParamEdit = ref(false);
- const paramFormRef = ref(null);
- const currentProcessId = ref(null);
- const paramForm = reactive({
- id: null,
- parameterCode: "",
- parameterName: "",
- parameterType2: "1",
- parameterType: "",
- parameterFormat: "",
- standardValue: "",
- unit: "",
- });
- const paramRules = {
- parameterCode: [
- { required: true, message: "璇疯緭鍏ュ弬鏁扮紪鍙�", trigger: "blur" },
- ],
- parameterName: [
- { required: true, message: "璇疯緭鍏ュ弬鏁板悕绉�", trigger: "blur" },
- ],
- parameterType: [
- { required: true, message: "璇烽�夋嫨鍙傛暟绫诲瀷", trigger: "change" },
- ],
- };
-
- // 閫夋嫨鍙傛暟瀵硅瘽妗�
- const selectParamDialogVisible = ref(false);
- const availableParamList = ref([]);
- const filteredParamList = ref([]);
- const selectedParam = ref(null);
- const paramSearchKeyword = ref("");
-
- // 鍙�夊弬鏁板垎椤�
- const paramPage = reactive({
- current: 1,
- size: 10,
- total: 0,
- });
-
- // 缂栬緫鍙傛暟瀵硅瘽妗�
- const editParamDialogVisible = ref(false);
- const editParamFormRef = ref(null);
- const editParamForm = reactive({
- id: null,
- processId: null,
- paramId: null,
- paramName: "",
- valueMode: "1",
- standardValue: null,
- minValue: null,
- maxValue: null,
- sort: 1,
- isRequired: 0,
- });
- const editParamRules = reactive({
- standardValue: [
- {
- required: true,
- message: "璇疯緭鍏ユ爣鍑嗗��",
- trigger: "blur",
- validator: (rule, value, callback) => {
- if (value === null || value === undefined || value === "") {
- callback(new Error("璇疯緭鍏ユ爣鍑嗗��"));
- } else {
- callback();
- }
- },
- },
- ],
- minValue: [
- {
- required: true,
- message: "璇疯緭鍏ユ渶灏忓��",
- trigger: "blur",
- validator: (rule, value, callback) => {
- if (value === null || value === undefined || value === "") {
- callback(new Error("璇疯緭鍏ユ渶灏忓��"));
- } else {
- callback();
- }
- },
- },
- ],
- maxValue: [
- {
- required: true,
- message: "璇疯緭鍏ユ渶澶у��",
- trigger: "blur",
- validator: (rule, value, callback) => {
- if (value === null || value === undefined || value === "") {
- callback(new Error("璇疯緭鍏ユ渶澶у��"));
- } else {
- callback();
- }
- },
- },
- ],
- sort: [
- {
- required: true,
- message: "璇疯緭鍏ユ帓搴�",
- trigger: "blur",
- validator: (rule, value, callback) => {
- if (value === null || value === undefined || value === "") {
- callback(new Error("璇疯緭鍏ユ帓搴�"));
- } else if (isNaN(value) || value < 1) {
- callback(new Error("鎺掑簭蹇呴』鏄ぇ浜�0鐨勬暣鏁�"));
- } else {
- callback();
- }
- },
- },
- ],
- });
-
- // 鎷栨嫿鐩稿叧
- const draggedItem = ref(null);
- const draggedRouteId = ref(null);
-
- // 鑾峰彇宸ヨ壓璺嚎鍒楄〃
- const getRouteList = () => {
- // 瀵煎叆 listPage 鏂规硶
- import("@/api/productionManagement/processRoute.js").then(({ listPage }) => {
- listPage({ pageNum: routePage.current, pageSize: routePage.size })
- .then(res => {
- // 澶勭悊杩斿洖鐨勬暟鎹紝鏄犲皠鍒伴〉闈㈤渶瑕佺殑鏍煎紡
- routeList.value = (res.data?.records || []).map(item => ({
- id: item.id,
- productModelId: item.productModelId,
- productName: item.productName,
- productModelName: item.model || item.productModelName,
- bomId: item.bomId,
- bomNo: item.bomNo,
- routeCode: item.processRouteCode || item.routeCode,
- description: item.description || item.description,
- status: item.status,
- expanded: false,
- processList: (item.processList || []).map(process => ({
- ...process,
- processId: process.processId || process.id,
- expanded: false,
- })),
- }));
- // 鏇存柊鍒嗛〉鎬绘暟
- routePage.total = res.data?.total || 0;
- })
- .catch(err => {
- console.error("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触锛�", err);
- routeList.value = [];
- routePage.total = 0;
- });
- });
- };
-
- // 灞曞紑/鏀惰捣宸ヨ壓璺嚎
- const toggleExpand = route => {
- route.expanded = !route.expanded;
- if (route.expanded) {
- // 璋冪敤鎺ュ彛鑾峰彇宸ュ簭鍒楄〃
- findProcessRouteItemList({ routeId: route.id })
- .then(res => {
- route.processList = (res.data || []).map(process => ({
- ...process,
- processId: process.processId || process.id,
- expanded: false,
- }));
- })
- .catch(err => {
- console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
- route.processList = [];
- });
- }
- };
-
- // 灞曞紑/鏀惰捣宸ュ簭鍙傛暟
- const toggleProcessParams = process => {
- process.expanded = !process.expanded;
- if (process.expanded && process.id) {
- // 璋冪敤鎺ュ彛鑾峰彇鍙傛暟鍒楄〃
- getProcessParamList({
- routeItemId: process.id,
- pageNum: 1,
- pageSize: 1000,
+ const getList = () => {
+ tableLoading.value = true;
+ const params = { ...searchForm.value, ...page };
+ params.entryDate = undefined;
+ listPage(params)
+ .then(res => {
+ tableLoading.value = false;
+ tableData.value = res.data.records.map(item => ({
+ ...item,
+ }));
+ page.total = res.data.total;
})
- .then(res => {
- if (res.code === 200) {
- process.paramList = res.data?.records || [];
- process.paramCount = process.paramList.length;
- } else {
- ElMessage.error(res.msg || "鑾峰彇鍙傛暟鍒楄〃澶辫触");
- process.paramList = [];
- process.paramCount = 0;
- }
- })
- .catch(err => {
- console.error("鑾峰彇鍙傛暟鍒楄〃澶辫触锛�", err);
- ElMessage.error("鑾峰彇鍙傛暟鍒楄〃澶辫触");
- process.paramList = [];
- process.paramCount = 0;
- });
- }
+ .catch(err => {
+ tableLoading.value = false;
+ });
};
- const toggleProcessParams2 = process => {
- if (process.expanded && process.id) {
- // 璋冪敤鎺ュ彛鑾峰彇鍙傛暟鍒楄〃
- getProcessParamList({
- routeItemId: process.id,
- pageNum: 1,
- pageSize: 1000,
+ // 琛ㄦ牸閫夋嫨鏁版嵁
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ // 鎵撳紑鏂板寮规
+ const showNewModal = () => {
+ isShowNewModal.value = true;
+ };
+
+ const showEditModal = row => {
+ isShowEditModal.value = true;
+ record.value = row;
+ };
+
+ const showItemModal = row => {
+ router.push({
+ path: "/productionManagement/processRouteItem",
+ query: {
+ id: row.id,
+ processRouteCode: row.processRouteCode || "",
+ productName: row.productName || "",
+ model: row.model || "",
+ bomNo: row.bomNo || "",
+ bomId: row.bomId || null,
+ description: row.description || "",
+ type: "route",
+ },
+ });
+ };
+
+ // 鍒犻櫎
+ function handleDelete() {
+ const ids = selectedRows.value.map(item => item.id);
+ proxy.$modal
+ .confirm("鏄惁纭鍒犻櫎宸插嬀閫夌殑鏁版嵁椤癸紵")
+ .then(function () {
+ return del(ids);
})
- .then(res => {
- if (res.code === 200) {
- process.paramList = res.data?.records || [];
- process.paramCount = process.paramList.length;
- } else {
- ElMessage.error(res.msg || "鑾峰彇鍙傛暟鍒楄〃澶辫触");
- process.paramList = [];
- process.paramCount = 0;
- }
- })
- .catch(err => {
- console.error("鑾峰彇鍙傛暟鍒楄〃澶辫触锛�", err);
- ElMessage.error("鑾峰彇鍙傛暟鍒楄〃澶辫触");
- process.paramList = [];
- process.paramCount = 0;
- });
- }
- };
- // 宸ヨ壓璺嚎鎿嶄綔
- const handleAddRoute = () => {
- isRouteEdit.value = false;
- routeForm.id = null;
- routeForm.productModelId = null;
- routeForm.productName = "";
- routeForm.productModelName = "";
- routeForm.bomId = null;
- routeForm.routeCode = "";
- routeForm.description = "";
- routeForm.status = false;
- bomOptions.value = [];
- routeDialogVisible.value = true;
- };
+ .then(() => {
+ getList();
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ })
+ .catch(() => {});
+ }
- const handleEditRoute = route => {
- isRouteEdit.value = true;
- routeForm.id = route.id;
- routeForm.productModelId = route.productModelId;
- routeForm.productName = route.productName;
- routeForm.productModelName = route.productModelName;
- routeForm.bomId = route.bomId;
- routeForm.routeCode = route.routeCode;
- routeForm.description = route.description;
- routeForm.status = route.status;
- routeDialogVisible.value = true;
- };
-
- const handleDeleteRoute = route => {
- ElMessageBox.confirm("纭畾瑕佸垹闄よ宸ヨ壓璺嚎鍚楋紵", "鎻愮ず", {
- confirmButtonText: "纭畾",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- }).then(() => {
- del(route.id)
- .then(res => {
- ElMessage.success("鍒犻櫎鎴愬姛");
- getRouteList();
- })
- .catch(err => {
- ElMessage.error("鍒犻櫎澶辫触");
- });
- });
- };
-
- const handleRouteSubmit = () => {
- routeFormRef.value.validate(valid => {
- if (valid) {
- // 鏋勫缓鎻愪氦鏁版嵁
- const submitData = {
- ...routeForm,
- // 娉ㄦ剰锛欰PI 鏈熸湜鐨勫瓧娈靛悕鍙兘涓庤〃鍗曞瓧娈靛悕涓嶅悓
- productId: routeForm.productModelId,
- productModelId: routeForm.productModelId,
- description: routeForm.description,
- };
-
- if (isRouteEdit.value) {
- // 缂栬緫鎿嶄綔
- update(submitData)
- .then(res => {
- ElMessage.success("缂栬緫鎴愬姛");
- routeDialogVisible.value = false;
- getRouteList();
- })
- .catch(err => {
- ElMessage.error("缂栬緫澶辫触");
- });
- } else {
- // 鏂板鎿嶄綔
- add(submitData)
- .then(res => {
- ElMessage.success("鏂板鎴愬姛");
- routeDialogVisible.value = false;
- getRouteList();
- })
- .catch(err => {
- ElMessage.error("鏂板澶辫触");
- });
- }
- }
- });
- };
- const isform2 = ref(null);
- const handleProcessProductSelectClick = () => {
- isform2.value = true;
- showProductSelectDialog.value = true;
- };
- const handleProcessProductSelectClick2 = () => {
- isform2.value = false;
- showProductSelectDialog.value = true;
- };
-
- // 浜у搧閫夋嫨澶勭悊
- const handleProductSelect = async products => {
- if (isform2.value) {
- // 甯垜鍐欏伐搴忎腑鐨勯�夋嫨浜у搧鐨勫洖璋�,骞朵笖鎶婂瓧娈靛姞杩沺rocessForm
- if (products && products.length > 0) {
- const product = products[0];
- console.log("product:", product);
- // 鎶妏roduct涓殑瀛楁娣诲姞鍒皃rocessForm涓�
- // Object.assign(processForm, product);
- processForm.productModelId = product.id;
- processForm.productName = product.productName;
- processForm.model = product.model;
- processForm.unit = product.unit || "";
- console.log("processForm:", processForm);
-
- // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
- proxy.$refs["processFormRef"]?.validateField("productModelId");
- }
- } else {
- if (products && products.length > 0) {
- const product = products[0];
- // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
- try {
- const res = await getByModel(product.id);
- // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈data瀛楁
- let bomList = [];
- if (Array.isArray(res)) {
- bomList = res;
- } else if (res && res.data) {
- bomList = Array.isArray(res.data) ? res.data : [res.data];
- } else if (res && typeof res === "object") {
- bomList = [res];
- }
- console.log("bomList:", bomList);
- if (bomList.length > 0) {
- routeForm.productModelId = product.id;
- routeForm.productName = product.productName;
- routeForm.productModelName = product.model;
- routeForm.bomId = undefined; // 閲嶇疆BOM閫夋嫨
- bomOptions.value = bomList;
- showProductSelectDialog.value = false;
- // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
- proxy.$refs["routeFormRef"]?.validateField("productModelId");
- } else {
- proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
- }
- } catch (error) {
- // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
- proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
- }
- }
- }
- };
-
+ // 鎵瑰噯宸ヨ壓璺嚎
const handleApproveRoute = route => {
ElMessageBox.confirm("纭畾瑕佹壒鍑嗚宸ヨ壓璺嚎鍚楋紵", "鎻愮ず", {
confirmButtonText: "纭畾",
@@ -1206,7 +237,7 @@
update({ id: route.id, status: true })
.then(res => {
ElMessage.success("鎵瑰噯鎴愬姛");
- getRouteList();
+ getList();
})
.catch(err => {
ElMessage.error("鎵瑰噯澶辫触");
@@ -1214,6 +245,7 @@
});
};
+ // 鍙栨秷鎵瑰噯宸ヨ壓璺嚎
const handleRevokeApproveRoute = route => {
ElMessageBox.confirm("纭畾瑕佹挙閿�鎵瑰噯璇ュ伐鑹鸿矾绾垮悧锛�", "鎻愮ず", {
confirmButtonText: "纭畾",
@@ -1224,1194 +256,17 @@
update({ id: route.id, status: false })
.then(res => {
ElMessage.success("鎾ら攢鎵瑰噯鎴愬姛");
- getRouteList();
+ getList();
})
.catch(err => {
ElMessage.error("鎾ら攢鎵瑰噯澶辫触");
});
});
};
- // 宸ュ簭鎿嶄綔
- const handleSelectProcess = (route, index) => {
- console.log("route:", route);
- currentRouteId.value = route.id;
- currentRouteIndex.value = index;
- // 閲嶇疆鎼滅储鍜岄�夋嫨鐘舵��
- filteredProcessList.value = availableProcessList.value;
- processSearchKeyword.value = "";
- selectedProcessItem.value = null;
- selectProcessDialogVisible.value = true;
- };
- const dragSort = ref(0);
- const currentId = ref(null);
- // 淇敼宸ュ簭
- const handleEditProcessSelect = (route, index, process) => {
- console.log("route:", route);
- console.log("process:", process);
- currentId.value = process.id;
- currentRouteId.value = route.id;
- currentRouteIndex.value = index;
- // 閲嶇疆鎼滅储鍜岄�夋嫨鐘舵��
- filteredProcessList.value = availableProcessList.value;
- processSearchKeyword.value = "";
- // 璁剧疆閫変腑鐨勫伐搴�
- filteredProcessList.value.map(item => {
- if (item.id === process.processId) {
- selectedProcessItem.value = item;
- }
- });
- dragSort.value = process.dragSort;
- // selectedProcessItem.value = process;
- // 濉厖浜у搧閫夋嫨琛ㄥ崟
- processForm.productModelId = process.productModelId;
- processForm.productName = process.productName;
- processForm.model = process.model;
- processForm.processId = process.no;
- // processForm.name = process.name;
- processForm.unit = process.unit || "";
- processForm.isQuality = process.isQuality || false;
- selectProcessDialogVisible.value = true;
- };
- const handleEditProcess = (routeId, process) => {
- currentRouteId.value = routeId;
- isProcessEdit.value = true;
- processForm.id = process.id;
- processForm.no = process.no;
- processForm.name = process.name;
- processForm.remark = process.remark;
- processForm.status = process.status;
- processDialogVisible.value = true;
- };
-
- const handleDeleteProcess = (routeId, process) => {
- ElMessageBox.confirm("纭畾瑕佸垹闄よ宸ュ簭鍚楋紵", "鎻愮ず", {
- confirmButtonText: "纭畾",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- }).then(() => {
- // 璋冪敤API鍒犻櫎宸ュ簭
- batchDeleteProcessRouteItem([process.id])
- .then(res => {
- ElMessage.success("鍒犻櫎鎴愬姛");
- // 璋冪敤鎺ュ彛鏇存柊宸ュ簭鍒楄〃
- findProcessRouteItemList({ routeId: routeId })
- .then(res => {
- const route = routeList.value.find(r => r.id === routeId);
- if (route) {
- route.processList = (res.data || []).map(process => ({
- ...process,
- processId: process.processId || process.id,
- expanded: false,
- }));
- }
- })
- .catch(err => {
- console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
- });
- })
- .catch(err => {
- ElMessage.error("鍒犻櫎澶辫触");
- console.error("鍒犻櫎宸ュ簭澶辫触锛�", err);
- });
- });
- };
-
- const handleProcessSubmit = () => {
- processFormRef.value.validate(valid => {
- if (valid) {
- ElMessage.success(isProcessEdit.value ? "缂栬緫鎴愬姛" : "鏂板鎴愬姛");
- processDialogVisible.value = false;
- // 璋冪敤鎺ュ彛鏇存柊宸ュ簭鍒楄〃
- if (currentRouteId.value) {
- findProcessRouteItemList({ routeId: currentRouteId.value })
- .then(res => {
- const route = routeList.value.find(
- r => r.id === currentRouteId.value
- );
- if (route) {
- route.processList = (res.data || []).map(process => ({
- ...process,
- processId: process.processId || process.id,
- expanded: false,
- }));
- }
- })
- .catch(err => {
- console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
- });
- }
- }
- });
- };
-
- // 閫夋嫨宸ュ簭鐩稿叧鏂规硶
- const handleProcessSearch = () => {
- const keyword = processSearchKeyword.value.trim().toLowerCase();
- if (!keyword) {
- filteredProcessList.value = availableProcessList.value;
- } else {
- filteredProcessList.value = availableProcessList.value.filter(
- item =>
- (item.name && item.name.toLowerCase().includes(keyword)) ||
- (item.no && item.no.toLowerCase().includes(keyword))
- );
- }
- };
-
- const handleProcessSelect = row => {
- selectedProcessItem.value = row;
- // 閲嶇疆浜у搧閫夋嫨琛ㄥ崟
- processForm.productModelId = undefined;
- processForm.productName = "";
- processForm.productModelName = "";
- processForm.unit = "";
- processForm.isQuality = row.isQuality || false;
- };
-
- // 澶勭悊宸ュ簭閫夋嫨鏃剁殑浜у搧閫夋嫨
- const handleProcessProductSelect = async products => {
- if (products && products.length > 0) {
- const product = products[0];
- processForm.productModelId = product.id;
- processForm.productName = product.productName;
- processForm.productModelName = product.model;
- processForm.unit = product.unit || "";
- showProductSelectDialog.value = false;
- }
- };
-
- const handleProcessSelectSubmit = () => {
- if (!selectedProcessItem.value) {
- ElMessage.warning("璇峰厛閫夋嫨涓�涓伐搴�");
- return;
- }
-
- if (!processForm.productModelId) {
- ElMessage.warning("璇烽�夋嫨浜у搧");
- return;
- }
-
- // 鏋勫缓璇锋眰鍙傛暟
- const params = {
- routeId: currentRouteId.value,
- processId: selectedProcessItem.value.id,
- dragSort: routePage.total + 1,
- ...processForm,
- };
-
- // 濡傛灉鏄慨鏀规搷浣滐紝娣诲姞id鍙傛暟
- if (selectedProcessItem.value.id) {
- params.id = currentId.value;
- params.dragSort = dragSort.value;
- }
-
- // 璋冪敤API娣诲姞宸ュ簭鎴栦慨鏀瑰伐搴�
- addOrUpdateProcessRouteItem(params)
- .then(res => {
- ElMessage.success(
- selectedProcessItem.value.id ? "淇敼宸ュ簭鎴愬姛" : "娣诲姞宸ュ簭鎴愬姛"
- );
- selectProcessDialogVisible.value = false;
- // 璋冪敤鎺ュ彛鏇存柊宸ュ簭鍒楄〃
- findProcessRouteItemList({ routeId: currentRouteId.value })
- .then(res => {
- const route = routeList.value.find(
- r => r.id === currentRouteId.value
- );
- if (route) {
- route.processList = (res.data || []).map(process => ({
- ...process,
- processId: process.processId || process.id,
- expanded: false,
- }));
- }
- })
- .catch(err => {
- console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
- });
- })
- .catch(err => {
- ElMessage.error(
- selectedProcessItem.value.id ? "淇敼宸ュ簭澶辫触" : "娣诲姞宸ュ簭澶辫触"
- );
- console.error(
- selectedProcessItem.value.id ? "淇敼宸ュ簭澶辫触锛�" : "娣诲姞宸ュ簭澶辫触锛�",
- err
- );
- });
- };
-
- // 鍙傛暟鎿嶄綔
- const handleAddParam = (routeId, process) => {
- currentRouteId.value = routeId;
- currentProcessId.value = process.id;
- selectedParam.value = null;
- paramSearchKeyword.value = "";
- paramPage.current = 1;
- // 鑾峰彇鍙�夊弬鏁板垪琛�
- getBaseParamList({
- paramName: paramSearchKeyword.value,
- current: paramPage.current,
- size: paramPage.size,
- }).then(res => {
- if (res.code === 200) {
- filteredParamList.value = res.data?.records || [];
- paramPage.total = res.data?.total || 0;
- } else {
- ElMessage.error(res.msg || "鏌ヨ澶辫触");
- }
- });
- selectParamDialogVisible.value = true;
- };
-
- const handleEditParam = (routeId, process, param) => {
- currentRouteId.value = routeId;
- currentProcessId.value = process.id;
- editParamForm.id = param.id;
- editParamForm.processId = process.id;
- editParamForm.paramId = param.paramId;
- editParamForm.paramName = param.parameterName || param.paramName;
- editParamForm.valueMode = param.parameterType2 || param.valueMode || "1";
- editParamForm.standardValue = param.standardValue;
- editParamForm.minValue = param.minValue;
- editParamForm.maxValue = param.maxValue;
- editParamForm.sort = param.sort || 1;
- editParamForm.isRequired = param.isRequired || 0;
- editParamForm.paramType = param.parameterType || param.paramType;
- editParamForm.paramFormat = param.parameterFormat || param.paramFormat;
- editParamForm.unit = param.unit || param.unit;
- editParamDialogVisible.value = true;
- };
-
- const handleDeleteParam = (routeId, process, param) => {
- ElMessageBox.confirm("纭畾瑕佸垹闄よ鍙傛暟鍚楋紵", "鎻愮ず", {
- confirmButtonText: "纭畾",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- }).then(() => {
- // 璋冪敤API鍒犻櫎鍙傛暟
- delProcessRouteItemParam(param.id)
- .then(res => {
- ElMessage.success("鍒犻櫎鎴愬姛");
- // 鍒锋柊鍙傛暟鍒楄〃
- toggleProcessParams2(process);
- })
- .catch(err => {
- ElMessage.error("鍒犻櫎鍙傛暟澶辫触");
- console.error("鍒犻櫎鍙傛暟澶辫触锛�", err);
- });
- });
- };
-
- const handleParamSubmit = () => {
- paramFormRef.value.validate(valid => {
- if (valid) {
- ElMessage.success(isParamEdit.value ? "缂栬緫鎴愬姛" : "鏂板鎴愬姛");
- paramDialogVisible.value = false;
- getRouteList();
- }
- });
- };
-
- const handleParamTypeChange = () => {
- if (paramForm.parameterType === "鏁板�兼牸寮�") {
- paramForm.parameterFormat = "#.0000";
- } else if (paramForm.parameterType === "鏃堕棿鏍煎紡") {
- paramForm.parameterFormat = "YYYY-MM-DD HH:mm:ss";
- } else {
- paramForm.parameterFormat = "";
- }
- };
-
- const getParamTypeTag = type => {
- const typeMap = {
- 1: "primary",
- 2: "info",
- 3: "warning",
- 4: "success",
- };
- return typeMap[type] || "default";
- };
-
- const getParamTypeText = type => {
- const typeMap = {
- 1: "鏁板�兼牸寮�",
- 2: "鏂囨湰鏍煎紡",
- 3: "涓嬫媺閫夐」",
- 4: "鏃堕棿鏍煎紡",
- };
- return typeMap[type] || "鏈煡鍙傛暟绫诲瀷";
- };
-
- // 閫夋嫨鍙傛暟鐩稿叧鏂规硶
- const handleParamSearch = () => {
- // 閲嶇疆鍒嗛〉
- paramPage.current = 1;
- // 閲嶆柊鍔犺浇鏁版嵁
- getBaseParamList({
- paramName: paramSearchKeyword.value,
- current: paramPage.current,
- size: paramPage.size,
- }).then(res => {
- if (res.code === 200) {
- filteredParamList.value = res.data?.records || [];
- paramPage.total = res.data?.total || 0;
- } else {
- ElMessage.error(res.msg || "鏌ヨ澶辫触");
- }
- });
- };
-
- const handleParamSelect = row => {
- selectedParam.value = row;
- };
-
- // 澶勭悊鍒嗛〉澶у皬鍙樺寲
- const handleParamSizeChange = size => {
- paramPage.size = size;
- getBaseParamList({
- paramName: paramSearchKeyword.value,
- current: paramPage.current,
- size: paramPage.size,
- }).then(res => {
- if (res.code === 200) {
- filteredParamList.value = res.data?.records || [];
- paramPage.total = res.data?.total || 0;
- } else {
- ElMessage.error(res.msg || "鏌ヨ澶辫触");
- }
- });
- };
-
- // 澶勭悊褰撳墠椤电爜鍙樺寲
- const handleParamCurrentChange = current => {
- paramPage.current = current;
- getBaseParamList({
- paramName: paramSearchKeyword.value,
- current: paramPage.current,
- size: paramPage.size,
- }).then(res => {
- if (res.code === 200) {
- filteredParamList.value = res.data?.records || [];
- paramPage.total = res.data?.total || 0;
- } else {
- ElMessage.error(res.msg || "鏌ヨ澶辫触");
- }
- });
- };
-
- // 宸ヨ壓璺嚎鍒嗛〉澶勭悊
- const handleRouteSizeChange = size => {
- routePage.size = size;
- getRouteList();
- };
-
- const handleRouteCurrentChange = current => {
- routePage.current = current;
- getRouteList();
- };
-
- const handleParamSelectSubmit = () => {
- if (!selectedParam.value) {
- ElMessage.warning("璇峰厛閫夋嫨涓�涓弬鏁�");
- return;
- }
-
- // 鎵惧埌瀵瑰簲鐨勫伐鑹鸿矾绾垮拰宸ュ簭
- const route = routeList.value.find(r => r.id === currentRouteId.value);
- const process = route?.processList.find(p => p.id === currentProcessId.value);
-
- if (route && process) {
- // 妫�鏌ュ弬鏁版槸鍚﹀凡瀛樺湪
- // const exists = process.paramList?.some(
- // p =>
- // p.paramId === selectedParam.value.id ||
- // p.parameterCode === selectedParam.value.paramCode
- // );
- // if (exists) {
- // ElMessage.warning("璇ュ弬鏁板凡瀛樺湪浜庡伐搴忎腑");
- // return;
- // }
-
- // 鍒ゆ柇鍙傛暟绫诲瀷锛屽彧鏈夋暟鍊肩被鍨嬫墠浼犳爣鍑嗗�笺�佹渶澶у�煎拰鏈�灏忓��
- const isNumericMode = selectedParam.value.valueMode === 1;
-
- // 璋冪敤API鏂板鍙傛暟
- addProcessRouteItemParam({
- routeItemId: process.id,
- paramId: selectedParam.value.id,
- standardValue: isNumericMode
- ? selectedParam.value.standardValue || ""
- : "",
- minValue: isNumericMode ? selectedParam.value.minValue || 0 : null,
- maxValue: isNumericMode ? selectedParam.value.maxValue || 0 : null,
- isRequired: selectedParam.value.isRequired || 0,
- })
- .then(res => {
- ElMessage.success("娣诲姞鍙傛暟鎴愬姛");
- selectParamDialogVisible.value = false;
- // 鍒锋柊鍙傛暟鍒楄〃
- toggleProcessParams2(process);
- })
- .catch(err => {
- ElMessage.error("娣诲姞鍙傛暟澶辫触");
- console.error("娣诲姞鍙傛暟澶辫触锛�", err);
- });
- }
- };
-
- const handleEditParamSubmit = () => {
- editParamFormRef.value.validate(valid => {
- if (valid) {
- // 鍒ゆ柇鍙傛暟绫诲瀷锛屽彧鏈夋暟鍊肩被鍨嬫墠浼犳爣鍑嗗�笺�佹渶澶у�煎拰鏈�灏忓��
- const isNumericMode = editParamForm.valueMode == 1;
-
- // 璋冪敤API淇敼鍙傛暟
- editProcessRouteItemParam({
- id: editParamForm.id,
- routeItemId: currentProcessId.value,
- paramId: editParamForm.paramId,
- standardValue: isNumericMode ? editParamForm.standardValue || "" : "",
- minValue: isNumericMode ? editParamForm.minValue || 0 : null,
- maxValue: isNumericMode ? editParamForm.maxValue || 0 : null,
- isRequired: editParamForm.isRequired || 0,
- })
- .then(res => {
- ElMessage.success("缂栬緫鎴愬姛");
- editParamDialogVisible.value = false;
- // 鎵惧埌瀵瑰簲鐨勫伐鑹鸿矾绾垮拰宸ュ簭
- const route = routeList.value.find(
- r => r.id === currentRouteId.value
- );
- const process = route?.processList.find(
- p => p.id === currentProcessId.value
- );
- // 鍒锋柊鍙傛暟鍒楄〃
- if (process) {
- toggleProcessParams2(process);
- }
- })
- .catch(err => {
- ElMessage.error("缂栬緫鍙傛暟澶辫触");
- console.error("缂栬緫鍙傛暟澶辫触锛�", err);
- });
- }
- });
- };
-
- // 鎷栨嫿鎺掑簭
- const handleDragStart = (event, index, routeId) => {
- draggedItem.value = index;
- draggedRouteId.value = routeId;
- event.dataTransfer.effectAllowed = "move";
- };
-
- const handleDragOver = event => {
- event.preventDefault();
- event.dataTransfer.dropEffect = "move";
- };
-
- const handleDrop = (event, dropIndex, routeId) => {
- event.preventDefault();
- if (draggedItem.value === null || draggedItem.value === dropIndex) return;
-
- const route = routeList.value.find(r => r.id === routeId);
- if (route && route.processList) {
- const draggedProcess = route.processList[draggedItem.value];
-
- // 璁$畻鏂扮殑鎺掑簭鍊�
- const newDragSort = dropIndex + 1;
-
- // 璋冪敤API鎺掑簭宸ュ簭
- sortProcessRouteItem({
- id: draggedProcess.id,
- dragSort: newDragSort,
- })
- .then(res => {
- // 璋冪敤鎺ュ彛鑾峰彇鏈�鏂扮殑宸ュ簭鍒楄〃
- findProcessRouteItemList({ routeId: routeId })
- .then(res => {
- if (route) {
- route.processList = (res.data || []).map(process => ({
- ...process,
- processId: process.processId || process.id,
- expanded: false,
- }));
- }
- ElMessage.success("鎺掑簭鎴愬姛");
- })
- .catch(err => {
- console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
- ElMessage.success("鎺掑簭鎴愬姛");
- });
- })
- .catch(err => {
- ElMessage.error("鎺掑簭澶辫触");
- console.error("鎺掑簭宸ュ簭澶辫触锛�", err);
- });
- }
- };
-
- const handleDragEnd = () => {
- draggedItem.value = null;
- draggedRouteId.value = null;
- };
-
- // 鑾峰彇鏁版嵁瀛楀吀
- const getDictTypes = () => {
- listType({ pageNum: 1, pageSize: 1000 }).then(res => {
- dictTypes.value = res.rows || [];
- });
- };
-
- getRouteList();
- getDictTypes();
-
- // 椤甸潰鍔犺浇鏃惰幏鍙栧伐搴忓垪琛�
onMounted(() => {
- getProcessListApi()
- .then(res => {
- // 澶勭悊杩斿洖鐨勬暟鎹紝鏄犲皠鍒伴〉闈㈤渶瑕佺殑鏍煎紡
- availableProcessList.value = (res.data || []).map(item => ({
- id: item.id,
- no: item.no || item.no,
- name: item.name || item.name,
- remark: item.remark || item.remark,
- status: item.status,
- isQuality: item.isQuality,
- }));
- filteredProcessList.value = availableProcessList.value;
- })
- .catch(() => {
- ElMessage.error("鑾峰彇宸ュ簭鍒楄〃澶辫触");
- });
+ getList();
});
</script>
-<style scoped lang="scss">
- .app-container {
- padding: 20px;
- padding-bottom: 80px;
- background-color: #f0f2f5;
- min-height: calc(100vh - 84px);
- overflow: hidden;
- }
-
- .route-header {
- margin-bottom: 20px;
-
- .add-route-btn {
- width: 100%;
- display: inline-flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- min-width: 120px;
- height: 100px;
- border: 2px dashed #dcdfe6;
- border-radius: 12px;
- background: #fafafa;
- cursor: pointer;
- transition: all 0.3s ease;
- color: #909399;
- padding: 0 20px;
-
- .el-icon {
- font-size: 24px;
- margin-bottom: 8px;
- }
-
- span {
- font-size: 13px;
- }
-
- &:hover {
- border-color: #409eff;
- background: #ecf5ff;
- color: #409eff;
- }
- }
- }
-
- .route-card-list {
- display: grid;
- grid-template-columns: repeat(1, 1fr);
- gap: 20px;
- max-height: calc(100vh - 240px);
- overflow-y: auto;
- padding-right: 10px;
- }
-
- /* 鑷畾涔夋粴鍔ㄦ潯鏍峰紡 */
- .route-card-list::-webkit-scrollbar {
- width: 8px;
- }
-
- .route-card-list::-webkit-scrollbar-track {
- background: #f1f1f1;
- border-radius: 4px;
- }
-
- .route-card-list::-webkit-scrollbar-thumb {
- background: #c1c1c1;
- border-radius: 4px;
- }
-
- .route-card-list::-webkit-scrollbar-thumb:hover {
- background: #a8a8a8;
- }
-
- .route-card {
- background: #fff;
- border-radius: 8px;
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
- overflow: hidden;
-
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 20px 40px;
- border-bottom: 1px solid #ebeef5;
- background: #f8f9fa;
-
- .route-info {
- display: flex;
- // flex-direction: column;
- // justify-content: center;
- // items-align: center;
- gap: 4px;
-
- .route-code {
- font-size: 12px;
- color: #909399;
- font-family: "Courier New", monospace;
- line-height: 30px;
- }
-
- .route-name {
- font-size: 18px;
- font-weight: 600;
- color: #303133;
- display: flex;
- align-items: center;
- }
- }
-
- .route-actions {
- display: flex;
- gap: 8px;
-
- // .el-button {
- // color: #409eff;
- // }
- }
- }
-
- .card-body {
- padding: 16px 40px;
-
- .route-desc {
- font-size: 14px;
- color: #606266;
- margin-bottom: 12px;
- }
-
- .route-meta {
- display: flex;
- gap: 24px;
- margin-bottom: 12px;
- padding: 10px 14px;
- background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
- border-radius: 8px;
- border-left: 3px solid #409eff;
-
- .meta-item {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 13px;
- margin-right: 40px;
-
- .el-icon {
- font-size: 14px;
- color: #409eff;
- }
-
- .meta-label {
- color: #909399;
- font-weight: 500;
- }
-
- .meta-value {
- color: #303133;
- font-weight: 600;
- }
- }
- }
-
- .expand-btn-wrapper {
- display: flex;
- justify-content: center;
- margin-top: 8px;
-
- .expand-btn {
- padding: 8px 20px;
- border-radius: 20px;
- background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%);
- border: 1px solid #b3d8ff;
- transition: all 0.3s ease;
-
- .btn-text {
- font-size: 13px;
- font-weight: 500;
- color: #409eff;
- margin-right: 6px;
- }
-
- .expand-icon {
- font-size: 14px;
- color: #409eff;
- transition: transform 0.3s ease;
- }
-
- &:hover {
- background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
- border-color: #409eff;
- box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
-
- .btn-text,
- .expand-icon {
- color: #fff;
- }
- }
-
- &.expanded {
- background: linear-gradient(135deg, #f0f9eb 0%, #e1f3d8 100%);
- border-color: #a5d69a;
-
- .btn-text,
- .expand-icon {
- color: #67c23a;
- }
-
- &:hover {
- background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
- border-color: #67c23a;
- box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
-
- .btn-text,
- .expand-icon {
- color: #fff;
- }
- }
- }
- }
- }
- }
-
- .process-route {
- padding: 0 20px 20px;
- background: #f5f7fa;
- border-top: 1px solid #ebeef5;
-
- .process-flow {
- display: flex;
- align-items: flex-start;
- gap: 8px;
- padding: 20px 0;
- overflow-x: auto;
- overflow-y: hidden;
-
- .process-flow-item {
- display: flex;
- align-items: center;
- gap: 8px;
-
- .process-node {
- background: #fff;
- border-radius: 12px;
- padding: 16px;
- border: 2px solid #ebeef5;
- cursor: move;
- transition: all 0.3s ease;
- // min-width: 180px;
- // max-width: 220px;
- width: 300px;
-
- &.expanded {
- width: 400px;
- }
-
- &:hover {
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
- transform: translateY(-2px);
- border-color: #409eff;
- }
-
- &:active {
- cursor: grabbing;
- }
-
- .process-node-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 12px;
-
- .process-number {
- width: 28px;
- height: 28px;
- border-radius: 50%;
- background: #409eff;
- color: #ffffff;
- font-size: 12px;
- font-weight: 600;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .process-actions {
- display: flex;
- gap: 4px;
- }
- }
-
- .process-node-body {
- text-align: center;
- margin-bottom: 12px;
-
- .process-code {
- font-size: 11px;
- color: #909399;
- font-family: "Courier New", monospace;
- margin-bottom: 4px;
- }
-
- .process-name {
- font-size: 15px;
- font-weight: 600;
- color: #303133;
- margin-bottom: 6px;
- }
-
- .process-desc {
- font-size: 12px;
- color: #606266;
- overflow: hidden;
- text-overflow: ellipsis;
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- }
- }
-
- .process-node-footer {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- padding-top: 10px;
- border-top: 1px solid #ebeef5;
- }
-
- .process-params-section {
- margin-top: 12px;
- padding-top: 12px;
- border-top: 1px solid #ebeef5;
-
- .params-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 8px;
- font-size: 13px;
- font-weight: 600;
- color: #303133;
- }
-
- .params-list {
- display: flex;
- flex-direction: column;
- gap: 6px;
- max-height: 200px;
- overflow-y: auto;
-
- .param-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 6px 8px;
- background: #fafafa;
- border-radius: 4px;
- border-left: 2px solid #409eff;
- font-size: 12px;
-
- .param-info {
- display: flex;
- flex-direction: row;
- align-items: center;
- gap: 6px;
- flex: 1;
- min-width: 0;
-
- .param-code {
- font-size: 11px;
- color: #e6a23c;
- font-family: "Courier New", monospace;
- margin-right: 20px;
- }
-
- .param-name {
- font-size: 12px;
- color: #303133;
- font-weight: 500;
- margin-right: 20px;
- }
-
- .param-value {
- font-size: 11px;
- color: #606266;
- }
- }
-
- .param-actions {
- display: flex;
- gap: 4px;
- flex-shrink: 0;
- }
- }
- }
- }
- }
-
- .flow-arrow {
- display: flex;
- align-items: center;
- color: #c0c4cc;
- font-size: 24px;
- padding: 0 4px;
-
- .el-icon {
- font-size: 20px;
- }
- }
- }
-
- .add-process-node {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- min-width: 100px;
- height: 137px;
- border: 2px dashed #dcdfe6;
- border-radius: 12px;
- background: #fafafa;
- cursor: pointer;
- transition: all 0.3s ease;
- color: #909399;
- // margin-left: 10px;
-
- .el-icon {
- font-size: 24px;
- margin-bottom: 8px;
- }
-
- span {
- font-size: 13px;
- }
-
- &:hover {
- border-color: #409eff;
- background: #ecf5ff;
- color: #409eff;
- }
- }
- }
- }
- }
-
- // 鎷栨嫿鏃剁殑鏍峰紡
- .process-flow-item.dragging {
- opacity: 0.5;
- transform: scale(0.98);
- }
-
- // 閫夋嫨宸ュ簭瀵硅瘽妗嗘牱寮�
- .process-select-container {
- display: flex;
- gap: 20px;
- height: 450px;
-
- .process-list-area {
- flex: 1;
- display: flex;
- flex-direction: column;
-
- .area-title {
- font-size: 14px;
- font-weight: 600;
- color: #303133;
- margin-bottom: 12px;
- padding-bottom: 8px;
- border-bottom: 1px solid #ebeef5;
- }
-
- .search-box {
- margin-bottom: 12px;
-
- .el-input {
- width: 100%;
- }
- }
- }
-
- .process-detail-area {
- width: 380px;
- display: flex;
- flex-direction: column;
- background: #f5f7fa;
- border-radius: 8px;
- padding: 16px;
-
- .area-title {
- font-size: 14px;
- font-weight: 600;
- color: #303133;
- margin-bottom: 16px;
- padding-bottom: 8px;
- border-bottom: 1px solid #ebeef5;
- }
-
- .process-detail-form {
- .el-form-item {
- margin-bottom: 12px;
-
- .el-form-item__label {
- color: #606266;
- font-weight: 500;
- }
- }
-
- .detail-text {
- color: #303133;
- font-weight: 500;
- }
- }
- }
- }
-
- // 閫夋嫨鍙傛暟瀵硅瘽妗嗘牱寮�
- .param-select-container {
- display: flex;
- gap: 20px;
- height: 450px;
-
- .param-list-area {
- // flex: 1;
- width: 380px;
- display: flex;
- flex-direction: column;
-
- .area-title {
- font-size: 14px;
- font-weight: 600;
- color: #303133;
- margin-bottom: 12px;
- padding-bottom: 8px;
- border-bottom: 1px solid #ebeef5;
- }
-
- .search-box {
- margin-bottom: 12px;
-
- .el-input {
- width: 100%;
- }
- }
- }
-
- .param-detail-area {
- // width: 380px;
- flex: 1;
- display: flex;
- flex-direction: column;
- background: #f5f7fa;
- border-radius: 8px;
- padding: 16px;
-
- .area-title {
- font-size: 14px;
- font-weight: 600;
- color: #303133;
- margin-bottom: 16px;
- padding-bottom: 8px;
- border-bottom: 1px solid #ebeef5;
- }
-
- .param-detail-form {
- .el-form-item {
- margin-bottom: 12px;
-
- .el-form-item__label {
- color: #606266;
- font-weight: 500;
- }
- }
-
- .detail-text {
- color: #303133;
- font-weight: 500;
- }
- }
- }
- }
-
- // 鍒嗛〉鎺т欢鏍峰紡
- .pagination-container {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- display: flex;
- justify-content: flex-end;
- padding: 16px 20px;
- background-color: #fff !important;
- border-top: 1px solid #ebeef5;
- box-shadow: 0 -2px 12px 0 rgba(0, 0, 0, 0.1);
- z-index: 100;
-
- .el-pagination {
- .el-pagination__sizes {
- margin-right: 16px;
- }
-
- .el-pagination__jump {
- margin-left: 16px;
- }
-
- .el-pagination__total {
- color: #606266;
- font-size: 14px;
- }
-
- .el-pagination__button {
- border-radius: 4px;
- transition: all 0.3s ease;
-
- &:hover:not(:disabled) {
- color: #409eff;
- border-color: #409eff;
- }
- }
-
- .el-pagination__button--active {
- background-color: #409eff;
- border-color: #409eff;
- color: #fff;
- }
- }
- }
-</style>
+<style scoped></style>
diff --git a/src/views/productionManagement/processRoute/index2.vue b/src/views/productionManagement/processRoute/index2.vue
new file mode 100644
index 0000000..40ab129
--- /dev/null
+++ b/src/views/productionManagement/processRoute/index2.vue
@@ -0,0 +1,2417 @@
+<template>
+ <div class="app-container">
+ <div class="route-header">
+ <div class="add-route-btn"
+ @click="handleAddRoute">
+ <el-icon>
+ <Plus />
+ </el-icon>
+ <span>鏂板宸ヨ壓璺嚎</span>
+ </div>
+ </div>
+ <div class="route-card-list">
+ <div v-for="route in routeList"
+ :key="route.id"
+ class="route-card">
+ <div class="card-header">
+ <div class="route-info">
+ <span class="route-name"><el-icon style="margin-right: 8px;line-height: 30px;">
+ <ScaleToOriginal />
+ </el-icon>{{route.routeCode }}<el-tag style="margin-left: 8px"
+ :type="!route.status ? 'warning' : 'success'">{{ !route.status ? '鑽夌' : '鎵瑰噯' }}</el-tag></span>
+ <!-- <span class="route-code">{{ route.routeCode }}</span> -->
+ </div>
+ <div class="route-actions">
+ <el-button v-if="!route.status"
+ link
+ type="success"
+ @click="handleApproveRoute(route)">
+ <el-icon>
+ <Check />
+ </el-icon>
+ 鎵瑰噯
+ </el-button>
+ <el-button v-if="route.status"
+ link
+ type="warning"
+ @click="handleRevokeApproveRoute(route)">
+ <el-icon>
+ <Close />
+ </el-icon>
+ 鎾ら攢鎵瑰噯
+ </el-button>
+ <el-button link
+ type="primary"
+ @click="handleEditRoute(route)">
+ <el-icon>
+ <Edit />
+ </el-icon>
+ 缂栬緫
+ </el-button>
+ <el-button link
+ type="danger"
+ @click="handleDeleteRoute(route)">
+ <el-icon>
+ <Delete />
+ </el-icon>
+ 鍒犻櫎
+ </el-button>
+ </div>
+ </div>
+ <div class="card-body">
+ <div class="route-meta">
+ <span class="meta-item">
+ <el-icon>
+ <Box />
+ </el-icon>
+ <span class="meta-label">浜у搧:</span>
+ <span class="meta-value">{{ route.productName }} - {{ route.productModelName }}</span>
+ </span>
+ <span class="meta-item">
+ <el-icon>
+ <Document />
+ </el-icon>
+ <span class="meta-label">BOM:</span>
+ <span class="meta-value">{{ route.bomNo || '-' }}</span>
+ </span>
+ <span class="meta-item">
+ <el-icon>
+ <Document />
+ </el-icon>
+ <span class="meta-label">澶囨敞:</span>
+ <span class="meta-value">{{ route.description || '鏆傛棤鎻忚堪' }}</span>
+ </span>
+ </div>
+ <div class="expand-btn-wrapper">
+ <el-button class="expand-btn"
+ :class="{ expanded: route.expanded }"
+ type="primary"
+ text
+ @click="toggleExpand(route)">
+ <span class="btn-text">{{ route.expanded ? '鏀惰捣宸ュ簭璺嚎' : '灞曞紑宸ュ簭璺嚎' }}</span>
+ <el-icon class="expand-icon">
+ <component :is="route.expanded ? 'ArrowUp' : 'ArrowDown'" />
+ </el-icon>
+ </el-button>
+ </div>
+ </div>
+ <div v-if="route.expanded"
+ class="process-route">
+ <div class="process-flow">
+ <div v-for="(process, index) in route.processList"
+ :key="process.id"
+ class="process-flow-item"
+ draggable="true"
+ @dragstart="handleDragStart($event, index, route.id)"
+ @dragover="handleDragOver($event)"
+ @drop="handleDrop($event, index, route.id)"
+ @dragend="handleDragEnd">
+ <div class="process-node"
+ :class="{ expanded: process.expanded }">
+ <div class="process-node-header">
+ <div class="process-number">{{ index + 1 }}</div>
+ <div class="process-actions">
+ <el-button link
+ type="primary"
+ @click="handleEditProcessSelect(route, index, process)">
+ <el-icon>
+ <Edit />
+ </el-icon>
+ </el-button>
+ <el-button link
+ type="danger"
+ @click="handleDeleteProcess(route.id, process)">
+ <el-icon>
+ <Delete />
+ </el-icon>
+ </el-button>
+ </div>
+ </div>
+ <div class="process-node-body">
+ <!-- <div class="process-code">{{ process.processId }}</div> -->
+ <div class="process-name">{{ process.processName }}</div>
+ <!-- <div class="process-desc">{{ process.remark || '鏆傛棤鎻忚堪' }}</div> -->
+ </div>
+ <div class="process-node-footer">
+ <!-- <el-tag size="small"
+ :type="process.status === '1' ? 'success' : 'info'">
+ {{ process.status === '1' ? '鍚敤' : '鍋滅敤' }}
+ </el-tag> -->
+ <el-button type="primary"
+ link
+ size="small"
+ @click="toggleProcessParams(process)">
+ {{ process.expanded ? '鏀惰捣鍙傛暟' : '灞曞紑鍙傛暟' }}
+ ({{ process.paramCount }})
+ </el-button>
+ </div>
+ <div v-if="process.expanded"
+ class="process-params-section">
+ <div class="params-header">
+ <span>鍙傛暟鍒楄〃</span>
+ <el-button type="primary"
+ link
+ size="small"
+ @click="handleAddParam(route.id, process)">
+ <el-icon>
+ <Plus />
+ </el-icon>鏂板
+ </el-button>
+ </div>
+ <div class="params-list">
+ <div v-for="param in process.paramList"
+ :key="param.id"
+ class="param-item">
+ <div class="param-info">
+ <span class="param-code">{{ param.paramName }}</span>
+ <!-- <span class="param-name">{{ param.paramName }}</span> -->
+ <!-- <el-tag size="small"
+ style="margin-right: 20px;"
+ :type="getParamTypeTag(param.parameterType)">
+ {{ param.parameterType }}
+ </el-tag> -->
+ <span v-if="param.valueMode==1"
+ class="param-value">鏍囧噯鍊硷細{{ param.standardValue || "-" }} {{ param.unit }}</span>
+ <span v-else
+ class="param-value">鏍囧噯鍊硷細{{ param.minValue || "-" }}-{{ param.maxValue || "-" }} {{ param.unit }}</span>
+ </div>
+ <div class="param-actions">
+ <el-button link
+ type="primary"
+ size="small"
+ @click="handleEditParam(route.id, process, param)">
+ 缂栬緫
+ </el-button>
+ <el-button link
+ type="danger"
+ size="small"
+ @click="handleDeleteParam(route.id, process, param)">
+ 鍒犻櫎
+ </el-button>
+ </div>
+ </div>
+ <el-empty v-if="!process.paramList || process.paramList.length === 0"
+ description="鏆傛棤鍙傛暟"
+ :image-size="50" />
+ </div>
+ </div>
+ </div>
+ <div v-if="index < route.processList.length - 1"
+ class="flow-arrow">
+ <el-icon>
+ <Right />
+ </el-icon>
+ </div>
+ </div>
+ <div class="add-process-node"
+ @click="handleSelectProcess(route, index)">
+ <el-icon>
+ <Plus />
+ </el-icon>
+ <span>鏂板宸ュ簭</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <!-- 鍒嗛〉鎺т欢 -->
+ <div class="pagination-container">
+ <el-pagination v-model:current-page="routePage.current"
+ v-model:page-size="routePage.size"
+ :page-sizes="[10, 20, 50, 100]"
+ layout="total, sizes, prev, pager, next, jumper"
+ :total="routePage.total"
+ @size-change="handleRouteSizeChange"
+ @current-change="handleRouteCurrentChange" />
+ </div>
+ <!-- 宸ヨ壓璺嚎鏂板/缂栬緫瀵硅瘽妗� -->
+ <el-dialog v-model="routeDialogVisible"
+ :title="isRouteEdit ? '缂栬緫宸ヨ壓璺嚎' : '鏂板宸ヨ壓璺嚎'"
+ width="500px">
+ <el-form :model="routeForm"
+ :rules="routeRules"
+ ref="routeFormRef"
+ label-width="120px">
+ <el-form-item label="浜у搧鍚嶇О"
+ prop="productModelId">
+ <el-button type="primary"
+ @click="handleProcessProductSelectClick2">
+ {{ routeForm.productName && routeForm.productModelName
+ ? `${routeForm.productName} - ${routeForm.productModelName}`
+ : '閫夋嫨浜у搧' }}
+ </el-button>
+ </el-form-item>
+ <el-form-item label="BOM"
+ prop="bomId">
+ <el-select v-model="routeForm.bomId"
+ placeholder="璇烽�夋嫨BOM"
+ clearable
+ :disabled="!routeForm.productModelId || bomOptions.length === 0"
+ style="width: 100%">
+ <el-option v-for="item in bomOptions"
+ :key="item.id"
+ :label="item.bomNo || `BOM-${item.id}`"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="璺嚎缂栫爜"
+ prop="routeCode">
+ <el-input v-model="routeForm.routeCode"
+ disabled
+ placeholder="鑷姩鐢熸垚" />
+ </el-form-item>
+ <el-form-item label="澶囨敞"
+ prop="description">
+ <el-input v-model="routeForm.description"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ヨ矾绾挎弿杩�" />
+ </el-form-item>
+ <!-- <el-form-item label="鐘舵��"
+ prop="status">
+ <el-radio-group v-model="routeForm.status">
+ <el-radio label="1">鍚敤</el-radio>
+ <el-radio label="0">鍋滅敤</el-radio>
+ </el-radio-group>
+ </el-form-item> -->
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="routeDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="handleRouteSubmit">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <!-- 浜у搧閫夋嫨寮圭獥 -->
+ <ProductSelectDialog v-model="showProductSelectDialog"
+ @confirm="handleProductSelect"
+ single />
+ <!-- 宸ュ簭鏂板/缂栬緫瀵硅瘽妗� -->
+ <el-dialog v-model="processDialogVisible"
+ :title="isProcessEdit ? '缂栬緫宸ュ簭' : '鏂板宸ュ簭'"
+ width="500px">
+ <el-form :model="processForm"
+ :rules="processRules"
+ ref="processFormRef"
+ label-width="120px">
+ <el-form-item label="宸ュ簭缂栫爜"
+ prop="no">
+ <el-input v-model="processForm.no"
+ placeholder="璇疯緭鍏ュ伐搴忕紪鐮�" />
+ </el-form-item>
+ <el-form-item label="宸ュ簭鍚嶇О"
+ prop="name">
+ <el-input v-model="processForm.name"
+ placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
+ </el-form-item>
+ <el-form-item label="宸ュ簭鎻忚堪"
+ prop="remark">
+ <el-input v-model="processForm.remark"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ュ伐搴忔弿杩�" />
+ </el-form-item>
+ <el-form-item label="鐘舵��"
+ prop="status">
+ <el-radio-group v-model="processForm.status">
+ <el-radio :label="true">鍚敤</el-radio>
+ <el-radio :label="false">鍋滅敤</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="processDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="handleProcessSubmit">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <!-- 閫夋嫨宸ュ簭瀵硅瘽妗� -->
+ <el-dialog v-model="selectProcessDialogVisible"
+ title="閫夋嫨宸ュ簭"
+ width="1000px">
+ <div class="process-select-container">
+ <!-- 宸︿晶宸ュ簭鍒楄〃 -->
+ <div class="process-list-area">
+ <div class="area-title">鍙�夊伐搴�</div>
+ <div class="search-box">
+ <el-input v-model="processSearchKeyword"
+ placeholder="璇疯緭鍏ュ伐搴忓悕绉版悳绱�"
+ clearable
+ size="small"
+ @input="handleProcessSearch">
+ <template #prefix>
+ <el-icon>
+ <Search />
+ </el-icon>
+ </template>
+ </el-input>
+ </div>
+ <el-table :data="filteredProcessList"
+ height="360"
+ border
+ highlight-current-row
+ @current-change="handleProcessSelect">
+ <el-table-column prop="no"
+ label="宸ュ簭缂栧彿"
+ width="100" />
+ <el-table-column prop="name"
+ label="宸ュ簭鍚嶇О" />
+ <el-table-column prop="remark"
+ label="宸ュ簭鎻忚堪" />
+ <el-table-column prop="status"
+ label="鐘舵��"
+ width="80">
+ <template #default="scope">
+ <el-tag size="small"
+ :type="scope.row.status ? 'success' : 'info'">
+ {{ scope.row.status ? '鍚敤' : '鍋滅敤' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ <!-- 鍙充晶宸ュ簭璇︽儏 -->
+ <div class="process-detail-area">
+ <div class="area-title">宸ュ簭璇︽儏</div>
+ <el-form v-if="selectedProcessItem"
+ :model="processForm"
+ label-width="100px"
+ class="process-detail-form">
+ <el-form-item label="宸ュ簭缂栧彿">
+ <span class="detail-text">{{ selectedProcessItem.no }}</span>
+ </el-form-item>
+ <el-form-item label="宸ュ簭鍚嶇О">
+ <span class="detail-text">{{ selectedProcessItem.name }}</span>
+ </el-form-item>
+ <el-form-item label="宸ュ簭鎻忚堪">
+ <span class="detail-text">{{ selectedProcessItem.remark || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鐘舵��">
+ <el-tag size="small"
+ :type="selectedProcessItem.status ? 'success' : 'info'">
+ {{ selectedProcessItem.status ? '鍚敤' : '鍋滅敤' }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鏄惁璐ㄦ">
+ <el-tag size="small"
+ :type="selectedProcessItem.isQuality ? 'success' : 'info'">
+ {{ selectedProcessItem.isQuality ? '璐ㄦ' : '闈炶川妫�' }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="浜у搧鍚嶇О"
+ prop="productModelId">
+ <el-button type="primary"
+ @click="handleProcessProductSelectClick">
+ {{ processForm.productName && processForm.model
+ ? `${processForm.productName} - ${processForm.model}`
+ : '閫夋嫨浜у搧' }}
+ </el-button>
+ </el-form-item>
+ <el-form-item label="鍗曚綅"
+ prop="unit">
+ <el-input v-model="processForm.unit"
+ :placeholder="processForm.productModelId ? '鏍规嵁閫夋嫨鐨勪骇鍝佽嚜鍔ㄥ甫鍑�' : '璇峰厛閫夋嫨浜у搧' "
+ clearable
+ :disabled="true" />
+ </el-form-item>
+ <el-form-item label="鏄惁璐ㄦ"
+ prop="isQuality">
+ <el-switch v-model="processForm.isQuality"
+ :active-value="true"
+ inactive-value="false" />
+ </el-form-item>
+ </el-form>
+ <el-empty v-else
+ description="璇蜂粠宸︿晶閫夋嫨宸ュ簭" />
+ </div>
+ </div>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="selectProcessDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ :disabled="!selectedProcessItem || !processForm.productModelId"
+ @click="handleProcessSelectSubmit">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <!-- 鍙傛暟鏂板/缂栬緫瀵硅瘽妗� -->
+ <el-dialog v-model="paramDialogVisible"
+ :title="isParamEdit ? '缂栬緫鍙傛暟' : '鏂板鍙傛暟'"
+ width="500px">
+ <el-form :model="paramForm"
+ :rules="paramRules"
+ ref="paramFormRef"
+ label-width="120px">
+ <el-form-item label="鍙傛暟缂栧彿"
+ prop="parameterCode">
+ <el-input v-model="paramForm.parameterCode"
+ placeholder="璇疯緭鍏ュ弬鏁扮紪鍙�" />
+ </el-form-item>
+ <el-form-item label="鍙傛暟鍚嶇О"
+ prop="parameterName">
+ <el-input v-model="paramForm.parameterName"
+ placeholder="璇疯緭鍏ュ弬鏁板悕绉�" />
+ </el-form-item>
+ <el-form-item label="鍙傛暟妯″紡"
+ prop="parameterType2">
+ <el-select v-model="paramForm.parameterType2"
+ placeholder="璇烽�夋嫨鍙傛暟妯″紡">
+ <el-option label="鍗曞��"
+ value="1" />
+ <el-option label="鍖洪棿"
+ value="2" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍙傛暟绫诲瀷"
+ prop="parameterType">
+ <el-select v-model="paramForm.parameterType"
+ @change="handleParamTypeChange"
+ placeholder="璇烽�夋嫨鍙傛暟绫诲瀷">
+ <el-option label="鏁板�兼牸寮�"
+ value="鏁板�兼牸寮�" />
+ <el-option label="鏂囨湰鏍煎紡"
+ value="鏂囨湰鏍煎紡" />
+ <el-option label="涓嬫媺閫夐」"
+ value="涓嬫媺閫夐」" />
+ <el-option label="鏃堕棿鏍煎紡"
+ value="鏃堕棿鏍煎紡" />
+ </el-select>
+ </el-form-item>
+ <el-form-item v-if="paramForm.parameterType === '涓嬫媺閫夐」'"
+ label="鏁版嵁瀛楀吀"
+ prop="parameterFormat">
+ <el-select v-model="paramForm.parameterFormat"
+ placeholder="璇烽�夋嫨鏁版嵁瀛楀吀">
+ <el-option v-for="item in dictTypes"
+ :key="item.dictType"
+ :label="item.dictName"
+ :value="item.dictType" />
+ </el-select>
+ </el-form-item>
+ <el-form-item v-else-if="paramForm.parameterType === '鏃堕棿鏍煎紡'"
+ label="鏃堕棿鏍煎紡"
+ prop="parameterFormat">
+ <el-select v-model="paramForm.parameterFormat"
+ placeholder="璇烽�夋嫨鏃堕棿鏍煎紡">
+ <el-option label="YYYY-MM-DD HH:mm:ss"
+ value="YYYY-MM-DD HH:mm:ss" />
+ <el-option label="YYYY-MM-DD"
+ value="YYYY-MM-DD" />
+ </el-select>
+ </el-form-item>
+ <el-form-item v-else
+ label="鍙傛暟鏍煎紡"
+ prop="parameterFormat">
+ <el-input v-model="paramForm.parameterFormat"
+ placeholder="璇疯緭鍏ュ弬鏁版牸寮�" />
+ </el-form-item>
+ <el-form-item label="鏍囧噯鍊�"
+ prop="standardValue">
+ <el-input v-model="paramForm.standardValue"
+ placeholder="璇疯緭鍏ユ爣鍑嗗��" />
+ </el-form-item>
+ <el-form-item label="鍗曚綅"
+ prop="unit">
+ <el-input v-model="paramForm.unit"
+ placeholder="璇疯緭鍏ュ崟浣�" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="paramDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="handleParamSubmit">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <!-- 閫夋嫨鍙傛暟瀵硅瘽妗� -->
+ <el-dialog v-model="selectParamDialogVisible"
+ title="閫夋嫨鍙傛暟"
+ width="1000px">
+ <div class="param-select-container">
+ <!-- 宸︿晶鍙傛暟鍒楄〃 -->
+ <div class="param-list-area">
+ <div class="area-title">鍙�夊弬鏁�</div>
+ <div class="search-box">
+ <el-input v-model="paramSearchKeyword"
+ placeholder="璇疯緭鍏ュ弬鏁板悕绉版悳绱�"
+ clearable
+ size="small"
+ @input="handleParamSearch">
+ <template #prefix>
+ <el-icon>
+ <Search />
+ </el-icon>
+ </template>
+ </el-input>
+ </div>
+ <el-table :data="filteredParamList"
+ height="300"
+ border
+ highlight-current-row
+ @current-change="handleParamSelect">
+ <el-table-column prop="paramName"
+ label="鍙傛暟鍚嶇О" />
+ <el-table-column prop="paramType"
+ label="鍙傛暟绫诲瀷">
+ <template #default="scope">
+ <el-tag size="small"
+ :type="getParamTypeTag(scope.row.paramType)">
+ {{ getParamTypeText(scope.row.paramType) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+ <!-- 鍒嗛〉鎺т欢 -->
+ <div class="pagination-container"
+ style="margin-top: 10px;">
+ <el-pagination v-model:current-page="paramPage.current"
+ v-model:page-size="paramPage.size"
+ :page-sizes="[10, 20, 50, 100]"
+ layout="total, sizes, prev, pager, next, jumper"
+ :total="paramPage.total"
+ @size-change="handleParamSizeChange"
+ @current-change="handleParamCurrentChange"
+ size="small" />
+ </div>
+ </div>
+ <!-- 鍙充晶鍙傛暟璇︽儏 -->
+ <div class="param-detail-area">
+ <div class="area-title">鍙傛暟璇︽儏</div>
+ <el-form v-if="selectedParam"
+ :model="selectedParam"
+ label-width="100px"
+ class="param-detail-form">
+ <el-form-item label="鍙傛暟鍚嶇О">
+ <span class="detail-text">{{ selectedParam.paramName }}</span>
+ </el-form-item>
+ <el-form-item label="鍙傛暟妯″紡">
+ <el-tag size="small"
+ :type="selectedParam.valueMode == '1' ? 'success' : 'warning'">
+ {{ selectedParam.valueMode == '1' ? '鍗曞��' : '鍖洪棿' }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鍙傛暟绫诲瀷">
+ <el-tag size="small"
+ :type="getParamTypeTag(selectedParam.paramType)">
+ {{ getParamTypeText(selectedParam.paramType) }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鍙傛暟鏍煎紡">
+ <span class="detail-text">{{ selectedParam.paramFormat || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鍗曚綅">
+ <span class="detail-text">{{ selectedParam.unit || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鏍囧噯鍊�"
+ v-if="selectedParam.valueMode == '1' && selectedParam.paramType == '1'">
+ <el-input v-model="selectedParam.standardValue"
+ type="number"
+ placeholder="璇疯緭鍏ラ粯璁ゅ��" />
+ </el-form-item>
+ <el-form-item label="鏈�灏忓��"
+ v-if="selectedParam.valueMode == '2' && selectedParam.paramType == '1'">
+ <el-input v-model="selectedParam.minValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ渶灏忓��" />
+ </el-form-item>
+ <el-form-item label="鏈�澶у��"
+ v-if="selectedParam.valueMode == '2' && selectedParam.paramType == '1'">
+ <el-input v-model="selectedParam.maxValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ渶澶у��" />
+ </el-form-item>
+ <el-form-item label="鎺掑簭">
+ <el-input v-model="selectedParam.sort"
+ type="number"
+ placeholder="璇疯緭鍏ユ帓搴�" />
+ </el-form-item>
+ <el-form-item label="鏄惁蹇呭~">
+ <el-switch v-model="selectedParam.isRequired"
+ :active-value="1"
+ :inactive-value="0" />
+ </el-form-item>
+ </el-form>
+ <el-empty v-else
+ description="璇蜂粠宸︿晶閫夋嫨鍙傛暟" />
+ </div>
+ </div>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="selectParamDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ :disabled="!selectedParam"
+ @click="handleParamSelectSubmit">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <!-- 缂栬緫鍙傛暟瀵硅瘽妗� -->
+ <el-dialog v-model="editParamDialogVisible"
+ title="缂栬緫鍙傛暟"
+ width="600px">
+ <el-form :model="editParamForm"
+ :rules="editParamRules"
+ ref="editParamFormRef"
+ label-width="120px">
+ <el-form-item label="鍙傛暟鍚嶇О">
+ <span class="detail-text">{{ editParamForm.paramName }}</span>
+ </el-form-item>
+ <el-form-item label="鍙傛暟妯″紡">
+ <el-tag size="small"
+ :type="editParamForm.valueMode == '1' ? 'success' : 'warning'">
+ {{ editParamForm.valueMode == '1' ? '鍗曞��' : '鍖洪棿' }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鍙傛暟绫诲瀷">
+ <el-tag size="small"
+ :type="getParamTypeTag(editParamForm.paramType)">
+ {{ getParamTypeText(editParamForm.paramType) }}
+ </el-tag>
+ </el-form-item>
+ <el-form-item label="鍙傛暟鏍煎紡">
+ <span class="detail-text">{{ editParamForm.paramFormat || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鍗曚綅">
+ <span class="detail-text">{{ editParamForm.unit || '-' }}</span>
+ </el-form-item>
+ <el-form-item label="鏍囧噯鍊�"
+ v-if="editParamForm.valueMode == '1' && editParamForm.paramType == '1'"
+ prop="standardValue">
+ <el-input v-model="editParamForm.standardValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ爣鍑嗗��" />
+ </el-form-item>
+ <el-form-item label="鏈�灏忓��"
+ v-if="editParamForm.valueMode == '2' && editParamForm.paramType == '1'"
+ prop="minValue">
+ <el-input v-model="editParamForm.minValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ渶灏忓��" />
+ </el-form-item>
+ <el-form-item label="鏈�澶у��"
+ v-if="editParamForm.valueMode == '2' && editParamForm.paramType == '1'"
+ prop="maxValue">
+ <el-input v-model="editParamForm.maxValue"
+ type="number"
+ placeholder="璇疯緭鍏ユ渶澶у��" />
+ </el-form-item>
+ <el-form-item label="鎺掑簭"
+ prop="sort">
+ <el-input v-model="editParamForm.sort"
+ type="number"
+ placeholder="璇疯緭鍏ユ帓搴�" />
+ </el-form-item>
+ <el-form-item label="鏄惁蹇呭~"
+ prop="isRequired">
+ <el-switch v-model="editParamForm.isRequired"
+ :active-value="1"
+ :inactive-value="0" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="editParamDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="handleEditParamSubmit">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+ import { ref, reactive, getCurrentInstance, onMounted } from "vue";
+ import { ElMessage, ElMessageBox } from "element-plus";
+ import {
+ Plus,
+ Edit,
+ Delete,
+ ArrowUp,
+ ArrowDown,
+ Right,
+ Search,
+ Check,
+ Close,
+ Box,
+ Document,
+ } from "@element-plus/icons-vue";
+ import { listType } from "@/api/system/dict/type";
+ import { getByModel } from "@/api/productionManagement/productBom.js";
+ import { add, update, del } from "@/api/productionManagement/processRoute.js";
+ import {
+ addOrUpdateProcessRouteItem,
+ batchDeleteProcessRouteItem,
+ sortProcessRouteItem,
+ findProcessRouteItemList,
+ getProcessParamList,
+ addProcessRouteItemParam,
+ editProcessRouteItemParam,
+ delProcessRouteItemParam,
+ } from "@/api/productionManagement/processRouteItem.js";
+ import { list as getProcessListApi } from "@/api/productionManagement/productionProcess.js";
+ import { getBaseParamList } from "@/api/basicData/parameterMaintenance.js";
+ import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+
+ // 宸ヨ壓璺嚎鍒楄〃
+ const routeList = ref([]);
+ const dictTypes = ref([]);
+
+ // 宸ヨ壓璺嚎鍒嗛〉
+ const routePage = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+
+ // 鑾峰彇鍏ㄥ眬瀹炰緥
+ const { proxy } = getCurrentInstance();
+
+ // 浜у搧閫夋嫨鍜孊OM鐩稿叧
+ const showProductSelectDialog = ref(false);
+ const bomOptions = ref([]);
+
+ // 宸ヨ壓璺嚎瀵硅瘽妗�
+ const routeDialogVisible = ref(false);
+ const isRouteEdit = ref(false);
+ const routeFormRef = ref(null);
+ const routeForm = reactive({
+ id: null,
+ productModelId: null,
+ productName: "",
+ productModelName: "",
+ bomId: null,
+ routeCode: "",
+ description: "",
+ status: true,
+ });
+ const routeRules = {
+ productModelId: [
+ { required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" },
+ ],
+ bomId: [{ required: true, message: "璇烽�夋嫨BOM", trigger: "change" }],
+ };
+
+ // 宸ュ簭瀵硅瘽妗�
+ const processDialogVisible = ref(false);
+ const isProcessEdit = ref(false);
+ const processFormRef = ref(null);
+ const currentRouteId = ref(null);
+ const processForm = reactive({
+ id: null,
+ no: "",
+ name: "",
+ remark: "",
+ status: true,
+ });
+ const processRules = {
+ no: [{ required: true, message: "璇疯緭鍏ュ伐搴忕紪鐮�", trigger: "blur" }],
+ name: [{ required: true, message: "璇疯緭鍏ュ伐搴忓悕绉�", trigger: "blur" }],
+ };
+
+ // 閫夋嫨宸ュ簭瀵硅瘽妗�
+ const selectProcessDialogVisible = ref(false);
+ const availableProcessList = ref([]);
+ const filteredProcessList = ref([]);
+ const selectedProcessItem = ref(null);
+ const processSearchKeyword = ref("");
+ const currentRouteIndex = ref(null);
+
+ // 鍙傛暟瀵硅瘽妗�
+ const paramDialogVisible = ref(false);
+ const isParamEdit = ref(false);
+ const paramFormRef = ref(null);
+ const currentProcessId = ref(null);
+ const paramForm = reactive({
+ id: null,
+ parameterCode: "",
+ parameterName: "",
+ parameterType2: "1",
+ parameterType: "",
+ parameterFormat: "",
+ standardValue: "",
+ unit: "",
+ });
+ const paramRules = {
+ parameterCode: [
+ { required: true, message: "璇疯緭鍏ュ弬鏁扮紪鍙�", trigger: "blur" },
+ ],
+ parameterName: [
+ { required: true, message: "璇疯緭鍏ュ弬鏁板悕绉�", trigger: "blur" },
+ ],
+ parameterType: [
+ { required: true, message: "璇烽�夋嫨鍙傛暟绫诲瀷", trigger: "change" },
+ ],
+ };
+
+ // 閫夋嫨鍙傛暟瀵硅瘽妗�
+ const selectParamDialogVisible = ref(false);
+ const availableParamList = ref([]);
+ const filteredParamList = ref([]);
+ const selectedParam = ref(null);
+ const paramSearchKeyword = ref("");
+
+ // 鍙�夊弬鏁板垎椤�
+ const paramPage = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+
+ // 缂栬緫鍙傛暟瀵硅瘽妗�
+ const editParamDialogVisible = ref(false);
+ const editParamFormRef = ref(null);
+ const editParamForm = reactive({
+ id: null,
+ processId: null,
+ paramId: null,
+ paramName: "",
+ valueMode: "1",
+ standardValue: null,
+ minValue: null,
+ maxValue: null,
+ sort: 1,
+ isRequired: 0,
+ });
+ const editParamRules = reactive({
+ standardValue: [
+ {
+ required: true,
+ message: "璇疯緭鍏ユ爣鍑嗗��",
+ trigger: "blur",
+ validator: (rule, value, callback) => {
+ if (value === null || value === undefined || value === "") {
+ callback(new Error("璇疯緭鍏ユ爣鍑嗗��"));
+ } else {
+ callback();
+ }
+ },
+ },
+ ],
+ minValue: [
+ {
+ required: true,
+ message: "璇疯緭鍏ユ渶灏忓��",
+ trigger: "blur",
+ validator: (rule, value, callback) => {
+ if (value === null || value === undefined || value === "") {
+ callback(new Error("璇疯緭鍏ユ渶灏忓��"));
+ } else {
+ callback();
+ }
+ },
+ },
+ ],
+ maxValue: [
+ {
+ required: true,
+ message: "璇疯緭鍏ユ渶澶у��",
+ trigger: "blur",
+ validator: (rule, value, callback) => {
+ if (value === null || value === undefined || value === "") {
+ callback(new Error("璇疯緭鍏ユ渶澶у��"));
+ } else {
+ callback();
+ }
+ },
+ },
+ ],
+ sort: [
+ {
+ required: true,
+ message: "璇疯緭鍏ユ帓搴�",
+ trigger: "blur",
+ validator: (rule, value, callback) => {
+ if (value === null || value === undefined || value === "") {
+ callback(new Error("璇疯緭鍏ユ帓搴�"));
+ } else if (isNaN(value) || value < 1) {
+ callback(new Error("鎺掑簭蹇呴』鏄ぇ浜�0鐨勬暣鏁�"));
+ } else {
+ callback();
+ }
+ },
+ },
+ ],
+ });
+
+ // 鎷栨嫿鐩稿叧
+ const draggedItem = ref(null);
+ const draggedRouteId = ref(null);
+
+ // 鑾峰彇宸ヨ壓璺嚎鍒楄〃
+ const getRouteList = () => {
+ // 瀵煎叆 listPage 鏂规硶
+ import("@/api/productionManagement/processRoute.js").then(({ listPage }) => {
+ listPage({ pageNum: routePage.current, pageSize: routePage.size })
+ .then(res => {
+ // 澶勭悊杩斿洖鐨勬暟鎹紝鏄犲皠鍒伴〉闈㈤渶瑕佺殑鏍煎紡
+ routeList.value = (res.data?.records || []).map(item => ({
+ id: item.id,
+ productModelId: item.productModelId,
+ productName: item.productName,
+ productModelName: item.model || item.productModelName,
+ bomId: item.bomId,
+ bomNo: item.bomNo,
+ routeCode: item.processRouteCode || item.routeCode,
+ description: item.description || item.description,
+ status: item.status,
+ expanded: false,
+ processList: (item.processList || []).map(process => ({
+ ...process,
+ processId: process.processId || process.id,
+ expanded: false,
+ })),
+ }));
+ // 鏇存柊鍒嗛〉鎬绘暟
+ routePage.total = res.data?.total || 0;
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触锛�", err);
+ routeList.value = [];
+ routePage.total = 0;
+ });
+ });
+ };
+
+ // 灞曞紑/鏀惰捣宸ヨ壓璺嚎
+ const toggleExpand = route => {
+ route.expanded = !route.expanded;
+ if (route.expanded) {
+ // 璋冪敤鎺ュ彛鑾峰彇宸ュ簭鍒楄〃
+ findProcessRouteItemList({ routeId: route.id })
+ .then(res => {
+ route.processList = (res.data || []).map(process => ({
+ ...process,
+ processId: process.processId || process.id,
+ expanded: false,
+ }));
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
+ route.processList = [];
+ });
+ }
+ };
+
+ // 灞曞紑/鏀惰捣宸ュ簭鍙傛暟
+ const toggleProcessParams = process => {
+ process.expanded = !process.expanded;
+ if (process.expanded && process.id) {
+ // 璋冪敤鎺ュ彛鑾峰彇鍙傛暟鍒楄〃
+ getProcessParamList({
+ routeItemId: process.id,
+ pageNum: 1,
+ pageSize: 1000,
+ })
+ .then(res => {
+ if (res.code === 200) {
+ process.paramList = res.data?.records || [];
+ process.paramCount = process.paramList.length;
+ } else {
+ ElMessage.error(res.msg || "鑾峰彇鍙傛暟鍒楄〃澶辫触");
+ process.paramList = [];
+ process.paramCount = 0;
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇鍙傛暟鍒楄〃澶辫触锛�", err);
+ ElMessage.error("鑾峰彇鍙傛暟鍒楄〃澶辫触");
+ process.paramList = [];
+ process.paramCount = 0;
+ });
+ }
+ };
+ const toggleProcessParams2 = process => {
+ if (process.expanded && process.id) {
+ // 璋冪敤鎺ュ彛鑾峰彇鍙傛暟鍒楄〃
+ getProcessParamList({
+ routeItemId: process.id,
+ pageNum: 1,
+ pageSize: 1000,
+ })
+ .then(res => {
+ if (res.code === 200) {
+ process.paramList = res.data?.records || [];
+ process.paramCount = process.paramList.length;
+ } else {
+ ElMessage.error(res.msg || "鑾峰彇鍙傛暟鍒楄〃澶辫触");
+ process.paramList = [];
+ process.paramCount = 0;
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇鍙傛暟鍒楄〃澶辫触锛�", err);
+ ElMessage.error("鑾峰彇鍙傛暟鍒楄〃澶辫触");
+ process.paramList = [];
+ process.paramCount = 0;
+ });
+ }
+ };
+ // 宸ヨ壓璺嚎鎿嶄綔
+ const handleAddRoute = () => {
+ isRouteEdit.value = false;
+ routeForm.id = null;
+ routeForm.productModelId = null;
+ routeForm.productName = "";
+ routeForm.productModelName = "";
+ routeForm.bomId = null;
+ routeForm.routeCode = "";
+ routeForm.description = "";
+ routeForm.status = false;
+ bomOptions.value = [];
+ routeDialogVisible.value = true;
+ };
+
+ const handleEditRoute = route => {
+ isRouteEdit.value = true;
+ routeForm.id = route.id;
+ routeForm.productModelId = route.productModelId;
+ routeForm.productName = route.productName;
+ routeForm.productModelName = route.productModelName;
+ routeForm.bomId = route.bomId;
+ routeForm.routeCode = route.routeCode;
+ routeForm.description = route.description;
+ routeForm.status = route.status;
+ routeDialogVisible.value = true;
+ };
+
+ const handleDeleteRoute = route => {
+ ElMessageBox.confirm("纭畾瑕佸垹闄よ宸ヨ壓璺嚎鍚楋紵", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ del(route.id)
+ .then(res => {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ getRouteList();
+ })
+ .catch(err => {
+ ElMessage.error("鍒犻櫎澶辫触");
+ });
+ });
+ };
+
+ const handleRouteSubmit = () => {
+ routeFormRef.value.validate(valid => {
+ if (valid) {
+ // 鏋勫缓鎻愪氦鏁版嵁
+ const submitData = {
+ ...routeForm,
+ // 娉ㄦ剰锛欰PI 鏈熸湜鐨勫瓧娈靛悕鍙兘涓庤〃鍗曞瓧娈靛悕涓嶅悓
+ productId: routeForm.productModelId,
+ productModelId: routeForm.productModelId,
+ description: routeForm.description,
+ };
+
+ if (isRouteEdit.value) {
+ // 缂栬緫鎿嶄綔
+ update(submitData)
+ .then(res => {
+ ElMessage.success("缂栬緫鎴愬姛");
+ routeDialogVisible.value = false;
+ getRouteList();
+ })
+ .catch(err => {
+ ElMessage.error("缂栬緫澶辫触");
+ });
+ } else {
+ // 鏂板鎿嶄綔
+ add(submitData)
+ .then(res => {
+ ElMessage.success("鏂板鎴愬姛");
+ routeDialogVisible.value = false;
+ getRouteList();
+ })
+ .catch(err => {
+ ElMessage.error("鏂板澶辫触");
+ });
+ }
+ }
+ });
+ };
+ const isform2 = ref(null);
+ const handleProcessProductSelectClick = () => {
+ isform2.value = true;
+ showProductSelectDialog.value = true;
+ };
+ const handleProcessProductSelectClick2 = () => {
+ isform2.value = false;
+ showProductSelectDialog.value = true;
+ };
+
+ // 浜у搧閫夋嫨澶勭悊
+ const handleProductSelect = async products => {
+ if (isform2.value) {
+ // 甯垜鍐欏伐搴忎腑鐨勯�夋嫨浜у搧鐨勫洖璋�,骞朵笖鎶婂瓧娈靛姞杩沺rocessForm
+ if (products && products.length > 0) {
+ const product = products[0];
+ console.log("product:", product);
+ // 鎶妏roduct涓殑瀛楁娣诲姞鍒皃rocessForm涓�
+ // Object.assign(processForm, product);
+ processForm.productModelId = product.id;
+ processForm.productName = product.productName;
+ processForm.model = product.model;
+ processForm.unit = product.unit || "";
+ console.log("processForm:", processForm);
+
+ // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+ proxy.$refs["processFormRef"]?.validateField("productModelId");
+ }
+ } else {
+ if (products && products.length > 0) {
+ const product = products[0];
+ // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
+ try {
+ const res = await getByModel(product.id);
+ // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈data瀛楁
+ let bomList = [];
+ if (Array.isArray(res)) {
+ bomList = res;
+ } else if (res && res.data) {
+ bomList = Array.isArray(res.data) ? res.data : [res.data];
+ } else if (res && typeof res === "object") {
+ bomList = [res];
+ }
+ console.log("bomList:", bomList);
+ if (bomList.length > 0) {
+ routeForm.productModelId = product.id;
+ routeForm.productName = product.productName;
+ routeForm.productModelName = product.model;
+ routeForm.bomId = undefined; // 閲嶇疆BOM閫夋嫨
+ bomOptions.value = bomList;
+ showProductSelectDialog.value = false;
+ // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+ proxy.$refs["routeFormRef"]?.validateField("productModelId");
+ } else {
+ proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+ }
+ } catch (error) {
+ // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
+ proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+ }
+ }
+ }
+ };
+
+ const handleApproveRoute = route => {
+ ElMessageBox.confirm("纭畾瑕佹壒鍑嗚宸ヨ壓璺嚎鍚楋紵", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "info",
+ }).then(() => {
+ // 璋冪敤淇敼鎺ュ彛锛屽彧淇敼status瀛楁涓烘壒鍑嗙姸鎬�
+ update({ id: route.id, status: true })
+ .then(res => {
+ ElMessage.success("鎵瑰噯鎴愬姛");
+ getRouteList();
+ })
+ .catch(err => {
+ ElMessage.error("鎵瑰噯澶辫触");
+ });
+ });
+ };
+
+ const handleRevokeApproveRoute = route => {
+ ElMessageBox.confirm("纭畾瑕佹挙閿�鎵瑰噯璇ュ伐鑹鸿矾绾垮悧锛�", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ // 璋冪敤淇敼鎺ュ彛锛屽彧淇敼status瀛楁涓鸿崏绋跨姸鎬�
+ update({ id: route.id, status: false })
+ .then(res => {
+ ElMessage.success("鎾ら攢鎵瑰噯鎴愬姛");
+ getRouteList();
+ })
+ .catch(err => {
+ ElMessage.error("鎾ら攢鎵瑰噯澶辫触");
+ });
+ });
+ };
+ // 宸ュ簭鎿嶄綔
+ const handleSelectProcess = (route, index) => {
+ console.log("route:", route);
+ currentRouteId.value = route.id;
+ currentRouteIndex.value = index;
+ // 閲嶇疆鎼滅储鍜岄�夋嫨鐘舵��
+ filteredProcessList.value = availableProcessList.value;
+ processSearchKeyword.value = "";
+ selectedProcessItem.value = null;
+ selectProcessDialogVisible.value = true;
+ };
+ const dragSort = ref(0);
+ const currentId = ref(null);
+ // 淇敼宸ュ簭
+ const handleEditProcessSelect = (route, index, process) => {
+ console.log("route:", route);
+ console.log("process:", process);
+ currentId.value = process.id;
+ currentRouteId.value = route.id;
+ currentRouteIndex.value = index;
+ // 閲嶇疆鎼滅储鍜岄�夋嫨鐘舵��
+ filteredProcessList.value = availableProcessList.value;
+ processSearchKeyword.value = "";
+ // 璁剧疆閫変腑鐨勫伐搴�
+ filteredProcessList.value.map(item => {
+ if (item.id === process.processId) {
+ selectedProcessItem.value = item;
+ }
+ });
+ dragSort.value = process.dragSort;
+ // selectedProcessItem.value = process;
+ // 濉厖浜у搧閫夋嫨琛ㄥ崟
+ processForm.productModelId = process.productModelId;
+ processForm.productName = process.productName;
+ processForm.model = process.model;
+ processForm.processId = process.no;
+ // processForm.name = process.name;
+ processForm.unit = process.unit || "";
+ processForm.isQuality = process.isQuality || false;
+ selectProcessDialogVisible.value = true;
+ };
+
+ const handleEditProcess = (routeId, process) => {
+ currentRouteId.value = routeId;
+ isProcessEdit.value = true;
+ processForm.id = process.id;
+ processForm.no = process.no;
+ processForm.name = process.name;
+ processForm.remark = process.remark;
+ processForm.status = process.status;
+ processDialogVisible.value = true;
+ };
+
+ const handleDeleteProcess = (routeId, process) => {
+ ElMessageBox.confirm("纭畾瑕佸垹闄よ宸ュ簭鍚楋紵", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ // 璋冪敤API鍒犻櫎宸ュ簭
+ batchDeleteProcessRouteItem([process.id])
+ .then(res => {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ // 璋冪敤鎺ュ彛鏇存柊宸ュ簭鍒楄〃
+ findProcessRouteItemList({ routeId: routeId })
+ .then(res => {
+ const route = routeList.value.find(r => r.id === routeId);
+ if (route) {
+ route.processList = (res.data || []).map(process => ({
+ ...process,
+ processId: process.processId || process.id,
+ expanded: false,
+ }));
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
+ });
+ })
+ .catch(err => {
+ ElMessage.error("鍒犻櫎澶辫触");
+ console.error("鍒犻櫎宸ュ簭澶辫触锛�", err);
+ });
+ });
+ };
+
+ const handleProcessSubmit = () => {
+ processFormRef.value.validate(valid => {
+ if (valid) {
+ ElMessage.success(isProcessEdit.value ? "缂栬緫鎴愬姛" : "鏂板鎴愬姛");
+ processDialogVisible.value = false;
+ // 璋冪敤鎺ュ彛鏇存柊宸ュ簭鍒楄〃
+ if (currentRouteId.value) {
+ findProcessRouteItemList({ routeId: currentRouteId.value })
+ .then(res => {
+ const route = routeList.value.find(
+ r => r.id === currentRouteId.value
+ );
+ if (route) {
+ route.processList = (res.data || []).map(process => ({
+ ...process,
+ processId: process.processId || process.id,
+ expanded: false,
+ }));
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
+ });
+ }
+ }
+ });
+ };
+
+ // 閫夋嫨宸ュ簭鐩稿叧鏂规硶
+ const handleProcessSearch = () => {
+ const keyword = processSearchKeyword.value.trim().toLowerCase();
+ if (!keyword) {
+ filteredProcessList.value = availableProcessList.value;
+ } else {
+ filteredProcessList.value = availableProcessList.value.filter(
+ item =>
+ (item.name && item.name.toLowerCase().includes(keyword)) ||
+ (item.no && item.no.toLowerCase().includes(keyword))
+ );
+ }
+ };
+
+ const handleProcessSelect = row => {
+ selectedProcessItem.value = row;
+ // 閲嶇疆浜у搧閫夋嫨琛ㄥ崟
+ processForm.productModelId = undefined;
+ processForm.productName = "";
+ processForm.productModelName = "";
+ processForm.unit = "";
+ processForm.isQuality = row.isQuality || false;
+ };
+
+ // 澶勭悊宸ュ簭閫夋嫨鏃剁殑浜у搧閫夋嫨
+ const handleProcessProductSelect = async products => {
+ if (products && products.length > 0) {
+ const product = products[0];
+ processForm.productModelId = product.id;
+ processForm.productName = product.productName;
+ processForm.productModelName = product.model;
+ processForm.unit = product.unit || "";
+ showProductSelectDialog.value = false;
+ }
+ };
+
+ const handleProcessSelectSubmit = () => {
+ if (!selectedProcessItem.value) {
+ ElMessage.warning("璇峰厛閫夋嫨涓�涓伐搴�");
+ return;
+ }
+
+ if (!processForm.productModelId) {
+ ElMessage.warning("璇烽�夋嫨浜у搧");
+ return;
+ }
+
+ // 鏋勫缓璇锋眰鍙傛暟
+ const params = {
+ routeId: currentRouteId.value,
+ processId: selectedProcessItem.value.id,
+ dragSort: routePage.total + 1,
+ ...processForm,
+ };
+
+ // 濡傛灉鏄慨鏀规搷浣滐紝娣诲姞id鍙傛暟
+ if (selectedProcessItem.value.id) {
+ params.id = currentId.value;
+ params.dragSort = dragSort.value;
+ }
+
+ // 璋冪敤API娣诲姞宸ュ簭鎴栦慨鏀瑰伐搴�
+ addOrUpdateProcessRouteItem(params)
+ .then(res => {
+ ElMessage.success(
+ selectedProcessItem.value.id ? "淇敼宸ュ簭鎴愬姛" : "娣诲姞宸ュ簭鎴愬姛"
+ );
+ selectProcessDialogVisible.value = false;
+ // 璋冪敤鎺ュ彛鏇存柊宸ュ簭鍒楄〃
+ findProcessRouteItemList({ routeId: currentRouteId.value })
+ .then(res => {
+ const route = routeList.value.find(
+ r => r.id === currentRouteId.value
+ );
+ if (route) {
+ route.processList = (res.data || []).map(process => ({
+ ...process,
+ processId: process.processId || process.id,
+ expanded: false,
+ }));
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
+ });
+ })
+ .catch(err => {
+ ElMessage.error(
+ selectedProcessItem.value.id ? "淇敼宸ュ簭澶辫触" : "娣诲姞宸ュ簭澶辫触"
+ );
+ console.error(
+ selectedProcessItem.value.id ? "淇敼宸ュ簭澶辫触锛�" : "娣诲姞宸ュ簭澶辫触锛�",
+ err
+ );
+ });
+ };
+
+ // 鍙傛暟鎿嶄綔
+ const handleAddParam = (routeId, process) => {
+ currentRouteId.value = routeId;
+ currentProcessId.value = process.id;
+ selectedParam.value = null;
+ paramSearchKeyword.value = "";
+ paramPage.current = 1;
+ // 鑾峰彇鍙�夊弬鏁板垪琛�
+ getBaseParamList({
+ paramName: paramSearchKeyword.value,
+ current: paramPage.current,
+ size: paramPage.size,
+ }).then(res => {
+ if (res.code === 200) {
+ filteredParamList.value = res.data?.records || [];
+ paramPage.total = res.data?.total || 0;
+ } else {
+ ElMessage.error(res.msg || "鏌ヨ澶辫触");
+ }
+ });
+ selectParamDialogVisible.value = true;
+ };
+
+ const handleEditParam = (routeId, process, param) => {
+ currentRouteId.value = routeId;
+ currentProcessId.value = process.id;
+ editParamForm.id = param.id;
+ editParamForm.processId = process.id;
+ editParamForm.paramId = param.paramId;
+ editParamForm.paramName = param.parameterName || param.paramName;
+ editParamForm.valueMode = param.parameterType2 || param.valueMode || "1";
+ editParamForm.standardValue = param.standardValue;
+ editParamForm.minValue = param.minValue;
+ editParamForm.maxValue = param.maxValue;
+ editParamForm.sort = param.sort || 1;
+ editParamForm.isRequired = param.isRequired || 0;
+ editParamForm.paramType = param.parameterType || param.paramType;
+ editParamForm.paramFormat = param.parameterFormat || param.paramFormat;
+ editParamForm.unit = param.unit || param.unit;
+ editParamDialogVisible.value = true;
+ };
+
+ const handleDeleteParam = (routeId, process, param) => {
+ ElMessageBox.confirm("纭畾瑕佸垹闄よ鍙傛暟鍚楋紵", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ // 璋冪敤API鍒犻櫎鍙傛暟
+ delProcessRouteItemParam(param.id)
+ .then(res => {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ // 鍒锋柊鍙傛暟鍒楄〃
+ toggleProcessParams2(process);
+ })
+ .catch(err => {
+ ElMessage.error("鍒犻櫎鍙傛暟澶辫触");
+ console.error("鍒犻櫎鍙傛暟澶辫触锛�", err);
+ });
+ });
+ };
+
+ const handleParamSubmit = () => {
+ paramFormRef.value.validate(valid => {
+ if (valid) {
+ ElMessage.success(isParamEdit.value ? "缂栬緫鎴愬姛" : "鏂板鎴愬姛");
+ paramDialogVisible.value = false;
+ getRouteList();
+ }
+ });
+ };
+
+ const handleParamTypeChange = () => {
+ if (paramForm.parameterType === "鏁板�兼牸寮�") {
+ paramForm.parameterFormat = "#.0000";
+ } else if (paramForm.parameterType === "鏃堕棿鏍煎紡") {
+ paramForm.parameterFormat = "YYYY-MM-DD HH:mm:ss";
+ } else {
+ paramForm.parameterFormat = "";
+ }
+ };
+
+ const getParamTypeTag = type => {
+ const typeMap = {
+ 1: "primary",
+ 2: "info",
+ 3: "warning",
+ 4: "success",
+ };
+ return typeMap[type] || "default";
+ };
+
+ const getParamTypeText = type => {
+ const typeMap = {
+ 1: "鏁板�兼牸寮�",
+ 2: "鏂囨湰鏍煎紡",
+ 3: "涓嬫媺閫夐」",
+ 4: "鏃堕棿鏍煎紡",
+ };
+ return typeMap[type] || "鏈煡鍙傛暟绫诲瀷";
+ };
+
+ // 閫夋嫨鍙傛暟鐩稿叧鏂规硶
+ const handleParamSearch = () => {
+ // 閲嶇疆鍒嗛〉
+ paramPage.current = 1;
+ // 閲嶆柊鍔犺浇鏁版嵁
+ getBaseParamList({
+ paramName: paramSearchKeyword.value,
+ current: paramPage.current,
+ size: paramPage.size,
+ }).then(res => {
+ if (res.code === 200) {
+ filteredParamList.value = res.data?.records || [];
+ paramPage.total = res.data?.total || 0;
+ } else {
+ ElMessage.error(res.msg || "鏌ヨ澶辫触");
+ }
+ });
+ };
+
+ const handleParamSelect = row => {
+ selectedParam.value = row;
+ };
+
+ // 澶勭悊鍒嗛〉澶у皬鍙樺寲
+ const handleParamSizeChange = size => {
+ paramPage.size = size;
+ getBaseParamList({
+ paramName: paramSearchKeyword.value,
+ current: paramPage.current,
+ size: paramPage.size,
+ }).then(res => {
+ if (res.code === 200) {
+ filteredParamList.value = res.data?.records || [];
+ paramPage.total = res.data?.total || 0;
+ } else {
+ ElMessage.error(res.msg || "鏌ヨ澶辫触");
+ }
+ });
+ };
+
+ // 澶勭悊褰撳墠椤电爜鍙樺寲
+ const handleParamCurrentChange = current => {
+ paramPage.current = current;
+ getBaseParamList({
+ paramName: paramSearchKeyword.value,
+ current: paramPage.current,
+ size: paramPage.size,
+ }).then(res => {
+ if (res.code === 200) {
+ filteredParamList.value = res.data?.records || [];
+ paramPage.total = res.data?.total || 0;
+ } else {
+ ElMessage.error(res.msg || "鏌ヨ澶辫触");
+ }
+ });
+ };
+
+ // 宸ヨ壓璺嚎鍒嗛〉澶勭悊
+ const handleRouteSizeChange = size => {
+ routePage.size = size;
+ getRouteList();
+ };
+
+ const handleRouteCurrentChange = current => {
+ routePage.current = current;
+ getRouteList();
+ };
+
+ const handleParamSelectSubmit = () => {
+ if (!selectedParam.value) {
+ ElMessage.warning("璇峰厛閫夋嫨涓�涓弬鏁�");
+ return;
+ }
+
+ // 鎵惧埌瀵瑰簲鐨勫伐鑹鸿矾绾垮拰宸ュ簭
+ const route = routeList.value.find(r => r.id === currentRouteId.value);
+ const process = route?.processList.find(p => p.id === currentProcessId.value);
+
+ if (route && process) {
+ // 妫�鏌ュ弬鏁版槸鍚﹀凡瀛樺湪
+ // const exists = process.paramList?.some(
+ // p =>
+ // p.paramId === selectedParam.value.id ||
+ // p.parameterCode === selectedParam.value.paramCode
+ // );
+ // if (exists) {
+ // ElMessage.warning("璇ュ弬鏁板凡瀛樺湪浜庡伐搴忎腑");
+ // return;
+ // }
+
+ // 鍒ゆ柇鍙傛暟绫诲瀷锛屽彧鏈夋暟鍊肩被鍨嬫墠浼犳爣鍑嗗�笺�佹渶澶у�煎拰鏈�灏忓��
+ const isNumericMode = selectedParam.value.valueMode === 1;
+
+ // 璋冪敤API鏂板鍙傛暟
+ addProcessRouteItemParam({
+ routeItemId: process.id,
+ paramId: selectedParam.value.id,
+ standardValue: isNumericMode
+ ? selectedParam.value.standardValue || ""
+ : "",
+ minValue: isNumericMode ? selectedParam.value.minValue || 0 : null,
+ maxValue: isNumericMode ? selectedParam.value.maxValue || 0 : null,
+ isRequired: selectedParam.value.isRequired || 0,
+ })
+ .then(res => {
+ ElMessage.success("娣诲姞鍙傛暟鎴愬姛");
+ selectParamDialogVisible.value = false;
+ // 鍒锋柊鍙傛暟鍒楄〃
+ toggleProcessParams2(process);
+ })
+ .catch(err => {
+ ElMessage.error("娣诲姞鍙傛暟澶辫触");
+ console.error("娣诲姞鍙傛暟澶辫触锛�", err);
+ });
+ }
+ };
+
+ const handleEditParamSubmit = () => {
+ editParamFormRef.value.validate(valid => {
+ if (valid) {
+ // 鍒ゆ柇鍙傛暟绫诲瀷锛屽彧鏈夋暟鍊肩被鍨嬫墠浼犳爣鍑嗗�笺�佹渶澶у�煎拰鏈�灏忓��
+ const isNumericMode = editParamForm.valueMode == 1;
+
+ // 璋冪敤API淇敼鍙傛暟
+ editProcessRouteItemParam({
+ id: editParamForm.id,
+ routeItemId: currentProcessId.value,
+ paramId: editParamForm.paramId,
+ standardValue: isNumericMode ? editParamForm.standardValue || "" : "",
+ minValue: isNumericMode ? editParamForm.minValue || 0 : null,
+ maxValue: isNumericMode ? editParamForm.maxValue || 0 : null,
+ isRequired: editParamForm.isRequired || 0,
+ })
+ .then(res => {
+ ElMessage.success("缂栬緫鎴愬姛");
+ editParamDialogVisible.value = false;
+ // 鎵惧埌瀵瑰簲鐨勫伐鑹鸿矾绾垮拰宸ュ簭
+ const route = routeList.value.find(
+ r => r.id === currentRouteId.value
+ );
+ const process = route?.processList.find(
+ p => p.id === currentProcessId.value
+ );
+ // 鍒锋柊鍙傛暟鍒楄〃
+ if (process) {
+ toggleProcessParams2(process);
+ }
+ })
+ .catch(err => {
+ ElMessage.error("缂栬緫鍙傛暟澶辫触");
+ console.error("缂栬緫鍙傛暟澶辫触锛�", err);
+ });
+ }
+ });
+ };
+
+ // 鎷栨嫿鎺掑簭
+ const handleDragStart = (event, index, routeId) => {
+ draggedItem.value = index;
+ draggedRouteId.value = routeId;
+ event.dataTransfer.effectAllowed = "move";
+ };
+
+ const handleDragOver = event => {
+ event.preventDefault();
+ event.dataTransfer.dropEffect = "move";
+ };
+
+ const handleDrop = (event, dropIndex, routeId) => {
+ event.preventDefault();
+ if (draggedItem.value === null || draggedItem.value === dropIndex) return;
+
+ const route = routeList.value.find(r => r.id === routeId);
+ if (route && route.processList) {
+ const draggedProcess = route.processList[draggedItem.value];
+
+ // 璁$畻鏂扮殑鎺掑簭鍊�
+ const newDragSort = dropIndex + 1;
+
+ // 璋冪敤API鎺掑簭宸ュ簭
+ sortProcessRouteItem({
+ id: draggedProcess.id,
+ dragSort: newDragSort,
+ })
+ .then(res => {
+ // 璋冪敤鎺ュ彛鑾峰彇鏈�鏂扮殑宸ュ簭鍒楄〃
+ findProcessRouteItemList({ routeId: routeId })
+ .then(res => {
+ if (route) {
+ route.processList = (res.data || []).map(process => ({
+ ...process,
+ processId: process.processId || process.id,
+ expanded: false,
+ }));
+ }
+ ElMessage.success("鎺掑簭鎴愬姛");
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
+ ElMessage.success("鎺掑簭鎴愬姛");
+ });
+ })
+ .catch(err => {
+ ElMessage.error("鎺掑簭澶辫触");
+ console.error("鎺掑簭宸ュ簭澶辫触锛�", err);
+ });
+ }
+ };
+
+ const handleDragEnd = () => {
+ draggedItem.value = null;
+ draggedRouteId.value = null;
+ };
+
+ // 鑾峰彇鏁版嵁瀛楀吀
+ const getDictTypes = () => {
+ listType({ pageNum: 1, pageSize: 1000 }).then(res => {
+ dictTypes.value = res.rows || [];
+ });
+ };
+
+ getRouteList();
+ getDictTypes();
+
+ // 椤甸潰鍔犺浇鏃惰幏鍙栧伐搴忓垪琛�
+ onMounted(() => {
+ getProcessListApi()
+ .then(res => {
+ // 澶勭悊杩斿洖鐨勬暟鎹紝鏄犲皠鍒伴〉闈㈤渶瑕佺殑鏍煎紡
+ availableProcessList.value = (res.data || []).map(item => ({
+ id: item.id,
+ no: item.no || item.no,
+ name: item.name || item.name,
+ remark: item.remark || item.remark,
+ status: item.status,
+ isQuality: item.isQuality,
+ }));
+ filteredProcessList.value = availableProcessList.value;
+ })
+ .catch(() => {
+ ElMessage.error("鑾峰彇宸ュ簭鍒楄〃澶辫触");
+ });
+ });
+</script>
+
+<style scoped lang="scss">
+ .app-container {
+ padding: 20px;
+ padding-bottom: 80px;
+ background-color: #f0f2f5;
+ min-height: calc(100vh - 84px);
+ overflow: hidden;
+ }
+
+ .route-header {
+ margin-bottom: 20px;
+
+ .add-route-btn {
+ width: 100%;
+ display: inline-flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-width: 120px;
+ height: 100px;
+ border: 2px dashed #dcdfe6;
+ border-radius: 12px;
+ background: #fafafa;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ color: #909399;
+ padding: 0 20px;
+
+ .el-icon {
+ font-size: 24px;
+ margin-bottom: 8px;
+ }
+
+ span {
+ font-size: 13px;
+ }
+
+ &:hover {
+ border-color: #409eff;
+ background: #ecf5ff;
+ color: #409eff;
+ }
+ }
+ }
+
+ .route-card-list {
+ display: grid;
+ grid-template-columns: repeat(1, 1fr);
+ gap: 20px;
+ max-height: calc(100vh - 240px);
+ overflow-y: auto;
+ padding-right: 10px;
+ }
+
+ /* 鑷畾涔夋粴鍔ㄦ潯鏍峰紡 */
+ .route-card-list::-webkit-scrollbar {
+ width: 8px;
+ }
+
+ .route-card-list::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 4px;
+ }
+
+ .route-card-list::-webkit-scrollbar-thumb {
+ background: #c1c1c1;
+ border-radius: 4px;
+ }
+
+ .route-card-list::-webkit-scrollbar-thumb:hover {
+ background: #a8a8a8;
+ }
+
+ .route-card {
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px 40px;
+ border-bottom: 1px solid #ebeef5;
+ background: #f8f9fa;
+
+ .route-info {
+ display: flex;
+ // flex-direction: column;
+ // justify-content: center;
+ // items-align: center;
+ gap: 4px;
+
+ .route-code {
+ font-size: 12px;
+ color: #909399;
+ font-family: "Courier New", monospace;
+ line-height: 30px;
+ }
+
+ .route-name {
+ font-size: 18px;
+ font-weight: 600;
+ color: #303133;
+ display: flex;
+ align-items: center;
+ }
+ }
+
+ .route-actions {
+ display: flex;
+ gap: 8px;
+
+ // .el-button {
+ // color: #409eff;
+ // }
+ }
+ }
+
+ .card-body {
+ padding: 16px 40px;
+
+ .route-desc {
+ font-size: 14px;
+ color: #606266;
+ margin-bottom: 12px;
+ }
+
+ .route-meta {
+ display: flex;
+ gap: 24px;
+ margin-bottom: 12px;
+ padding: 10px 14px;
+ background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
+ border-radius: 8px;
+ border-left: 3px solid #409eff;
+
+ .meta-item {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 13px;
+ margin-right: 40px;
+
+ .el-icon {
+ font-size: 14px;
+ color: #409eff;
+ }
+
+ .meta-label {
+ color: #909399;
+ font-weight: 500;
+ }
+
+ .meta-value {
+ color: #303133;
+ font-weight: 600;
+ }
+ }
+ }
+
+ .expand-btn-wrapper {
+ display: flex;
+ justify-content: center;
+ margin-top: 8px;
+
+ .expand-btn {
+ padding: 8px 20px;
+ border-radius: 20px;
+ background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%);
+ border: 1px solid #b3d8ff;
+ transition: all 0.3s ease;
+
+ .btn-text {
+ font-size: 13px;
+ font-weight: 500;
+ color: #409eff;
+ margin-right: 6px;
+ }
+
+ .expand-icon {
+ font-size: 14px;
+ color: #409eff;
+ transition: transform 0.3s ease;
+ }
+
+ &:hover {
+ background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
+ border-color: #409eff;
+ box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
+
+ .btn-text,
+ .expand-icon {
+ color: #fff;
+ }
+ }
+
+ &.expanded {
+ background: linear-gradient(135deg, #f0f9eb 0%, #e1f3d8 100%);
+ border-color: #a5d69a;
+
+ .btn-text,
+ .expand-icon {
+ color: #67c23a;
+ }
+
+ &:hover {
+ background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
+ border-color: #67c23a;
+ box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
+
+ .btn-text,
+ .expand-icon {
+ color: #fff;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .process-route {
+ padding: 0 20px 20px;
+ background: #f5f7fa;
+ border-top: 1px solid #ebeef5;
+
+ .process-flow {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ padding: 20px 0;
+ overflow-x: auto;
+ overflow-y: hidden;
+
+ .process-flow-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .process-node {
+ background: #fff;
+ border-radius: 12px;
+ padding: 16px;
+ border: 2px solid #ebeef5;
+ cursor: move;
+ transition: all 0.3s ease;
+ // min-width: 180px;
+ // max-width: 220px;
+ width: 300px;
+
+ &.expanded {
+ width: 400px;
+ }
+
+ &:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
+ border-color: #409eff;
+ }
+
+ &:active {
+ cursor: grabbing;
+ }
+
+ .process-node-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+
+ .process-number {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background: #409eff;
+ color: #ffffff;
+ font-size: 12px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .process-actions {
+ display: flex;
+ gap: 4px;
+ }
+ }
+
+ .process-node-body {
+ text-align: center;
+ margin-bottom: 12px;
+
+ .process-code {
+ font-size: 11px;
+ color: #909399;
+ font-family: "Courier New", monospace;
+ margin-bottom: 4px;
+ }
+
+ .process-name {
+ font-size: 15px;
+ font-weight: 600;
+ color: #303133;
+ margin-bottom: 6px;
+ }
+
+ .process-desc {
+ font-size: 12px;
+ color: #606266;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ }
+ }
+
+ .process-node-footer {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ padding-top: 10px;
+ border-top: 1px solid #ebeef5;
+ }
+
+ .process-params-section {
+ margin-top: 12px;
+ padding-top: 12px;
+ border-top: 1px solid #ebeef5;
+
+ .params-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+ font-size: 13px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ .params-list {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ max-height: 200px;
+ overflow-y: auto;
+
+ .param-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 6px 8px;
+ background: #fafafa;
+ border-radius: 4px;
+ border-left: 2px solid #409eff;
+ font-size: 12px;
+
+ .param-info {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 6px;
+ flex: 1;
+ min-width: 0;
+
+ .param-code {
+ font-size: 11px;
+ color: #e6a23c;
+ font-family: "Courier New", monospace;
+ margin-right: 20px;
+ }
+
+ .param-name {
+ font-size: 12px;
+ color: #303133;
+ font-weight: 500;
+ margin-right: 20px;
+ }
+
+ .param-value {
+ font-size: 11px;
+ color: #606266;
+ }
+ }
+
+ .param-actions {
+ display: flex;
+ gap: 4px;
+ flex-shrink: 0;
+ }
+ }
+ }
+ }
+ }
+
+ .flow-arrow {
+ display: flex;
+ align-items: center;
+ color: #c0c4cc;
+ font-size: 24px;
+ padding: 0 4px;
+
+ .el-icon {
+ font-size: 20px;
+ }
+ }
+ }
+
+ .add-process-node {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-width: 100px;
+ height: 137px;
+ border: 2px dashed #dcdfe6;
+ border-radius: 12px;
+ background: #fafafa;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ color: #909399;
+ // margin-left: 10px;
+
+ .el-icon {
+ font-size: 24px;
+ margin-bottom: 8px;
+ }
+
+ span {
+ font-size: 13px;
+ }
+
+ &:hover {
+ border-color: #409eff;
+ background: #ecf5ff;
+ color: #409eff;
+ }
+ }
+ }
+ }
+ }
+
+ // 鎷栨嫿鏃剁殑鏍峰紡
+ .process-flow-item.dragging {
+ opacity: 0.5;
+ transform: scale(0.98);
+ }
+
+ // 閫夋嫨宸ュ簭瀵硅瘽妗嗘牱寮�
+ .process-select-container {
+ display: flex;
+ gap: 20px;
+ height: 450px;
+
+ .process-list-area {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+
+ .area-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #303133;
+ margin-bottom: 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #ebeef5;
+ }
+
+ .search-box {
+ margin-bottom: 12px;
+
+ .el-input {
+ width: 100%;
+ }
+ }
+ }
+
+ .process-detail-area {
+ width: 380px;
+ display: flex;
+ flex-direction: column;
+ background: #f5f7fa;
+ border-radius: 8px;
+ padding: 16px;
+
+ .area-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #303133;
+ margin-bottom: 16px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #ebeef5;
+ }
+
+ .process-detail-form {
+ .el-form-item {
+ margin-bottom: 12px;
+
+ .el-form-item__label {
+ color: #606266;
+ font-weight: 500;
+ }
+ }
+
+ .detail-text {
+ color: #303133;
+ font-weight: 500;
+ }
+ }
+ }
+ }
+
+ // 閫夋嫨鍙傛暟瀵硅瘽妗嗘牱寮�
+ .param-select-container {
+ display: flex;
+ gap: 20px;
+ height: 450px;
+
+ .param-list-area {
+ // flex: 1;
+ width: 380px;
+ display: flex;
+ flex-direction: column;
+
+ .area-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #303133;
+ margin-bottom: 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #ebeef5;
+ }
+
+ .search-box {
+ margin-bottom: 12px;
+
+ .el-input {
+ width: 100%;
+ }
+ }
+ }
+
+ .param-detail-area {
+ // width: 380px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ background: #f5f7fa;
+ border-radius: 8px;
+ padding: 16px;
+
+ .area-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #303133;
+ margin-bottom: 16px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #ebeef5;
+ }
+
+ .param-detail-form {
+ .el-form-item {
+ margin-bottom: 12px;
+
+ .el-form-item__label {
+ color: #606266;
+ font-weight: 500;
+ }
+ }
+
+ .detail-text {
+ color: #303133;
+ font-weight: 500;
+ }
+ }
+ }
+ }
+
+ // 鍒嗛〉鎺т欢鏍峰紡
+ .pagination-container {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ justify-content: flex-end;
+ padding: 16px 20px;
+ background-color: #fff !important;
+ border-top: 1px solid #ebeef5;
+ box-shadow: 0 -2px 12px 0 rgba(0, 0, 0, 0.1);
+ z-index: 100;
+
+ .el-pagination {
+ .el-pagination__sizes {
+ margin-right: 16px;
+ }
+
+ .el-pagination__jump {
+ margin-left: 16px;
+ }
+
+ .el-pagination__total {
+ color: #606266;
+ font-size: 14px;
+ }
+
+ .el-pagination__button {
+ border-radius: 4px;
+ transition: all 0.3s ease;
+
+ &:hover:not(:disabled) {
+ color: #409eff;
+ border-color: #409eff;
+ }
+ }
+
+ .el-pagination__button--active {
+ background-color: #409eff;
+ border-color: #409eff;
+ color: #fff;
+ }
+ }
+ }
+</style>
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
new file mode 100644
index 0000000..ce3ee0b
--- /dev/null
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -0,0 +1,1024 @@
+<template>
+ <div class="app-container">
+ <PageHeader content="宸ヨ壓璺嚎椤圭洰" />
+ <!-- 宸ヨ壓璺嚎淇℃伅灞曠ず -->
+ <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">
+ <span class="info-label">宸ヨ壓璺嚎缂栧彿</span>
+ </div>
+ <div class="info-value-wrapper">
+ <span class="info-value">{{ routeInfo.processRouteCode }}</span>
+ </div>
+ </div>
+ <div class="info-item">
+ <div class="info-label-wrapper">
+ <span class="info-label">浜у搧鍚嶇О</span>
+ </div>
+ <div class="info-value-wrapper">
+ <span class="info-value">{{ routeInfo.productName || '-' }}</span>
+ </div>
+ </div>
+ <div class="info-item">
+ <div class="info-label-wrapper">
+ <span class="info-label">瑙勬牸鍚嶇О</span>
+ </div>
+ <div class="info-value-wrapper">
+ <span class="info-value">{{ routeInfo.model || '-' }}</span>
+ </div>
+ </div>
+ <div class="info-item">
+ <div class="info-label-wrapper">
+ <span class="info-label">BOM缂栧彿</span>
+ </div>
+ <div class="info-value-wrapper">
+ <span class="info-value">{{ routeInfo.bomNo || '-' }}</span>
+ </div>
+ </div>
+ <div class="info-item full-width"
+ v-if="routeInfo.description">
+ <div class="info-label-wrapper">
+ <span class="info-label">鎻忚堪</span>
+ </div>
+ <div class="info-value-wrapper">
+ <span class="info-value">{{ routeInfo.description }}</span>
+ </div>
+ </div>
+ </div>
+ </el-card>
+ <!-- bom灞曠ず -->
+ <!-- 琛ㄦ牸瑙嗗浘 -->
+ <div v-if="viewMode === 'table'"
+ class="section-header">
+ <div class="section-title">宸ヨ壓璺嚎椤圭洰鍒楄〃</div>
+ <div class="section-actions">
+ <el-button icon="Grid"
+ @click="toggleView"
+ style="margin-right: 10px;">
+ 鍗$墖瑙嗗浘
+ </el-button>
+ <el-button type="primary"
+ @click="handleAdd">鏂板</el-button>
+ </div>
+ </div>
+ <el-table v-if="viewMode === 'table'"
+ ref="tableRef"
+ v-loading="tableLoading"
+ border
+ :data="tableData"
+ :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+ row-key="id"
+ tooltip-effect="dark"
+ class="lims-table">
+ <el-table-column align="center"
+ label="搴忓彿"
+ width="60"
+ type="index" />
+ <el-table-column label="宸ュ簭鍚嶇О"
+ prop="processId"
+ width="200">
+ <template #default="scope">
+ {{ getProcessName(scope.row.processId) || '-' }}
+ </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="鍙傛暟鍒楄〃"
+ min-width="160">
+ <template #default="scope">
+ <el-button type="primary"
+ link
+ size="small"
+ @click="handleViewParams(scope.row)">鍙傛暟鍒楄〃</el-button>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍗曚綅"
+ prop="unit"
+ width="100" />
+ <el-table-column label="鏄惁璐ㄦ"
+ prop="isQuality"
+ width="100">
+ <template #default="scope">
+ {{scope.row.isQuality ? "鏄�" : "鍚�"}}
+ </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)"
+ :disabled="scope.row.isComplete">缂栬緫</el-button>
+ <!-- <el-button type="info"
+ link
+ size="small"
+ @click="handleViewParams(scope.row)">鍙傛暟鍒楄〃</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>
+ <!-- 鍗$墖瑙嗗浘 -->
+ <template v-else>
+ <div class="section-header">
+ <div class="section-title">宸ヨ壓璺嚎椤圭洰鍒楄〃</div>
+ <div class="section-actions">
+ <el-button icon="Menu"
+ @click="toggleView"
+ style="margin-right: 10px;">
+ 琛ㄦ牸瑙嗗浘
+ </el-button>
+ <el-button type="primary"
+ @click="handleAdd">鏂板</el-button>
+ </div>
+ </div>
+ <div v-loading="tableLoading"
+ class="card-container">
+ <div ref="cardsContainer"
+ class="cards-wrapper">
+ <div v-for="(item, index) in tableData"
+ :key="item.id || index"
+ class="process-card"
+ :data-index="index">
+ <!-- 搴忓彿鍦嗗湀 -->
+ <div class="card-header">
+ <div class="card-number">{{ index + 1 }}</div>
+ <div class="card-process-name">{{ getProcessName(item.processId) || '-' }}</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>
+ <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)"
+ :disabled="item.isComplete">缂栬緫</el-button>
+ <el-button type="info"
+ link
+ size="small"
+ @click="handleViewParams(item)">鍙傛暟鍒楄〃</el-button>
+ <el-button type="danger"
+ link
+ size="small"
+ @click="handleDelete(item)"
+ :disabled="item.isComplete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </template>
+ <!-- 鏂板/缂栬緫寮圭獥 -->
+ <el-dialog v-model="dialogVisible"
+ :title="operationType === 'add' ? '鏂板宸ヨ壓璺嚎椤圭洰' : '缂栬緫宸ヨ壓璺嚎椤圭洰'"
+ width="500px"
+ @close="closeDialog">
+ <el-form ref="formRef"
+ :model="form"
+ :rules="rules"
+ label-width="120px">
+ <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-form-item label="鏄惁璐ㄦ"
+ prop="isQuality">
+ <el-switch v-model="form.isQuality"
+ :active-value="true"
+ inactive-value="false" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="closeDialog">鍙栨秷</el-button>
+ <el-button type="primary"
+ @click="handleSubmit"
+ :loading="submitLoading">纭畾</el-button>
+ </template>
+ </el-dialog>
+ <!-- 浜у搧閫夋嫨瀵硅瘽妗� -->
+ <ProductSelectDialog v-model="showProductSelectDialog"
+ @confirm="handleProductSelect"
+ single />
+ <!-- 鍙傛暟鍒楄〃瀵硅瘽妗� -->
+ <ProcessParamListDialog v-model="showParamListDialog"
+ :title="`${currentProcess ? getProcessName(currentProcess.processId) : ''} - 鍙傛暟鍒楄〃`"
+ :route-id="routeId"
+ :editable="false"
+ :process="currentProcess"
+ :param-list="paramList"
+ @refresh="refreshParamList" />
+ </div>
+</template>
+
+<script setup>
+ import {
+ ref,
+ computed,
+ getCurrentInstance,
+ onMounted,
+ onUnmounted,
+ nextTick,
+ } from "vue";
+ import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+ import ProcessParamListDialog from "@/components/ProcessParamListDialog.vue";
+ import {
+ findProcessRouteItemList,
+ addOrUpdateProcessRouteItem,
+ sortProcessRouteItem,
+ batchDeleteProcessRouteItem,
+ getProcessParamList,
+ } from "@/api/productionManagement/processRouteItem.js";
+ import {
+ findProductProcessRouteItemList,
+ deleteRouteItem,
+ addRouteItem,
+ addOrUpdateProductProcessRouteItem,
+ sortRouteItem,
+ } from "@/api/productionManagement/productProcessRoute.js";
+ import { processList } from "@/api/productionManagement/productionProcess.js";
+ import { useRoute } from "vue-router";
+ import { ElMessageBox, ElMessage } from "element-plus";
+ import Sortable from "sortablejs";
+
+ const route = useRoute();
+ const { proxy } = getCurrentInstance() || {};
+
+ const routeId = computed(() => route.query.id);
+ const orderId = computed(() => route.query.orderId);
+ const pageType = computed(() => route.query.type);
+
+ const tableLoading = ref(false);
+ const tableData = ref([]);
+ const dialogVisible = ref(false);
+ const operationType = ref("add"); // add | edit
+ const formRef = ref(null);
+ const submitLoading = ref(false);
+ const cardsContainer = ref(null);
+ const tableRef = ref(null);
+ const viewMode = ref("table"); // table | card
+ const routeInfo = ref({
+ processRouteCode: "",
+ productName: "",
+ model: "",
+ bomNo: "",
+ bomId: null,
+ description: "",
+ });
+
+ const processOptions = ref([]);
+ const showProductSelectDialog = ref(false);
+ const showParamListDialog = ref(false);
+ const currentProcess = ref(null);
+ const paramList = ref([]);
+ let tableSortable = null;
+ let cardSortable = null;
+
+ // 鍒囨崲瑙嗗浘
+ const toggleView = () => {
+ viewMode.value = viewMode.value === "table" ? "card" : "table";
+ // 鍒囨崲瑙嗗浘鍚庨噸鏂板垵濮嬪寲鎷栨嫿鎺掑簭
+ nextTick(() => {
+ initSortable();
+ });
+ };
+
+ const form = ref({
+ id: undefined,
+ routeId: routeId.value,
+ processId: undefined,
+ productModelId: undefined,
+ productName: "",
+ model: "",
+ unit: "",
+ isQuality: false,
+ });
+
+ const rules = {
+ processId: [{ 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 getList = () => {
+ tableLoading.value = true;
+ const listPromise =
+ pageType.value === "order"
+ ? findProductProcessRouteItemList({ orderId: orderId.value })
+ : findProcessRouteItemList({ routeId: routeId.value });
+
+ listPromise
+ .then(res => {
+ tableData.value = res.data || [];
+ tableLoading.value = false;
+ // 鍒楄〃鍔犺浇瀹屾垚鍚庡垵濮嬪寲鎷栨嫿鎺掑簭
+ nextTick(() => {
+ initSortable();
+ });
+ })
+ .catch(err => {
+ tableLoading.value = false;
+ console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+ proxy?.$modal?.msgError("鑾峰彇鍒楄〃澶辫触");
+ });
+ };
+
+ // 鑾峰彇宸ュ簭鍒楄〃
+ const getProcessList = () => {
+ processList({})
+ .then(res => {
+ processOptions.value = res.data || [];
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
+ });
+ };
+
+ // 鑾峰彇宸ヨ壓璺嚎璇︽儏锛堜粠璺敱鍙傛暟鑾峰彇锛�
+ const getRouteInfo = () => {
+ routeInfo.value = {
+ processRouteCode: route.query.processRouteCode || "",
+ productName: route.query.productName || "",
+ model: route.query.model || "",
+ bomNo: route.query.bomNo || "",
+ bomId: route.query.bomId || null,
+ description: route.query.description || "",
+ };
+ };
+
+ // 鏂板
+ const handleAdd = () => {
+ operationType.value = "add";
+ resetForm();
+ dialogVisible.value = true;
+ };
+
+ // 缂栬緫
+ const handleEdit = row => {
+ operationType.value = "edit";
+ form.value = {
+ id: row.id,
+ routeId: routeId.value,
+ processId: row.processId,
+ productModelId: row.productModelId,
+ productName: row.productName || "",
+ model: row.model || "",
+ unit: row.unit || "",
+ isQuality: row.isQuality,
+ };
+ dialogVisible.value = true;
+ };
+
+ // 鍒犻櫎
+ const handleDelete = row => {
+ ElMessageBox.confirm("纭鍒犻櫎璇ュ伐鑹鸿矾绾块」鐩紵", "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ // 鐢熶骇璁㈠崟涓嬩娇鐢� productProcessRoute 鐨勫垹闄ゆ帴鍙o紙璺敱鍚庢嫾鎺� id锛夛紝鍏跺畠鎯呭喌浣跨敤宸ヨ壓璺嚎椤圭洰鎵归噺鍒犻櫎鎺ュ彛
+ const deletePromise =
+ pageType.value === "order"
+ ? deleteRouteItem(row.id)
+ : batchDeleteProcessRouteItem([row.id]);
+
+ deletePromise
+ .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.model = product.model;
+ form.value.unit = product.unit || "";
+ showProductSelectDialog.value = false;
+ // 瑙﹀彂琛ㄥ崟楠岃瘉
+ formRef.value?.validateField("productModelId");
+ }
+ };
+
+ // 鎻愪氦
+ const handleSubmit = () => {
+ formRef.value.validate(valid => {
+ if (valid) {
+ submitLoading.value = true;
+
+ if (operationType.value === "add") {
+ // 鏂板锛氫紶鍗曚釜瀵硅薄锛屽寘鍚玠ragSort瀛楁
+ // dragSort = 褰撳墠鍒楄〃闀垮害 + 1锛岃〃绀烘柊澧炶褰曟帓鍦ㄦ渶鍚�
+ const dragSort = tableData.value.length + 1;
+ const isOrderPage = pageType.value === "order";
+
+ const addPromise = isOrderPage
+ ? addRouteItem({
+ productOrderId: orderId.value,
+ 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,
+ });
+
+ addPromise
+ .then(() => {
+ proxy?.$modal?.msgSuccess("鏂板鎴愬姛");
+ closeDialog();
+ getList();
+ })
+ .catch(() => {
+ proxy?.$modal?.msgError("鏂板澶辫触");
+ })
+ .finally(() => {
+ submitLoading.value = false;
+ });
+ } else {
+ // 缂栬緫锛氱敓浜ц鍗曚笅浣跨敤 productProcessRoute/updateRouteItem锛屽叾瀹冩儏鍐典娇鐢ㄥ伐鑹鸿矾绾块」鐩洿鏂版帴鍙�
+ const isOrderPage = pageType.value === "order";
+
+ const updatePromise = isOrderPage
+ ? addOrUpdateProductProcessRouteItem({
+ 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
+ .then(() => {
+ proxy?.$modal?.msgSuccess("淇敼鎴愬姛");
+ closeDialog();
+ getList();
+ })
+ .catch(() => {
+ proxy?.$modal?.msgError("淇敼澶辫触");
+ })
+ .finally(() => {
+ submitLoading.value = false;
+ });
+ }
+ }
+ });
+ };
+
+ // 閲嶇疆琛ㄥ崟
+ const resetForm = () => {
+ form.value = {
+ id: undefined,
+ routeId: routeId.value,
+ processId: undefined,
+ productModelId: undefined,
+ productName: "",
+ model: "",
+ unit: "",
+ };
+ formRef.value?.resetFields();
+ };
+
+ // 鍏抽棴寮圭獥
+ const closeDialog = () => {
+ dialogVisible.value = false;
+ resetForm();
+ };
+
+ // 鍒濆鍖栨嫋鎷芥帓搴�
+ const initSortable = () => {
+ destroySortable();
+
+ if (viewMode.value === "table") {
+ // 琛ㄦ牸瑙嗗浘鐨勬嫋鎷芥帓搴�
+ if (!tableRef.value) return;
+
+ const tbody =
+ tableRef.value.$el.querySelector(".el-table__body tbody") ||
+ tableRef.value.$el.querySelector(
+ ".el-table__body-wrapper > table > tbody"
+ );
+
+ if (!tbody) return;
+
+ tableSortable = new Sortable(tbody, {
+ animation: 150,
+ ghostClass: "sortable-ghost",
+ handle: ".el-table__row",
+ filter: ".el-button, .el-select",
+ onEnd: evt => {
+ if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex])
+ return;
+
+ // 閲嶆柊鎺掑簭鏁扮粍
+ const moveItem = tableData.value.splice(evt.oldIndex, 1)[0];
+ tableData.value.splice(evt.newIndex, 0, moveItem);
+
+ // 璁$畻鏂扮殑搴忓彿锛坉ragSort浠�1寮�濮嬶級
+ const newIndex = evt.newIndex;
+ const dragSort = newIndex + 1;
+
+ // 璋冪敤鎺掑簭鎺ュ彛
+ if (moveItem.id) {
+ const isOrderPage = pageType.value === "order";
+ const sortPromise = isOrderPage
+ ? sortRouteItem({
+ id: moveItem.id,
+ dragSort: dragSort,
+ })
+ : sortProcessRouteItem({
+ id: moveItem.id,
+ dragSort: dragSort,
+ });
+
+ sortPromise
+ .then(() => {
+ // 鏇存柊鎵�鏈夎鐨刣ragSort
+ tableData.value.forEach((item, index) => {
+ if (item.id) {
+ item.dragSort = index + 1;
+ }
+ });
+ proxy?.$modal?.msgSuccess("鎺掑簭鎴愬姛");
+ })
+ .catch(err => {
+ // 鎺掑簭澶辫触锛屾仮澶嶅師鏁扮粍
+ tableData.value.splice(newIndex, 1);
+ tableData.value.splice(evt.oldIndex, 0, moveItem);
+ proxy?.$modal?.msgError("鎺掑簭澶辫触");
+ console.error("鎺掑簭澶辫触锛�", err);
+ });
+ }
+ },
+ });
+ } else {
+ // 鍗$墖瑙嗗浘鐨勬嫋鎷芥帓搴�
+ if (!cardsContainer.value) return;
+
+ cardSortable = new Sortable(cardsContainer.value, {
+ animation: 150,
+ ghostClass: "sortable-ghost",
+ handle: ".process-card",
+ filter: ".el-button",
+ onEnd: evt => {
+ if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex])
+ return;
+
+ // 閲嶆柊鎺掑簭鏁扮粍
+ const moveItem = tableData.value.splice(evt.oldIndex, 1)[0];
+ tableData.value.splice(evt.newIndex, 0, moveItem);
+
+ // 璁$畻鏂扮殑搴忓彿锛坉ragSort浠�1寮�濮嬶級
+ const newIndex = evt.newIndex;
+ const dragSort = newIndex + 1;
+
+ // 璋冪敤鎺掑簭鎺ュ彛
+ if (moveItem.id) {
+ const isOrderPage = pageType.value === "order";
+ const sortPromise = isOrderPage
+ ? sortRouteItem({
+ id: moveItem.id,
+ dragSort: dragSort,
+ })
+ : sortProcessRouteItem({
+ id: moveItem.id,
+ dragSort: dragSort,
+ });
+
+ sortPromise
+ .then(() => {
+ // 鏇存柊鎵�鏈夎鐨刣ragSort
+ tableData.value.forEach((item, index) => {
+ if (item.id) {
+ item.dragSort = index + 1;
+ }
+ });
+ proxy?.$modal?.msgSuccess("鎺掑簭鎴愬姛");
+ })
+ .catch(err => {
+ // 鎺掑簭澶辫触锛屾仮澶嶅師鏁扮粍
+ tableData.value.splice(newIndex, 1);
+ tableData.value.splice(evt.oldIndex, 0, moveItem);
+ proxy?.$modal?.msgError("鎺掑簭澶辫触");
+ console.error("鎺掑簭澶辫触锛�", err);
+ });
+ }
+ },
+ });
+ }
+ };
+
+ // 閿�姣佹嫋鎷芥帓搴�
+ const destroySortable = () => {
+ if (tableSortable) {
+ tableSortable.destroy();
+ tableSortable = null;
+ }
+ if (cardSortable) {
+ cardSortable.destroy();
+ cardSortable = null;
+ }
+ };
+
+ onMounted(() => {
+ getRouteInfo();
+ getList();
+ getProcessList();
+ });
+
+ // 鏌ョ湅鍙傛暟鍒楄〃
+ const handleViewParams = process => {
+ currentProcess.value = process;
+ // 璋冪敤API鑾峰彇鍙傛暟鍒楄〃
+ getProcessParamList({
+ routeItemId: process.id,
+ pageNum: 1,
+ pageSize: 1000,
+ })
+ .then(res => {
+ if (res.code === 200) {
+ paramList.value = res.data?.records || [];
+ } else {
+ ElMessage.error(res.msg || "鑾峰彇鍙傛暟鍒楄〃澶辫触");
+ paramList.value = [];
+ }
+ showParamListDialog.value = true;
+ })
+ .catch(err => {
+ console.error("鑾峰彇鍙傛暟鍒楄〃澶辫触锛�", err);
+ ElMessage.error("鑾峰彇鍙傛暟鍒楄〃澶辫触");
+ paramList.value = [];
+ showParamListDialog.value = true;
+ });
+ };
+
+ // 鍒锋柊鍙傛暟鍒楄〃
+ const refreshParamList = () => {
+ if (!currentProcess.value) return;
+ // 閲嶆柊璋冪敤API鑾峰彇鍙傛暟鍒楄〃
+ getProcessParamList({
+ routeItemId: currentProcess.value.id,
+ pageNum: 1,
+ pageSize: 1000,
+ })
+ .then(res => {
+ if (res.code === 200) {
+ paramList.value = res.data?.records || [];
+ } else {
+ ElMessage.error(res.msg || "鑾峰彇鍙傛暟鍒楄〃澶辫触");
+ paramList.value = [];
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇鍙傛暟鍒楄〃澶辫触锛�", err);
+ ElMessage.error("鑾峰彇鍙傛暟鍒楄〃澶辫触");
+ paramList.value = [];
+ });
+ };
+
+ onUnmounted(() => {
+ destroySortable();
+ });
+</script>
+
+<style scoped>
+ .card-container {
+ padding: 20px 0;
+ }
+
+ .cards-wrapper {
+ display: flex;
+ gap: 16px;
+ overflow-x: auto;
+ padding: 10px 0;
+ min-height: 200px;
+ }
+
+ .cards-wrapper::-webkit-scrollbar {
+ height: 8px;
+ }
+
+ .cards-wrapper::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 4px;
+ }
+
+ .cards-wrapper::-webkit-scrollbar-thumb {
+ background: #c1c1c1;
+ border-radius: 4px;
+ }
+
+ .cards-wrapper::-webkit-scrollbar-thumb:hover {
+ background: #a8a8a8;
+ }
+
+ .process-card {
+ flex-shrink: 0;
+ width: 220px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+ cursor: move;
+ transition: all 0.3s;
+ }
+
+ .process-card:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ transform: translateY(-2px);
+ }
+
+ .card-header {
+ text-align: center;
+ margin-bottom: 12px;
+ }
+
+ .card-number {
+ width: 36px;
+ height: 36px;
+ line-height: 36px;
+ border-radius: 50%;
+ background: #409eff;
+ color: #fff;
+ font-weight: bold;
+ font-size: 16px;
+ margin: 0 auto 8px;
+ }
+
+ .card-process-name {
+ font-size: 14px;
+ color: #333;
+ font-weight: 500;
+ word-break: break-all;
+ }
+
+ .card-content {
+ flex: 1;
+ margin-bottom: 12px;
+ min-height: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .product-info {
+ font-size: 13px;
+ color: #666;
+ text-align: center;
+ width: 100%;
+ }
+
+ .product-info.empty {
+ color: #999;
+ text-align: center;
+ padding: 20px 0;
+ }
+
+ .product-name {
+ margin-bottom: 6px;
+ word-break: break-all;
+ line-height: 1.5;
+ text-align: center;
+ }
+
+ .product-model {
+ color: #909399;
+ font-size: 12px;
+ word-break: break-all;
+ line-height: 1.5;
+ text-align: center;
+ }
+
+ .product-unit {
+ margin-left: 4px;
+ color: #409eff;
+ }
+
+ .product-tag {
+ margin: 10px 0;
+ }
+
+ .card-footer {
+ display: flex;
+ justify-content: space-around;
+ padding-top: 12px;
+ border-top: 1px solid #f0f0f0;
+ }
+
+ .card-footer .el-button {
+ padding: 0;
+ font-size: 12px;
+ }
+
+ :deep(.sortable-ghost) {
+ opacity: 0.5;
+ background-color: #f5f7fa !important;
+ }
+
+ :deep(.sortable-drag) {
+ opacity: 0.8;
+ }
+
+ /* 琛ㄦ牸瑙嗗浘鏍峰紡 */
+ :deep(.el-table__row) {
+ transition: background-color 0.2s;
+ cursor: move;
+ }
+
+ :deep(.el-table__row:hover) {
+ background-color: #f9fafc !important;
+ }
+
+ /* 鍖哄煙鏍囬鏍峰紡 */
+ .section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+ }
+
+ .section-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ padding-left: 12px;
+ position: relative;
+ margin-bottom: 0;
+ }
+
+ .section-title::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 3px;
+ height: 16px;
+ background: #409eff;
+ border-radius: 2px;
+ }
+
+ .section-actions {
+ display: flex;
+ align-items: center;
+ }
+
+ /* 宸ヨ壓璺嚎淇℃伅鍗$墖鏍峰紡 */
+ .route-info-card {
+ margin-bottom: 20px;
+ border: 1px solid #e4e7ed;
+ background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+ border-radius: 8px;
+ overflow: hidden;
+ }
+
+ .route-info {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 16px;
+ padding: 4px;
+ }
+
+ .info-item {
+ display: flex;
+ flex-direction: column;
+ background: #ffffff;
+ border-radius: 6px;
+ padding: 14px 16px;
+ border: 1px solid #f0f2f5;
+ transition: all 0.3s ease;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+ }
+
+ .info-item:hover {
+ border-color: #409eff;
+ box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
+ transform: translateY(-1px);
+ }
+
+ .info-item.full-width {
+ grid-column: 1 / -1;
+ }
+
+ .info-label-wrapper {
+ margin-bottom: 8px;
+ }
+
+ .info-label {
+ display: inline-block;
+ color: #909399;
+ font-size: 12px;
+ font-weight: 500;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ padding: 2px 0;
+ position: relative;
+ }
+
+ .info-label::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 20px;
+ height: 2px;
+ background: linear-gradient(90deg, #409eff, transparent);
+ border-radius: 1px;
+ }
+
+ .info-value-wrapper {
+ flex: 1;
+ }
+
+ .info-value {
+ display: block;
+ color: #303133;
+ font-size: 15px;
+ font-weight: 500;
+ line-height: 1.5;
+ word-break: break-all;
+ }
+</style>
diff --git a/src/views/productionPlan/productionPlan/index.vue b/src/views/productionPlan/productionPlan/index.vue
index d2a3efd..e6f209b 100644
--- a/src/views/productionPlan/productionPlan/index.vue
+++ b/src/views/productionPlan/productionPlan/index.vue
@@ -117,6 +117,21 @@
value-format="YYYY-MM-DD"
style="width: 100%" />
</el-form-item>
+ <el-form-item label="寮哄害"
+ v-if="mergeForm.productName === '鐮屽潡'">
+ <div v-if="strengthError"
+ class="strength-error"
+ style="color: red; margin-bottom: 8px;">{{ strengthError }}</div>
+ <el-select v-model="mergeForm.strength"
+ placeholder="璇烽�夋嫨寮哄害"
+ style="width: 100%"
+ required>
+ <el-option v-for="item in block_strength"
+ :key="item.id"
+ :label="item.label"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
<el-form-item label="鐢熶骇鏂规暟">
<el-input-number v-model="mergeForm.totalAssignedQuantity"
:min="0"
@@ -301,14 +316,15 @@
placeholder="璇烽�夋嫨璁″垝缁撴潫鏃ユ湡" />
</el-form-item>
<el-form-item label="寮哄害"
- prop="strength">
+ prop="strength"
+ v-if="form.productName === '鐮屽潡'">
<el-select v-model="form.strength"
placeholder="璇烽�夋嫨寮哄害"
style="width: 100%">
- <el-option label="A3.5"
- value="A3.5" />
- <el-option label="A5.0"
- value="A5.0" />
+ <el-option v-for="item in block_strength"
+ :key="item.id"
+ :label="item.label"
+ :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="澶囨敞 1"
@@ -334,11 +350,12 @@
</template>
<script setup>
- import { onMounted, ref, reactive, getCurrentInstance } from "vue";
+ import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue";
import { ElMessage } from "element-plus";
import dayjs from "dayjs";
import ImportDialog from "@/components/Dialog/ImportDialog.vue";
import { getToken } from "@/utils/auth";
+ import { useDict } from "@/utils/dict";
import {
productionPlanListPage,
loadProdData,
@@ -490,6 +507,11 @@
{
label: "寮哄害",
prop: "strength",
+ formatData: cell => {
+ if (!cell) return "";
+ const strengthItem = block_strength.value.find(item => item.id === cell);
+ return strengthItem ? strengthItem.label : cell;
+ },
},
{
@@ -546,6 +568,7 @@
clickFun: row => {
// 鍗曠嫭涓嬪彂鎿嶄綔
// 璁剧疆琛ㄥ崟鏁版嵁
+ strengthError.value = "";
mergeForm.ids = [row.id];
mergeForm.materialCode = row.materialCode;
mergeForm.productName = row.productName || "";
@@ -556,6 +579,7 @@
mergeForm.totalAssignedQuantity =
(Number(row.volume) - Number(row.assignedQuantity)).toFixed(4) || 0;
mergeForm.planCompleteTime = row.planCompleteTime || "";
+ mergeForm.strength = row.strength || "";
sumAssignedQuantity.value = mergeForm.totalAssignedQuantity;
// 鎵撳紑寮圭獥
isShowNewModal.value = true;
@@ -597,6 +621,7 @@
height: 0,
totalAssignedQuantity: 0,
planCompleteTime: "",
+ strength: "",
});
// 杩借釜杩涘害寮圭獥鎺у埗
@@ -625,6 +650,8 @@
const productOptions = ref([]);
const specificationOptions = ref([]);
const formRef = ref(null);
+ // 鑾峰彇寮哄害瀛楀吀
+ const { block_strength } = useDict("block_strength");
const form = reactive({
id: undefined,
applyNo: "",
@@ -656,6 +683,19 @@
volume: [{ required: true, message: "璇疯緭鍏ユ柟鏁�", trigger: "blur" }],
productMaterialId: [
{ required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" },
+ ],
+ strength: [
+ {
+ validator: (rule, value, callback) => {
+ if (form.productName === "鐮屽潡" && !value) {
+ callback(new Error("鐮屽潡浜у搧鐨勫己搴︿负蹇呭~椤�"));
+ } else {
+ callback();
+ }
+ },
+ trigger: ["blur", "change"],
+ required: false,
+ },
],
});
@@ -709,6 +749,26 @@
const handleProductChange = value => {
form.productMaterialSkuId = undefined;
+ // 鏌ユ壘閫変腑鐨勪骇鍝佸悕绉�
+ const findProductName = (options, value) => {
+ for (const option of options) {
+ if (option.value === value) {
+ return option.label;
+ }
+ if (option.children) {
+ const found = findProductName(option.children, value);
+ if (found) {
+ return found;
+ }
+ }
+ }
+ return "";
+ };
+ form.productName = findProductName(productOptions.value, value);
+ // 瑙﹀彂寮哄害瀛楁楠岃瘉
+ if (formRef.value) {
+ formRef.value.validateField("strength");
+ }
fetchSpecificationOptions(value);
};
@@ -931,6 +991,9 @@
.catch(() => {});
};
const sumAssignedQuantity = ref(0);
+ // 鍝嶅簲寮忔暟鎹�
+ const strengthError = ref("");
+
// 澶勭悊鍚堝苟涓嬪彂鎸夐挳鐐瑰嚮
const handleMerge = () => {
if (selectedRows.value.length === 0) {
@@ -938,6 +1001,26 @@
return;
}
console.log(selectedRows.value);
+ // 妫�鏌ュ己搴︿竴鑷存��
+ const firstRow = selectedRows.value[0];
+ const productName = firstRow.productName || "";
+ let strengthConsistent = true;
+ let firstStrength = firstRow.strength || "";
+ strengthError.value = "";
+
+ if (productName === "鐮屽潡") {
+ for (const row of selectedRows.value) {
+ if (row.strength !== firstStrength) {
+ strengthConsistent = false;
+ break;
+ }
+ }
+
+ if (!strengthConsistent) {
+ strengthError.value = "閫夋嫨鐨勭爩鍧楀己搴︿笉涓�鑷达紝璇烽噸鏂伴�夋嫨";
+ }
+ }
+
// 璁$畻鎬诲埗閫犳暟閲�
const totalAssignedQuantity = selectedRows.value.reduce((sum, row) => {
return (
@@ -950,15 +1033,15 @@
sumAssignedQuantity.value = totalAssignedQuantity;
console.log(totalAssignedQuantity);
// 璁剧疆琛ㄥ崟鏁版嵁
- const firstRow = selectedRows.value[0];
mergeForm.materialCode = selectedserialNo.value;
- mergeForm.productName = firstRow.productName || "";
+ mergeForm.productName = productName;
mergeForm.model = firstRow.model || "";
mergeForm.length = firstRow.length || 0;
mergeForm.width = firstRow.width || 0;
mergeForm.height = firstRow.height || 0;
mergeForm.totalAssignedQuantity = totalAssignedQuantity;
mergeForm.planCompleteTime = firstRow.planCompleteTime || "";
+ mergeForm.strength = firstStrength;
mergeForm.ids = selectedRows.value.map(row => row.id);
// 鎵撳紑寮圭獥
@@ -971,6 +1054,11 @@
ElMessage.warning("璇疯緭鍏ョ敓浜ф柟鏁�");
return;
}
+ // 楠岃瘉鐮屽潡浜у搧鐨勫己搴�
+ if (mergeForm.productName === "鐮屽潡" && !mergeForm.strength) {
+ ElMessage.error("鐮屽潡浜у搧鐨勫己搴︿负蹇呭~椤�");
+ return;
+ }
console.log(sumAssignedQuantity.value, "sumAssignedQuantity");
// 璁$畻褰撳墠閫変腑琛岀殑鎬绘柟鏁�
const totalVolume = selectedRows.value.reduce((sum, row) => {
--
Gitblit v1.9.3