You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1303 lines
43 KiB
1303 lines
43 KiB
<template>
|
|
<div class="pda-container">
|
|
<div class="header-bar">
|
|
<div class="header-left" :class="{ 'header-action-disabled': interfaceBusy }" @click="handleBack">
|
|
<i class="el-icon-arrow-left"></i>
|
|
<span>组盘入库</span>
|
|
</div>
|
|
<div class="header-right" :class="{ 'header-action-disabled': interfaceBusy }" @click="goHome">首页</div>
|
|
</div>
|
|
|
|
<div class="table-body" style="max-height: 500px; overflow-y: auto;">
|
|
<div class="main-content form-section">
|
|
<!-- 第一行:栈板扫描 - rqrq -->
|
|
<div class="input-group">
|
|
<label class="input-label">栈板编码</label>
|
|
<div style="display: flex; gap: 8px;">
|
|
<el-input
|
|
v-model="palletCode"
|
|
placeholder="请扫描栈板编码"
|
|
class="form-input"
|
|
style="flex: 0.75;"
|
|
clearable
|
|
inputmode="none"
|
|
autocomplete="off"
|
|
autocorrect="off"
|
|
spellcheck="false"
|
|
@keyup.enter.native="handlePalletScan"
|
|
ref="palletInput"
|
|
/>
|
|
<button
|
|
class="action-btn secondary"
|
|
style="flex: 0.25; margin: 0;"
|
|
@click="handleCallPallet"
|
|
:disabled="interfaceBusy"
|
|
>
|
|
Call栈板
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 第二行:栈板类型和自动分拣 (扫描栈板后显示) - rqrq -->
|
|
<div v-if="palletScanned" class="input-group">
|
|
<div style="display: flex; gap: 8px; align-items: end;">
|
|
<div style="flex: 0.65;">
|
|
<label class="input-label">栈板类型</label>
|
|
<el-select
|
|
v-model="currentPalletType"
|
|
placeholder="请选择栈板类型"
|
|
style="width: 100%;"
|
|
:disabled="palletTypeDisabled"
|
|
@change="handlePalletTypeChange"
|
|
>
|
|
<el-option
|
|
v-for="type in palletTypeOptions"
|
|
:key="type.palletType"
|
|
:label="`${type.palletType} - ${type.typeDesc}`"
|
|
:value="type.palletType"
|
|
/>
|
|
</el-select>
|
|
</div>
|
|
<div style="flex: 0.35;">
|
|
<label class="input-label">自动分拣</label>
|
|
<el-select
|
|
v-model="currentAutoSort"
|
|
placeholder="请选择"
|
|
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>
|
|
|
|
<!-- 第2.5行:是否强制整托出库 (扫描栈板后显示) - rqrq -->
|
|
<div v-if="palletScanned" class="input-group">
|
|
<el-checkbox
|
|
v-model="forceFullPalletOut"
|
|
:disabled="palletTypeDisabled"
|
|
@change="handleForceFullPalletOutChange"
|
|
style="width: 100%;"
|
|
>
|
|
是否强制整托出库
|
|
</el-checkbox>
|
|
</div>
|
|
|
|
<!-- 第三行:当前栈板站点显示 (扫描栈板后显示) - rqrq -->
|
|
<div v-if="palletScanned" class="input-group">
|
|
<label class="input-label">当前站点</label>
|
|
<div style="display: flex; gap: 8px;">
|
|
<el-input
|
|
v-model="currentPalletStation"
|
|
placeholder="栈板当前站点"
|
|
class="form-input"
|
|
readonly
|
|
style="flex: 0.75;"
|
|
/>
|
|
<button
|
|
class="action-btn secondary"
|
|
style="flex: 0.25; margin: 0; white-space: nowrap;"
|
|
@click="refreshTable"
|
|
:disabled="interfaceBusy"
|
|
>
|
|
刷新
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 第四行:扫进/扫出选择 (扫描栈板后显示) - rqrq -->
|
|
<div v-if="palletScanned" class="input-group">
|
|
<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="out" style="white-space: nowrap;">扫出</el-radio>
|
|
</el-radio-group>
|
|
</div>
|
|
<button
|
|
class="action-btn secondary"
|
|
style="flex: 0.25; margin: 0; white-space: nowrap;"
|
|
:disabled="interfaceBusy || currentPalletType === ''"
|
|
@click="showScanModal"
|
|
>
|
|
扫描条码
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 栈板明细表格 (扫描栈板后显示) - rqrq -->
|
|
<div v-if="palletScanned" class="rma-list">
|
|
<div class="list-title-row" style="display: flex; gap: 8px; align-items: center; padding: 0;">
|
|
<div class="list-title" style="flex: 0.4; margin: 0;">
|
|
<button class="action-btn primary" style="margin: 0;" @click="showDetailModal">
|
|
{{ '明细' }}
|
|
</button>
|
|
</div>
|
|
<button class="action-btn warning" style="flex: 0.3; margin: 0;" @click="showScanOutModal" :disabled="interfaceBusy || scanOutList.length === 0">
|
|
扫出({{ scanOutList.length }})
|
|
</button>
|
|
<button class="action-btn secondary" style="flex: 0.3; margin: 0;" @click="openTransportModal" :disabled="interfaceBusy || !currentPalletStation">{{ transportLoading ? '处理中...' : '运输入库' }}</button>
|
|
</div>
|
|
<div class="list-title" style="flex: 0.5; margin: 0;">
|
|
<label style="margin-left: 5px;">条码数:{{ detailList.length }}</label>
|
|
</div>
|
|
<div class="detail-table">
|
|
<div class="table-header">
|
|
<div class="col-position">位置</div>
|
|
<div class="col-layer">层数</div>
|
|
<div class="col-serial">标签号</div>
|
|
</div>
|
|
<div
|
|
v-for="(detail, index) in detailList"
|
|
:key="index"
|
|
class="table-row"
|
|
>
|
|
<div class="col-position">{{ detail.position }}</div>
|
|
<div class="col-layer">{{ detail.layer }}</div>
|
|
<div class="col-serial">{{ detail.serialNo }}</div>
|
|
</div>
|
|
<div v-if="detailList.length === 0" class="table-row empty-row">
|
|
<div class="empty-hint">暂无栈板明细数据</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 浏览明细弹窗 - rqrq -->
|
|
<el-dialog
|
|
:title="'已扫描数量:' + detailList.length"
|
|
:visible.sync="detailModalVisible"
|
|
width="90%"
|
|
:close-on-click-modal="false"
|
|
:show-close="false"
|
|
:append-to-body="true"
|
|
>
|
|
<div class="table-body" style="max-height: 240px; overflow-y: auto;">
|
|
<div class="detail-table">
|
|
<div class="table-header">
|
|
<div class="col-position">位置</div>
|
|
<div class="col-layer">层数</div>
|
|
<div class="col-serial">标签号</div>
|
|
</div>
|
|
<div v-for="(detail, index) in detailList" :key="index" class="table-row">
|
|
<div class="col-position">{{ detail.position }}</div>
|
|
<div class="col-layer">{{ detail.layer }}</div>
|
|
<div class="col-serial">{{ detail.serialNo }}</div>
|
|
</div>
|
|
<div v-if="detailList.length === 0" class="table-row empty-row">
|
|
<div class="empty-hint">暂无栈板明细数据</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div slot="footer" class="dialog-footer">
|
|
<button class="action-btn secondary" style="margin-left: 10px;" @click="detailModalVisible = false" :disabled="interfaceBusy">取消</button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<!-- 查看扫出弹窗 - rqrq -->
|
|
<el-dialog
|
|
:title="'扫出记录 (共' + scanOutList.length + '条)'"
|
|
:visible.sync="scanOutModalVisible"
|
|
width="90%"
|
|
:close-on-click-modal="false"
|
|
:show-close="false"
|
|
:append-to-body="true"
|
|
>
|
|
<div class="table-body" style="max-height: 300px; overflow-y: auto;">
|
|
<div class="detail-table">
|
|
<div class="table-header">
|
|
<div class="col-position">位置</div>
|
|
<div class="col-layer">层数</div>
|
|
<div class="col-serial">标签号</div>
|
|
</div>
|
|
<div
|
|
v-for="(detail, index) in scanOutList"
|
|
:key="index"
|
|
class="table-row"
|
|
>
|
|
<div class="col-position">{{ detail.position }}</div>
|
|
<div class="col-layer">{{ detail.layer }}</div>
|
|
<div class="col-serial">{{ detail.serialNo }}</div>
|
|
</div>
|
|
<div v-if="scanOutList.length === 0" class="table-row empty-row">
|
|
<div class="empty-hint">暂无扫出记录</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div slot="footer" class="dialog-footer">
|
|
<button class="action-btn secondary" @click="scanOutModalVisible = false" :disabled="interfaceBusy">关闭</button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<!-- 扫码模态框:网格层数与原PDA页面保持一致 - rqrq -->
|
|
<el-dialog
|
|
title="扫描标签"
|
|
:visible.sync="scanModalVisible"
|
|
width="90%"
|
|
:close-on-click-modal="false"
|
|
:show-close="false"
|
|
:modal="true"
|
|
:modal-append-to-body="true"
|
|
:append-to-body="true"
|
|
>
|
|
<div class="scan-modal-content">
|
|
<!-- 扫进时显示层数选择和位置网格 - rqrq -->
|
|
<div v-if="operationType === 'in'" class="modal-form">
|
|
<!-- 层数选择(混装托盘不显示) - rqrq -->
|
|
<div v-if="!currentMixedMode && scanLayerOptions.length > 0" class="input-group">
|
|
<label class="input-label">层数(当前选择:第{{ scanLayer }}层)</label>
|
|
<div class="layer-grid">
|
|
<div
|
|
v-for="layer in scanLayerOptions"
|
|
:key="layer"
|
|
class="layer-item"
|
|
:class="{
|
|
'layer-selected': scanLayer === layer
|
|
}"
|
|
@click="handleLayerClick(layer)"
|
|
>
|
|
{{ layer }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 位置网格选择器 - rqrq -->
|
|
<div class="input-group">
|
|
<label class="input-label">
|
|
位置{{ currentSelectedPosition ? `(当前选择:${currentSelectedPosition})` : '' }}
|
|
<span v-if="positionGridLoading" style="color: #909399; font-size: 12px;">(加载中...)</span>
|
|
</label>
|
|
<div class="position-grid" :class="[
|
|
{'position-grid-loading': positionGridLoading},
|
|
getPositionGridClass
|
|
]">
|
|
<div
|
|
v-for="(item, index) in displayPositionGrid"
|
|
:key="index"
|
|
class="position-item"
|
|
:class="{
|
|
'position-disabled': positionGridLoading || !availablePositions.includes(item.position),
|
|
'position-selected': currentSelectedPosition === item.position,
|
|
'position-loading': positionGridLoading,
|
|
'position-placeholder': item.isPlaceholder
|
|
}"
|
|
@click="handlePositionClick(item.position)"
|
|
>
|
|
<template v-if="!item.isPlaceholder">{{ item.position }}({{ item.count }})</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 标签扫描 - rqrq -->
|
|
<div class="input-group">
|
|
<label class="input-label">标签二维码</label>
|
|
<el-input
|
|
ref="scanInput"
|
|
v-model="scanCode"
|
|
placeholder="请扫描标签二维码"
|
|
class="form-input"
|
|
clearable
|
|
:disabled="operationType === 'in' && !currentSelectedPosition"
|
|
@keyup.enter.native="handleLabelScan"
|
|
inputmode="none"
|
|
autocomplete="off"
|
|
autocorrect="off"
|
|
spellcheck="false"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div slot="footer" class="dialog-footer">
|
|
<button class="action-btn secondary" style="margin-left: 10px;" @click="closeScanModal" :disabled="interfaceBusy">取消</button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<!-- Call空托盘弹窗 - rqrq -->
|
|
<el-dialog
|
|
title="调用空托盘"
|
|
:visible.sync="callEmptyModalVisible"
|
|
width="90%"
|
|
:close-on-click-modal="false"
|
|
:show-close="false"
|
|
:append-to-body="true"
|
|
>
|
|
<div class="input-group">
|
|
<label class="input-label">区域</label>
|
|
<el-select v-model="callEmptyForm.areaId" placeholder="请选择区域" style="width: 100%;" @change="handleAreaChangeForCall">
|
|
<el-option v-for="a in areaOptions" :key="a.areaId" :label="a.areaDesc" :value="a.areaId" />
|
|
</el-select>
|
|
</div>
|
|
<div class="input-group">
|
|
<label class="input-label">站点</label>
|
|
<el-select v-model="callEmptyForm.stationCode" placeholder="请选择站点" style="width: 100%;" :disabled="!callEmptyForm.areaId">
|
|
<el-option v-for="s in stationOptions" :key="s.stationCode" :label="`${s.stationCode} (${s.status || '-'})`" :value="s.stationCode" />
|
|
</el-select>
|
|
</div>
|
|
<div class="input-group">
|
|
<label class="input-label">栈板类型</label>
|
|
<el-select v-model="callEmptyForm.palletType" placeholder="请选择栈板类型" style="width: 100%;">
|
|
<el-option v-for="t in callEmptyPalletTypeOptions" :key="t.palletType" :label="`${t.palletType} - ${t.typeDesc}`" :value="t.palletType" />
|
|
</el-select>
|
|
</div>
|
|
<div slot="footer" class="dialog-footer">
|
|
<button class="action-btn primary" @click="submitCallEmpty" :disabled="interfaceBusy || callEmptyLoading">{{ callEmptyLoading ? '处理中...' : '确认' }}</button>
|
|
<button class="action-btn secondary" style="margin-left: 8px;" @click="callEmptyModalVisible = false" :disabled="interfaceBusy || callEmptyLoading">取消</button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
checkPalletExists,
|
|
getPalletDetails,
|
|
savePalletDetail,
|
|
deletePalletDetail,
|
|
getPalletInfo,
|
|
getPalletTypeList,
|
|
updatePalletTypeAndAutoSort,
|
|
getAvailablePositionsForLayer,
|
|
callEmptyPalletToStation,
|
|
callPalletToStationWithUpdateZuPan
|
|
} from '../../../api/haianAutoWarehouse/palletPacking'
|
|
import { getAreaOptionsForChange, getStationsByArea } from '../../../api/haianAutoWarehouse/palletChangeStation'
|
|
|
|
export default {
|
|
name: 'haianPalletPacking',
|
|
data() {
|
|
return {
|
|
site: this.$store.state.user.site,
|
|
palletCode: '',
|
|
palletScanned: false,
|
|
operationType: 'in',
|
|
currentPalletFamily: '',
|
|
currentPalletType: '',
|
|
currentAutoSort: 'N',
|
|
currentPalletStation: '',
|
|
forceFullPalletOut: false,
|
|
palletTypeDisabled: false,
|
|
autoSortDisabled: false,
|
|
currentWcsAutoSort: '',
|
|
currentMaxLayer: 0,
|
|
positionOptions: [],
|
|
palletTypeOptions: [],
|
|
callEmptyPalletTypeOptions: [],
|
|
detailList: [],
|
|
scanOutList: [],
|
|
detailModalVisible: false,
|
|
scanOutModalVisible: false,
|
|
palletInfo: {
|
|
palletType: '',
|
|
autoSort: 'N',
|
|
locationCode: ''
|
|
},
|
|
// 扫码模态框字段:与原PDA页面保持一致,按网格位置 + 层数扫入 - rqrq
|
|
scanModalVisible: false,
|
|
scanCode: '',
|
|
scanPosition: '',
|
|
scanLayer: 1,
|
|
scanLayerOptions: [],
|
|
needRefreshOnClose: false,
|
|
positionGrid: [],
|
|
availablePositions: [],
|
|
currentSelectedPosition: '',
|
|
currentMixedMode: false,
|
|
positionGridLoading: false,
|
|
areaOptions: [],
|
|
stationOptions: [],
|
|
callEmptyModalVisible: false,
|
|
callEmptyLoading: false,
|
|
callEmptyForm: {
|
|
areaId: '',
|
|
stationCode: '',
|
|
palletType: ''
|
|
},
|
|
transportLoading: false,
|
|
// 海安页面接口防连点锁:按钮触发接口期间统一置为true,返回后解锁 - rqrq
|
|
palletScanLoading: false,
|
|
palletConfigLoading: false,
|
|
scanRequestLoading: false
|
|
}
|
|
},
|
|
computed: {
|
|
// 根据位置数量决定网格布局,确保与原PDA扫码弹窗一致 - rqrq
|
|
getPositionGridClass() {
|
|
const len = this.positionGrid.length
|
|
if (len === 1) return 'position-grid-1'
|
|
if (len === 2) return 'position-grid-4'
|
|
if (len === 4) return 'position-grid-4'
|
|
return 'position-grid-9'
|
|
},
|
|
// 2宫格场景采用对角展示,其余按原位置顺序展示 - rqrq
|
|
displayPositionGrid() {
|
|
const len = this.positionGrid.length
|
|
if (len === 2) {
|
|
const pos1 = this.positionGrid.find((p) => p.position === '1') || { position: '1', count: 0 }
|
|
const pos2 = this.positionGrid.find((p) => p.position === '2') || { position: '2', count: 0 }
|
|
return [
|
|
{ ...pos1, isPlaceholder: false },
|
|
{ position: '', count: 0, isPlaceholder: true },
|
|
{ position: '', count: 0, isPlaceholder: true },
|
|
{ ...pos2, isPlaceholder: false }
|
|
]
|
|
}
|
|
return this.positionGrid.map((p) => ({ ...p, isPlaceholder: false }))
|
|
},
|
|
// 统一接口忙碌状态:用于禁用按钮、回退和首页,避免请求未返回时重复触发 - rqrq
|
|
interfaceBusy() {
|
|
return this.palletScanLoading || this.palletConfigLoading || this.scanRequestLoading || this.callEmptyLoading || this.transportLoading
|
|
}
|
|
},
|
|
methods: {
|
|
// 统一校验site是否存在,海安接口必须显式传site - rqrq
|
|
ensureSite() {
|
|
if (!this.site) {
|
|
this.$alert('当前用户缺少site,无法执行海安组盘业务', '提示', { confirmButtonText: '确定' })
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
handleBack() {
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
// 返回首页前先清空海安组盘页面状态,避免keep-alive导致二次进入残留旧数据 - rqrq
|
|
this.resetPage(false)
|
|
this.$router.back()
|
|
},
|
|
// 接口处理中禁用首页跳转,避免用户误离开导致重复提交和状态错乱 - rqrq
|
|
goHome() {
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
this.$router.push({ path: '/' })
|
|
},
|
|
// 海安组盘页统一重置方法:退出和重进都走同一套清理逻辑,保证状态一致性 - rqrq
|
|
resetPage(needFocus = true) {
|
|
this.site = this.$store.state.user.site
|
|
this.palletCode = ''
|
|
this.palletScanned = false
|
|
this.operationType = 'in'
|
|
this.currentPalletFamily = ''
|
|
this.currentPalletType = ''
|
|
this.currentAutoSort = 'N'
|
|
this.currentPalletStation = ''
|
|
this.forceFullPalletOut = false
|
|
this.palletTypeDisabled = false
|
|
this.autoSortDisabled = false
|
|
this.currentWcsAutoSort = ''
|
|
this.currentMaxLayer = 0
|
|
this.positionOptions = []
|
|
this.palletTypeOptions = []
|
|
this.callEmptyPalletTypeOptions = []
|
|
this.detailList = []
|
|
this.scanOutList = []
|
|
this.detailModalVisible = false
|
|
this.scanOutModalVisible = false
|
|
this.palletInfo = {
|
|
palletType: '',
|
|
autoSort: 'N',
|
|
locationCode: ''
|
|
}
|
|
this.scanModalVisible = false
|
|
this.scanCode = ''
|
|
this.scanPosition = ''
|
|
this.scanLayer = 1
|
|
this.scanLayerOptions = []
|
|
this.needRefreshOnClose = false
|
|
this.positionGrid = []
|
|
this.availablePositions = []
|
|
this.currentSelectedPosition = ''
|
|
this.currentMixedMode = false
|
|
this.positionGridLoading = false
|
|
this.areaOptions = []
|
|
this.stationOptions = []
|
|
this.callEmptyModalVisible = false
|
|
this.callEmptyLoading = false
|
|
this.callEmptyForm = {
|
|
areaId: '',
|
|
stationCode: '',
|
|
palletType: ''
|
|
}
|
|
this.transportLoading = false
|
|
this.palletScanLoading = false
|
|
this.palletConfigLoading = false
|
|
this.scanRequestLoading = false
|
|
if (needFocus) {
|
|
this.$nextTick(() => {
|
|
if (this.$refs.palletInput) {
|
|
this.$refs.palletInput.focus()
|
|
}
|
|
})
|
|
}
|
|
},
|
|
handlePalletScan() {
|
|
if (!this.ensureSite()) {
|
|
return
|
|
}
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
if (!this.palletCode || !this.palletCode.trim()) {
|
|
this.$alert('请输入栈板编码', '提示', { confirmButtonText: '确定' })
|
|
return
|
|
}
|
|
this.palletScanLoading = true
|
|
// 组盘入库场景 sortFlag=0:后端拦截手工分拣区/被分拣站栈板,并按区域 pack_able 处理后续扫标签 - rqrq
|
|
checkPalletExists({
|
|
site: this.site,
|
|
palletId: this.palletCode.trim(),
|
|
sortFlag: 0
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.palletCode = data.palletId
|
|
this.positionOptions = data.positions || []
|
|
this.palletScanned = true
|
|
this.scanOutList = []
|
|
this.scanCode = ''
|
|
this.scanPosition = ''
|
|
this.scanLayer = 1
|
|
this.currentSelectedPosition = ''
|
|
this.loadPalletInfo()
|
|
this.refreshTable()
|
|
} else {
|
|
this.$alert((data && data.msg) || '栈板校验失败', '错误', { confirmButtonText: '确定' })
|
|
}
|
|
}).catch((error) => {
|
|
this.$alert(error.message || '栈板校验失败', '错误', { confirmButtonText: '确定' })
|
|
}).finally(() => {
|
|
this.palletScanLoading = false
|
|
})
|
|
},
|
|
loadPalletInfo() {
|
|
getPalletInfo({
|
|
site: this.site,
|
|
palletId: this.palletCode
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
const row = data.row || {}
|
|
this.currentPalletFamily = row.palletFamily || ''
|
|
this.currentPalletType = row.palletType || ''
|
|
this.currentAutoSort = row.autoSort || 'N'
|
|
this.currentPalletStation = row.locationCode || ''
|
|
this.forceFullPalletOut = Number(row.soreType) === 3
|
|
this.palletInfo.palletType = this.currentPalletType
|
|
this.palletInfo.autoSort = this.currentAutoSort
|
|
this.palletInfo.locationCode = this.currentPalletStation
|
|
this.loadPalletTypeList()
|
|
}
|
|
})
|
|
},
|
|
// 当前栈板改类型时,必须按该栈板大分类过滤可选类型,避免跨大类误选 - rqrq
|
|
loadPalletTypeList() {
|
|
getPalletTypeList({
|
|
site: this.site,
|
|
palletFamily: this.currentPalletFamily || '',
|
|
active: 'Y'
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.palletTypeOptions = data.rows || []
|
|
const selectedType = this.palletTypeOptions.find((item) => item.palletType === this.currentPalletType)
|
|
this.currentWcsAutoSort = selectedType ? (selectedType.wcsAutoSort || 'N') : ''
|
|
this.currentMaxLayer = selectedType ? Number(selectedType.maxLayer || 0) : 0
|
|
this.updateAutoSortControl()
|
|
}
|
|
})
|
|
},
|
|
// 调用空托盘必须获取全量可用类型,不按当前栈板大分类做过滤 - rqrq
|
|
loadAllPalletTypeListForCallEmpty() {
|
|
getPalletTypeList({
|
|
site: this.site,
|
|
palletFamily: '',
|
|
active: 'Y'
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.callEmptyPalletTypeOptions = data.rows || []
|
|
const hasSelectedType = this.callEmptyPalletTypeOptions.some((item) => item.palletType === this.callEmptyForm.palletType)
|
|
if (!hasSelectedType) {
|
|
this.callEmptyForm.palletType = ''
|
|
}
|
|
} else {
|
|
this.callEmptyPalletTypeOptions = []
|
|
this.callEmptyForm.palletType = ''
|
|
}
|
|
}).catch(() => {
|
|
this.callEmptyPalletTypeOptions = []
|
|
this.callEmptyForm.palletType = ''
|
|
})
|
|
},
|
|
refreshTable() {
|
|
getPalletDetails({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
position: '',
|
|
layer: null
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.detailList = data.details || []
|
|
} else {
|
|
this.detailList = []
|
|
}
|
|
// 明细存在时锁定托盘类型,避免已组盘后改类型导致规则不一致 - rqrq
|
|
this.palletTypeDisabled = this.detailList.length > 0
|
|
this.updateAutoSortControl()
|
|
}).catch(() => {
|
|
this.detailList = []
|
|
this.palletTypeDisabled = false
|
|
this.updateAutoSortControl()
|
|
})
|
|
},
|
|
handlePalletTypeChange() {
|
|
const selectedType = this.palletTypeOptions.find((item) => item.palletType === this.currentPalletType)
|
|
this.applyPalletTypeDefaults(selectedType)
|
|
this.savePalletTypeConfig()
|
|
},
|
|
// 改小类时全部选项回到类型主数据:强制整托看wcs_sore_type=3,自动分拣看wcs_auto_sort;从强制改到非强制必须取消勾选 - rqrq
|
|
applyPalletTypeDefaults(selectedType) {
|
|
this.currentWcsAutoSort = selectedType ? (selectedType.wcsAutoSort || 'N') : 'N'
|
|
this.currentMaxLayer = selectedType ? Number(selectedType.maxLayer || 0) : 0
|
|
const wcsSoreType = selectedType && selectedType.wcsSoreType != null ? Number(selectedType.wcsSoreType) : 0
|
|
this.forceFullPalletOut = wcsSoreType === 3
|
|
|| this.currentPalletType === 'A0103'
|
|
|| this.currentPalletType === 'A0206'
|
|
this.currentAutoSort = this.currentWcsAutoSort
|
|
this.updateAutoSortControl()
|
|
},
|
|
handleAutoSortChange() {
|
|
this.savePalletTypeConfig()
|
|
},
|
|
handleForceFullPalletOutChange() {
|
|
this.savePalletTypeConfig()
|
|
},
|
|
// 根据托盘类型能力与强制整托规则动态控制自动分拣可编辑性 - rqrq
|
|
updateAutoSortControl() {
|
|
if (this.forceFullPalletOut || this.currentPalletType === 'A0103') {
|
|
this.currentAutoSort = 'N'
|
|
this.autoSortDisabled = true
|
|
return
|
|
}
|
|
if (this.currentWcsAutoSort === 'N') {
|
|
this.currentAutoSort = 'N'
|
|
this.autoSortDisabled = true
|
|
return
|
|
}
|
|
this.autoSortDisabled = this.detailList.length > 0
|
|
},
|
|
// 更新托盘类型与自动分拣设置,保持海安后端状态一致 - rqrq
|
|
savePalletTypeConfig() {
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
this.palletConfigLoading = true
|
|
updatePalletTypeAndAutoSort({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
palletType: this.currentPalletType,
|
|
autoSort: this.currentAutoSort,
|
|
forceFullPalletOut: this.forceFullPalletOut
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.palletInfo.palletType = this.currentPalletType
|
|
this.palletInfo.autoSort = this.currentAutoSort
|
|
this.$message.success('托盘配置已更新')
|
|
} else {
|
|
this.$alert((data && data.msg) || '托盘配置更新失败', '错误', { confirmButtonText: '确定' })
|
|
}
|
|
}).catch((error) => {
|
|
this.$alert(error.message || '托盘配置更新失败', '错误', { confirmButtonText: '确定' })
|
|
}).finally(() => {
|
|
this.palletConfigLoading = false
|
|
})
|
|
},
|
|
// 沿用原页面入口命名,点击Call栈板时打开空托盘调用弹窗 - rqrq
|
|
handleCallPallet() {
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
this.openCallEmptyModal()
|
|
},
|
|
// 打开扫码弹窗并初始化网格数据,交互与原PDA页面一致 - rqrq
|
|
showScanModal() {
|
|
this.scanModalVisible = true
|
|
this.scanCode = ''
|
|
this.currentSelectedPosition = ''
|
|
this.needRefreshOnClose = false
|
|
if (this.operationType === 'in') {
|
|
this.scanLayer = 1
|
|
this.loadLayerOptions()
|
|
} else {
|
|
this.$nextTick(() => {
|
|
if (this.$refs.scanInput) {
|
|
this.$refs.scanInput.focus()
|
|
}
|
|
})
|
|
}
|
|
},
|
|
showDetailModal() {
|
|
this.detailModalVisible = true
|
|
},
|
|
showScanOutModal() {
|
|
this.scanOutModalVisible = true
|
|
},
|
|
// 计算当前可选层数并刷新位置网格,保持“按层扫描”体验一致 - rqrq
|
|
loadLayerOptions() {
|
|
getPalletDetails({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
position: '',
|
|
layer: null
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
const details = data.details || []
|
|
let maxExistingLayer = 0
|
|
if (details.length > 0) {
|
|
maxExistingLayer = Math.max(...details.map((d) => d.layer || 0))
|
|
}
|
|
if (this.currentMaxLayer === 0) {
|
|
this.currentMixedMode = false
|
|
this.scanLayerOptions = Array.from({ length: maxExistingLayer + 1 }, (_, i) => i + 1)
|
|
} else {
|
|
this.currentMixedMode = false
|
|
this.scanLayerOptions = Array.from({ length: this.currentMaxLayer }, (_, i) => i + 1)
|
|
}
|
|
this.loadAvailablePositions()
|
|
} else {
|
|
this.$alert((data && data.msg) || '获取栈板信息失败', '错误', { confirmButtonText: '确定' })
|
|
}
|
|
}).catch(() => {
|
|
this.$alert('获取栈板信息失败', '错误', { confirmButtonText: '确定' })
|
|
})
|
|
},
|
|
// 按层加载位置网格与可扫位置,确保禁用态/加载态与原页面一致 - rqrq
|
|
loadAvailablePositions() {
|
|
this.positionGridLoading = true
|
|
this.availablePositions = []
|
|
getPalletDetails({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
position: '',
|
|
layer: this.scanLayer
|
|
}).then(({ data: detailData }) => {
|
|
const positionCountMap = {}
|
|
if (detailData && detailData.code === 0) {
|
|
const details = detailData.details || []
|
|
details.forEach((detail) => {
|
|
const pos = detail.position
|
|
positionCountMap[pos] = (positionCountMap[pos] || 0) + 1
|
|
})
|
|
}
|
|
return getAvailablePositionsForLayer({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
layer: this.scanLayer
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
const result = data.data || {}
|
|
const positions = result.positions || []
|
|
this.availablePositions = result.availablePositions || []
|
|
this.positionGrid = positions.map((position) => ({
|
|
position,
|
|
count: positionCountMap[position] || 0
|
|
}))
|
|
} else {
|
|
this.availablePositions = []
|
|
this.positionGrid = []
|
|
this.$alert((data && data.msg) || '获取位置信息失败', '错误', { confirmButtonText: '确定' })
|
|
}
|
|
})
|
|
}).catch(() => {
|
|
this.availablePositions = []
|
|
this.positionGrid = []
|
|
this.$alert('获取位置信息失败', '错误', { confirmButtonText: '确定' })
|
|
}).finally(() => {
|
|
this.positionGridLoading = false
|
|
})
|
|
},
|
|
handleLayerClick(layer) {
|
|
if (this.scanLayer === layer) {
|
|
return
|
|
}
|
|
this.scanLayer = layer
|
|
this.currentSelectedPosition = ''
|
|
this.loadAvailablePositions()
|
|
},
|
|
handlePositionClick(position) {
|
|
if (this.positionGridLoading) {
|
|
this.$message.warning('位置信息加载中,请稍候')
|
|
return
|
|
}
|
|
if (!this.availablePositions.includes(position)) {
|
|
this.$message.warning('该位置不可用')
|
|
return
|
|
}
|
|
this.currentSelectedPosition = position
|
|
this.scanPosition = position
|
|
this.$nextTick(() => {
|
|
if (this.$refs.scanInput) {
|
|
this.$refs.scanInput.focus()
|
|
}
|
|
})
|
|
},
|
|
moveFocusToScanInput() {
|
|
this.$nextTick(() => {
|
|
if (this.$refs.scanInput) {
|
|
this.$refs.scanInput.focus()
|
|
}
|
|
})
|
|
},
|
|
closeScanModal() {
|
|
this.scanModalVisible = false
|
|
if (this.needRefreshOnClose) {
|
|
this.refreshTable()
|
|
this.needRefreshOnClose = false
|
|
}
|
|
},
|
|
// 扫码提交:扫进用网格位置信息,扫出沿用原有删除逻辑 - rqrq
|
|
handleLabelScan() {
|
|
if (!this.ensureSite()) {
|
|
return
|
|
}
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
if (!this.scanCode || !this.scanCode.trim()) {
|
|
this.scanCode = ''
|
|
this.$alert('请输入标签编码', '错误', { confirmButtonText: '确定' })
|
|
this.moveFocusToScanInput()
|
|
return
|
|
}
|
|
if (this.operationType === 'in') {
|
|
if (!this.scanPosition) {
|
|
this.scanCode = ''
|
|
this.$alert('请选择位置', '错误', { confirmButtonText: '确定' })
|
|
this.moveFocusToScanInput()
|
|
return
|
|
}
|
|
if (!this.scanLayer) {
|
|
this.scanCode = ''
|
|
this.$alert('请选择层数', '错误', { confirmButtonText: '确定' })
|
|
this.moveFocusToScanInput()
|
|
return
|
|
}
|
|
this.scanRequestLoading = true
|
|
savePalletDetail({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
position: this.scanPosition,
|
|
layer: this.scanLayer,
|
|
serialNo: this.scanCode,
|
|
sortFlag: 0
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.$message.success('扫进成功')
|
|
this.needRefreshOnClose = true
|
|
this.scanCode = ''
|
|
this.loadLayerOptions()
|
|
this.moveFocusToScanInput()
|
|
} else {
|
|
this.scanCode = ''
|
|
this.$alert((data && data.msg) || '扫进失败', '错误', { confirmButtonText: '确定' })
|
|
this.moveFocusToScanInput()
|
|
}
|
|
}).catch((error) => {
|
|
this.scanCode = ''
|
|
this.$alert(error.message || '扫进失败', '错误', { confirmButtonText: '确定' })
|
|
this.moveFocusToScanInput()
|
|
}).finally(() => {
|
|
this.scanRequestLoading = false
|
|
})
|
|
} else {
|
|
const scanOutItem = this.detailList.find((item) => item.serialNo === this.scanCode)
|
|
this.scanRequestLoading = true
|
|
deletePalletDetail({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
serialNo: this.scanCode,
|
|
sortFlag: 0
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
if (scanOutItem) {
|
|
this.scanOutList.push({
|
|
position: scanOutItem.position,
|
|
layer: scanOutItem.layer,
|
|
serialNo: scanOutItem.serialNo
|
|
})
|
|
}
|
|
this.$message.success('扫出成功')
|
|
this.needRefreshOnClose = true
|
|
this.scanCode = ''
|
|
this.moveFocusToScanInput()
|
|
} else {
|
|
this.scanCode = ''
|
|
this.$alert((data && data.msg) || '扫出失败', '错误', { confirmButtonText: '确定' })
|
|
this.moveFocusToScanInput()
|
|
}
|
|
}).catch((error) => {
|
|
this.scanCode = ''
|
|
this.$alert(error.message || '扫出失败', '错误', { confirmButtonText: '确定' })
|
|
this.moveFocusToScanInput()
|
|
}).finally(() => {
|
|
this.scanRequestLoading = false
|
|
})
|
|
}
|
|
},
|
|
loadAreaOptions() {
|
|
getAreaOptionsForChange({ site: this.site }).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.areaOptions = data.rows || []
|
|
} else {
|
|
this.areaOptions = []
|
|
}
|
|
}).catch(() => {
|
|
this.areaOptions = []
|
|
})
|
|
},
|
|
openCallEmptyModal() {
|
|
if (!this.ensureSite()) {
|
|
return
|
|
}
|
|
this.callEmptyForm = { areaId: '', stationCode: '', palletType: this.currentPalletType || '' }
|
|
this.stationOptions = []
|
|
this.loadAreaOptions()
|
|
// 调用空托盘弹窗固定加载全量类型,不能复用当前栈板“按大分类过滤”的下拉 - rqrq
|
|
this.loadAllPalletTypeListForCallEmpty()
|
|
this.callEmptyModalVisible = true
|
|
},
|
|
handleAreaChangeForCall() {
|
|
this.callEmptyForm.stationCode = ''
|
|
if (!this.callEmptyForm.areaId) {
|
|
this.stationOptions = []
|
|
return
|
|
}
|
|
getStationsByArea({
|
|
site: this.site,
|
|
areaId: this.callEmptyForm.areaId
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.stationOptions = data.rows || []
|
|
} else {
|
|
this.stationOptions = []
|
|
}
|
|
}).catch(() => {
|
|
this.stationOptions = []
|
|
})
|
|
},
|
|
submitCallEmpty() {
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
if (!this.callEmptyForm.areaId || !this.callEmptyForm.stationCode || !this.callEmptyForm.palletType) {
|
|
this.$alert('请完整选择区域、站点和栈板类型', '提示', { confirmButtonText: '确定' })
|
|
return
|
|
}
|
|
this.callEmptyLoading = true
|
|
callEmptyPalletToStation({
|
|
site: this.site,
|
|
// 调用空托盘下发时,targetArea传业务区域ID,由后端映射haian_area.wcs_area_code - rqrq
|
|
areaId: this.callEmptyForm.areaId,
|
|
targetArea: this.callEmptyForm.areaId,
|
|
stationCode: this.callEmptyForm.stationCode,
|
|
palletType: this.callEmptyForm.palletType
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.$message.success('调用空托盘成功')
|
|
this.callEmptyModalVisible = false
|
|
} else {
|
|
this.$alert((data && data.msg) || '调用空托盘失败', '错误', { confirmButtonText: '确定' })
|
|
}
|
|
}).catch((error) => {
|
|
this.$alert(error.message || '调用空托盘失败', '错误', { confirmButtonText: '确定' })
|
|
}).finally(() => {
|
|
this.callEmptyLoading = false
|
|
})
|
|
},
|
|
openTransportModal() {
|
|
if (!this.ensureSite()) {
|
|
return
|
|
}
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
if (!this.palletScanned) {
|
|
this.$alert('请先扫描栈板', '提示', { confirmButtonText: '确定' })
|
|
return
|
|
}
|
|
// 栈板未绑定当前库位时禁止手工触发运输入库,避免无站点上下文误发请求 - rqrq
|
|
if (!this.currentPalletStation || !this.currentPalletStation.trim()) {
|
|
this.$alert('当前栈板没有库位,不允许执行运输入库', '提示', { confirmButtonText: '确定' })
|
|
return
|
|
}
|
|
this.$confirm('是否对此栈板发起入库请求?', '提示', {
|
|
confirmButtonText: '确认',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
this.submitTransport()
|
|
}).catch(() => {
|
|
// 用户取消确认弹框时不执行任何操作,避免误触后仍发请求 - rqrq
|
|
})
|
|
},
|
|
submitTransport() {
|
|
if (this.interfaceBusy) {
|
|
return
|
|
}
|
|
this.transportLoading = true
|
|
// 海安运输入库不再选择目标区域/站点,只对当前栈板发起入库请求 - rqrq
|
|
callPalletToStationWithUpdateZuPan({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
startStation: this.palletInfo.locationCode || ''
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.$message.success('运输入库任务已提交')
|
|
// 与老组盘页一致:运输入库成功后清空整页,避免继续操作已提交入库的栈板 - rqrq
|
|
this.resetPage(true)
|
|
} else {
|
|
this.$alert((data && data.msg) || '运输入库失败', '错误', { confirmButtonText: '确定' })
|
|
}
|
|
}).catch((error) => {
|
|
this.$alert(error.message || '运输入库失败', '错误', { confirmButtonText: '确定' })
|
|
}).finally(() => {
|
|
this.transportLoading = false
|
|
})
|
|
}
|
|
},
|
|
// keep-alive页面每次重新激活都强制重置,避免“退出再进入”出现旧数据残留 - rqrq
|
|
activated() {
|
|
this.resetPage(true)
|
|
},
|
|
// 离开海安组盘页时统一清理状态,防止返回菜单后缓存脏数据 - rqrq
|
|
beforeRouteLeave(to, from, next) {
|
|
this.resetPage(false)
|
|
next()
|
|
},
|
|
mounted() {
|
|
this.resetPage(true)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.header-action-disabled {
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.detail-table {
|
|
background: white;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
border: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.table-header,
|
|
.table-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 8px;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.table-header {
|
|
background: #f5f5f5;
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.col-position {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.col-layer {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.col-serial {
|
|
flex: 4;
|
|
text-align: center;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.empty-row {
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 20px;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.empty-hint {
|
|
text-align: center;
|
|
color: #999;
|
|
width: 100%;
|
|
}
|
|
|
|
.scan-modal-content {
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.modal-form {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.dialog-footer {
|
|
text-align: center;
|
|
}
|
|
|
|
.action-btn:disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
background-color: #ccc !important;
|
|
border-color: #ccc !important;
|
|
}
|
|
|
|
.action-btn.warning {
|
|
background-color: #f59e0b;
|
|
border-color: #f59e0b;
|
|
color: #fff;
|
|
}
|
|
|
|
::v-deep .el-dialog__wrapper {
|
|
z-index: 2000 !important;
|
|
}
|
|
|
|
::v-deep .el-overlay {
|
|
z-index: 2000 !important;
|
|
}
|
|
|
|
::v-deep .el-radio {
|
|
margin-right: 8px !important;
|
|
}
|
|
|
|
::v-deep .el-radio__label {
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* 层数网格样式,保持与原PDA页面一致 - rqrq */
|
|
.layer-grid {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.layer-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 50px;
|
|
height: 45px;
|
|
padding: 0 12px;
|
|
background-color: #fff;
|
|
border: 2px solid #dcdfe6;
|
|
border-radius: 6px;
|
|
font-size: 15px;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
user-select: none;
|
|
}
|
|
|
|
.layer-item:hover {
|
|
border-color: #409eff;
|
|
background-color: #ecf5ff;
|
|
}
|
|
|
|
.layer-item.layer-selected {
|
|
border-color: #409eff;
|
|
background-color: #409eff;
|
|
color: #fff;
|
|
}
|
|
|
|
/* 位置网格样式,保持与原PDA页面一致 - rqrq */
|
|
.position-grid {
|
|
display: grid;
|
|
gap: 10px;
|
|
margin-top: 8px;
|
|
position: relative;
|
|
}
|
|
|
|
.position-grid.position-grid-1 {
|
|
grid-template-columns: 1fr;
|
|
grid-template-rows: 1fr;
|
|
}
|
|
|
|
.position-grid.position-grid-4 {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
grid-template-rows: repeat(2, 1fr);
|
|
grid-auto-flow: column;
|
|
}
|
|
|
|
.position-grid.position-grid-9 {
|
|
grid-template-columns: repeat(3, 1fr);
|
|
grid-template-rows: repeat(3, 1fr);
|
|
grid-auto-flow: row;
|
|
}
|
|
|
|
.position-item.position-placeholder {
|
|
background-color: #f0f0f0 !important;
|
|
border: 1px dashed #d0d0d0 !important;
|
|
color: transparent !important;
|
|
cursor: not-allowed !important;
|
|
pointer-events: none !important;
|
|
}
|
|
|
|
.position-grid.position-grid-loading {
|
|
opacity: 0.7;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.position-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 60px;
|
|
background-color: #fff;
|
|
border: 2px solid #dcdfe6;
|
|
border-radius: 6px;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
user-select: none;
|
|
}
|
|
|
|
.position-item:hover:not(.position-disabled) {
|
|
border-color: #409eff;
|
|
background-color: #ecf5ff;
|
|
}
|
|
|
|
.position-item.position-selected {
|
|
border-color: #409eff;
|
|
background-color: #409eff;
|
|
color: #fff;
|
|
}
|
|
|
|
.position-item.position-disabled {
|
|
background-color: #f5f7fa;
|
|
color: #c0c4cc;
|
|
cursor: not-allowed;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.position-item.position-loading {
|
|
background: linear-gradient(90deg, #f5f7fa 25%, #e4e7ed 50%, #f5f7fa 75%);
|
|
background-size: 200% 100%;
|
|
animation: loading 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes loading {
|
|
0% {
|
|
background-position: 200% 0;
|
|
}
|
|
100% {
|
|
background-position: -200% 0;
|
|
}
|
|
}
|
|
</style>
|