From 5f6cce2e7d5d752c00ec25b5658857c7e0beea46 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期四, 25 十二月 2025 15:18:32 +0800
Subject: [PATCH] 工艺路线项目页面拖拽排序
---
src/views/productionManagement/processRoute/ItemsForm.vue | 226 +++++++++++++++++++++++++++++++++++++------------------
1 files changed, 151 insertions(+), 75 deletions(-)
diff --git a/src/views/productionManagement/processRoute/ItemsForm.vue b/src/views/productionManagement/processRoute/ItemsForm.vue
index 7ac04f2..525b205 100644
--- a/src/views/productionManagement/processRoute/ItemsForm.vue
+++ b/src/views/productionManagement/processRoute/ItemsForm.vue
@@ -3,10 +3,18 @@
<el-dialog
v-model="isShow"
title="宸ヨ壓璺嚎椤圭洰"
- width="800"
+ width="800px"
@close="closeModal"
>
- <el-button type="primary" @click="isShowProductSelectDialog = true" class="mb5">閫夋嫨浜у搧</el-button>
+ <el-button
+ type="primary"
+ @click="isShowProductSelectDialog = true"
+ class="mb5"
+ style="margin-bottom: 10px;"
+ >
+ 閫夋嫨浜у搧
+ </el-button>
+
<el-table
ref="multipleTable"
v-loading="tableLoading"
@@ -16,10 +24,17 @@
row-key="id"
tooltip-effect="dark"
class="lims-table"
+ style="cursor: move;"
>
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
- <el-table-column v-for="(item, index) in tableColumn" :key="index" :label="item.label" :width="item.width" show-overflow-tooltip>
+ <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"
@@ -27,7 +42,7 @@
:type="op.type"
:link="op.link"
size="small"
- @click="op.clickFun(scope.row)"
+ @click.stop="op.clickFun(scope.row)"
>
{{ op.name }}
</el-button>
@@ -35,7 +50,11 @@
<template #default="scope" v-else>
<template v-if="item.prop === 'processId'">
- <el-select v-model="scope.row[item.prop]" style="width: 100%">
+ <el-select
+ v-model="scope.row[item.prop]"
+ style="width: 100%;"
+ @mousedown.stop
+ >
<el-option
v-for="process in processOptions"
:key="process.id"
@@ -45,10 +64,9 @@
</el-select>
</template>
<template v-else>
- {{ scope.row[item.prop] }}
+ {{ scope.row[item.prop] || '-' }}
</template>
</template>
-
</el-table-column>
</el-table>
@@ -59,31 +77,42 @@
</div>
</template>
</el-dialog>
- </div>
- <ProductSelectDialog v-if="isShowProductSelectDialog" v-model:model-value="isShowProductSelectDialog" @confirm="handelSelectProducts" />
+ <ProductSelectDialog
+ v-model="isShowProductSelectDialog"
+ @confirm="handelSelectProducts"
+ />
+ </div>
</template>
<script setup>
-import {ref, computed, getCurrentInstance, onMounted} from "vue";
+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 { 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 sortable = null;
+const multipleTable = ref(null);
const isShow = computed({
get() {
@@ -91,119 +120,166 @@
},
set(val) {
emit('update:visible', val);
- },
+ }
});
const tableColumn = ref([
- {
- label: "浜у搧鍚嶇О",
- prop: "productName",
- },
- {
- label: "瑙勬牸鍚嶇О",
- prop: "model",
- },
- {
- label: "鍗曚綅",
- prop: "unit",
- },
- {
- label: "宸ュ簭鍚嶇О",
- prop: "processId",
- },
+ { 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 index = routeItems.value.indexOf(row);
- if (index !== -1) {
- routeItems.value.splice(index, 1);
+ const idx = routeItems.value.findIndex(item => item.id === row.id);
+ if (idx > -1) {
+ routeItems.value.splice(idx, 1);
}
}
- },
+ }
]
}
-])
-
-const tableLoading = ref(false);
-
-const isShowProductSelectDialog = ref(false)
-const routeItems = ref([])
-
-let { proxy } = getCurrentInstance()
+]);
const closeModal = () => {
isShow.value = false;
};
const handelSelectProducts = (products) => {
- const data = products.map(({ id, ...product }) => ({
+ const newData = products.map(({ id, ...product }) => ({
...product,
productModelId: id,
- routeId: props.record.id
+ routeId: props.record.id,
+ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`, // 鐢熸垚鏃犵壒娈婂瓧绗︾殑ID
+ processId: undefined
}));
+ routeItems.value.push(...newData);
- routeItems.value.push(...data);
-}
+ nextTick(() => 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
- }))
- }).catch(err => {
- tableLoading.value = false;
- })
+ findProcessRouteItemList({ routeId: props.record.id })
+ .then(res => {
+ tableLoading.value = false;
+ routeItems.value = res.data.map(item => ({
+ ...item,
+ processId: item.processId === 0 ? undefined : item.processId
+ }));
+ nextTick(() => initSortable());
+ })
+ .catch(err => {
+ tableLoading.value = false;
+ console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+ });
};
const findProcessList = () => {
- processList({}).then(res => {
- processOptions.value = res.data
- })
-}
+ processList({})
+ .then(res => {
+ processOptions.value = res.data;
+ })
+ .catch(err => {
+ console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
+ });
+};
+
+const { proxy } = getCurrentInstance() || {};
const handleSubmit = () => {
if (routeItems.value.length === 0) {
- proxy.$modal.msgError("璇锋坊鍔犺矾绾块」鐩�");
+ proxy?.$modal?.msgError("璇锋坊鍔犺矾绾块」鐩�");
return;
}
- // 鏄惁鏈夋湭閫夋嫨鐨勫伐搴�
- const hasUnselectedProcess = routeItems.value.some(item => !item.processId);
- if (hasUnselectedProcess) {
- proxy.$modal.msgError("璇烽�夋嫨宸ュ簭");
+ const hasEmptyProcess = routeItems.value.some(item => !item.processId);
+ if (hasEmptyProcess) {
+ proxy?.$modal?.msgError("璇蜂负鎵�鏈夐」鐩�夋嫨宸ュ簭");
return;
}
- addOrUpdateProcessRouteItem({routeId: props.record.id, processRouteItem: routeItems.value}).then(res => {
- // 鍏抽棴妯℃�佹
- isShow.value = false;
- // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
- emit('completed');
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ 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 initSortable = () => {
+ if (sortable) {
+ sortable.destroy();
+ sortable = null;
+ }
+ 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);
+ }
+ });
+};
+
+onMounted(() => {
+ findProcessRouteItems();
+ findProcessList();
+});
+
+onUnmounted(() => {
+ if (sortable) {
+ sortable.destroy();
+ }
+});
+
+// 淇锛氭毚闇叉柟娉曟椂閬垮厤璇硶閿欒
defineExpose({
closeModal,
handleSubmit,
- isShow,
+ isShow
});
-
-onMounted(() => {
- findProcessRouteItems()
- findProcessList()
-})
</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;
+}
+
+.mb5 {
+ margin-bottom: 5px;
+}
+</style>
\ No newline at end of file
--
Gitblit v1.9.3