Browse Source

提交栈板

master
常熟吴彦祖 5 months ago
parent
commit
1ef2ffaa5b
  1. 12
      src/api/automatedWarehouse/palletPacking.js
  2. 404
      src/views/modules/automatedWarehouse/palletPacking.vue

12
src/api/automatedWarehouse/palletPacking.js

@ -41,3 +41,15 @@ export const callPalletToStation = data => createAPI(`/wcsIntegration/callPallet
// Call栈板到指定站点(包含组盘处理) - AI制作 // Call栈板到指定站点(包含组盘处理) - AI制作
export const callPalletToStationWithUpdateZuPan = data => createAPI(`/wcsIntegration/callPalletToStationWithUpdateZuPan`,'post',data) export const callPalletToStationWithUpdateZuPan = data => createAPI(`/wcsIntegration/callPalletToStationWithUpdateZuPan`,'post',data)
// 获取栈板详细信息(包含palletType和autoSort) - AI制作
export const getPalletInfo = data => createAPI(`/wcsIntegration/getPalletInfo`,'post',data)
// 获取托盘类型列表(根据palletFamily过滤) - AI制作
export const getPalletTypeList = data => createAPI(`/base/palletType/list`,'post',data)
// 获取托盘类型的区域列表 - AI制作
export const getPalletTypeAreas = data => createAPI(`/base/palletTypeArea/list`,'post',data)
// 更新栈板类型和自动分拣标志 - AI制作
export const updatePalletTypeAndAutoSort = data => createAPI(`/wcsIntegration/updatePalletTypeAndAutoSort`,'post',data)

404
src/views/modules/automatedWarehouse/palletPacking.vue

@ -36,45 +36,62 @@
</div> </div>
</div> </div>
<!-- 第二行筛选条件 (扫描栈板后显示) -->
<!-- 第二行栈板类型和自动分拣 (扫描栈板后显示) -->
<div v-if="palletScanned" class="input-group"> <div v-if="palletScanned" class="input-group">
<div style="display: flex; gap: 8px; align-items: end;"> <div style="display: flex; gap: 8px; align-items: end;">
<div style="flex: 1;">
<label class="input-label">位置</label>
<div style="flex: 0.65;">
<label class="input-label">栈板类型</label>
<el-select <el-select
v-model="selectedPosition"
placeholder="请选择位置"
v-model="currentPalletType"
placeholder="请选择栈板类型"
style="width: 100%;" style="width: 100%;"
@change="handlePositionChange"
:disabled="palletTypeDisabled"
@change="handlePalletTypeChange"
> >
<el-option label="ALL" value=""></el-option>
<el-option <el-option
v-for="position in positionOptions"
:key="position"
:label="position"
:value="position"
v-for="type in palletTypeOptions"
:key="type.palletType"
:label="`${type.palletType} - ${type.typeDesc}`"
:value="type.palletType"
/> />
</el-select> </el-select>
</div> </div>
<div style="flex: 1;">
<label class="input-label">层数</label>
<div style="flex: 0.35;">
<label class="input-label">自动分拣</label>
<el-select <el-select
v-model="selectedLayer"
placeholder="请选择层数"
v-model="currentAutoSort"
placeholder="请选择"
style="width: 100%;" style="width: 100%;"
:disabled="autoSortDisabled"
@change="handleAutoSortChange"
>
<el-option label="是" value="Y"></el-option>
<el-option label="否" value="N"></el-option>
</el-select>
</div>
</div>
</div>
<!-- 第三行筛选条件 (扫描栈板后显示) -->
<div v-if="palletScanned" class="input-group">
<label class="input-label">位置</label>
<div style="display: flex; gap: 8px;">
<el-select
v-model="selectedPosition"
placeholder="请选择位置"
style="flex: 0.75;"
> >
<el-option label="ALL" value=""></el-option> <el-option label="ALL" value=""></el-option>
<el-option <el-option
v-for="layer in layerOptions"
:key="layer"
:label="`第${layer}层`"
:value="layer"
v-for="position in positionOptions"
:key="position"
:label="position"
:value="position"
/> />
</el-select> </el-select>
</div>
<button <button
class="action-btn secondary" class="action-btn secondary"
style="margin: 0; white-space: nowrap;"
style="flex: 0.25; margin: 0; white-space: nowrap;"
@click="refreshTable" @click="refreshTable"
> >
刷新 刷新
@ -82,21 +99,21 @@
</div> </div>
</div> </div>
<!-- 扫进/扫出选择 (扫描栈板后显示) -->
<!-- 扫进/扫出选择 (扫描栈板后显示) -->
<div v-if="palletScanned" class="input-group"> <div v-if="palletScanned" class="input-group">
<div style="display: flex; gap: 8px; align-items: center; flex-wrap: nowrap;">
<div style="flex: 1; min-width: 0;">
<el-radio-group v-model="operationType" style="display: flex; flex-wrap: nowrap;">
<div style="display: flex; gap: 8px; align-items: center;">
<div style="flex: 0.75;">
<el-radio-group v-model="operationType" style="display: flex;">
<el-radio label="in" style="margin-right: 0px; white-space: nowrap;">扫进</el-radio> <el-radio label="in" style="margin-right: 0px; white-space: nowrap;">扫进</el-radio>
<el-radio label="out" style="white-space: nowrap;">扫出</el-radio> <el-radio label="out" style="white-space: nowrap;">扫出</el-radio>
</el-radio-group> </el-radio-group>
</div> </div>
<button <button
class="action-btn secondary" class="action-btn secondary"
style="margin: 0; white-space: nowrap; flex-shrink: 0;"
style="flex: 0.25; margin: 0; white-space: nowrap;"
@click="showScanModal" @click="showScanModal"
> >
扫描二维
扫描
</button> </button>
</div> </div>
</div> </div>
@ -104,16 +121,15 @@
<!-- 栈板明细表格 (扫描栈板后显示) --> <!-- 栈板明细表格 (扫描栈板后显示) -->
<div v-if="palletScanned" class="rma-list"> <div v-if="palletScanned" class="rma-list">
<div class="list-title-row">
<div class="list-title">栈板明细</div>
<button class="action-btn secondary" style="margin-left: 10px;" @click="handleTransportOrder">运输指令</button>
<div class="list-title-row" style="display: flex; gap: 8px; align-items: center; padding: 0;">
<div class="list-title" style="flex: 0.75; margin: 0;">栈板明细</div>
<button class="action-btn secondary" style="flex: 0.25; margin: 0;" @click="handleTransportOrder">运输指令</button>
</div> </div>
<div class="detail-table"> <div class="detail-table">
<div class="table-header"> <div class="table-header">
<div class="col-position">位置</div> <div class="col-position">位置</div>
<div class="col-layer">层数</div> <div class="col-layer">层数</div>
<div class="col-serial">标签号</div> <div class="col-serial">标签号</div>
<div class="col-part">物料编码</div>
</div> </div>
<div <div
v-for="(detail, index) in detailList" v-for="(detail, index) in detailList"
@ -124,7 +140,6 @@
<div class="col-position">{{ detail.position }}</div> <div class="col-position">{{ detail.position }}</div>
<div class="col-layer">{{ detail.layer }}</div> <div class="col-layer">{{ detail.layer }}</div>
<div class="col-serial">{{ detail.serialNo }}</div> <div class="col-serial">{{ detail.serialNo }}</div>
<div class="col-part">{{ detail.partNo }}</div>
</div> </div>
<!-- 暂无数据提示 --> <!-- 暂无数据提示 -->
<div v-if="detailList.length === 0" class="table-row empty-row"> <div v-if="detailList.length === 0" class="table-row empty-row">
@ -261,8 +276,10 @@
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<button class="action-btn primary" @click="confirmEditPosition">确定</button>
<button class="action-btn secondary" style="margin-left: 10px;" @click="closeEditPositionModal">取消</button>
<button class="action-btn primary" @click="confirmEditPosition" :disabled="editPositionLoading">
{{ editPositionLoading ? '处理中...' : '确定' }}
</button>
<button class="action-btn secondary" style="margin-left: 10px;" @click="closeEditPositionModal" :disabled="editPositionLoading">取消</button>
</div> </div>
</el-dialog> </el-dialog>
@ -300,27 +317,29 @@
/> />
</div> </div>
<!-- 目标站点选择 -->
<!-- 目标区域选择 -->
<div class="input-group"> <div class="input-group">
<label class="input-label">目标站点</label>
<label class="input-label">目标区域</label>
<el-select <el-select
v-model="selectedTargetStation"
placeholder="请选择目标站点"
v-model="selectedTargetArea"
placeholder="请选择目标区域"
style="width: 100%;" style="width: 100%;"
> >
<el-option <el-option
v-for="station in transportStationOptions"
:key="station.stationCode"
:label="`${station.stationCode} - ${station.stationName}`"
:value="station.stationCode"
v-for="area in transportAreaOptions"
:key="area.stationArea"
:label="area.stationArea"
:value="area.stationArea"
/> />
</el-select> </el-select>
</div> </div>
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<button class="action-btn primary" @click="confirmTransportTask">确定</button>
<button class="action-btn secondary" style="margin-left: 10px;" @click="closeTransportModal">取消</button>
<button class="action-btn primary" @click="confirmTransportTask" :disabled="transportTaskLoading">
{{ transportTaskLoading ? '创建中...' : '确定' }}
</button>
<button class="action-btn secondary" style="margin-left: 10px;" @click="closeTransportModal" :disabled="transportTaskLoading">取消</button>
</div> </div>
</el-dialog> </el-dialog>
@ -368,8 +387,10 @@
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<button class="action-btn primary" @click="confirmCallPallet">确定</button>
<button class="action-btn secondary" style="margin-left: 10px;" @click="closeCallPalletModal">取消</button>
<button class="action-btn primary" @click="confirmCallPallet" :disabled="callPalletLoading">
{{ callPalletLoading ? '调用中...' : '确定' }}
</button>
<button class="action-btn secondary" style="margin-left: 10px;" @click="closeCallPalletModal" :disabled="callPalletLoading">取消</button>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
@ -388,7 +409,11 @@ import {
updatePalletDetailPosition, updatePalletDetailPosition,
getAgvStations, getAgvStations,
callPalletToStation, callPalletToStation,
callPalletToStationWithUpdateZuPan
callPalletToStationWithUpdateZuPan,
getPalletInfo,
getPalletTypeList,
getPalletTypeAreas,
updatePalletTypeAndAutoSort
} from '../../../api/automatedWarehouse/palletPacking' } from '../../../api/automatedWarehouse/palletPacking'
export default { export default {
@ -399,6 +424,16 @@ export default {
palletScanned: false, palletScanned: false,
operationType: 'in', // 'in' 'out' operationType: 'in', // 'in' 'out'
//
currentPalletFamily: '', //
currentPalletType: '', //
currentAutoSort: '', // Y/N
palletTypeOptions: [], //
palletTypeDisabled: false, //
autoSortDisabled: false, //
currentWcsAutoSort: '', // wcsAutoSort
currentMaxLayer: 0, // 0=
// //
selectedPosition: '', selectedPosition: '',
selectedLayer: '', selectedLayer: '',
@ -418,9 +453,9 @@ export default {
// //
transportModalVisible: false, transportModalVisible: false,
transportStationOptions: [],
transportAreaOptions: [], //
currentPalletStation: '', currentPalletStation: '',
selectedTargetStation: '',
selectedTargetArea: '', //
// Call // Call
callPalletModalVisible: false, callPalletModalVisible: false,
@ -437,6 +472,11 @@ export default {
editLayerOptions: [], editLayerOptions: [],
editOriginalPosition: '', editOriginalPosition: '',
editOriginalLayer: '', editOriginalLayer: '',
// loading
editPositionLoading: false, //
transportTaskLoading: false, //
callPalletLoading: false, // Call
}; };
}, },
methods: { methods: {
@ -458,6 +498,10 @@ export default {
if (data.code === 0) { if (data.code === 0) {
this.palletScanned = true; this.palletScanned = true;
this.positionOptions = data.positions || []; this.positionOptions = data.positions || [];
// palletTypeautoSort
this.loadPalletInfo();
this.refreshTable(); this.refreshTable();
} else { } else {
this.$message.error(data.msg || '栈板不存在'); this.$message.error(data.msg || '栈板不存在');
@ -468,6 +512,137 @@ export default {
}); });
}, },
//
loadPalletInfo() {
getPalletInfo({
site: this.site,
palletId: this.palletCode
}).then(({ data }) => {
if (data.code === 0) {
const palletInfo = data.row || {};
this.currentPalletFamily = palletInfo.palletFamily || '';
this.currentPalletType = palletInfo.palletType || '';
this.currentAutoSort = palletInfo.autoSort || 'N';
// palletFamily
this.loadPalletTypeList();
} else {
this.$message.error(data.msg || '获取栈板信息失败');
}
}).catch(error => {
console.error('获取栈板信息失败:', error);
this.$message.error('获取栈板信息失败');
});
},
//
loadPalletTypeList() {
getPalletTypeList({
site: this.site,
palletFamily: this.currentPalletFamily,
active: 'Y'
}).then(({ data }) => {
if (data.code === 0) {
this.palletTypeOptions = data.rows || [];
// wcsAutoSortmaxLayer
const currentType = this.palletTypeOptions.find(t => t.palletType === this.currentPalletType);
if (currentType) {
this.currentWcsAutoSort = currentType.wcsAutoSort || 'N';
this.currentMaxLayer = currentType.maxLayer || 0;
this.updateAutoSortControl();
//
this.loadPalletTypeAreas();
}
} else {
this.palletTypeOptions = [];
}
}).catch(error => {
console.error('获取托盘类型列表失败:', error);
this.palletTypeOptions = [];
});
},
//
loadPalletTypeAreas() {
getPalletTypeAreas({
site: this.site,
palletType: this.currentPalletType
}).then(({ data }) => {
if (data.code === 0) {
// pallet_type_areaposition
const areas = data.rows || [];
this.positionOptions = areas.map(area => area.position);
} else {
this.positionOptions = [];
}
}).catch(error => {
console.error('获取托盘区域列表失败:', error);
this.positionOptions = [];
});
},
//
handlePalletTypeChange() {
//
const selectedType = this.palletTypeOptions.find(t => t.palletType === this.currentPalletType);
if (selectedType) {
this.currentWcsAutoSort = selectedType.wcsAutoSort || 'N';
this.currentMaxLayer = selectedType.maxLayer || 0;
//
this.currentAutoSort = this.currentWcsAutoSort;
//
this.updateAutoSortControl();
// pallet_type_area
this.loadPalletTypeAreas();
//
this.savePalletTypeAndAutoSort();
}
},
//
updateAutoSortControl() {
if (this.currentWcsAutoSort === 'N') {
// N
this.currentAutoSort = 'N';
this.autoSortDisabled = true;
} else {
// YN
this.autoSortDisabled = false;
}
},
//
handleAutoSortChange() {
//
this.savePalletTypeAndAutoSort();
},
//
savePalletTypeAndAutoSort() {
updatePalletTypeAndAutoSort({
site: this.site,
palletId: this.palletCode,
palletType: this.currentPalletType,
autoSort: this.currentAutoSort
}).then(({ data }) => {
if (data.code === 0) {
this.$message.success('更新成功');
} else {
this.$message.error(data.msg || '更新失败');
}
}).catch(error => {
console.error('更新失败:', error);
this.$message.error('更新失败');
});
},
// Call - // Call -
handleCallPallet() { handleCallPallet() {
this.callPalletModalVisible = true; this.callPalletModalVisible = true;
@ -530,12 +705,27 @@ export default {
}).then(({ data }) => { }).then(({ data }) => {
if (data.code === 0) { if (data.code === 0) {
this.detailList = data.details || []; this.detailList = data.details || [];
//
const hasDetails = this.detailList.length > 0;
this.palletTypeDisabled = hasDetails;
// wcsAutoSort
if (hasDetails) {
this.autoSortDisabled = true;
} else {
this.updateAutoSortControl();
}
} else { } else {
this.detailList = []; this.detailList = [];
this.palletTypeDisabled = false;
this.updateAutoSortControl();
} }
}).catch(error => { }).catch(error => {
console.error('获取栈板明细失败:', error); console.error('获取栈板明细失败:', error);
this.detailList = []; this.detailList = [];
this.palletTypeDisabled = false;
this.updateAutoSortControl();
}); });
}, },
@ -574,17 +764,29 @@ export default {
// //
handleScanPositionChange() { handleScanPositionChange() {
if (this.scanPosition) { if (this.scanPosition) {
// maxLayer=01
if (this.currentMaxLayer === 0) {
this.scanLayerOptions = [1];
this.scanLayer = 1; // 1
this.moveFocusToScanInput();
return;
}
// maxLayer>0maxLayer
getLayersByPosition({ getLayersByPosition({
site: this.site, site: this.site,
palletId: this.palletCode, palletId: this.palletCode,
position: this.scanPosition position: this.scanPosition
}).then(({ data }) => { }).then(({ data }) => {
if (data.code === 0) { if (data.code === 0) {
const maxLayer = data.layers && data.layers.length > 0
const existingMaxLayer = data.layers && data.layers.length > 0
? Math.max(...data.layers) ? Math.max(...data.layers)
: 0; : 0;
this.scanLayerOptions = Array.from({ length: maxLayer+1 }, (_, i) => i + 1)
// maxLayer+1maxLayer
const layerCount = Math.min(existingMaxLayer + 1, this.currentMaxLayer);
this.scanLayerOptions = Array.from({ length: layerCount }, (_, i) => i + 1);
} }
}).catch(error => { }).catch(error => {
console.error('获取层数失败:', error); console.error('获取层数失败:', error);
@ -673,6 +875,14 @@ export default {
// //
handleEditPositionChange() { handleEditPositionChange() {
if (this.editPosition) { if (this.editPosition) {
// maxLayer=01
if (this.currentMaxLayer === 0) {
this.editLayerOptions = [1];
this.editLayer = 1; // 1
return;
}
// maxLayer>0maxLayer
getLayersForEdit({ getLayersForEdit({
site: this.site, site: this.site,
palletId: this.palletCode, palletId: this.palletCode,
@ -680,7 +890,15 @@ export default {
excludeSerialNo: this.editSerialNo excludeSerialNo: this.editSerialNo
}).then(({ data }) => { }).then(({ data }) => {
if (data.code === 0) { if (data.code === 0) {
this.editLayerOptions = data.layers || [];
let layerOptions = data.layers || [];
// maxLayer
if (this.currentMaxLayer > 0) {
// maxLayer
layerOptions = layerOptions.filter(layer => layer <= this.currentMaxLayer);
}
this.editLayerOptions = layerOptions;
// //
// if (!this.editLayerOptions.includes(this.editLayer)) { // if (!this.editLayerOptions.includes(this.editLayer)) {
// this.editLayer = ''; // this.editLayer = '';
@ -713,6 +931,9 @@ export default {
return; return;
} }
// loading
this.editPositionLoading = true;
updatePalletDetailPosition({ updatePalletDetailPosition({
site: this.site, site: this.site,
palletId: this.palletCode, palletId: this.palletCode,
@ -730,6 +951,9 @@ export default {
}).catch(error => { }).catch(error => {
console.error('位置修改失败:', error); console.error('位置修改失败:', error);
this.$message.error('位置修改失败'); this.$message.error('位置修改失败');
}).finally(() => {
//
this.editPositionLoading = false;
}); });
}, },
@ -765,14 +989,38 @@ export default {
} }
this.transportModalVisible = true; this.transportModalVisible = true;
this.selectedTargetStation = '';
this.transportStationOptions = [];
this.selectedTargetArea = '';
this.transportAreaOptions = [];
// AGVstatusDb = 0
// AGVstatusDb = 0
getAgvStations({}).then(({ data }) => { getAgvStations({}).then(({ data }) => {
if (data.code === 0) { if (data.code === 0) {
// statusDb0 // statusDb0
this.transportStationOptions = (data.stations || []).filter(station => station.statusDb === '0');
const freeStations = (data.stations || []).filter(station => station.statusDb === 0);
console.log('空闲站点数量:', freeStations.length);
// station_areastationArea
const areaMap = new Map();
freeStations.forEach(station => {
if (station.stationArea && station.stationArea.trim() !== '') {
if (!areaMap.has(station.stationArea)) {
areaMap.set(station.stationArea, {
stationArea: station.stationArea,
areaType: station.areaType
});
}
}
});
this.transportAreaOptions = Array.from(areaMap.values());
console.log('可用目标区域数量:', this.transportAreaOptions.length);
//
if (this.transportAreaOptions.length === 0) {
this.$message.warning('当前没有可用的目标区域,所有站点可能都已被占用');
}
} else { } else {
this.$message.error(data.msg || '获取站点列表失败'); this.$message.error(data.msg || '获取站点列表失败');
} }
@ -795,22 +1043,19 @@ export default {
this.$message.error('无法获取当前栈板位置'); this.$message.error('无法获取当前栈板位置');
return; return;
} }
if (!this.selectedTargetStation) {
this.$message.error('请选择目标站点');
if (!this.selectedTargetArea) {
this.$message.error('请选择目标区域');
return; return;
} }
//
if (this.currentPalletStation === this.selectedTargetStation) {
this.$message.error('起始站点和目标站点不能相同');
return;
}
// loading
this.transportTaskLoading = true;
//
//
callPalletToStationWithUpdateZuPan({ callPalletToStationWithUpdateZuPan({
site: this.site, site: this.site,
startStation: this.currentPalletStation, startStation: this.currentPalletStation,
targetStation: this.selectedTargetStation
targetArea: this.selectedTargetArea
}).then(({ data }) => { }).then(({ data }) => {
if (data.code === 0) { if (data.code === 0) {
this.$message.success('栈板运输任务创建成功'); this.$message.success('栈板运输任务创建成功');
@ -823,6 +1068,9 @@ export default {
}).catch(error => { }).catch(error => {
console.error('创建运输任务失败:', error); console.error('创建运输任务失败:', error);
this.$message.error('创建运输任务失败'); this.$message.error('创建运输任务失败');
}).finally(() => {
//
this.transportTaskLoading = false;
}); });
}, },
@ -830,8 +1078,8 @@ export default {
closeTransportModal() { closeTransportModal() {
this.transportModalVisible = false; this.transportModalVisible = false;
this.currentPalletStation = ''; this.currentPalletStation = '';
this.selectedTargetStation = '';
this.transportStationOptions = [];
this.selectedTargetArea = '';
this.transportAreaOptions = [];
}, },
// Call // Call
@ -851,6 +1099,9 @@ export default {
return; return;
} }
// loading
this.callPalletLoading = true;
callPalletToStation({ callPalletToStation({
site: this.site, site: this.site,
startStation: this.selectedCallStartStation, startStation: this.selectedCallStartStation,
@ -865,6 +1116,9 @@ export default {
}).catch(error => { }).catch(error => {
console.error('调用空托盘失败:', error); console.error('调用空托盘失败:', error);
this.$message.error('异常:'+error); this.$message.error('异常:'+error);
}).finally(() => {
//
this.callPalletLoading = false;
}); });
}, },
@ -930,13 +1184,9 @@ export default {
} }
.col-serial { .col-serial {
flex: 2;
text-align: center;
}
.col-part {
flex: 2;
flex: 4;
text-align: center; text-align: center;
word-break: break-all;
} }
/* 空数据提示 */ /* 空数据提示 */
@ -1006,4 +1256,12 @@ export default {
color: #999; color: #999;
width: 100%; width: 100%;
} }
/* 按钮禁用状态样式 */
.action-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
background-color: #ccc !important;
border-color: #ccc !important;
}
</style> </style>
Loading…
Cancel
Save