389 lines
9.0 KiB
Vue
389 lines
9.0 KiB
Vue
<template>
|
|
<view class="page-container">
|
|
<view class="query-card">
|
|
<view class="section-header">
|
|
<text class="section-title">库存查询</text>
|
|
<text class="section-subtitle">托盘码</text>
|
|
</view>
|
|
|
|
<view class="input-row">
|
|
<input
|
|
class="tray-input"
|
|
v-model="trayCode"
|
|
placeholder="请扫描或输入托盘码"
|
|
confirm-type="search"
|
|
@confirm="queryStock"
|
|
/>
|
|
<button class="icon-button" @click="scanTray">
|
|
<uni-icons type="scan" size="22" color="#1890ff"></uni-icons>
|
|
</button>
|
|
</view>
|
|
|
|
<button class="primary-button" :disabled="loading" @click="queryStock">
|
|
{{ loading ? '查询中...' : '查询库存' }}
|
|
</button>
|
|
</view>
|
|
|
|
<view v-if="searched" class="result-section">
|
|
<view v-if="materialList.length > 0">
|
|
<view class="summary-card">
|
|
<view class="summary-main">
|
|
<text class="summary-tray">{{ currentTrayCode }}</text>
|
|
<text class="summary-location">{{ warehouseName }} / {{ cargoSpaceName }}</text>
|
|
</view>
|
|
<view class="summary-grid">
|
|
<view class="summary-item">
|
|
<text class="summary-value">{{ materialCount }}</text>
|
|
<text class="summary-label">物料种类</text>
|
|
</view>
|
|
<view class="summary-divider"></view>
|
|
<view class="summary-item">
|
|
<text class="summary-value">{{ totalQuantity }}</text>
|
|
<text class="summary-label">库存总量</text>
|
|
</view>
|
|
<view class="summary-divider"></view>
|
|
<view class="summary-item">
|
|
<text class="summary-value">{{ availableQuantity }}</text>
|
|
<text class="summary-label">可用库存</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="material-list">
|
|
<view class="material-card" v-for="(item, index) in materialList" :key="item.stockDetailId || index">
|
|
<view class="material-header">
|
|
<text class="material-name">{{ formatValue(item.materialName || item.productName) }}</text>
|
|
<text class="material-code">{{ formatValue(item.materialCode || item.productId) }}</text>
|
|
</view>
|
|
<view class="material-body">
|
|
<view class="info-item">
|
|
<text class="info-label">批次</text>
|
|
<text class="info-value">{{ formatValue(item.lotNo) }}</text>
|
|
</view>
|
|
<view class="info-item">
|
|
<text class="info-label">库存数量</text>
|
|
<text class="info-value">{{ formatNumber(item.quantity) }}</text>
|
|
</view>
|
|
<view class="info-item">
|
|
<text class="info-label">占用数量</text>
|
|
<text class="info-value">{{ formatNumber(item.outQuantity) }}</text>
|
|
</view>
|
|
<view class="info-item">
|
|
<text class="info-label">可用数量</text>
|
|
<text class="info-value strong">{{ formatNumber(item.availableQuantity) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-else class="empty-state">
|
|
<uni-icons type="info" size="30" color="#bfbfbf"></uni-icons>
|
|
<text class="empty-text">未查询到托盘库存</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { getTrayStock } from '@/api/warehouse/stock'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
trayCode: '',
|
|
currentTrayCode: '',
|
|
loading: false,
|
|
searched: false,
|
|
materialList: []
|
|
}
|
|
},
|
|
computed: {
|
|
materialCount() {
|
|
var map = {}
|
|
this.materialList.forEach(item => {
|
|
var key = item.materialCode || item.productId || item.stockDetailId
|
|
if (key) map[key] = true
|
|
})
|
|
return Object.keys(map).length
|
|
},
|
|
totalQuantity() {
|
|
return this.sumBy('quantity')
|
|
},
|
|
availableQuantity() {
|
|
return this.sumBy('availableQuantity')
|
|
},
|
|
warehouseName() {
|
|
return this.formatValue(this.materialList[0] && this.materialList[0].warehouseName)
|
|
},
|
|
cargoSpaceName() {
|
|
return this.formatValue(this.materialList[0] && this.materialList[0].cargoSpaceName)
|
|
}
|
|
},
|
|
methods: {
|
|
scanTray() {
|
|
uni.scanCode({
|
|
success: res => {
|
|
this.trayCode = res.result || ''
|
|
this.queryStock()
|
|
}
|
|
})
|
|
},
|
|
queryStock() {
|
|
var code = (this.trayCode || '').trim()
|
|
if (!code) {
|
|
this.$modal.msg('请扫描或输入托盘码')
|
|
return
|
|
}
|
|
this.loading = true
|
|
getTrayStock(code).then(res => {
|
|
this.currentTrayCode = code
|
|
this.materialList = res.data || []
|
|
this.searched = true
|
|
}).catch(() => {
|
|
this.materialList = []
|
|
this.searched = true
|
|
}).then(() => {
|
|
this.loading = false
|
|
})
|
|
},
|
|
sumBy(field) {
|
|
var total = 0
|
|
this.materialList.forEach(item => {
|
|
total += this.toNumber(item[field])
|
|
})
|
|
return this.formatNumber(total)
|
|
},
|
|
toNumber(value) {
|
|
var number = Number(value)
|
|
return isNaN(number) ? 0 : number
|
|
},
|
|
formatNumber(value) {
|
|
var number = this.toNumber(value)
|
|
return Number.isInteger(number) ? String(number) : String(Number(number.toFixed(3)))
|
|
},
|
|
formatValue(value) {
|
|
return value === undefined || value === null || value === '' ? '-' : value
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
page {
|
|
background-color: #f5f7fa;
|
|
min-height: 100%;
|
|
}
|
|
|
|
.page-container {
|
|
padding: 24rpx;
|
|
}
|
|
|
|
.query-card,
|
|
.summary-card,
|
|
.material-card,
|
|
.empty-state {
|
|
background: #fff;
|
|
border-radius: 16rpx;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
.query-card {
|
|
padding: 28rpx;
|
|
margin-bottom: 28rpx;
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 22rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 32rpx;
|
|
font-weight: 500;
|
|
color: #333;
|
|
}
|
|
|
|
.section-subtitle {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.input-row {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.tray-input {
|
|
flex: 1;
|
|
height: 82rpx;
|
|
padding: 0 24rpx;
|
|
border: 1rpx solid #e5e6eb;
|
|
border-radius: 12rpx;
|
|
background: #fafafa;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.icon-button {
|
|
width: 82rpx;
|
|
height: 82rpx;
|
|
padding: 0;
|
|
margin-left: 18rpx;
|
|
border-radius: 12rpx;
|
|
background: #e6f7ff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.icon-button::after,
|
|
.primary-button::after {
|
|
border: none;
|
|
}
|
|
|
|
.primary-button {
|
|
height: 84rpx;
|
|
line-height: 84rpx;
|
|
border-radius: 12rpx;
|
|
background: #1890ff;
|
|
color: #fff;
|
|
font-size: 30rpx;
|
|
}
|
|
|
|
.primary-button[disabled] {
|
|
background: #91caff;
|
|
color: #fff;
|
|
}
|
|
|
|
.result-section {
|
|
margin-bottom: 28rpx;
|
|
}
|
|
|
|
.summary-card {
|
|
padding: 30rpx 28rpx;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.summary-main {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding-bottom: 24rpx;
|
|
margin-bottom: 24rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.summary-tray {
|
|
font-size: 34rpx;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.summary-location {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.summary-grid {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.summary-item {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.summary-divider {
|
|
width: 1rpx;
|
|
height: 54rpx;
|
|
background: #f0f0f0;
|
|
}
|
|
|
|
.summary-value {
|
|
font-size: 36rpx;
|
|
font-weight: 600;
|
|
color: #1890ff;
|
|
margin-bottom: 6rpx;
|
|
}
|
|
|
|
.summary-label {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.material-card {
|
|
padding: 28rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.material-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
padding-bottom: 18rpx;
|
|
margin-bottom: 16rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.material-name {
|
|
flex: 1;
|
|
font-size: 30rpx;
|
|
font-weight: 500;
|
|
color: #333;
|
|
margin-right: 20rpx;
|
|
}
|
|
|
|
.material-code {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.material-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.info-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 9rpx 0;
|
|
}
|
|
|
|
.info-label {
|
|
font-size: 26rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.info-value {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.info-value.strong {
|
|
color: #1890ff;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.empty-state {
|
|
padding: 86rpx 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.empty-text {
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
margin-top: 16rpx;
|
|
}
|
|
</style>
|