From add86852b44af3dfb55c70c4d7b3ea9bb4fca227 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 15 四月 2026 17:41:20 +0800
Subject: [PATCH] 中兴实强 1.新增工艺路线的时候产品可以多选 2.绑定工艺路线时,可以删减工序并且每道工序可以多选报工人 3.报工列表展示报工人,非本订单报工人不可报工 其他
---
src/views/basicData/product/ProductSelectDialog.vue | 80 +
src/views/productionManagement/productionOrder/New.vue | 450 +++++++++++---
src/views/productionManagement/processRoute/processRouteItem/index.vue | 277 +++++---
src/views/productionManagement/productionOrder/index.vue | 977 +++++++++++++++++++-------------
4 files changed, 1,144 insertions(+), 640 deletions(-)
diff --git a/src/views/basicData/product/ProductSelectDialog.vue b/src/views/basicData/product/ProductSelectDialog.vue
index ded23cc..358e6e4 100644
--- a/src/views/basicData/product/ProductSelectDialog.vue
+++ b/src/views/basicData/product/ProductSelectDialog.vue
@@ -18,7 +18,7 @@
<!-- 鍒楄〃 -->
<el-table ref="tableRef" v-loading="loading" :data="tableData" height="420" highlight-current-row row-key="id"
@selection-change="handleSelectionChange" @select="handleSelect">
- <el-table-column type="selection" width="55" />
+ <el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column type="index" label="搴忓彿" width="60" />
<el-table-column prop="productName" label="浜у搧澶х被" min-width="160" />
<el-table-column prop="model" label="鍨嬪彿鍚嶇О" min-width="200" />
@@ -54,7 +54,7 @@
const props = defineProps<{
modelValue: boolean;
- single?: boolean; // 鏄惁鍙兘閫夋嫨涓�涓紝榛樿false锛堝彲閫夋嫨澶氫釜锛�
+ single?: boolean;
}>();
const emit = defineEmits(['update:modelValue', 'confirm']);
@@ -78,46 +78,68 @@
const tableData = ref<ProductRow[]>([]);
const total = ref(0);
const multipleSelection = ref<ProductRow[]>([]);
+const selectedMap = ref<Map<number, ProductRow>>(new Map());
const tableRef = ref();
function close() {
visible.value = false;
}
+function syncCurrentPageSelection() {
+ nextTick(() => {
+ if (!tableRef.value) {
+ return;
+ }
+
+ tableData.value.forEach((item) => {
+ tableRef.value.toggleRowSelection(item, selectedMap.value.has(item.id));
+ });
+ });
+}
+
const handleSelectionChange = (val: ProductRow[]) => {
if (props.single && val.length > 1) {
- // 濡傛灉闄愬埗涓哄崟涓�夋嫨锛屽彧淇濈暀鏈�鍚庝竴涓�変腑鐨�
const lastSelected = val[val.length - 1];
multipleSelection.value = [lastSelected];
- // 娓呯┖琛ㄦ牸閫変腑鐘舵�侊紝鐒跺悗閲嶆柊閫変腑鏈�鍚庝竴涓�
+ selectedMap.value = new Map(lastSelected ? [[lastSelected.id, lastSelected]] : []);
nextTick(() => {
if (tableRef.value) {
tableRef.value.clearSelection();
- tableRef.value.toggleRowSelection(lastSelected, true);
+ if (lastSelected) {
+ tableRef.value.toggleRowSelection(lastSelected, true);
+ }
}
});
- } else {
- multipleSelection.value = val;
+ return;
}
+
+ const currentPageIds = new Set(tableData.value.map((item) => item.id));
+ currentPageIds.forEach((id) => {
+ selectedMap.value.delete(id);
+ });
+ val.forEach((item) => {
+ selectedMap.value.set(item.id, item);
+ });
+ multipleSelection.value = Array.from(selectedMap.value.values());
}
-// 澶勭悊鍗曚釜閫夋嫨
const handleSelect = (selection: ProductRow[], row: ProductRow) => {
- if (props.single) {
- // 濡傛灉闄愬埗涓哄崟涓紝娓呯┖鍏朵粬閫夋嫨锛屽彧淇濈暀褰撳墠琛�
- if (selection.includes(row)) {
- // 閫変腑褰撳墠琛屾椂锛屾竻绌哄叾浠栭�変腑
- multipleSelection.value = [row];
- nextTick(() => {
- if (tableRef.value) {
- tableData.value.forEach((item) => {
- if (item.id !== row.id) {
- tableRef.value.toggleRowSelection(item, false);
- }
- });
- }
- });
- }
+ if (!props.single) {
+ return;
+ }
+
+ if (selection.includes(row)) {
+ multipleSelection.value = [row];
+ selectedMap.value = new Map([[row.id, row]]);
+ nextTick(() => {
+ if (tableRef.value) {
+ tableData.value.forEach((item) => {
+ if (item.id !== row.id) {
+ tableRef.value.toggleRowSelection(item, false);
+ }
+ });
+ }
+ });
}
}
@@ -139,7 +161,7 @@
function onConfirm() {
if (multipleSelection.value.length === 0) {
- ElMessage.warning("璇烽�夋嫨涓�鏉′骇鍝�");
+ ElMessage.warning("璇烽�夋嫨浜у搧");
return;
}
if (props.single && multipleSelection.value.length > 1) {
@@ -153,24 +175,26 @@
async function loadData() {
loading.value = true;
try {
- multipleSelection.value = []; // 缈婚〉/鎼滅储鍚庢竻绌洪�夋嫨鏇寸鍚堥鏈�
const res: any = await productModelList({
productName: query.productName.trim(),
model: query.model.trim(),
current: page.pageNum,
size: page.pageSize,
});
- tableData.value = res.records;
- total.value = res.total;
+ tableData.value = res.records || [];
+ total.value = res.total || 0;
+ syncCurrentPageSelection();
} finally {
loading.value = false;
}
}
-// 鐩戝惉寮圭獥鎵撳紑锛岄噸缃�夋嫨
watch(() => props.modelValue, (visible) => {
if (visible) {
multipleSelection.value = [];
+ selectedMap.value = new Map();
+ page.pageNum = 1;
+ loadData();
}
});
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index c1c490c..213c6aa 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -179,18 +179,27 @@
</el-select>
</el-form-item>
- <el-form-item label="浜у搧鍚嶇О" prop="productModelId">
+ <el-form-item label="浜у搧鍚嶇О" prop="selectedProducts">
<el-button type="primary" @click="showProductSelectDialog = true">
- {{ form.productName && form.model
- ? `${form.productName} - ${form.model}`
- : '閫夋嫨浜у搧' }}
+ {{ form.selectedProducts.length ? '閲嶆柊閫夋嫨浜у搧' : '閫夋嫨浜у搧' }}
</el-button>
+ <div v-if="form.selectedProducts.length" class="selected-product-tags">
+ <el-tag
+ v-for="product in form.selectedProducts"
+ :key="product.id"
+ class="selected-product-tag"
+ type="info"
+ effect="plain"
+ >
+ {{ product.productName }} - {{ product.model }}
+ </el-tag>
+ </div>
</el-form-item>
<el-form-item label="鍗曚綅" prop="unit">
<el-input
v-model="form.unit"
- :placeholder="form.productModelId ? '鏍规嵁閫夋嫨鐨勪骇鍝佽嚜鍔ㄥ甫鍑�' : '璇峰厛閫夋嫨浜у搧'"
+ :placeholder="form.selectedProducts.length === 1 ? '鏍规嵁閫夋嫨鐨勪骇鍝佽嚜鍔ㄥ甫鍑�' : '澶氫釜浜у搧鏃朵笉灞曠ず鍗曚釜鍗曚綅'"
clearable
:disabled="true"
/>
@@ -211,7 +220,6 @@
<ProductSelectDialog
v-model="showProductSelectDialog"
@confirm="handleProductSelect"
- single
/>
</div>
</template>
@@ -269,6 +277,8 @@
routeId: routeId.value,
processId: undefined,
productModelId: undefined,
+ productModelIds: "",
+ selectedProducts: [],
productName: "",
model: "",
unit: "",
@@ -277,94 +287,118 @@
const rules = {
processId: [{ required: true, message: '璇烽�夋嫨宸ュ簭', trigger: 'change' }],
- productModelId: [{ required: true, message: '璇烽�夋嫨浜у搧', trigger: 'change' }],
+ selectedProducts: [{
+ required: true,
+ validator: (_, value, callback) => {
+ if (Array.isArray(value) && value.length > 0) {
+ callback();
+ return;
+ }
+ callback(new Error('璇烽�夋嫨浜у搧'));
+ },
+ trigger: 'change',
+ }],
};
-// 鏍规嵁宸ュ簭ID鑾峰彇宸ュ簭鍚嶇О
const getProcessName = (processId) => {
if (!processId) return '';
- const process = processOptions.value.find(p => p.id === processId);
+ const process = processOptions.value.find((p) => p.id === processId);
return process ? process.name : '';
};
-// 鑾峰彇鍒楄〃
const getList = () => {
tableLoading.value = true;
const listPromise =
- pageType.value === "order"
+ pageType.value === 'order'
? findProductProcessRouteItemList({ orderId: orderId.value })
: findProcessRouteItemList({ routeId: routeId.value });
listPromise
- .then(res => {
+ .then((res) => {
tableData.value = res.data || [];
tableLoading.value = false;
- // 鍒楄〃鍔犺浇瀹屾垚鍚庡垵濮嬪寲鎷栨嫿鎺掑簭
nextTick(() => {
initSortable();
});
})
- .catch(err => {
+ .catch((err) => {
tableLoading.value = false;
- console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
- proxy?.$modal?.msgError("鑾峰彇鍒楄〃澶辫触");
+ console.error('鑾峰彇鍒楄〃澶辫触:', err);
+ proxy?.$modal?.msgError('鑾峰彇鍒楄〃澶辫触');
});
};
-// 鑾峰彇宸ュ簭鍒楄〃
const getProcessList = () => {
processList({})
- .then(res => {
+ .then((res) => {
processOptions.value = res.data || [];
})
- .catch(err => {
- console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
+ .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 || '',
- description: route.query.description || ''
+ description: route.query.description || '',
};
};
-// 鏂板
+const getEditSelectedProducts = (row) => {
+ const idList = String(row.productModelIds || row.productModelId || '')
+ .split(',')
+ .map((item) => item.trim())
+ .filter(Boolean);
+ const nameList = String(row.productName || '')
+ .split(',')
+ .map((item) => item.trim());
+ const modelList = String(row.model || '')
+ .split(',')
+ .map((item) => item.trim());
+
+ return idList.map((id, index) => ({
+ id: Number(id) || id,
+ productName: nameList[index] || row.productName || '',
+ model: modelList[index] || row.model || '',
+ unit: row.unit || '',
+ }));
+};
+
const handleAdd = () => {
operationType.value = 'add';
resetForm();
dialogVisible.value = true;
};
-// 缂栬緫
const handleEdit = (row) => {
operationType.value = 'edit';
+ const selectedProducts = getEditSelectedProducts(row);
form.value = {
id: row.id,
routeId: routeId.value,
processId: row.processId,
productModelId: row.productModelId,
- productName: row.productName || "",
- model: row.model || "",
- unit: row.unit || "",
+ productModelIds: row.productModelIds || (row.productModelId ? String(row.productModelId) : ''),
+ selectedProducts,
+ productName: row.productName || '',
+ model: row.model || '',
+ unit: row.unit || '',
isQuality: row.isQuality,
};
dialogVisible.value = true;
};
-// 鍒犻櫎
const handleDelete = (row) => {
ElMessageBox.confirm('纭鍒犻櫎璇ュ伐鑹鸿矾绾块」鐩紵', '鎻愮ず', {
confirmButtonText: '纭',
cancelButtonText: '鍙栨秷',
- type: 'warning'
+ type: 'warning',
})
.then(() => {
- // 鐢熶骇璁㈠崟涓嬩娇鐢� productProcessRoute 鐨勫垹闄ゆ帴鍙o紙璺敱鍚庢嫾鎺� id锛夛紝鍏跺畠鎯呭喌浣跨敤宸ヨ壓璺嚎椤圭洰鎵归噺鍒犻櫎鎺ュ彛
const deletePromise =
pageType.value === 'order'
? deleteRouteItem(row.id)
@@ -382,112 +416,115 @@
.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 || "";
+ const firstProduct = products[0];
+ form.value.selectedProducts = products;
+ form.value.productModelIds = products.map((item) => item.id).join(',');
+ form.value.productModelId = products.length === 1 ? firstProduct.id : undefined;
+ form.value.productName = products.length === 1 ? firstProduct.productName : '';
+ form.value.model = products.length === 1 ? firstProduct.model : '';
+ form.value.unit = products.length === 1 ? (firstProduct.unit || '') : '';
showProductSelectDialog.value = false;
- // 瑙﹀彂琛ㄥ崟楠岃瘉
- formRef.value?.validateField('productModelId');
+ formRef.value?.validateField('selectedProducts');
}
};
-// 鎻愪氦
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;
- });
- }
+ if (!valid) {
+ return;
}
+
+ submitLoading.value = true;
+
+ if (operationType.value === 'add') {
+ 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,
+ productModelIds: form.value.productModelIds,
+ isQuality: form.value.isQuality,
+ dragSort,
+ })
+ : addOrUpdateProcessRouteItem({
+ routeId: routeId.value,
+ processId: form.value.processId,
+ productModelId: form.value.productModelId,
+ productModelIds: form.value.productModelIds,
+ isQuality: form.value.isQuality,
+ dragSort,
+ });
+
+ addPromise
+ .then(() => {
+ proxy?.$modal?.msgSuccess('鏂板鎴愬姛');
+ closeDialog();
+ getList();
+ })
+ .catch(() => {
+ proxy?.$modal?.msgError('鏂板澶辫触');
+ })
+ .finally(() => {
+ submitLoading.value = false;
+ });
+ return;
+ }
+
+ const isOrderPage = pageType.value === 'order';
+ const updatePromise = isOrderPage
+ ? addOrUpdateProductProcessRouteItem({
+ id: form.value.id,
+ processId: form.value.processId,
+ productModelId: form.value.productModelId,
+ productModelIds: form.value.productModelIds,
+ isQuality: form.value.isQuality,
+ })
+ : addOrUpdateProcessRouteItem({
+ routeId: routeId.value,
+ processId: form.value.processId,
+ productModelId: form.value.productModelId,
+ productModelIds: form.value.productModelIds,
+ 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: "",
+ productModelIds: '',
+ selectedProducts: [],
+ productName: '',
+ model: '',
+ unit: '',
+ isQuality: false,
};
formRef.value?.resetFields();
};
-// 鍏抽棴寮圭獥
const closeDialog = () => {
dialogVisible.value = false;
resetForm();
@@ -753,6 +790,18 @@
margin: 10px 0;
}
+.selected-product-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-top: 10px;
+}
+
+.selected-product-tag {
+ max-width: 100%;
+}
+
+
.card-footer {
display: flex;
justify-content: space-around;
diff --git a/src/views/productionManagement/productionOrder/New.vue b/src/views/productionManagement/productionOrder/New.vue
index c9c478b..edd8bb8 100644
--- a/src/views/productionManagement/productionOrder/New.vue
+++ b/src/views/productionManagement/productionOrder/New.vue
@@ -1,68 +1,137 @@
<template>
<div>
<el-dialog
- v-model="isShow"
- title="鏂板鐢熶骇璁㈠崟"
- width="800"
- @close="closeModal"
+ v-model="isShow"
+ title="鏂板鐢熶骇璁㈠崟"
+ width="800"
+ @close="closeModal"
>
- <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
+ <el-form
+ ref="formRef"
+ :model="formState"
+ label-width="140px"
+ label-position="top"
+ >
<el-form-item
- label="浜у搧鍚嶇О"
- prop="productModelId"
- :rules="[
- {
- required: true,
- message: '璇烽�夋嫨浜у搧',
- trigger: 'change',
- }
- ]"
+ label="浜у搧鍚嶇О"
+ prop="productModelId"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨浜у搧',
+ trigger: 'change',
+ },
+ ]"
>
<el-button type="primary" @click="showProductSelectDialog = true">
- {{ formState.productName ? formState.productName : '閫夋嫨浜у搧' }}
+ {{ formState.productName || "閫夋嫨浜у搧" }}
</el-button>
</el-form-item>
- <el-form-item
- label="瑙勬牸"
- prop="productModelName"
- >
- <el-input v-model="formState.productModelName" disabled />
+ <el-form-item label="瑙勬牸" prop="productModelName">
+ <el-input v-model="formState.productModelName" disabled />
+ </el-form-item>
+
+ <el-form-item label="鍗曚綅" prop="unit">
+ <el-input v-model="formState.unit" disabled />
</el-form-item>
<el-form-item
- label="鍗曚綅"
- prop="unit"
+ label="宸ヨ壓璺嚎"
+ prop="routeId"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨宸ヨ壓璺嚎',
+ trigger: 'change',
+ },
+ ]"
>
- <el-input v-model="formState.unit" disabled />
- </el-form-item>
-
- <el-form-item label="宸ヨ壓璺嚎">
- <el-select v-model="formState.routeId"
- placeholder="璇烽�夋嫨宸ヨ壓璺嚎"
- style="width: 100%;"
- :loading="bindRouteLoading">
- <el-option v-for="item in routeOptions"
- :key="item.id"
- :label="`${item.processRouteCode || ''}`"
- :value="item.id" />
+ <el-select
+ v-model="formState.routeId"
+ placeholder="璇烽�夋嫨宸ヨ壓璺嚎"
+ style="width: 100%"
+ :loading="bindRouteLoading"
+ @change="handleRouteChange"
+ >
+ <el-option
+ v-for="item in routeOptions"
+ :key="item.id"
+ :label="item.processRouteCode || ''"
+ :value="item.id"
+ />
</el-select>
</el-form-item>
<el-form-item
- label="闇�姹傛暟閲�"
- prop="quantity"
+ v-if="processListData.length"
+ label="宸ュ簭鎶ュ伐浜�"
+ prop="processUserList"
+ :rules="[
+ {
+ validator: validateProcessUsers,
+ trigger: 'change',
+ },
+ ]"
>
- <el-input-number v-model="formState.quantity" :step="1" :min="1" style="width: 100%" />
+ <div class="process-user-list">
+ <div
+ v-for="(item, index) in processListData"
+ :key="item.id || `${item.processId}-${index}`"
+ class="process-user-item"
+ >
+ <div class="process-user-header">
+ <div class="process-user-name">
+ {{ item.name || item.processName || item.no || `宸ュ簭${index + 1}` }}
+ </div>
+ <el-button
+ type="danger"
+ link
+ class="process-user-remove"
+ @click="removeProcessItem(index)"
+ >
+ 鍒犻櫎
+ </el-button>
+ </div>
+ <el-select
+ v-model="formState.processUserList[index].userIds"
+ placeholder="璇烽�夋嫨鎶ュ伐浜�"
+ class="process-user-select"
+ filterable
+ clearable
+ multiple
+ collapse-tags
+ collapse-tags-tooltip
+ :max-collapse-tags="3"
+ @change="handleProcessUserChange(index, $event)"
+ >
+ <el-option
+ v-for="user in userOptions"
+ :key="user.userId"
+ :label="user.nickName"
+ :value="user.userId"
+ />
+ </el-select>
+ </div>
+ </div>
+ </el-form-item>
+
+ <el-form-item label="闇�姹傛暟閲�" prop="quantity">
+ <el-input-number
+ v-model="formState.quantity"
+ :step="1"
+ :min="1"
+ style="width: 100%"
+ />
</el-form-item>
</el-form>
- <!-- 浜у搧閫夋嫨寮圭獥 -->
<ProductSelectDialog
- v-model="showProductSelectDialog"
- @confirm="handleProductSelect"
- single
+ v-model="showProductSelectDialog"
+ single
+ @confirm="handleProductSelect"
/>
+
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit">纭</el-button>
@@ -74,115 +143,234 @@
</template>
<script setup>
-import {ref, computed, getCurrentInstance} from "vue";
+import { computed, getCurrentInstance, nextTick, ref } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import {addProductOrder, listProcessRoute} from "@/api/productionManagement/productionOrder.js";
+import { addProductOrder, listProcessRoute } from "@/api/productionManagement/productionOrder.js";
+import { processList } from "@/api/productionManagement/productionProcess.js";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
},
-
type: {
type: String,
- required: true,
- default: 'qualified',
+ default: "qualified",
},
});
-const emit = defineEmits(['update:visible', 'completed']);
+const emit = defineEmits(["update:visible", "completed"]);
-// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
-const formState = ref({
+const createDefaultFormState = () => ({
productId: undefined,
productModelId: undefined,
routeId: undefined,
productName: "",
productModelName: "",
unit: "",
- quantity: 0,
+ quantity: 1,
+ processUserList: [],
});
+
+const formState = ref(createDefaultFormState());
const isShow = computed({
get() {
return props.visible;
},
set(val) {
- emit('update:visible', val);
+ emit("update:visible", val);
},
});
const showProductSelectDialog = ref(false);
+const routeOptions = ref([]);
+const bindRouteLoading = ref(false);
+const processListData = ref([]);
+const userOptions = ref([]);
+const userLoading = ref(false);
-let { proxy } = getCurrentInstance()
+const { proxy } = getCurrentInstance();
+const formRef = ref();
+
+const validateProcessUserField = async () => {
+ await nextTick();
+ if (!formRef.value) return;
+
+ if (processListData.value.length) {
+ formRef.value.validateField("processUserList");
+ return;
+ }
+
+ formRef.value.clearValidate("processUserList");
+};
+
+const resetProcessUsers = () => {
+ processListData.value = [];
+ formState.value.processUserList = [];
+};
const closeModal = () => {
- // 閲嶇疆琛ㄥ崟鏁版嵁
- formState.value = {
- productId: undefined,
- productModelId: undefined,
- routeId: undefined,
- productName: "",
- productModelName: "",
- quantity: '',
- };
+ formState.value = createDefaultFormState();
+ routeOptions.value = [];
+ resetProcessUsers();
isShow.value = false;
};
-// 浜у搧閫夋嫨澶勭悊
-const handleProductSelect = async (products) => {
- if (products && products.length > 0) {
- const product = products[0];
- formState.value.productId = product.productId;
- formState.value.productName = product.productName;
- formState.value.productModelName = product.model;
- formState.value.productModelId = product.id;
- formState.value.unit = product.unit;
- showProductSelectDialog.value = false;
- fetchRouteOptions( product.id);
- // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
- proxy.$refs["formRef"]?.validateField('productModelId');
- }
+const ensureUserOptions = () => {
+ if (userOptions.value.length || userLoading.value) return;
+
+ userLoading.value = true;
+ userListNoPageByTenantId()
+ .then(res => {
+ userOptions.value = res.data || [];
+ })
+ .finally(() => {
+ userLoading.value = false;
+ });
};
-const routeOptions = ref([]);
-const bindRouteLoading = ref(false);
-const fetchRouteOptions = (productModelId) => {
+const createProcessUserList = list =>
+ list.map(item => ({
+ processId: item.id,
+ processName: item.name || item.processName || item.no || "",
+ userIds: [],
+ userNames: "",
+ }));
+
+const fetchProcessList = routeId => {
+ if (!routeId) {
+ resetProcessUsers();
+ return;
+ }
+
+ processList({ routeId })
+ .then(res => {
+ processListData.value = res.data || [];
+ formState.value.processUserList = createProcessUserList(processListData.value);
+ ensureUserOptions();
+ validateProcessUserField();
+ })
+ .catch(() => {
+ resetProcessUsers();
+ });
+};
+
+const handleRouteChange = routeId => {
+ fetchProcessList(routeId);
+};
+
+const handleProcessUserChange = (index, userIds) => {
+ const selectedUsers = userOptions.value.filter(user => Array.isArray(userIds) && userIds.includes(user.userId));
+ formState.value.processUserList[index].userNames = selectedUsers.map(user => user.nickName).join(",");
+ validateProcessUserField();
+};
+
+const removeProcessItem = index => {
+ processListData.value.splice(index, 1);
+ formState.value.processUserList.splice(index, 1);
+ validateProcessUserField();
+};
+
+const validateProcessUsers = (_, value, callback) => {
+ if (!formState.value.routeId) {
+ callback();
+ return;
+ }
+ if (!processListData.value.length) {
+ callback(new Error("褰撳墠宸ヨ壓璺嚎涓嬫病鏈夊伐搴�"));
+ return;
+ }
+ if (!Array.isArray(value) || value.length !== processListData.value.length) {
+ callback(new Error("璇蜂负姣忛亾宸ュ簭閫夋嫨鎶ュ伐浜�"));
+ return;
+ }
+
+ const hasEmptyUser = value.some(item => !Array.isArray(item.userIds) || item.userIds.length === 0);
+ if (hasEmptyUser) {
+ callback(new Error("璇蜂负姣忛亾宸ュ簭閫夋嫨鎶ュ伐浜�"));
+ return;
+ }
+ callback();
+};
+
+const handleProductSelect = products => {
+ if (!products?.length) return;
+
+ const product = products[0];
+ formState.value.productId = product.productId;
+ formState.value.productName = product.productName;
+ formState.value.productModelName = product.model;
+ formState.value.productModelId = product.id;
+ formState.value.unit = product.unit;
formState.value.routeId = undefined;
- routeOptions.value = []
+ routeOptions.value = [];
+ resetProcessUsers();
+ showProductSelectDialog.value = false;
+ fetchRouteOptions(product.id);
+ formRef.value?.validateField("productModelId");
+};
+
+const fetchRouteOptions = productModelId => {
+ formState.value.routeId = undefined;
+ routeOptions.value = [];
+ resetProcessUsers();
bindRouteLoading.value = true;
- listProcessRoute({ productModelId: productModelId }).then(res => {
- routeOptions.value = res.data || [];
- }).finally(() => {
- bindRouteLoading.value = false;
- })
-}
+ listProcessRoute({ productModelId })
+ .then(res => {
+ routeOptions.value = res.data || [];
+ })
+ .finally(() => {
+ bindRouteLoading.value = false;
+ });
+};
+
+const buildProcessRouteItems = () =>
+ processListData.value.map((item, index) => {
+ const processUser = formState.value.processUserList[index] || {};
+ return {
+ productOrderId: undefined,
+ productRouteId: formState.value.routeId,
+ processId: item.id,
+ productModelId: formState.value.productModelId,
+ dragSort: item.dragSort ?? index + 1,
+ isQuality: item.isQuality ?? false,
+ reportUserIds: Array.isArray(processUser.userIds) ? processUser.userIds.join(",") : "",
+ };
+ });
const handleSubmit = () => {
- proxy.$refs["formRef"].validate(valid => {
- if (valid) {
- // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰瑙勬牸
- if (!formState.value.productModelId) {
- proxy.$modal.msgError("璇烽�夋嫨浜у搧");
- return;
- }
- if (!formState.value.productModelId) {
- proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
- return;
- }
+ formRef.value.validate(valid => {
+ if (!valid) return;
- addProductOrder(formState.value).then(res => {
- // 鍏抽棴妯℃�佹
- isShow.value = false;
- // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
- emit('completed');
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- })
+ if (!formState.value.productModelId) {
+ proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+ return;
}
- })
-};
+ if (!formState.value.routeId) {
+ proxy.$modal.msgError("璇烽�夋嫨宸ヨ壓璺嚎");
+ return;
+ }
+ if (!formState.value.processUserList.length) {
+ proxy.$modal.msgError("褰撳墠宸ヨ壓璺嚎涓嬫病鏈夊伐搴�");
+ return;
+ }
+ if (formState.value.processUserList.some(item => !Array.isArray(item.userIds) || item.userIds.length === 0)) {
+ proxy.$modal.msgError("璇蜂负姣忛亾宸ュ簭閫夋嫨鎶ュ伐浜�");
+ return;
+ }
+ addProductOrder({
+ ...formState.value,
+ processRouteItems: buildProcessRouteItems(),
+ }).then(() => {
+ isShow.value = false;
+ emit("completed");
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ });
+ });
+};
defineExpose({
closeModal,
@@ -190,3 +378,55 @@
isShow,
});
</script>
+
+<style scoped>
+.process-user-list {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ padding: 14px;
+ border-radius: 12px;
+ background: #f7f9fc;
+ border: 1px solid #e8eef5;
+}
+
+.process-user-item {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr);
+ gap: 16px;
+ padding: 12px 14px;
+ border-radius: 10px;
+ background: #fff;
+ border: 1px solid #edf2f7;
+}
+
+.process-user-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.process-user-name {
+ color: #1f2d3d;
+ font-weight: 500;
+ line-height: 1.4;
+}
+
+.process-user-remove {
+ flex-shrink: 0;
+ padding: 0;
+}
+
+.process-user-select {
+ width: 100%;
+}
+
+@media (max-width: 768px) {
+ .process-user-item {
+ grid-template-columns: 1fr;
+ gap: 10px;
+ }
+}
+</style>
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index fc64063..451dc34 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -1,43 +1,49 @@
<template>
<div class="app-container">
<div class="search_form">
- <el-form :model="searchForm"
- :inline="true">
- <el-form-item label="瀹㈡埛鍚嶇О:">
- <el-input v-model="searchForm.customerName"
- placeholder="璇疯緭鍏�"
- clearable
- prefix-icon="Search"
- style="width: 160px;"
- @change="handleQuery" />
+ <el-form :model="searchForm" :inline="true">
+ <el-form-item label="瀹㈡埛鍚嶇О">
+ <el-input
+ v-model="searchForm.customerName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :prefix-icon="Search"
+ style="width: 160px;"
+ @change="handleQuery"
+ />
</el-form-item>
- <el-form-item label="鍚堝悓鍙�:">
- <el-input v-model="searchForm.salesContractNo"
- placeholder="璇疯緭鍏�"
- clearable
- prefix-icon="Search"
- style="width: 160px;"
- @change="handleQuery" />
+ <el-form-item label="閿�鍞悎鍚屽彿">
+ <el-input
+ v-model="searchForm.salesContractNo"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :prefix-icon="Search"
+ style="width: 160px;"
+ @change="handleQuery"
+ />
</el-form-item>
- <el-form-item label="浜у搧鍚嶇О:">
- <el-input v-model="searchForm.productCategory"
- placeholder="璇疯緭鍏�"
- clearable
- prefix-icon="Search"
- style="width: 160px;"
- @change="handleQuery" />
+ <el-form-item label="浜у搧鍚嶇О">
+ <el-input
+ v-model="searchForm.productCategory"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :prefix-icon="Search"
+ style="width: 160px;"
+ @change="handleQuery"
+ />
</el-form-item>
- <el-form-item label="瑙勬牸:">
- <el-input v-model="searchForm.specificationModel"
- placeholder="璇疯緭鍏�"
- clearable
- prefix-icon="Search"
- style="width: 160px;"
- @change="handleQuery" />
+ <el-form-item label="瑙勬牸鍨嬪彿">
+ <el-input
+ v-model="searchForm.specificationModel"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :prefix-icon="Search"
+ style="width: 160px;"
+ @change="handleQuery"
+ />
</el-form-item>
<el-form-item>
- <el-button type="primary"
- @click="handleQuery">鎼滅储</el-button>
+ <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
</el-form-item>
</el-form>
<div>
@@ -46,16 +52,19 @@
<el-button @click="handleOut">瀵煎嚭</el-button>
</div>
</div>
+
<div class="table_list">
- <PIMTable rowKey="id"
- :column="tableColumn"
- :tableData="tableData"
- :page="page"
- :tableLoading="tableLoading"
- :row-class-name="tableRowClassName"
- :isSelection="true"
- @selection-change="handleSelectionChange"
- @pagination="pagination">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :tableLoading="tableLoading"
+ :row-class-name="tableRowClassName"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ @pagination="pagination"
+ >
<template #completionStatus="{ row }">
<el-progress
:percentage="toProgressPercentage(row?.completionStatus)"
@@ -65,409 +74,591 @@
</template>
</PIMTable>
</div>
- <el-dialog v-model="bindRouteDialogVisible"
- title="缁戝畾宸ヨ壓璺嚎"
- width="500px">
+
+ <el-dialog
+ v-model="bindRouteDialogVisible"
+ title="缁戝畾宸ヨ壓璺嚎"
+ width="700px"
+ >
<el-form label-width="90px">
<el-form-item label="宸ヨ壓璺嚎">
- <el-select v-model="bindForm.routeId"
- placeholder="璇烽�夋嫨宸ヨ壓璺嚎"
- style="width: 100%;"
- :loading="bindRouteLoading">
- <el-option v-for="item in routeOptions"
- :key="item.id"
- :label="`${item.processRouteCode || ''}`"
- :value="item.id" />
+ <el-select
+ v-model="bindForm.routeId"
+ placeholder="璇烽�夋嫨宸ヨ壓璺嚎"
+ style="width: 100%;"
+ :loading="bindRouteLoading"
+ @change="handleBindRouteChange"
+ >
+ <el-option
+ v-for="item in routeOptions"
+ :key="item.id"
+ :label="item.processRouteCode || ''"
+ :value="item.id"
+ />
</el-select>
+ </el-form-item>
+ <el-form-item v-if="bindProcessList.length" label="鎶ュ伐浜哄憳">
+ <div class="process-user-list">
+ <div
+ v-for="(item, index) in bindProcessList"
+ :key="item.id || `${item.processId}-${index}`"
+ class="process-user-item"
+ >
+ <div class="process-user-header">
+ <div class="process-user-name">
+ {{ item.name || item.processName || item.no || `宸ュ簭${index + 1}` }}
+ </div>
+ <el-button
+ type="danger"
+ link
+ class="process-user-remove"
+ @click="removeBindProcessItem(index)"
+ >
+ 鍒犻櫎
+ </el-button>
+ </div>
+ <el-select
+ v-model="bindForm.processUserList[index].userIds"
+ class="process-user-select"
+ placeholder="璇烽�夋嫨鎶ュ伐浜哄憳"
+ filterable
+ clearable
+ multiple
+ collapse-tags
+ collapse-tags-tooltip
+ :max-collapse-tags="3"
+ @change="handleBindProcessUserChange(index, $event)"
+ >
+ <el-option
+ v-for="user in userOptions"
+ :key="user.userId"
+ :label="user.nickName"
+ :value="user.userId"
+ />
+ </el-select>
+ </div>
+ </div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
- <el-button type="primary"
- :loading="bindRouteSaving"
- @click="handleBindRouteConfirm">纭� 璁�</el-button>
- <el-button @click="bindRouteDialogVisible = false">鍙� 娑�</el-button>
+ <el-button
+ type="primary"
+ :loading="bindRouteSaving"
+ @click="handleBindRouteConfirm"
+ >
+ 纭
+ </el-button>
+ <el-button @click="bindRouteDialogVisible = false">鍙栨秷</el-button>
</span>
</template>
</el-dialog>
- <new-product-order v-if="isShowNewModal"
- v-model:visible="isShowNewModal"
- @completed="handleQuery" />
+ <new-product-order
+ v-if="isShowNewModal"
+ v-model:visible="isShowNewModal"
+ @completed="handleQuery"
+ />
</div>
</template>
<script setup>
- import { onMounted, ref } from "vue";
- import { ElMessageBox } from "element-plus";
- import dayjs from "dayjs";
- import { useRouter } from "vue-router";
- import {
- productOrderListPage,
- listProcessRoute,
- bindingRoute,
- listProcessBom, delProductOrder,
- } from "@/api/productionManagement/productionOrder.js";
- import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
- import {fileDel} from "@/api/financialManagement/revenueManagement.js";
- import PIMTable from "@/components/PIMTable/PIMTable.vue";
- const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
+import { defineAsyncComponent, getCurrentInstance, onMounted, reactive, ref, toRefs } from "vue";
+import { ElMessageBox } from "element-plus";
+import { Search } from "@element-plus/icons-vue";
+import dayjs from "dayjs";
+import { useRouter } from "vue-router";
+import {
+ bindingRoute,
+ delProductOrder,
+ listProcessRoute,
+ productOrderListPage,
+} from "@/api/productionManagement/productionOrder.js";
+import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
+import { processList } from "@/api/productionManagement/productionProcess.js";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
- const { proxy } = getCurrentInstance();
+const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
- const router = useRouter();
- const isShowNewModal = ref(false);
+const { proxy } = getCurrentInstance();
+const router = useRouter();
+const isShowNewModal = ref(false);
- const tableColumn = ref([
- {
- label: "鐢熶骇璁㈠崟鍙�",
- prop: "npsNo",
- width: '120px',
- },
- {
- label: "閿�鍞悎鍚屽彿",
- prop: "salesContractNo",
- width: '150px',
- },
- {
- label: "瀹㈡埛鍚嶇О",
- prop: "customerName",
- width: '200px',
- },
- {
- label: "浜у搧鍚嶇О",
- prop: "productCategory",
- width: '120px',
- },
- {
- label: "瑙勬牸",
- prop: "specificationModel",
- width: '120px',
- },
- {
- label: "宸ヨ壓璺嚎缂栧彿",
- prop: "processRouteCode",
- width: '200px',
- },
- {
- label: "闇�姹傛暟閲�",
- prop: "quantity",
- },
- {
- label: "瀹屾垚鏁伴噺",
- prop: "completeQuantity",
- },
- {
- dataType: "slot",
- label: "瀹屾垚杩涘害",
- prop: "completionStatus",
- slot: "completionStatus",
- width: 180,
- },
- {
- label: "寮�濮嬫棩鏈�",
- prop: "startTime",
- formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
- width: 120,
- },
- {
- label: "缁撴潫鏃ユ湡",
- prop: "endTime",
- formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
- width: 120,
- },
- {
- label: "浜や粯鏃ユ湡",
- prop: "deliveryDate",
- formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
- width: 120,
- },
- {
- dataType: "action",
- label: "鎿嶄綔",
- align: "center",
- fixed: "right",
- width: 200,
- operation: [
- {
- name: "宸ヨ壓璺嚎",
- type: "text",
- clickFun: row => {
- showRouteItemModal(row);
- },
+const tableColumn = ref([
+ {
+ label: "鐢熶骇璁㈠崟鍙�",
+ prop: "npsNo",
+ width: "120px",
+ },
+ {
+ label: "閿�鍞悎鍚屽彿",
+ prop: "salesContractNo",
+ width: "150px",
+ },
+ {
+ label: "瀹㈡埛鍚嶇О",
+ prop: "customerName",
+ width: "200px",
+ },
+ {
+ label: "浜у搧鍚嶇О",
+ prop: "productCategory",
+ width: "120px",
+ },
+ {
+ label: "瑙勬牸鍨嬪彿",
+ prop: "specificationModel",
+ width: "120px",
+ },
+ {
+ label: "宸ヨ壓璺嚎缂栧彿",
+ prop: "processRouteCode",
+ width: "200px",
+ },
+ {
+ label: "闇�姹傛暟閲�",
+ prop: "quantity",
+ },
+ {
+ label: "瀹屾垚鏁伴噺",
+ prop: "completeQuantity",
+ },
+ {
+ dataType: "slot",
+ label: "瀹屾垚杩涘害",
+ prop: "completionStatus",
+ slot: "completionStatus",
+ width: 180,
+ },
+ {
+ label: "寮�濮嬫棩鏈�",
+ prop: "startTime",
+ formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
+ width: 120,
+ },
+ {
+ label: "缁撴潫鏃ユ湡",
+ prop: "endTime",
+ formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
+ width: 120,
+ },
+ {
+ label: "浜や粯鏃ユ湡",
+ prop: "deliveryDate",
+ formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
+ width: 120,
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 200,
+ operation: [
+ {
+ name: "宸ヨ壓璺嚎",
+ type: "text",
+ clickFun: row => {
+ showRouteItemModal(row);
},
- {
- name: "缁戝畾宸ヨ壓璺嚎",
- type: "text",
- showHide: row => !row.processRouteCode,
- clickFun: row => {
- openBindRouteDialog(row);
- },
+ },
+ {
+ name: "缁戝畾宸ヨ壓璺嚎",
+ type: "text",
+ showHide: row => !row.processRouteCode,
+ clickFun: row => {
+ openBindRouteDialog(row);
},
- {
- name: "浜у搧缁撴瀯",
- type: "text",
- clickFun: row => {
- showProductStructure(row);
- },
+ },
+ {
+ name: "浜у搧缁撴瀯",
+ type: "text",
+ clickFun: row => {
+ showProductStructure(row);
},
- ],
- },
- ]);
- const tableData = ref([]);
- const tableLoading = ref(false);
- const page = reactive({
- current: 1,
- size: 100,
- total: 0,
- });
- const selectedRows = ref([]);
+ },
+ ],
+ },
+]);
- const data = reactive({
- searchForm: {
- customerName: "",
- salesContractNo: "",
- projectName: "",
- productCategory: "",
- specificationModel: "",
- },
- });
- const { searchForm } = toRefs(data);
+const tableData = ref([]);
+const tableLoading = ref(false);
+const page = reactive({
+ current: 1,
+ size: 100,
+ total: 0,
+});
+const selectedRows = ref([]);
- const toProgressPercentage = val => {
- const n = Number(val);
- if (!Number.isFinite(n)) return 0;
- if (n <= 0) return 0;
- if (n >= 100) return 100;
- return Math.round(n);
- };
+const data = reactive({
+ searchForm: {
+ customerName: "",
+ salesContractNo: "",
+ projectName: "",
+ productCategory: "",
+ specificationModel: "",
+ },
+});
+const { searchForm } = toRefs(data);
- // 30/50/80/100 鍒嗘棰滆壊锛氱孩/姗�/钃�/缁�
- const progressColor = percentage => {
- const p = toProgressPercentage(percentage);
- if (p < 30) return "#f56c6c";
- if (p < 50) return "#e6a23c";
- if (p < 80) return "#409eff";
- return "#67c23a";
- };
+const toProgressPercentage = val => {
+ const n = Number(val);
+ if (!Number.isFinite(n) || n <= 0) return 0;
+ if (n >= 100) return 100;
+ return Math.round(n);
+};
- // 娣诲姞琛ㄨ绫诲悕鏂规硶
- const tableRowClassName = ({ row }) => {
- if (!row.deliveryDate) return '';
- if (row.isFh) return '';
+const progressColor = percentage => {
+ const p = toProgressPercentage(percentage);
+ if (p < 30) return "#f56c6c";
+ if (p < 50) return "#e6a23c";
+ if (p < 80) return "#409eff";
+ return "#67c23a";
+};
- const diff = row.deliveryDaysDiff;
- if (diff === 15) {
- return 'yellow';
- } else if (diff === 10) {
- return 'pink';
- } else if (diff === 2) {
- return 'purple';
- } else if (diff < 2) {
- return 'red';
- }
- };
+const tableRowClassName = ({ row }) => {
+ if (!row.deliveryDate || row.isFh) return "";
- // 缁戝畾宸ヨ壓璺嚎寮规
- const bindRouteDialogVisible = ref(false);
- const bindRouteLoading = ref(false);
- const bindRouteSaving = ref(false);
- const routeOptions = ref([]);
- const bindForm = reactive({
- orderId: null,
- routeId: null,
+ const diff = row.deliveryDaysDiff;
+ if (diff === 15) return "yellow";
+ if (diff === 10) return "pink";
+ if (diff === 2) return "purple";
+ if (diff < 2) return "red";
+ return "";
+};
+
+const bindRouteDialogVisible = ref(false);
+const bindRouteLoading = ref(false);
+const bindRouteSaving = ref(false);
+const routeOptions = ref([]);
+const bindProcessList = ref([]);
+const userOptions = ref([]);
+const userLoading = ref(false);
+const bindForm = reactive({
+ orderId: null,
+ productModelId: null,
+ routeId: null,
+ processUserList: [],
+});
+
+const resetBindProcessUsers = () => {
+ bindProcessList.value = [];
+ bindForm.processUserList = [];
+};
+
+const ensureUserOptions = () => {
+ if (userOptions.value.length || userLoading.value) return;
+
+ userLoading.value = true;
+ userListNoPageByTenantId()
+ .then(res => {
+ userOptions.value = res.data || [];
+ })
+ .finally(() => {
+ userLoading.value = false;
+ });
+};
+
+const createBindProcessUserList = list =>
+ list.map(item => ({
+ processId: item.id,
+ processName: item.name || item.processName || item.no || "",
+ userIds: [],
+ userNames: "",
+ }));
+
+const buildBindProcessRouteItems = () =>
+ bindProcessList.value.map((item, index) => {
+ const processUser = bindForm.processUserList[index] || {};
+ return {
+ productOrderId: bindForm.orderId,
+ productRouteId: bindForm.routeId,
+ processId: item.id,
+ productModelId: bindForm.productModelId,
+ dragSort: item.dragSort ?? index + 1,
+ isQuality: item.isQuality ?? false,
+ reportUserIds: Array.isArray(processUser.userIds) ? processUser.userIds.join(",") : "",
+ };
});
- const openBindRouteDialog = async row => {
- bindForm.orderId = row.id;
- bindForm.routeId = null;
- bindRouteDialogVisible.value = true;
- routeOptions.value = [];
- if (!row.productModelId) {
- proxy.$modal.msgWarning("褰撳墠璁㈠崟缂哄皯浜у搧鍨嬪彿锛屾棤娉曟煡璇㈠伐鑹鸿矾绾�");
- bindRouteDialogVisible.value = false;
+const fetchBindProcessList = async routeId => {
+ if (!routeId) {
+ resetBindProcessUsers();
+ return;
+ }
+
+ try {
+ const res = await processList({ routeId });
+ bindProcessList.value = res.data || [];
+ bindForm.processUserList = createBindProcessUserList(bindProcessList.value);
+ ensureUserOptions();
+ } catch (error) {
+ console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触", error);
+ proxy.$modal.msgError("鑾峰彇宸ュ簭鍒楄〃澶辫触");
+ resetBindProcessUsers();
+ }
+};
+
+const handleBindRouteChange = routeId => {
+ fetchBindProcessList(routeId);
+};
+
+const handleBindProcessUserChange = (index, userIds) => {
+ const selectedUsers = userOptions.value.filter(user => Array.isArray(userIds) && userIds.includes(user.userId));
+ bindForm.processUserList[index].userNames = selectedUsers.map(user => user.nickName).join(",");
+};
+
+const removeBindProcessItem = index => {
+ bindProcessList.value.splice(index, 1);
+ bindForm.processUserList.splice(index, 1);
+};
+
+const openBindRouteDialog = async row => {
+ bindForm.orderId = row.id;
+ bindForm.productModelId = row.productModelId ?? null;
+ bindForm.routeId = null;
+ bindForm.processUserList = [];
+ bindRouteDialogVisible.value = true;
+ routeOptions.value = [];
+ resetBindProcessUsers();
+
+ if (!row.productModelId) {
+ proxy.$modal.msgWarning("褰撳墠璁㈠崟缂哄皯浜у搧鍨嬪彿锛屾棤娉曟煡璇㈠伐鑹鸿矾绾�");
+ bindRouteDialogVisible.value = false;
+ return;
+ }
+
+ bindRouteLoading.value = true;
+ try {
+ const res = await listProcessRoute({ productModelId: row.productModelId });
+ routeOptions.value = res.data || [];
+ } catch (error) {
+ console.error("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触", error);
+ proxy.$modal.msgError("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触");
+ } finally {
+ bindRouteLoading.value = false;
+ }
+};
+
+const handleBindRouteConfirm = async () => {
+ if (!bindForm.routeId) {
+ proxy.$modal.msgWarning("璇烽�夋嫨宸ヨ壓璺嚎");
+ return;
+ }
+ if (!bindForm.processUserList.length) {
+ proxy.$modal.msgWarning("褰撳墠宸ヨ壓璺嚎涓嬫病鏈夊伐搴�");
+ return;
+ }
+ if (bindForm.processUserList.some(item => !Array.isArray(item.userIds) || item.userIds.length === 0)) {
+ proxy.$modal.msgWarning("璇蜂负姣忛亾宸ュ簭閫夋嫨鎶ュ伐浜哄憳");
+ return;
+ }
+
+ bindRouteSaving.value = true;
+ try {
+ await bindingRoute({
+ id: bindForm.orderId,
+ routeId: bindForm.routeId,
+ processRouteItems: buildBindProcessRouteItems(),
+ processUserList: bindForm.processUserList.map(item => ({
+ ...item,
+ userIds: item.userIds.join(","),
+ })),
+ });
+ proxy.$modal.msgSuccess("缁戝畾鎴愬姛");
+ bindRouteDialogVisible.value = false;
+ getList();
+ } catch (error) {
+ console.error("缁戝畾宸ヨ壓璺嚎澶辫触", error);
+ proxy.$modal.msgError("缁戝畾宸ヨ壓璺嚎澶辫触");
+ } finally {
+ bindRouteSaving.value = false;
+ }
+};
+
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+
+const pagination = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+
+const getList = () => {
+ tableLoading.value = true;
+ const params = { ...searchForm.value, ...page };
+ params.entryDate = undefined;
+
+ productOrderListPage(params)
+ .then(res => {
+ tableData.value = res.data.records;
+ page.total = res.data.total;
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+};
+
+const showRouteItemModal = async row => {
+ const orderId = row.id;
+ try {
+ const res = await getOrderProcessRouteMain(orderId);
+ const detail = res.data || {};
+ if (!detail.id) {
+ proxy.$modal.msgWarning("鏈壘鍒板叧鑱旂殑宸ヨ壓璺嚎");
return;
}
- bindRouteLoading.value = true;
- try {
- const res = await listProcessRoute({ productModelId: row.productModelId });
- routeOptions.value = res.data || [];
- } catch (e) {
- console.error("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触锛�", e);
- proxy.$modal.msgError("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触");
- } finally {
- bindRouteLoading.value = false;
- }
- };
- const handleBindRouteConfirm = async () => {
- if (!bindForm.routeId) {
- proxy.$modal.msgWarning("璇烽�夋嫨宸ヨ壓璺嚎");
- return;
- }
- bindRouteSaving.value = true;
- try {
- await bindingRoute({
- id: bindForm.orderId,
- routeId: bindForm.routeId,
- });
- proxy.$modal.msgSuccess("缁戝畾鎴愬姛");
- bindRouteDialogVisible.value = false;
- getList();
- } catch (e) {
- console.error("缁戝畾宸ヨ壓璺嚎澶辫触锛�", e);
- proxy.$modal.msgError("缁戝畾宸ヨ壓璺嚎澶辫触");
- } finally {
- bindRouteSaving.value = false;
- }
- };
-
- // 鏌ヨ鍒楄〃
- /** 鎼滅储鎸夐挳鎿嶄綔 */
- const handleQuery = () => {
- page.current = 1;
- getList();
- };
- const pagination = obj => {
- page.current = obj.page;
- page.size = obj.limit;
- getList();
- };
- const changeDaterange = value => {
- if (value) {
- searchForm.value.entryDateStart = value[0];
- searchForm.value.entryDateEnd = value[1];
- } else {
- searchForm.value.entryDateStart = undefined;
- searchForm.value.entryDateEnd = undefined;
- }
- handleQuery();
- };
- const getList = () => {
- tableLoading.value = true;
- // 鏋勯�犱竴涓柊鐨勫璞★紝涓嶅寘鍚玡ntryDate瀛楁
- const params = { ...searchForm.value, ...page };
- params.entryDate = undefined;
- productOrderListPage(params)
- .then(res => {
- tableLoading.value = false;
- tableData.value = res.data.records;
- page.total = res.data.total;
- })
- .catch(() => {
- tableLoading.value = false;
- });
- };
-
- const showRouteItemModal = async row => {
- const orderId = row.id;
- try {
- const res = await getOrderProcessRouteMain(orderId);
- const data = res.data || {};
- if (!data || !data.id) {
- proxy.$modal.msgWarning("鏈壘鍒板叧鑱旂殑宸ヨ壓璺嚎");
- return;
- }
- router.push({
- path: "/productionManagement/processRouteItem",
- query: {
- id: data.id,
- processRouteCode: data.processRouteCode || "",
- productName: data.productName || "",
- model: data.model || "",
- bomNo: data.bomNo || "",
- description: data.description || "",
- orderId,
- type: "order",
- },
- });
- } catch (e) {
- console.error("鑾峰彇宸ヨ壓璺嚎涓讳俊鎭け璐ワ細", e);
- proxy.$modal.msgError("鑾峰彇宸ヨ壓璺嚎淇℃伅澶辫触");
- }
- };
-
- const showProductStructure = row => {
router.push({
- path: "/productionManagement/productStructureDetail",
+ path: "/productionManagement/processRouteItem",
query: {
- id: row.id,
- bomNo: row.bomNo || "",
- productName: row.productCategory || "",
- productModelName: row.specificationModel || "",
- orderId: row.id,
+ id: detail.id,
+ processRouteCode: detail.processRouteCode || "",
+ productName: detail.productName || "",
+ model: detail.model || "",
+ bomNo: detail.bomNo || "",
+ description: detail.description || "",
+ orderId,
type: "order",
},
});
- };
+ } catch (error) {
+ console.error("鑾峰彇宸ヨ壓璺嚎淇℃伅澶辫触", error);
+ proxy.$modal.msgError("鑾峰彇宸ヨ壓璺嚎淇℃伅澶辫触");
+ }
+};
- // 琛ㄦ牸閫夋嫨鏁版嵁
- const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
- };
-
- const handleDelete = () => {
- let ids = [];
- if (selectedRows.value.length > 0) {
- ids = selectedRows.value.map((item) => item.id);
- } else {
- proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
- return;
- }
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- }).then(() => {
- delProductOrder(ids).then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
- getList();
- });
- }).catch(() => {
- proxy.$modal.msg("宸插彇娑�");
- });
- };
-
- // 瀵煎嚭
- const handleOut = () => {
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- proxy.download("/productOrder/export", {...searchForm.value}, "鐢熶骇璁㈠崟.xlsx");
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
- });
- };
-
- const handleConfirmRoute = () => {};
-
- onMounted(() => {
- getList();
+const showProductStructure = row => {
+ router.push({
+ path: "/productionManagement/productStructureDetail",
+ query: {
+ id: row.id,
+ bomNo: row.bomNo || "",
+ productName: row.productCategory || "",
+ productModelName: row.specificationModel || "",
+ orderId: row.id,
+ type: "order",
+ },
});
+};
+
+const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+};
+
+const handleDelete = () => {
+ if (!selectedRows.value.length) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+
+ const ids = selectedRows.value.map(item => item.id);
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => delProductOrder(ids))
+ .then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑堝垹闄�");
+ });
+};
+
+const handleOut = () => {
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/productOrder/export", { ...searchForm.value }, "鐢熶骇璁㈠崟.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑堝鍑�");
+ });
+};
+
+onMounted(() => {
+ getList();
+});
</script>
<style scoped lang="scss">
-.search_form{
+.search_form {
align-items: start;
}
-::v-deep .yellow {
- background-color: #FAF0DE;
+:deep(.yellow) {
+ background-color: #faf0de;
}
-::v-deep .pink {
- background-color: #FAE1DE;
+:deep(.pink) {
+ background-color: #fae1de;
}
-::v-deep .red {
+:deep(.red) {
background-color: #f80202;
}
-::v-deep .purple{
- background-color: #F4DEFA;
+:deep(.purple) {
+ background-color: #f4defa;
+}
+
+.process-user-list {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ padding: 14px;
+ border-radius: 12px;
+ background: #f7f9fc;
+ border: 1px solid #e8eef5;
+}
+
+.process-user-item {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr);
+ gap: 16px;
+ padding: 12px 14px;
+ border-radius: 10px;
+ background: #fff;
+ border: 1px solid #edf2f7;
+}
+
+.process-user-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.process-user-name {
+ color: #1f2d3d;
+ font-weight: 500;
+ line-height: 1.4;
+}
+
+.process-user-remove {
+ flex-shrink: 0;
+ padding: 0;
+}
+
+.process-user-select {
+ width: 100%;
+}
+
+@media (max-width: 768px) {
+ .process-user-item {
+ grid-template-columns: 1fr;
+ gap: 10px;
+ }
}
</style>
--
Gitblit v1.9.3