|
|
@@ -7,14 +7,22 @@
|
|
|
</view>
|
|
|
|
|
|
<template v-else>
|
|
|
+ <!-- 补货单选择器 -->
|
|
|
+ <view class="order-selector" v-if="pendingOrders.length > 1">
|
|
|
+ <text class="selector-label">补货单</text>
|
|
|
+ <picker mode="selector" :range="pendingOrders" range-key="orderNo" :value="selectedOrderIndex" @change="onOrderChange">
|
|
|
+ <view class="selector-value"><text>{{ pendingOrders[selectedOrderIndex]?.orderNo || '' }}</text><text class="selector-arrow">▾</text></view>
|
|
|
+ </picker>
|
|
|
+ <text class="selector-status">{{ pendingOrders[selectedOrderIndex]?.statusLabel || '' }}</text>
|
|
|
+ </view>
|
|
|
<view class="order-banner" v-if="orderNo">
|
|
|
- <text class="order-banner-text">补货单 {{ orderNo }} · 数量已按计划预填</text>
|
|
|
+ <text class="order-banner-text">补货单 {{ orderNo }}</text>
|
|
|
</view>
|
|
|
|
|
|
<view class="device-card">
|
|
|
<view class="device-icon"></view>
|
|
|
<view class="device-info">
|
|
|
- <text class="device-name">{{ deviceInfo.name || deviceInfo.deviceId }}</text>
|
|
|
+ <text class="device-name">{{ deviceInfo.deviceId }} - {{ deviceInfo.name || '' }}</text>
|
|
|
<text class="device-addr" v-if="deviceInfo.address">{{ deviceInfo.address }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -75,7 +83,7 @@
|
|
|
<script setup lang="ts">
|
|
|
import { ref, computed, onMounted } from 'vue';
|
|
|
import NavBar from '@/components/NavBar.vue';
|
|
|
-import { getDeviceInventory, replenishStock } from '@/api/replenish';
|
|
|
+import { getDeviceInventory, replenishStock, getDevicePendingOrders, getReplenisherOrderDetail } from '@/api/replenish';
|
|
|
|
|
|
interface ReplenishInput {
|
|
|
productId: number;
|
|
|
@@ -96,13 +104,24 @@ const orderId = ref('');
|
|
|
const orderNo = ref('');
|
|
|
const showOrderOnly = ref(false);
|
|
|
const orderItemCodes = ref<Set<string>>(new Set());
|
|
|
+const pendingOrders = ref<any[]>([]);
|
|
|
+const selectedOrderIndex = ref(0);
|
|
|
|
|
|
const isInOrder = (item: any) => {
|
|
|
- const code = item.product_code || item.productCode || '';
|
|
|
- return orderItemCodes.value.has(code);
|
|
|
+ const code = (item.product_code || item.productCode || '').trim().toUpperCase();
|
|
|
+ for (const oc of orderItemCodes.value) { if (oc.toUpperCase() === code) return true; }
|
|
|
+ return false;
|
|
|
};
|
|
|
|
|
|
-const totalReplenishCount = computed(() => replenishItems.value.reduce((s, i) => s + (i.quantity || 0), 0));
|
|
|
+// 补货单预填数据
|
|
|
+const orderItemsData = ref<any[]>([]);
|
|
|
+
|
|
|
+const totalReplenishCount = computed(() => {
|
|
|
+ if (orderItemsData.value.length > 0) {
|
|
|
+ return orderItemsData.value.reduce((s, oi) => s + (oi.plannedQuantity || 0), 0);
|
|
|
+ }
|
|
|
+ return replenishItems.value.reduce((s, i) => s + (i.quantity || 0), 0);
|
|
|
+});
|
|
|
|
|
|
const isLowStock = (item: any) => { const s = item.stock || 0; const t = item.warning_threshold || item.warningThreshold || 5; return s > 0 && s <= t; };
|
|
|
const isOutOfStock = (item: any) => (item.stock || 0) === 0;
|
|
|
@@ -139,6 +158,44 @@ const handleSubmit = async () => {
|
|
|
} finally { submitting.value = false; }
|
|
|
};
|
|
|
|
|
|
+const loadOrderItems = async (oid: string) => {
|
|
|
+ try {
|
|
|
+ const data = await getReplenisherOrderDetail(oid as any);
|
|
|
+ if (!data?.items?.length) return;
|
|
|
+ orderId.value = oid;
|
|
|
+ orderNo.value = data.order?.orderNo || '';
|
|
|
+ const items = data.items.map((item: any) => ({
|
|
|
+ productCode: (item.productCode || '').trim(),
|
|
|
+ productName: item.productName,
|
|
|
+ plannedQuantity: item.plannedQuantity || item.quantity || 0,
|
|
|
+ shelfNum: item.shelfNum,
|
|
|
+ position: item.position
|
|
|
+ }));
|
|
|
+ orderItemsData.value = items;
|
|
|
+ orderItemCodes.value = new Set(items.map((oi: any) => oi.productCode.toUpperCase()));
|
|
|
+
|
|
|
+ // 重新匹配库存并更新数量
|
|
|
+ const list = inventoryList.value;
|
|
|
+ replenishItems.value = list.filter((it: any) => it.product_id || it.productId).map((it: any) => {
|
|
|
+ const code = (it.product_code || it.productCode || '').trim();
|
|
|
+ const m = items.find((oi: any) => oi.productCode.toUpperCase() === code.toUpperCase());
|
|
|
+ return {
|
|
|
+ productId: it.product_id || it.productId, productCode: code,
|
|
|
+ productName: it.product_name || it.productName || '',
|
|
|
+ quantity: m ? (m.plannedQuantity || 0) : 0,
|
|
|
+ shelfNum: it.shelf_num || it.shelfNum || null, position: it.position || null
|
|
|
+ };
|
|
|
+ });
|
|
|
+ } catch { /* ignore */ }
|
|
|
+};
|
|
|
+
|
|
|
+const onOrderChange = (e: any) => {
|
|
|
+ const idx = e.detail.value;
|
|
|
+ selectedOrderIndex.value = idx;
|
|
|
+ const o = pendingOrders.value[idx];
|
|
|
+ if (o) loadOrderItems(String(o.id));
|
|
|
+};
|
|
|
+
|
|
|
onMounted(async () => {
|
|
|
const pages = getCurrentPages();
|
|
|
const opts = (pages[pages.length - 1] as any)?.$page?.options || (pages[pages.length - 1] as any)?.options || {};
|
|
|
@@ -152,15 +209,51 @@ onMounted(async () => {
|
|
|
const list = data.inventoryList || [];
|
|
|
inventoryList.value = list;
|
|
|
|
|
|
+ // 加载待处理补货单列表
|
|
|
+ pendingOrders.value = await getDevicePendingOrders(deviceId.value);
|
|
|
+
|
|
|
let orderItems: any[] = [];
|
|
|
- const orderStr = uni.getStorageSync('pendingReplenishOrder');
|
|
|
- if (orderStr) {
|
|
|
- try { const od = JSON.parse(orderStr); if (od.orderId === orderId.value) { orderNo.value = od.orderNo || ''; orderItems = od.items || []; orderItemCodes.value = new Set(orderItems.map((oi: any) => oi.productCode)); showOrderOnly.value = true; } } catch { }
|
|
|
+ let targetIndex = 0;
|
|
|
+
|
|
|
+ if (pendingOrders.value.length > 0) {
|
|
|
+ // 如果有传入orderId,定位到对应补货单
|
|
|
+ if (orderId.value) {
|
|
|
+ targetIndex = pendingOrders.value.findIndex((o: any) => String(o.id) === String(orderId.value));
|
|
|
+ if (targetIndex < 0) targetIndex = 0;
|
|
|
+ }
|
|
|
+ selectedOrderIndex.value = targetIndex;
|
|
|
+ const targetOrder = pendingOrders.value[targetIndex];
|
|
|
+ if (targetOrder) {
|
|
|
+ // 拉取补货单详情
|
|
|
+ try {
|
|
|
+ const detail = await getReplenisherOrderDetail(targetOrder.id);
|
|
|
+ if (detail?.items) {
|
|
|
+ orderId.value = String(targetOrder.id);
|
|
|
+ orderNo.value = targetOrder.orderNo || '';
|
|
|
+ orderItems = detail.items.map((item: any) => ({
|
|
|
+ productCode: (item.productCode || '').trim(),
|
|
|
+ productName: item.productName,
|
|
|
+ plannedQuantity: item.plannedQuantity || item.quantity || 0,
|
|
|
+ shelfNum: item.shelfNum,
|
|
|
+ position: item.position
|
|
|
+ }));
|
|
|
+ orderItemsData.value = orderItems;
|
|
|
+ orderItemCodes.value = new Set(orderItems.map((oi: any) => oi.productCode.toUpperCase()));
|
|
|
+ showOrderOnly.value = true;
|
|
|
+ }
|
|
|
+ } catch { /* fallback */ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 没有待处理补货单时尝试从localStorage加载
|
|
|
+ const orderStr = uni.getStorageSync('pendingReplenishOrder');
|
|
|
+ if (orderStr) {
|
|
|
+ try { const od = JSON.parse(orderStr); if (String(od.orderId) === String(orderId.value)) { orderNo.value = od.orderNo || ''; orderItems = od.items || []; orderItemsData.value = orderItems; orderItemCodes.value = new Set(orderItems.map((oi: any) => (oi.productCode || '').trim().toUpperCase())); showOrderOnly.value = true; } } catch { }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
replenishItems.value = list.filter((it: any) => it.product_id || it.productId).map((it: any) => {
|
|
|
- const code = it.product_code || it.productCode || '';
|
|
|
- const m = orderItems.find((oi: any) => oi.productCode === code);
|
|
|
+ const code = (it.product_code || it.productCode || '').trim();
|
|
|
+ const m = orderItems.find((oi: any) => oi.productCode.toUpperCase() === code.toUpperCase());
|
|
|
return {
|
|
|
productId: it.product_id || it.productId, productCode: code,
|
|
|
productName: it.product_name || it.productName || '',
|
|
|
@@ -184,6 +277,13 @@ onMounted(async () => {
|
|
|
}
|
|
|
@keyframes pulse { 0%,80%,100% { transform: scale(0.5); opacity: 0.4; } 40% { transform: scale(1); opacity: 1; } }
|
|
|
|
|
|
+.order-selector { display: flex; align-items: center; gap: 12rpx; margin: $spacing-2 $spacing-3; padding: $spacing-2 $spacing-3; background: $primary-color-bg; border-radius: $radius-sm; }
|
|
|
+.selector-label { font-size: $font-size-sm; color: $text-color-muted; flex-shrink: 0; }
|
|
|
+.selector-value { display: flex; align-items: center; gap: 6rpx; padding: 6rpx 16rpx; background: $bg-color-card; border-radius: $radius-sm; border: 1rpx solid $border-color;
|
|
|
+ text { font-size: $font-size-sm; color: $text-color-primary; font-weight: 500; }
|
|
|
+}
|
|
|
+.selector-arrow { font-size: 18rpx; color: $text-color-muted; }
|
|
|
+.selector-status { font-size: 22rpx; color: $primary-color-dark; margin-left: auto; }
|
|
|
.order-banner { margin: $spacing-2 $spacing-3; padding: $spacing-2 $spacing-3; background: $primary-color-bg; border-radius: $radius-sm; }
|
|
|
.order-banner-text { font-size: $font-size-sm; color: $primary-color-dark; }
|
|
|
|