chore: 保存盘点开发前改动

This commit is contained in:
zgw
2026-06-25 17:42:39 +08:00
parent 1af5b3cbd3
commit 4b8bc03343
114 changed files with 10005 additions and 1392 deletions

View File

@@ -1,82 +1,170 @@
<template>
<view class="page-container">
<view class="module-header">
<uni-icons type="bars" size="22" color="#722ed1"></uni-icons>
<text class="header-title">库存统计</text>
<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 class="summary-section">
<view class="summary-card">
<view class="summary-row">
<view class="summary-item">
<text class="summary-value">--</text>
<text class="summary-label">库存总量</text>
<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-divider"></view>
<view class="summary-item">
<text class="summary-value">--</text>
<text class="summary-label">物料种类</text>
<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="summary-row">
<view class="summary-item">
<text class="summary-value">--</text>
<text class="summary-label">今日入库</text>
</view>
<view class="summary-divider"></view>
<view class="summary-item">
<text class="summary-value">--</text>
<text class="summary-label">今日出库</text>
<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>
<view class="feature-list">
<view class="feature-card" @click="$modal.showToast('功能开发中')">
<view class="feature-left">
<uni-icons type="search" size="20" color="#1890ff"></uni-icons>
<text class="feature-name">库存查询</text>
</view>
<uni-icons type="right" size="16" color="#ccc"></uni-icons>
<view v-else class="empty-state">
<uni-icons type="info" size="30" color="#bfbfbf"></uni-icons>
<text class="empty-text">未查询到托盘库存</text>
</view>
<view class="feature-card" @click="$modal.showToast('功能开发中')">
<view class="feature-left">
<uni-icons type="location" size="20" color="#fa8c16"></uni-icons>
<text class="feature-name">库位分布</text>
</view>
<uni-icons type="right" size="16" color="#ccc"></uni-icons>
</view>
<view class="feature-card" @click="$modal.showToast('功能开发中')">
<view class="feature-left">
<uni-icons type="pie-chart" size="20" color="#52c41a"></uni-icons>
<text class="feature-name">库存报表</text>
</view>
<uni-icons type="right" size="16" color="#ccc"></uni-icons>
</view>
<view class="feature-card" @click="$modal.showToast('功能开发中')">
<view class="feature-left">
<uni-icons type="alert" size="20" color="#f5222d"></uni-icons>
<text class="feature-name">库存预警</text>
</view>
<uni-icons type="right" size="16" color="#ccc"></uni-icons>
</view>
</view>
<view class="empty-tip">
<text class="tip-text">库存统计模块正在开发中,敬请期待</text>
</view>
</view>
</template>
<script>
import { getTrayStock } from '@/api/warehouse/stock'
export default {
data() {
return {}
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>
@@ -91,101 +179,210 @@
padding: 24rpx;
}
.module-header {
display: flex;
align-items: center;
padding: 24rpx 0;
margin-bottom: 20rpx;
.query-card,
.summary-card,
.material-card,
.empty-state {
background: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.header-title {
.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;
margin-left: 12rpx;
}
.summary-section {
margin-bottom: 32rpx;
.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 {
background: linear-gradient(135deg, #1890ff, #096dd9);
border-radius: 16rpx;
padding: 40rpx 28rpx;
padding: 30rpx 28rpx;
margin-bottom: 24rpx;
}
.summary-row {
.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-around;
}
.summary-row:first-child {
margin-bottom: 32rpx;
}
.summary-divider {
width: 1rpx;
height: 60rpx;
background: rgba(255, 255, 255, 0.3);
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: 40rpx;
font-weight: 500;
color: #fff;
margin-bottom: 8rpx;
font-size: 36rpx;
font-weight: 600;
color: #1890ff;
margin-bottom: 6rpx;
}
.summary-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
font-size: 22rpx;
color: #999;
}
.feature-list {
background: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
.material-card {
padding: 28rpx;
margin-bottom: 20rpx;
}
.feature-card {
.material-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 28rpx;
border-bottom: 1rpx solid #f5f5f5;
align-items: flex-start;
padding-bottom: 18rpx;
margin-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.feature-card:last-child {
border-bottom: none;
}
.feature-left {
display: flex;
align-items: center;
}
.feature-name {
font-size: 28rpx;
.material-name {
flex: 1;
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-left: 16rpx;
margin-right: 20rpx;
}
.empty-tip {
padding: 60rpx 0;
text-align: center;
.material-code {
font-size: 24rpx;
color: #999;
}
.tip-text {
.material-body {
display: flex;
flex-direction: column;
}
.info-item {
display: flex;
justify-content: space-between;
padding: 9rpx 0;
}
.info-label {
font-size: 26rpx;
color: #ccc;
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>