Files
FTC25009/pda/pages/warehouse/inbound/bindTray.vue

1315 lines
35 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page-container">
<!-- 任务信息 -->
<view class="task-info" v-if="stockinId || taskNo">
<text class="task-label">入库主单</text>
<text class="task-value">{{ documentN || stockinId || taskNo }}</text>
</view>
<!-- 托盘信息 -->
<view class="form-section">
<text class="form-title">托盘信息</text>
<view class="form-item">
<text class="form-label">仓库</text>
<picker class="form-picker" mode="selector" :range="warehouseOptions" range-key="warehouseName"
:value="warehouseIndex" :disabled="bindSuccess" @change="handleWarehouseChange">
<view class="picker-value" :class="{ placeholder: !warehouseId }">
{{ selectedWarehouseName || '请选择仓库' }}
</view>
</picker>
</view>
<view class="form-item">
<text class="form-label">托盘号</text>
<input class="form-input" v-model="trayCode" placeholder="请扫描托盘号" />
</view>
<view class="form-tip" v-if="!warehouseId || !trayCode">请先选择仓库和扫描托盘号</view>
<view class="form-tip" v-else-if="isDenseWarehouse">密集库入口号由 WCS 获取作业时分配</view>
<view class="form-item" v-else>
<text class="form-label">入库点位</text>
<picker class="form-picker" mode="selector" :range="stationPointOptions" range-key="pointName"
:value="stationPointIndex" :disabled="bindSuccess" @change="handleStationPointChange">
<view class="picker-value" :class="{ placeholder: !stationPointId }">
{{ selectedStationPointName || '请选择入库点位' }}
</view>
</picker>
</view>
</view>
<!-- 绑盘状态提示 -->
<view class="bind-status" v-if="bindSuccess">
<view class="bind-status-title">入库作业已生成</view>
<view class="bind-status-desc">物料已写入托盘绑定信息,并已生成入库作业。</view>
</view>
<!-- 扫码添加物料 -->
<view class="scan-section">
<view class="scan-header">
<text class="scan-title">扫描入库子单号</text>
<view class="scan-btn" @click="startScan">
<uni-icons type="scan" size="18" color="#1890ff"></uni-icons>
<text class="scan-btn-text">扫码</text>
</view>
</view>
<view class="input-wrap">
<input class="scan-input" v-model="barcode" placeholder="请输入或扫描入库子单号"
@confirm="handleBarcode" />
</view>
</view>
<!-- 主单下待绑盘子单 -->
<view class="list-section" v-if="stockinId">
<view class="section-header">
<text class="section-title">子单物料({{ availableDetailList.length }} 条)</text>
</view>
<view class="detail-empty" v-if="availableDetailList.length === 0">
<text class="empty-text">当前主单暂无待绑盘子单</text>
</view>
<view class="detail-card" v-for="(detail, index) in availableDetailList" :key="detail.stockinDetailId || index"
@click="selectDetail(detail)">
<view class="detail-row">
<text class="detail-label">子单号</text>
<text class="detail-value">{{ detail.stockinDetailId }}</text>
</view>
<view class="detail-row">
<text class="detail-label">物料编码</text>
<text class="detail-value">{{ detail.productId }}</text>
</view>
<view class="detail-row">
<text class="detail-label">物料名称</text>
<text class="detail-value">{{ detail.productName }}</text>
</view>
<view class="detail-row">
<text class="detail-label">待绑数量</text>
<text class="detail-value highlight">{{ getAvailableRemainQty(detail) }}</text>
</view>
<view class="detail-action">选择并填写数量</view>
</view>
</view>
<!-- 数量填写弹窗 -->
<view class="quantity-modal-mask" v-if="showQuantityPopup" @click="closeQuantityPopup">
<view class="quantity-modal" @click.stop>
<view class="modal-header">
<text class="modal-title">选择并填写数量</text>
<text class="modal-close" @click="closeQuantityPopup">×</text>
</view>
<view class="modal-body" v-if="currentMaterial">
<view class="info-row">
<text class="info-label">物料编码</text>
<text class="info-value">{{ currentMaterial.productId }}</text>
</view>
<view class="info-row">
<text class="info-label">物料名称</text>
<text class="info-value">{{ currentMaterial.productName }}</text>
</view>
<view class="info-row">
<text class="info-label">子单号</text>
<text class="info-value">{{ currentMaterial.stockinDetailId }}</text>
</view>
<view class="info-row">
<text class="info-label">入库单号</text>
<text class="info-value">{{ currentMaterial.stockinId || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">批次</text>
<text class="info-value">{{ currentMaterial.lotNo || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">待绑数量</text>
<text class="info-value highlight">{{ currentRemainQty }}</text>
</view>
<view class="modal-form-item">
<text class="modal-form-label">本次绑定数量</text>
<input class="modal-form-input" v-model="currentQty" type="number" placeholder="请修改本次绑定数量" />
</view>
</view>
<view class="modal-actions">
<button class="modal-btn modal-btn-cancel" @click="closeQuantityPopup">取消</button>
<button class="modal-btn modal-btn-confirm" @click="addItem">加入列表</button>
</view>
</view>
</view>
<!-- 绑盘成功结果弹层:承载长业务结果,避免系统 Toast 挤压换行。 -->
<view class="success-dialog-mask" v-if="showSuccessDialog">
<view class="success-dialog" @click.stop>
<view class="success-mark" aria-label="成功">
<view class="success-check"></view>
</view>
<text class="success-kicker">INBOUND COMPLETE</text>
<text class="success-title">绑盘及作业生成完成</text>
<text class="success-description">{{ successDescription }}</text>
<view class="success-summary">
<view class="success-summary-row">
<text class="success-summary-label">托盘条码</text>
<text class="success-summary-value">{{ trayCode }}</text>
</view>
<view class="success-summary-row">
<text class="success-summary-label">目标仓库</text>
<text class="success-summary-value">{{ selectedWarehouseName || warehouseId }}</text>
</view>
</view>
<button class="success-action" @click="continueBinding">继续绑盘</button>
</view>
</view>
<!-- 已添加物料列表 -->
<view class="list-section" v-if="items.length > 0">
<view class="section-header">
<text class="section-title">已绑定物料({{ items.length }} 种)</text>
</view>
<view class="item-card" v-for="(item, index) in items" :key="index">
<view class="item-body">
<view class="item-row">
<text class="item-label">物料编码</text>
<text class="item-value">{{ item.productId }}</text>
</view>
<view class="item-row">
<text class="item-label">物料名称</text>
<text class="item-value">{{ item.productName }}</text>
</view>
<view class="item-row">
<text class="item-label">绑定数量</text>
<text class="item-value highlight">{{ item.quantity }}</text>
</view>
</view>
<view class="item-delete" :class="{ disabled: bindSuccess }" @click="removeItem(index)">
<uni-icons type="trash" size="20" color="#f5222d"></uni-icons>
</view>
</view>
</view>
<!-- 空状态提示 -->
<view class="empty-state" v-if="items.length === 0">
<text class="empty-text">请扫码添加物料</text>
</view>
<!-- 操作按钮 -->
<view class="btn-section">
<button class="btn btn-primary"
:disabled="items.length === 0 || !trayCode || !warehouseId || (!isDenseWarehouse && !stationPointId) || bindSuccess || submitting"
@click="submitBindTrayAction">
{{ submitting ? '正在生成作业…' : (bindSuccess ? '已生成入库作业' : '确认绑盘并生成作业(' + items.length + ' 种物料)') }}
</button>
<button class="btn btn-default" @click="resetAll">全部重置</button>
</view>
</view>
</template>
<script>
import { getInboundDetailList, getInboundStationPointOptions, getMaterialByDetailId, getWarehouseOptions, submitBindTrayAndAddInJob } from '@/api/warehouse/inbound'
export default {
data() {
return {
taskNo: '',
stockinId: '',
documentN: '',
warehouseId: '',
warehouseName: '',
warehouseIndex: 0,
warehouseOptions: [],
stationPointId: '',
stationPointName: '',
stationPointIndex: 0,
stationPointOptions: [],
trayCode: '',
// 当前扫码
barcode: '',
currentMaterial: null,
currentRemainQty: 0,
currentQty: '',
showQuantityPopup: false,
showSuccessDialog: false,
successDescription: '',
submitting: false,
// 已完成绑盘并生成入库作业时保持页面状态,便于操作人员核对结果。
bindSuccess: false,
// 当前主单下待绑盘子单,操作人员选择子单后再修改本次绑定数量。
detailList: [],
// 已加入列表的物料
items: []
}
},
computed: {
selectedWarehouseName() {
if (this.warehouseName) {
return this.warehouseName
}
var selected = this.warehouseOptions[this.warehouseIndex]
return selected && selected.warehouseId === this.warehouseId ? selected.warehouseName : ''
},
selectedStationPointName() {
if (this.stationPointName) {
return this.stationPointName
}
var selected = this.stationPointOptions[this.stationPointIndex]
return selected && selected.pointId === this.stationPointId ? (selected.pointName || selected.pointId) : ''
},
isDenseWarehouse() {
return this.warehouseId === '03'
},
availableDetailList() {
return this.detailList.filter(function(detail) {
return this.getAvailableRemainQty(detail) > 0
}.bind(this))
}
},
watch: {
trayCode: function() {
if (this.bindSuccess) {
return
}
this.clearStationPoint()
if (!this.isDenseWarehouse) {
this.loadStationPointOptions()
}
}
},
onLoad(options) {
if (options.taskNo) {
this.taskNo = options.taskNo
}
if (options.stockinId) {
this.stockinId = decodeURIComponent(options.stockinId)
}
if (options.documentN) {
this.documentN = decodeURIComponent(options.documentN)
}
if (options.warehouseId) {
this.warehouseId = decodeURIComponent(options.warehouseId)
}
if (options.warehouseName) {
this.warehouseName = decodeURIComponent(options.warehouseName)
}
if (options.stationPointId) {
this.stationPointId = decodeURIComponent(options.stationPointId)
}
this.loadWarehouseOptions()
if (this.warehouseId && this.trayCode && !this.isDenseWarehouse) {
this.loadStationPointOptions()
}
if (this.stockinId) {
this.loadDetailList()
}
if (options.detailId) {
this.barcode = decodeURIComponent(options.detailId)
this.handleBarcode()
}
},
methods: {
loadWarehouseOptions() {
var that = this
getWarehouseOptions().then(function(res) {
that.warehouseOptions = res.data || res.rows || []
that.syncWarehouseIndex()
if (that.warehouseId && that.trayCode && !that.isDenseWarehouse) {
that.loadStationPointOptions()
}
}).catch(function() {
that.warehouseOptions = []
})
},
syncWarehouseIndex() {
if (!this.warehouseId || this.warehouseOptions.length === 0) {
return
}
for (var i = 0; i < this.warehouseOptions.length; i++) {
if (this.warehouseOptions[i].warehouseId === this.warehouseId) {
this.warehouseIndex = i
this.warehouseName = this.warehouseOptions[i].warehouseName
return
}
}
},
handleWarehouseChange(e) {
if (this.bindSuccess) {
this.$modal.msg('托盘已绑定,不能修改仓库')
return
}
var index = Number(e.detail.value)
var selected = this.warehouseOptions[index]
if (!selected) {
return
}
this.warehouseIndex = index
this.warehouseId = selected.warehouseId
this.warehouseName = selected.warehouseName
this.clearStationPoint()
if (!this.isDenseWarehouse) {
this.loadStationPointOptions()
}
},
clearStationPoint() {
this.stationPointId = ''
this.stationPointName = ''
this.stationPointIndex = 0
this.stationPointOptions = []
},
loadStationPointOptions() {
var that = this
if (this.isDenseWarehouse || !this.warehouseId || !this.trayCode) {
this.clearStationPoint()
return
}
getInboundStationPointOptions(this.warehouseId, this.trayCode).then(function(res) {
that.stationPointOptions = res.data || res.rows || []
that.syncStationPointIndex()
}).catch(function() {
that.clearStationPoint()
})
},
syncStationPointIndex() {
if (!this.stationPointId || this.stationPointOptions.length === 0) {
return
}
for (var i = 0; i < this.stationPointOptions.length; i++) {
if (this.stationPointOptions[i].pointId === this.stationPointId) {
this.stationPointIndex = i
this.stationPointName = this.stationPointOptions[i].pointName || this.stationPointOptions[i].pointId
return
}
}
this.stationPointId = ''
this.stationPointName = ''
this.stationPointIndex = 0
},
handleStationPointChange(e) {
if (this.bindSuccess) {
this.$modal.msg('托盘已绑定,不能修改入库点位')
return
}
var index = Number(e.detail.value)
var selected = this.stationPointOptions[index]
if (!selected) {
return
}
this.stationPointIndex = index
this.stationPointId = selected.pointId
this.stationPointName = selected.pointName || selected.pointId
},
loadDetailList() {
var that = this
getInboundDetailList({ stockinId: this.stockinId }).then(function(res) {
that.detailList = res.rows || []
}).catch(function() {
that.detailList = []
})
},
selectDetail(detail) {
if (this.bindSuccess) {
this.$modal.msg('托盘已绑定,不能再选择子单')
return
}
this.currentMaterial = detail
this.barcode = detail.stockinDetailId
this.currentRemainQty = this.getAvailableRemainQty(detail)
if (this.currentRemainQty <= 0) {
this.$modal.msg('该子单待绑数量已满')
return
}
this.currentQty = this.currentRemainQty > 0 ? String(this.currentRemainQty) : ''
this.showQuantityPopup = true
},
closeQuantityPopup() {
this.showQuantityPopup = false
this.currentMaterial = null
this.currentRemainQty = 0
this.currentQty = ''
},
// 调用摄像头扫码
startScan() {
var that = this
uni.scanCode({
onlyFromCamera: false,
scanType: ['barCode', 'qrCode'],
success: function(res) {
that.barcode = res.result
that.handleBarcode()
}
})
},
// 根据入库明细ID查询物料
handleBarcode() {
if (!this.barcode) {
this.$modal.msg('请输入入库子单号')
return
}
var that = this
getMaterialByDetailId(this.barcode).then(function(res) {
that.currentMaterial = res.data
if (res.data && res.data.stockinId && !that.stockinId) {
that.stockinId = res.data.stockinId
}
that.currentRemainQty = that.getAvailableRemainQty(res.data)
if (that.currentRemainQty <= 0) {
that.currentMaterial = null
that.currentQty = ''
that.showQuantityPopup = false
that.$modal.msg('该子单待绑数量已满')
return
}
that.currentQty = that.currentRemainQty > 0 ? String(that.currentRemainQty) : ''
that.showQuantityPopup = true
that.$modal.msgSuccess('物料信息获取成功')
}).catch(function() {
that.currentMaterial = null
that.currentRemainQty = 0
that.currentQty = ''
that.showQuantityPopup = false
that.$modal.msg('未找到该入库子单信息')
})
},
getRemainQty(material) {
var quantity = Number(material && material.quantity) || 0
var scanNum = Number(material && material.scanNum) || 0
var assignNum = Number(material && material.assignNum) || 0
var usedNum = scanNum > 0 ? scanNum : assignNum
var remainQty = quantity - usedNum
return remainQty > 0 ? remainQty : 0
},
getLocalBindQty(stockinDetailId) {
if (this.bindSuccess || !stockinDetailId) {
return 0
}
return this.items.reduce(function(total, item) {
if (item.stockinDetailId === stockinDetailId) {
return total + (Number(item.quantity) || 0)
}
return total
}, 0)
},
getAvailableRemainQty(material) {
var remainQty = this.getRemainQty(material)
var localBindQty = this.getLocalBindQty(material && material.stockinDetailId)
var availableQty = remainQty - localBindQty
return availableQty > 0 ? availableQty : 0
},
// 将当前物料加入绑定列表
addItem() {
if (!this.currentMaterial) {
this.$modal.msg('请先扫码查询物料')
return
}
if (!this.currentQty || Number(this.currentQty) <= 0) {
this.$modal.msg('请输入有效的绑定数量')
return
}
if (Number(this.currentQty) > Number(this.currentRemainQty)) {
this.$modal.msg('绑定数量不能超过待绑数量')
return
}
var currentQty = Number(this.currentQty)
var exists = this.items.find(function(i) {
return i.stockinDetailId === this.currentMaterial.stockinDetailId
}.bind(this))
if (exists) {
exists.quantity = Number(exists.quantity || 0) + currentQty
} else {
this.items.push({
stockinDetailId: this.currentMaterial.stockinDetailId,
stockinId: this.currentMaterial.stockinId,
productId: this.currentMaterial.productId,
productName: this.currentMaterial.productName,
lotNo: this.currentMaterial.lotNo,
quantity: currentQty
})
}
// 清空当前扫码区,继续扫下一个
this.barcode = ''
this.currentMaterial = null
this.currentRemainQty = 0
this.currentQty = ''
this.showQuantityPopup = false
this.$modal.msgSuccess('已加入列表')
},
// 从列表中移除某条物料
removeItem(index) {
if (this.bindSuccess) {
this.$modal.msg('托盘已绑定,不能再移除物料')
return
}
this.items.splice(index, 1)
},
// 提交绑盘
submitBindTrayAction() {
if (!this.trayCode) {
this.$modal.msg('请扫描托盘号')
return
}
if (!this.warehouseId) {
this.$modal.msg('请选择仓库')
return
}
if (!this.isDenseWarehouse && !this.stationPointId) {
this.$modal.msg('请选择入库点位')
return
}
if (this.items.length === 0) {
this.$modal.msg('请至少添加一种物料')
return
}
var data = {
trayCode: this.trayCode,
warehouseId: this.warehouseId,
items: this.items.map(function(i) {
return {
stockinDetailId: i.stockinDetailId,
productId: i.productId,
quantity: i.quantity
}
})
}
if (!this.isDenseWarehouse) {
data.stationPointId = this.stationPointId
}
var that = this
var confirmContent = this.isDenseWarehouse
? '确认将当前 ' + this.items.length + ' 种物料绑定到托盘 ' + this.trayCode + ',并生成密集库待执行作业?'
: '确认将当前 ' + this.items.length + ' 种物料绑定到托盘 ' + this.trayCode + ',并生成入库作业?'
this.$modal.confirm(confirmContent, '确认绑盘').then(function() {
that.submitting = true
return submitBindTrayAndAddInJob(data)
}).then(function(res) {
that.submitting = false
that.bindSuccess = true
that.successDescription = that.isDenseWarehouse
? '密集库入库作业已生成,当前为待执行状态,等待 WCS 主动获取。'
: '托盘绑定完成,入库作业已生成并进入仓库执行流程。'
that.showSuccessDialog = true
if (that.stockinId) {
that.loadDetailList()
}
}).catch(function() {
that.submitting = false
})
},
continueBinding() {
this.showSuccessDialog = false
this.resetBindPayload()
},
resetBindPayload() {
this.clearStationPoint()
this.trayCode = ''
this.barcode = ''
this.currentMaterial = null
this.currentRemainQty = 0
this.currentQty = ''
this.showQuantityPopup = false
this.showSuccessDialog = false
this.successDescription = ''
this.submitting = false
this.bindSuccess = false
this.items = []
},
// 全部重置
resetAll() {
this.stockinId = ''
this.documentN = ''
this.warehouseId = ''
this.warehouseName = ''
this.warehouseIndex = 0
this.clearStationPoint()
this.trayCode = ''
this.barcode = ''
this.currentMaterial = null
this.currentRemainQty = 0
this.currentQty = ''
this.showQuantityPopup = false
this.showSuccessDialog = false
this.successDescription = ''
this.submitting = false
this.bindSuccess = false
this.detailList = []
this.items = []
}
}
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f7fa;
min-height: 100%;
}
.page-container {
padding: 24rpx;
padding-bottom: 60rpx;
}
/* 任务信息 */
.task-info {
background: #e6f7ff;
border-radius: 12rpx;
padding: 20rpx 24rpx;
margin-bottom: 24rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.task-label {
font-size: 26rpx;
color: #1890ff;
}
.task-value {
font-size: 26rpx;
color: #333;
font-weight: 500;
}
/* 扫码区 */
.scan-section {
background: #fff;
border-radius: 16rpx;
padding: 28rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.scan-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.scan-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.scan-btn {
display: flex;
align-items: center;
padding: 8rpx 20rpx;
border: 1rpx solid #1890ff;
border-radius: 8rpx;
}
.scan-btn-text {
font-size: 24rpx;
color: #1890ff;
margin-left: 8rpx;
}
.input-wrap {
border: 1rpx solid #e8e8e8;
border-radius: 8rpx;
padding: 4rpx;
}
.scan-input {
font-size: 28rpx;
padding: 16rpx;
word-break: break-all;
width: 100%;
height: auto;
line-height: 1.4;
}
/* 通用 form-section */
.form-section {
background: #fff;
border-radius: 16rpx;
padding: 28rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.form-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 24rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
display: block;
}
.form-item {
display: flex;
align-items: center;
padding: 16rpx 0;
}
.form-tip {
padding: 8rpx 0 16rpx 160rpx;
font-size: 24rpx;
color: #909399;
line-height: 1.4;
}
.form-label {
font-size: 28rpx;
color: #666;
width: 160rpx;
flex-shrink: 0;
}
.form-input {
flex: 1;
font-size: 28rpx;
border: 1rpx solid #e8e8e8;
border-radius: 8rpx;
padding: 16rpx;
word-break: break-all;
min-width: 0;
height: auto;
line-height: 1.4;
}
.info-row {
display: flex;
justify-content: space-between;
padding: 16rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.info-label {
font-size: 28rpx;
color: #999;
}
.info-value {
font-size: 28rpx;
color: #333;
}
/* 加入列表按钮 */
.btn-add {
margin-top: 24rpx;
background: #1890ff;
color: #fff;
border-radius: 12rpx;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 0;
border: none;
}
.btn-add-text {
font-size: 28rpx;
color: #fff;
margin-left: 8rpx;
}
/* 已添加物料列表 */
.list-section {
margin-bottom: 24rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.item-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx 28rpx;
margin-bottom: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
display: flex;
align-items: center;
justify-content: space-between;
}
.item-body {
flex: 1;
margin-right: 20rpx;
}
.item-row {
display: flex;
justify-content: space-between;
padding: 8rpx 0;
}
.item-label {
font-size: 26rpx;
color: #999;
}
.item-value {
font-size: 26rpx;
color: #333;
}
.item-value.highlight {
color: #1890ff;
font-weight: 500;
}
.item-delete {
padding: 8rpx;
}
/* 空状态 */
.empty-state {
background: #fff;
border-radius: 16rpx;
padding: 60rpx 0;
margin-bottom: 24rpx;
display: flex;
align-items: center;
justify-content: center;
}
.empty-text {
font-size: 28rpx;
color: #ccc;
}
/* 底部按钮 */
.btn-section {
padding: 8rpx 0 24rpx;
}
.btn {
font-size: 30rpx;
border-radius: 12rpx;
margin-bottom: 20rpx;
}
.btn-primary {
background: #1890ff;
color: #fff;
}
.btn-success {
background: #0fe825;
color: #fff;
}
.btn-primary[disabled] {
background: #b0d4f5;
color: #fff;
}
.btn-default {
background: #fff;
color: #666;
border: 1rpx solid #d9d9d9;
}
.form-picker {
flex: 1;
min-height: 72rpx;
display: flex;
align-items: center;
}
.picker-value {
width: 100%;
font-size: 28rpx;
color: #333;
padding: 16rpx;
border: 1rpx solid #e8e8e8;
border-radius: 8rpx;
background: #fff;
}
.picker-value.placeholder {
color: #c0c4cc;
}
.bind-status {
background: #f6ffed;
border: 1rpx solid #b7eb8f;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
}
.bind-status-title {
font-size: 30rpx;
font-weight: 600;
color: #389e0d;
margin-bottom: 8rpx;
}
.bind-status-desc {
font-size: 26rpx;
color: #52c41a;
}
.detail-empty {
padding: 40rpx 0;
display: flex;
justify-content: center;
}
.detail-card {
background: #fafafa;
border: 1rpx solid #f0f0f0;
border-radius: 12rpx;
padding: 22rpx;
margin-bottom: 18rpx;
}
.detail-row {
display: flex;
justify-content: space-between;
padding: 6rpx 0;
}
.detail-label {
font-size: 24rpx;
color: #999;
}
.detail-value {
flex: 1;
margin-left: 20rpx;
text-align: right;
font-size: 24rpx;
color: #333;
word-break: break-all;
}
.detail-action {
margin-top: 14rpx;
padding-top: 14rpx;
border-top: 1rpx solid #f0f0f0;
font-size: 24rpx;
color: #1890ff;
text-align: right;
}
.item-delete.disabled {
opacity: 0.45;
}
/* 仓库下拉和普通输入框在同一表单行内保持等宽。 */
.form-picker,
.form-input {
flex: 1;
width: 0;
min-width: 0;
box-sizing: border-box;
}
.picker-value {
width: 100%;
box-sizing: border-box;
}
/* uni-app H5 会把 picker 内部再包一层无 class 节点,边框放在外层 picker 才能和输入框等宽。 */
.form-item .form-picker,
.form-item uni-picker.form-picker {
flex: 1;
width: 0;
min-width: 0;
min-height: 72rpx;
padding: 16rpx;
border: 1rpx solid #e8e8e8;
border-radius: 8rpx;
background: #fff;
box-sizing: border-box;
}
.form-item .form-picker .picker-value,
.form-item uni-picker.form-picker .picker-value {
display: flex;
align-items: center;
width: auto !important;
min-width: 0;
padding: 0 !important;
border: 0 !important;
background: transparent !important;
box-sizing: border-box;
}
.quantity-modal-mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
padding: 32rpx;
background: rgba(0, 0, 0, 0.45);
box-sizing: border-box;
}
.quantity-modal {
width: 100%;
max-width: 680rpx;
max-height: 86vh;
overflow-y: auto;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.18);
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.modal-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.modal-close {
width: 48rpx;
height: 48rpx;
line-height: 48rpx;
text-align: center;
font-size: 40rpx;
color: #999;
}
.modal-body {
padding: 24rpx 28rpx;
}
.modal-form-item {
margin-top: 24rpx;
}
.modal-form-label {
display: block;
margin-bottom: 12rpx;
font-size: 26rpx;
color: #666;
}
.modal-form-input {
width: 100%;
height: 72rpx;
padding: 0 20rpx;
border: 1rpx solid #d9d9d9;
border-radius: 8rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.modal-actions {
display: flex;
gap: 20rpx;
padding: 20rpx 28rpx 28rpx;
}
.modal-btn {
flex: 1;
height: 72rpx;
line-height: 72rpx;
border-radius: 8rpx;
font-size: 28rpx;
}
.modal-btn-cancel {
color: #666;
background: #f5f5f5;
}
.modal-btn-confirm {
color: #fff;
background: #1890ff;
}
.success-dialog-mask {
position: fixed;
inset: 0;
z-index: 1200;
display: flex;
align-items: center;
justify-content: center;
padding: 44rpx;
background: rgba(14, 25, 38, 0.62);
backdrop-filter: blur(8rpx);
box-sizing: border-box;
animation: success-mask-in 180ms ease-out both;
}
.success-dialog {
position: relative;
width: 100%;
max-width: 620rpx;
overflow: hidden;
padding: 54rpx 42rpx 38rpx;
border: 1rpx solid rgba(255, 255, 255, 0.88);
border-radius: 28rpx;
background:
linear-gradient(145deg, rgba(236, 252, 243, 0.92) 0, rgba(255, 255, 255, 0) 42%),
#fff;
box-shadow: 0 32rpx 90rpx rgba(5, 18, 31, 0.28);
box-sizing: border-box;
animation: success-card-in 260ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.success-dialog::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 8rpx;
background: linear-gradient(90deg, #12a66a 0%, #46c68b 55%, #bcefd4 100%);
}
.success-mark {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 112rpx;
height: 112rpx;
margin: 0 auto 28rpx;
border-radius: 50%;
background: #0f9f63;
box-shadow: 0 14rpx 30rpx rgba(15, 159, 99, 0.24);
}
.success-mark::after {
content: '';
position: absolute;
inset: -14rpx;
border: 2rpx solid rgba(15, 159, 99, 0.14);
border-radius: 50%;
}
.success-check {
width: 48rpx;
height: 25rpx;
margin-top: -8rpx;
border-left: 8rpx solid #fff;
border-bottom: 8rpx solid #fff;
transform: rotate(-45deg);
}
.success-kicker,
.success-title,
.success-description {
display: block;
text-align: center;
}
.success-kicker {
margin-bottom: 12rpx;
color: #0f9f63;
font-size: 20rpx;
font-weight: 700;
letter-spacing: 3rpx;
}
.success-title {
color: #132235;
font-size: 36rpx;
font-weight: 700;
line-height: 1.35;
}
.success-description {
margin: 18rpx auto 30rpx;
color: #607083;
font-size: 26rpx;
line-height: 1.7;
}
.success-summary {
margin-bottom: 30rpx;
padding: 6rpx 24rpx;
border: 1rpx solid #e6eee9;
border-radius: 16rpx;
background: rgba(247, 250, 248, 0.9);
}
.success-summary-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24rpx;
padding: 18rpx 0;
}
.success-summary-row + .success-summary-row {
border-top: 1rpx solid #e6eee9;
}
.success-summary-label {
flex-shrink: 0;
color: #8692a0;
font-size: 24rpx;
}
.success-summary-value {
color: #213247;
font-size: 25rpx;
font-weight: 600;
text-align: right;
word-break: break-all;
}
.success-action {
height: 82rpx;
line-height: 82rpx;
border: 0;
border-radius: 14rpx;
color: #fff;
background: #132235;
box-shadow: 0 12rpx 24rpx rgba(19, 34, 53, 0.18);
font-size: 28rpx;
font-weight: 600;
}
.success-action::after {
border: 0;
}
@keyframes success-mask-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes success-card-in {
from {
opacity: 0;
transform: translateY(28rpx) scale(0.96);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
</style>