<template>
|
<div>
|
<el-dialog
|
v-model="isShow"
|
title="工艺路线项目"
|
width="800px"
|
@close="closeModal"
|
>
|
<el-button
|
type="primary"
|
@click="isShowProductSelectDialog = true"
|
class="mb5"
|
style="margin-bottom: 10px;"
|
>
|
选择产品
|
</el-button>
|
|
<el-table
|
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="序号" type="index" width="60" />
|
|
<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>
|
|
<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 sortable = null;
|
const multipleTable = ref(null);
|
|
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) {
|
routeItems.value.splice(idx, 1);
|
}
|
}
|
}
|
]
|
}
|
]);
|
|
const closeModal = () => {
|
isShow.value = false;
|
};
|
|
const handelSelectProducts = (products) => {
|
const newData = products.map(({ id, ...product }) => ({
|
...product,
|
productModelId: id,
|
routeId: props.record.id,
|
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`, // 生成无特殊字符的ID
|
processId: undefined
|
}));
|
routeItems.value.push(...newData);
|
|
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
|
}));
|
nextTick(() => initSortable());
|
})
|
.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 = () => {
|
if (routeItems.value.length === 0) {
|
proxy?.$modal?.msgError("请添加路线项目");
|
return;
|
}
|
|
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 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
|
});
|
</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>
|