From 79dbd82e9d31e659a5ecb58da7fd011c03a8d58f Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期日, 04 一月 2026 15:38:31 +0800
Subject: [PATCH] 修改工艺路线拖拽排序
---
src/views/productionManagement/processRoute/ItemsForm.vue | 324 +++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 287 insertions(+), 37 deletions(-)
diff --git a/src/views/productionManagement/processRoute/ItemsForm.vue b/src/views/productionManagement/processRoute/ItemsForm.vue
index 525b205..5538b2c 100644
--- a/src/views/productionManagement/processRoute/ItemsForm.vue
+++ b/src/views/productionManagement/processRoute/ItemsForm.vue
@@ -6,16 +6,27 @@
width="800px"
@close="closeModal"
>
- <el-button
- type="primary"
- @click="isShowProductSelectDialog = true"
- class="mb5"
- style="margin-bottom: 10px;"
- >
- 閫夋嫨浜у搧
- </el-button>
+ <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
@@ -26,7 +37,11 @@
class="lims-table"
style="cursor: move;"
>
- <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <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"
@@ -70,6 +85,54 @@
</el-table-column>
</el-table>
+ <!-- 绠�鍖栧鍣ㄧ粨鏋勶紝鐩存帴缁檈l-steps鍔爎ef -->
+ <el-steps
+ v-else
+ ref="stepsContainer"
+ class="mb5 custom-steps"
+ :active="routeItems.length"
+ align-center
+ style="padding: 10px 0;"
+ >
+ <!-- 鍏抽敭锛氱粰el-step娣诲姞data-id锛岃�岄潪鍐呴儴鍗$墖 -->
+ <el-step
+ v-for="(item, index) in routeItems"
+ :key="item.id"
+ class="custom-step draggable-step"
+ :data-id="item.id"
+ style="cursor: move;"
+ >
+ <template #title>
+ <div class="step-content">
+ <div class="step-number">{{ index + 1 }}</div>
+ <el-card
+ :header="item.productName"
+ class="step-card"
+ style="cursor: move;"
+ >
+ <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>
+ <template #footer>
+ <el-button type="danger" link size="small" @click.stop="removeItemByID(item.id)">鍒犻櫎</el-button>
+ </template>
+ </el-card>
+ </div>
+ </template>
+ </el-step>
+ </el-steps>
+
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit">纭</el-button>
@@ -111,8 +174,11 @@
const tableLoading = ref(false);
const isShowProductSelectDialog = ref(false);
const routeItems = ref([]);
-let sortable = null;
+let tableSortable = null;
+let stepsSortable = null;
const multipleTable = ref(null);
+const stepsContainer = ref(null);
+const isTable = ref(true);
const isShow = computed({
get() {
@@ -142,7 +208,7 @@
clickFun: (row) => {
const idx = routeItems.value.findIndex(item => item.id === row.id);
if (idx > -1) {
- routeItems.value.splice(idx, 1);
+ removeItem(idx)
}
}
}
@@ -150,21 +216,51 @@
}
]);
+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)}`, // 鐢熸垚鏃犵壒娈婂瓧绗︾殑ID
+ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
processId: undefined
}));
- routeItems.value.push(...newData);
- nextTick(() => initSortable());
+ routeItems.value.push(...newData);
+ routeItems.value = [...routeItems.value];
+
+ // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+ nextTick(() => {
+ // 寮哄埗閲嶆柊娓叉煋缁勪欢
+ if (proxy?.$forceUpdate) {
+ proxy.$forceUpdate();
+ }
+
+ const temp = [...routeItems.value];
+ routeItems.value = [];
+ nextTick(() => {
+ routeItems.value = temp;
+ initSortable();
+ });
+ });
};
const findProcessRouteItems = () => {
@@ -176,7 +272,10 @@
...item,
processId: item.processId === 0 ? undefined : item.processId
}));
- nextTick(() => initSortable());
+ // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+ nextTick(() => {
+ setTimeout(() => initSortable(), 100);
+ });
})
.catch(err => {
tableLoading.value = false;
@@ -222,27 +321,89 @@
});
};
-const initSortable = () => {
- if (sortable) {
- sortable.destroy();
- sortable = null;
+const destroySortable = () => {
+ if (tableSortable) {
+ tableSortable.destroy();
+ tableSortable = null;
}
+ if (stepsSortable) {
+ stepsSortable.destroy();
+ stepsSortable = null;
+ }
+};
- if (!multipleTable.value) return;
+const initSortable = () => {
+ destroySortable();
- const tbody = multipleTable.value.$el.querySelector('.el-table__body tbody') ||
- multipleTable.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
- if (!tbody) return;
+ 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;
- sortable = new Sortable(tbody, {
- animation: 150,
- ghostClass: 'sortable-ghost',
- handle: '.el-table__row',
- filter: '.el-button, .el-select',
- onEnd: (evt) => {
- const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
- routeItems.value.splice(evt.newIndex, 0, moveItem);
+ 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;
+
+ // 鍏抽敭淇1锛氱簿鍑嗗畾浣嶆楠ゆ潯鍒楄〃瀹瑰櫒锛堝吋瀹笶lement Plus涓嶅悓鐗堟湰锛�
+ const stepsList = stepsContainer.value.$el.querySelector('.el-steps__items') ||
+ stepsContainer.value.$el ||
+ stepsContainer.value;
+ if (!stepsList) {
+ console.warn('鏈壘鍒版楠ゆ潯鎷栨嫿瀹瑰櫒');
+ return;
}
+
+ // 鍏抽敭淇2锛氭斁瀹芥嫋鎷借Е鍙戞潯浠讹紝鎭㈠鎷栨嫿鍔熻兘
+ stepsSortable = new Sortable(stepsList, {
+ animation: 150,
+ ghostClass: 'sortable-ghost',
+ draggable: '.draggable-step', // 鍙嫋鎷藉厓绱狅細el-step
+ handle: '.draggable-step, .step-card', // 鎷栨嫿鎵嬫焺锛歴tep鏈韩 + 鍗$墖锛堟墿澶цЕ鍙戝尯鍩燂級
+ filter: '.el-button, .el-select, .el-input', // 杩囨护鎸夐挳/閫夋嫨鍣紝閬垮厤璇Е鍙�
+ forceFallback: true, // 寮哄埗浣跨敤fallback妯″紡锛岄伩鍏嶅師鐢熸嫋鎷藉啿绐�
+ fallbackClass: 'sortable-fallback',
+ preventOnFilter: true, // 杩囨护鍏冪礌闃绘鎷栨嫿
+ scroll: true,
+ scrollSensitivity: 30,
+ scrollSpeed: 10,
+ bubbleScroll: true,
+ // 缁熶竴浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭锛屼笌琛ㄦ牸妯″紡淇濇寔涓�鑷�
+ 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);
});
};
@@ -252,12 +413,9 @@
});
onUnmounted(() => {
- if (sortable) {
- sortable.destroy();
- }
+ destroySortable();
});
-// 淇锛氭毚闇叉柟娉曟椂閬垮厤璇硶閿欒
defineExpose({
closeModal,
handleSubmit,
@@ -279,7 +437,99 @@
background-color: #f9fafc !important;
}
-.mb5 {
- margin-bottom: 5px;
+:deep(.el-card__footer){
+ padding: 0 !important;
}
+
+.operate-button {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+/* 鍏抽敭淇锛氫紭鍖栨楠ゆ潯鎷栨嫿鏍峰紡锛岀‘淇濆彲鐐瑰嚮鍖哄煙 */
+:deep(.el-steps__items) {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: flex-start;
+ gap: 20px;
+ min-height: 100px; /* 纭繚瀹瑰櫒鏈夐珮搴� */
+}
+
+:deep(.draggable-step) {
+ cursor: move !important; /* 寮哄埗鏄剧ず鎷栨嫿鍏夋爣 */
+ padding: 8px;
+ position: relative;
+ transition: all 0.2s ease;
+ flex: 0 0 auto;
+ min-width: 220px;
+ touch-action: none; /* 绂佺敤瑙︽懜鍔ㄤ綔锛岄伩鍏嶇Щ鍔ㄧ鍐茬獊 */
+}
+
+/* 鎷栨嫿鎮诞鏍峰紡锛屾彁绀哄彲鎷栨嫿 */
+:deep(.draggable-step:hover) {
+ background-color: rgba(64, 158, 255, 0.05);
+ transform: translateY(-2px);
+}
+
+:deep(.sortable-ghost) {
+ opacity: 0.4;
+ background-color: #f5f7fa !important;
+ border: 2px dashed #409eff;
+ margin: 10px;
+ transform: scale(1.02);
+}
+
+:deep(.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;
+}
+
+:deep(.step-card) {
+ cursor: move !important;
+ transition: box-shadow 0.2s ease;
+ user-select: none;
+ -webkit-user-select: none;
+ pointer-events: auto; /* 纭繚鍗$墖鍙Е鍙戦紶鏍囦簨浠� */
+ margin: 10px;
+ height: 300px;
+}
+
+:deep(.step-card:hover) {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.step-content {
+ width: 220px;
+ user-select: none;
+}
+
+/* 绂佺敤姝ラ鏉¢粯璁ょ殑澶撮儴鏍峰紡骞叉壈 */
+:deep(.el-step__head) {
+ display: none; /* 闅愯棌榛樿鐨勬楠ゅ渾鍦堝拰搴忓彿 */
+}
+
+/* 闅愯棌Element Plus鑷姩鐢熸垚鐨勮繛鎺ョ嚎 */
+:deep(.el-step__main::before) {
+ display: none; /* 闅愯棌杩炴帴绾� */
+}
+
+/* 鑷畾涔夊簭鍙锋牱寮忎紭鍖� */
+.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>
\ No newline at end of file
--
Gitblit v1.9.3