正在显示
31 个修改的文件
包含
1099 行增加
和
775 行删除
| @@ -59,13 +59,17 @@ public record InboundScannedRow( | @@ -59,13 +59,17 @@ public record InboundScannedRow( | ||
| 59 | 59 | ||
| 60 | 60 | ||
| 61 | public record OutboundPendingRow( | 61 | public record OutboundPendingRow( |
| 62 | - string? Barcode, | ||
| 63 | - string? DetailId, | ||
| 64 | - string? Location, | ||
| 65 | string? MaterialName, | 62 | string? MaterialName, |
| 66 | - int PendingQty, // instockQty | ||
| 67 | - int ScannedQty, // qty | ||
| 68 | - string? Spec); | 63 | + string? MaterialCode, |
| 64 | + string? Spec, | ||
| 65 | + string? Location, | ||
| 66 | + string? ProductionBatch, | ||
| 67 | + string? StockBatch, | ||
| 68 | + int OutstockQty, | ||
| 69 | + int Qty | ||
| 70 | +); | ||
| 71 | + | ||
| 72 | + | ||
| 69 | 73 | ||
| 70 | public record OutboundScannedRow( | 74 | public record OutboundScannedRow( |
| 71 | string Barcode, | 75 | string Barcode, |
| @@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
| 2 | <ContentPage x:Class="IndustrialControl.Pages.InboundMoldPage" | 2 | <ContentPage x:Class="IndustrialControl.Pages.InboundMoldPage" |
| 3 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 3 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 4 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 4 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 5 | - Title="仓储管理系统"> | 5 | + Shell.NavBarIsVisible="True"> |
| 6 | 6 | ||
| 7 | <Grid RowDefinitions="Auto,Auto,*,Auto,Auto" Padding="12" RowSpacing="12"> | 7 | <Grid RowDefinitions="Auto,Auto,*,Auto,Auto" Padding="12" RowSpacing="12"> |
| 8 | 8 |
| 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 3 | x:Class="IndustrialControl.Pages.OutboundMoldPage" | 3 | x:Class="IndustrialControl.Pages.OutboundMoldPage" |
| 4 | - BackgroundColor="White"> | 4 | + BackgroundColor="White" Shell.NavBarIsVisible="True"> |
| 5 | 5 | ||
| 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> | 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> |
| 7 | 7 |
| @@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
| 2 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 2 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 3 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 3 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 4 | x:Class="IndustrialControl.Pages.OutboundMoldSearchPage" | 4 | x:Class="IndustrialControl.Pages.OutboundMoldSearchPage" |
| 5 | - Title="工单查询"> | 5 | + Shell.NavBarIsVisible="True"> |
| 6 | 6 | ||
| 7 | <!-- 根布局:顶部(蓝条) / 条件区 / 列表 --> | 7 | <!-- 根布局:顶部(蓝条) / 条件区 / 列表 --> |
| 8 | <Grid RowDefinitions="Auto,Auto,*"> | 8 | <Grid RowDefinitions="Auto,Auto,*"> |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 4 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 5 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 5 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 6 | xmlns:conv="clr-namespace:IndustrialControl.Converters" | 6 | xmlns:conv="clr-namespace:IndustrialControl.Converters" |
| 7 | - Title="模具出库执行"> | 7 | + Shell.NavBarIsVisible="True"> |
| 8 | <ContentPage.Resources> | 8 | <ContentPage.Resources> |
| 9 | <ResourceDictionary> | 9 | <ResourceDictionary> |
| 10 | <!-- 转换器 --> | 10 | <!-- 转换器 --> |
| @@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
| 2 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 2 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 3 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 3 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 4 | x:Class="IndustrialControl.Pages.WorkOrderSearchPage" | 4 | x:Class="IndustrialControl.Pages.WorkOrderSearchPage" |
| 5 | - Title="工单查询"> | 5 | + Shell.NavBarIsVisible="True"> |
| 6 | 6 | ||
| 7 | <!-- 根布局:顶部(蓝条) / 条件区 / 列表 --> | 7 | <!-- 根布局:顶部(蓝条) / 条件区 / 列表 --> |
| 8 | <Grid RowDefinitions="Auto,Auto,*"> | 8 | <Grid RowDefinitions="Auto,Auto,*"> |
| 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 3 | x:Class="IndustrialControl.Pages.InboundMaterialPage" | 3 | x:Class="IndustrialControl.Pages.InboundMaterialPage" |
| 4 | - BackgroundColor="White"> | 4 | + BackgroundColor="White" |
| 5 | + Shell.NavBarIsVisible="True"> | ||
| 5 | 6 | ||
| 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> | 7 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> |
| 7 | 8 | ||
| @@ -231,7 +232,7 @@ | @@ -231,7 +232,7 @@ | ||
| 231 | VerticalOptions="Fill" | 232 | VerticalOptions="Fill" |
| 232 | HeightRequest="50"> | 233 | HeightRequest="50"> |
| 233 | <Grid.GestureRecognizers> | 234 | <Grid.GestureRecognizers> |
| 234 | - <TapGestureRecognizer Command="{Binding ConfirmCommand}" /> | 235 | + <TapGestureRecognizer Tapped="OnConfirmClicked" /> |
| 235 | </Grid.GestureRecognizers> | 236 | </Grid.GestureRecognizers> |
| 236 | <StackLayout Orientation="Horizontal" | 237 | <StackLayout Orientation="Horizontal" |
| 237 | HorizontalOptions="Center" | 238 | HorizontalOptions="Center" |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 4 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 5 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 5 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 6 | x:Name="Page" | 6 | x:Name="Page" |
| 7 | - Title="入库单查询"> | 7 | + Shell.NavBarIsVisible="True"> |
| 8 | 8 | ||
| 9 | <ScrollView> | 9 | <ScrollView> |
| 10 | <VerticalStackLayout Spacing="12" Padding="12"> | 10 | <VerticalStackLayout Spacing="12" Padding="12"> |
| 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 3 | x:Class="IndustrialControl.Pages.InboundProductionPage" | 3 | x:Class="IndustrialControl.Pages.InboundProductionPage" |
| 4 | - BackgroundColor="White"> | 4 | + BackgroundColor="White" Shell.NavBarIsVisible="True"> |
| 5 | 5 | ||
| 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> | 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> |
| 7 | 7 | ||
| @@ -45,7 +45,7 @@ | @@ -45,7 +45,7 @@ | ||
| 45 | <Label.FormattedText> | 45 | <Label.FormattedText> |
| 46 | <FormattedString> | 46 | <FormattedString> |
| 47 | <Span Text="工单号:" FontAttributes="Bold"/> | 47 | <Span Text="工单号:" FontAttributes="Bold"/> |
| 48 | - <Span Text="{Binding ArrivalNo}"/> | 48 | + <Span Text="{Binding WorkOrderNo}"/> |
| 49 | </FormattedString> | 49 | </FormattedString> |
| 50 | </Label.FormattedText> | 50 | </Label.FormattedText> |
| 51 | </Label> | 51 | </Label> |
| @@ -55,7 +55,7 @@ | @@ -55,7 +55,7 @@ | ||
| 55 | <Label.FormattedText> | 55 | <Label.FormattedText> |
| 56 | <FormattedString> | 56 | <FormattedString> |
| 57 | <Span Text="产品名称:" FontAttributes="Bold"/> | 57 | <Span Text="产品名称:" FontAttributes="Bold"/> |
| 58 | - <Span Text="{Binding PurchaseNo}"/> | 58 | + <Span Text="{Binding MaterialName}"/> |
| 59 | </FormattedString> | 59 | </FormattedString> |
| 60 | </Label.FormattedText> | 60 | </Label.FormattedText> |
| 61 | </Label> | 61 | </Label> |
| @@ -65,7 +65,7 @@ | @@ -65,7 +65,7 @@ | ||
| 65 | <Label.FormattedText> | 65 | <Label.FormattedText> |
| 66 | <FormattedString> | 66 | <FormattedString> |
| 67 | <Span Text="待入库数:" FontAttributes="Bold"/> | 67 | <Span Text="待入库数:" FontAttributes="Bold"/> |
| 68 | - <Span Text="{Binding SupplierName}"/> | 68 | + <Span Text="{Binding InstockQty}"/> |
| 69 | </FormattedString> | 69 | </FormattedString> |
| 70 | </Label.FormattedText> | 70 | </Label.FormattedText> |
| 71 | </Label> | 71 | </Label> |
| @@ -212,7 +212,7 @@ | @@ -212,7 +212,7 @@ | ||
| 212 | VerticalOptions="Fill" | 212 | VerticalOptions="Fill" |
| 213 | HeightRequest="50"> | 213 | HeightRequest="50"> |
| 214 | <Grid.GestureRecognizers> | 214 | <Grid.GestureRecognizers> |
| 215 | - <TapGestureRecognizer Command="{Binding ConfirmCommand}" /> | 215 | + <TapGestureRecognizer Tapped="OnConfirmClicked" /> |
| 216 | </Grid.GestureRecognizers> | 216 | </Grid.GestureRecognizers> |
| 217 | <StackLayout Orientation="Horizontal" | 217 | <StackLayout Orientation="Horizontal" |
| 218 | HorizontalOptions="Center" | 218 | HorizontalOptions="Center" |
| @@ -9,6 +9,9 @@ namespace IndustrialControl.Pages; | @@ -9,6 +9,9 @@ namespace IndustrialControl.Pages; | ||
| 9 | [QueryProperty(nameof(OrderTypeName), "orderTypeName")] | 9 | [QueryProperty(nameof(OrderTypeName), "orderTypeName")] |
| 10 | [QueryProperty(nameof(PurchaseNo), "purchaseNo")] | 10 | [QueryProperty(nameof(PurchaseNo), "purchaseNo")] |
| 11 | [QueryProperty(nameof(SupplierName), "supplierName")] | 11 | [QueryProperty(nameof(SupplierName), "supplierName")] |
| 12 | +[QueryProperty(nameof(WorkOrderNo), "workOrderNo")] | ||
| 13 | +[QueryProperty(nameof(MaterialName), "materialName")] | ||
| 14 | +[QueryProperty(nameof(InstockQty), "instockQty")] | ||
| 12 | [QueryProperty(nameof(CreatedTime), "createdTime")] | 15 | [QueryProperty(nameof(CreatedTime), "createdTime")] |
| 13 | public partial class InboundProductionPage : ContentPage | 16 | public partial class InboundProductionPage : ContentPage |
| 14 | { | 17 | { |
| @@ -21,6 +24,10 @@ public partial class InboundProductionPage : ContentPage | @@ -21,6 +24,10 @@ public partial class InboundProductionPage : ContentPage | ||
| 21 | public string? PurchaseNo { get; set; } | 24 | public string? PurchaseNo { get; set; } |
| 22 | public string? SupplierName { get; set; } | 25 | public string? SupplierName { get; set; } |
| 23 | public string? CreatedTime { get; set; } | 26 | public string? CreatedTime { get; set; } |
| 27 | + public string? WorkOrderNo { get; set; } | ||
| 28 | + public string? MaterialName { get; set; } | ||
| 29 | + public int InstockQty { get; set; } | ||
| 30 | + | ||
| 24 | private readonly IDialogService _dialogs; | 31 | private readonly IDialogService _dialogs; |
| 25 | 32 | ||
| 26 | public InboundProductionPage(InboundProductionViewModel vm, ScanService scanSvc, IDialogService dialogs) | 33 | public InboundProductionPage(InboundProductionViewModel vm, ScanService scanSvc, IDialogService dialogs) |
| @@ -53,6 +60,9 @@ public partial class InboundProductionPage : ContentPage | @@ -53,6 +60,9 @@ public partial class InboundProductionPage : ContentPage | ||
| 53 | orderTypeName: OrderTypeName ?? "", | 60 | orderTypeName: OrderTypeName ?? "", |
| 54 | purchaseNo: PurchaseNo ?? "", | 61 | purchaseNo: PurchaseNo ?? "", |
| 55 | supplierName: SupplierName ?? "", | 62 | supplierName: SupplierName ?? "", |
| 63 | + workOrderNo: WorkOrderNo ?? "", | ||
| 64 | + materialName: MaterialName ?? "", | ||
| 65 | + instockQty: InstockQty, | ||
| 56 | createdTime: CreatedTime ?? "" | 66 | createdTime: CreatedTime ?? "" |
| 57 | ); | 67 | ); |
| 58 | } | 68 | } |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 4 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 5 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 5 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 6 | x:Name="Page" | 6 | x:Name="Page" |
| 7 | - Title="入库单查询"> | 7 | + Shell.NavBarIsVisible="True"> |
| 8 | 8 | ||
| 9 | <ScrollView> | 9 | <ScrollView> |
| 10 | <VerticalStackLayout Spacing="12" Padding="12"> | 10 | <VerticalStackLayout Spacing="12" Padding="12"> |
| 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 3 | x:Class="IndustrialControl.Pages.OutboundFinishedPage" | 3 | x:Class="IndustrialControl.Pages.OutboundFinishedPage" |
| 4 | - BackgroundColor="White"> | 4 | + BackgroundColor="White" Shell.NavBarIsVisible="True"> |
| 5 | 5 | ||
| 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> | 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> |
| 7 | 7 | ||
| @@ -14,216 +14,243 @@ | @@ -14,216 +14,243 @@ | ||
| 14 | FontAttributes="Bold"/> | 14 | FontAttributes="Bold"/> |
| 15 | </Grid> | 15 | </Grid> |
| 16 | 16 | ||
| 17 | - <!-- 出库单/发货单扫描 --> | 17 | + <!-- 入库单/条码扫描 --> |
| 18 | <Grid Grid.Row="1" ColumnDefinitions="*,60" Padding="16,8"> | 18 | <Grid Grid.Row="1" ColumnDefinitions="*,60" Padding="16,8"> |
| 19 | <Entry x:Name="ScanEntry" | 19 | <Entry x:Name="ScanEntry" |
| 20 | - Placeholder="请输入/扫描出库单号/产品条码" | 20 | + Placeholder="请扫描出库单/产品/包装条码" |
| 21 | FontSize="14" | 21 | FontSize="14" |
| 22 | VerticalOptions="Center" | 22 | VerticalOptions="Center" |
| 23 | BackgroundColor="White" | 23 | BackgroundColor="White" |
| 24 | HeightRequest="40" | 24 | HeightRequest="40" |
| 25 | - Text="{Binding OrderNo}" /> | ||
| 26 | - <ImageButton Grid.Column="1" | ||
| 27 | - Source="scan.png" | ||
| 28 | - BackgroundColor="#E6F2FF" | ||
| 29 | - CornerRadius="4" | ||
| 30 | - Padding="10" | ||
| 31 | - Clicked="OnScanClicked"/> | 25 | + Text="{Binding ScanCode}" /> |
| 26 | + | ||
| 32 | </Grid> | 27 | </Grid> |
| 33 | 28 | ||
| 34 | <!-- 基础信息 --> | 29 | <!-- 基础信息 --> |
| 35 | - <Frame Grid.Row="2" Margin="16,0" Padding="8" BorderColor="#CCCCCC" BackgroundColor="White"> | ||
| 36 | - <Grid RowDefinitions="Auto,Auto,Auto,Auto" | ||
| 37 | - ColumnDefinitions="Auto,* ,Auto,*" | ||
| 38 | - ColumnSpacing="8" | ||
| 39 | - RowSpacing="6"> | ||
| 40 | - | ||
| 41 | - <!-- 出库单号(独占一行) --> | ||
| 42 | - <Label Grid.Row="0" Grid.Column="0" Text="出库单号:" FontAttributes="Bold" VerticalOptions="Center" /> | ||
| 43 | - <Label Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3" | ||
| 44 | - Text="{Binding OrderNo}" VerticalOptions="Center" /> | ||
| 45 | - | ||
| 46 | - <!-- 发货单号 + 客户 --> | ||
| 47 | - <Label Grid.Row="1" Grid.Column="0" Text="发货单号:" FontAttributes="Bold" VerticalOptions="Center" /> | ||
| 48 | - <Label Grid.Row="1" Grid.Column="1" Text="{Binding DeliveryNo}" VerticalOptions="Center" /> | ||
| 49 | - <Label Grid.Row="1" Grid.Column="2" Text="客户:" FontAttributes="Bold" VerticalOptions="Center" /> | ||
| 50 | - <Label Grid.Row="1" Grid.Column="3" Text="{Binding Customer}" VerticalOptions="Center" /> | ||
| 51 | - | ||
| 52 | - <!-- 要求发货时间 + 关联销售单 --> | ||
| 53 | - <Label Grid.Row="2" Grid.Column="0" Text="要求发货时间:" FontAttributes="Bold" VerticalOptions="Center" /> | ||
| 54 | - <Label Grid.Row="2" Grid.Column="1" Text="{Binding DeliveryTime}" VerticalOptions="Center" /> | ||
| 55 | - <Label Grid.Row="2" Grid.Column="2" Text="关联销售单:" FontAttributes="Bold" VerticalOptions="Center" /> | ||
| 56 | - <Label Grid.Row="2" Grid.Column="3" Text="{Binding SalesOrder}" VerticalOptions="Center" /> | ||
| 57 | - | ||
| 58 | - <!-- 发货单备注(独占一行) --> | ||
| 59 | - <Label Grid.Row="3" Grid.Column="0" Text="发货单备注:" FontAttributes="Bold" VerticalOptions="Center" /> | ||
| 60 | - <Label Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" | ||
| 61 | - Text="{Binding Remark}" VerticalOptions="Center" /> | 30 | + <Frame Grid.Row="2" Margin="8,0" Padding="8" BorderColor="#CCCCCC" BackgroundColor="White"> |
| 31 | + <Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,*" ColumnSpacing="16" RowSpacing="6"> | ||
| 62 | 32 | ||
| 63 | - </Grid> | ||
| 64 | - </Frame> | 33 | + <!-- 出库单号 --> |
| 34 | + <Label Grid.Row="0" Grid.Column="0" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 35 | + <Label.FormattedText> | ||
| 36 | + <FormattedString> | ||
| 37 | + <Span Text="出库单号:" FontAttributes="Bold"/> | ||
| 38 | + <Span Text="{Binding OutstockNo}"/> | ||
| 39 | + </FormattedString> | ||
| 40 | + </Label.FormattedText> | ||
| 41 | + </Label> | ||
| 65 | 42 | ||
| 43 | + <!-- 发货单号 --> | ||
| 44 | + <Label Grid.Row="1" Grid.Column="0" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 45 | + <Label.FormattedText> | ||
| 46 | + <FormattedString> | ||
| 47 | + <Span Text="发货单号:" FontAttributes="Bold"/> | ||
| 48 | + <Span Text="{Binding DeliveryNo}"/> | ||
| 49 | + </FormattedString> | ||
| 50 | + </Label.FormattedText> | ||
| 51 | + </Label> | ||
| 66 | 52 | ||
| 53 | + <!-- 客户 --> | ||
| 54 | + <Label Grid.Row="1" Grid.Column="1" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 55 | + <Label.FormattedText> | ||
| 56 | + <FormattedString> | ||
| 57 | + <Span Text="客户:" FontAttributes="Bold"/> | ||
| 58 | + <Span Text="{Binding PurchaseNo}"/> | ||
| 59 | + </FormattedString> | ||
| 60 | + </Label.FormattedText> | ||
| 61 | + </Label> | ||
| 62 | + | ||
| 63 | + <!-- 要求发货时间 --> | ||
| 64 | + <Label Grid.Row="2" Grid.Column="0" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 65 | + <Label.FormattedText> | ||
| 66 | + <FormattedString> | ||
| 67 | + <Span Text="要求发货时间:" FontAttributes="Bold"/> | ||
| 68 | + <Span Text="{Binding SupplierName}"/> | ||
| 69 | + </FormattedString> | ||
| 70 | + </Label.FormattedText> | ||
| 71 | + </Label> | ||
| 72 | + <!-- 关联销售单 --> | ||
| 73 | + <Label Grid.Row="2" Grid.Column="1" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 74 | + <Label.FormattedText> | ||
| 75 | + <FormattedString> | ||
| 76 | + <Span Text="关联销售单:" FontAttributes="Bold"/> | ||
| 77 | + <Span Text="{Binding SupplierName}"/> | ||
| 78 | + </FormattedString> | ||
| 79 | + </Label.FormattedText> | ||
| 80 | + </Label> | ||
| 81 | + <!-- 发货单备注 --> | ||
| 82 | + <Label Grid.Row="3" Grid.Column="0" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 83 | + <Label.FormattedText> | ||
| 84 | + <FormattedString> | ||
| 85 | + <Span Text="发货单备注:" FontAttributes="Bold"/> | ||
| 86 | + <Span Text="{Binding SupplierName}"/> | ||
| 87 | + </FormattedString> | ||
| 88 | + </Label.FormattedText> | ||
| 89 | + </Label> | ||
| 90 | + </Grid> | ||
| 91 | + </Frame> | ||
| 67 | 92 | ||
| 68 | - <!-- Tab切换 + 表格 --> | 93 | + <!-- Tab切换 --> |
| 69 | <Grid Grid.Row="3" RowDefinitions="Auto,Auto,*" Margin="0"> | 94 | <Grid Grid.Row="3" RowDefinitions="Auto,Auto,*" Margin="0"> |
| 70 | - <!-- Tab --> | ||
| 71 | <Grid ColumnDefinitions="*,*" BackgroundColor="White"> | 95 | <Grid ColumnDefinitions="*,*" BackgroundColor="White"> |
| 72 | - <Button Text="出库单明细" | 96 | + <Button Text="待入库明细" |
| 97 | + Command="{Binding ShowPendingCommand}" | ||
| 73 | BackgroundColor="{Binding PendingTabColor}" | 98 | BackgroundColor="{Binding PendingTabColor}" |
| 74 | - TextColor="{Binding PendingTextColor}" | ||
| 75 | - Clicked="OnPendingTabClicked"/> | 99 | + TextColor="{Binding PendingTextColor}" /> |
| 76 | <Button Text="扫描明细" | 100 | <Button Text="扫描明细" |
| 77 | Grid.Column="1" | 101 | Grid.Column="1" |
| 102 | + Command="{Binding ShowScannedCommand}" | ||
| 78 | BackgroundColor="{Binding ScannedTabColor}" | 103 | BackgroundColor="{Binding ScannedTabColor}" |
| 79 | - TextColor="{Binding ScannedTextColor}" | ||
| 80 | - Clicked="OnScannedTabClicked"/> | 104 | + TextColor="{Binding ScannedTextColor}" /> |
| 81 | </Grid> | 105 | </Grid> |
| 82 | 106 | ||
| 83 | - <!-- 出库单明细表头 --> | ||
| 84 | - <!-- 出库单明细表头 --> | ||
| 85 | - <Grid Grid.Row="1" | ||
| 86 | - ColumnDefinitions="2*,2*,1.5*,2*,2*,1.5*,1.5*" | ||
| 87 | - BackgroundColor="#F2F2F2" | ||
| 88 | - IsVisible="{Binding IsPendingVisible}" | ||
| 89 | - Padding="4" | ||
| 90 | - HeightRequest="50"> | ||
| 91 | - <Label Text="产品名称" FontAttributes="Bold" | ||
| 92 | - HorizontalTextAlignment="Center" | ||
| 93 | - VerticalTextAlignment="Center" | ||
| 94 | - LineBreakMode="WordWrap"/> | ||
| 95 | - <Label Grid.Column="1" Text="产品编码" FontAttributes="Bold" | ||
| 96 | - HorizontalTextAlignment="Center" | ||
| 97 | - VerticalTextAlignment="Center" | ||
| 98 | - LineBreakMode="WordWrap"/> | ||
| 99 | - <Label Grid.Column="2" Text="规格" FontAttributes="Bold" | ||
| 100 | - HorizontalTextAlignment="Center" | ||
| 101 | - VerticalTextAlignment="Center" | ||
| 102 | - LineBreakMode="WordWrap"/> | ||
| 103 | - <Label Grid.Column="3" Text="出库库位" FontAttributes="Bold" | ||
| 104 | - HorizontalTextAlignment="Center" | ||
| 105 | - VerticalTextAlignment="Center" | ||
| 106 | - LineBreakMode="WordWrap"/> | ||
| 107 | - <Label Grid.Column="4" Text="生产批号" FontAttributes="Bold" | ||
| 108 | - HorizontalTextAlignment="Center" | ||
| 109 | - VerticalTextAlignment="Center" | ||
| 110 | - LineBreakMode="WordWrap"/> | ||
| 111 | - <Label Grid.Column="5" Text="出库数量" FontAttributes="Bold" | ||
| 112 | - HorizontalTextAlignment="Center" | ||
| 113 | - VerticalTextAlignment="Center" | ||
| 114 | - LineBreakMode="WordWrap"/> | ||
| 115 | - <Label Grid.Column="6" Text="已扫描数" FontAttributes="Bold" | ||
| 116 | - HorizontalTextAlignment="Center" | ||
| 117 | - VerticalTextAlignment="Center" | ||
| 118 | - LineBreakMode="WordWrap"/> | 107 | + <!-- 待出库明细表头 --> |
| 108 | + <Grid Grid.Row="1" ColumnDefinitions="*,*,*,*,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsPendingVisible}" Padding="8"> | ||
| 109 | + <Label Text="产品名称" FontAttributes="Bold" /> | ||
| 110 | + <Label Grid.Column="1" Text="产品编码" FontAttributes="Bold" /> | ||
| 111 | + <Label Grid.Column="2" Text="规格" FontAttributes="Bold" /> | ||
| 112 | + <Label Grid.Column="3" Text="出库库位" FontAttributes="Bold" /> | ||
| 113 | + <Label Grid.Column="4" Text="生产批号" FontAttributes="Bold" /> | ||
| 114 | + <Label Grid.Column="5" Text="出库数量" FontAttributes="Bold" /> | ||
| 115 | + <Label Grid.Column="6" Text="已扫描数" FontAttributes="Bold" /> | ||
| 119 | </Grid> | 116 | </Grid> |
| 120 | 117 | ||
| 121 | - <!-- 出库单明细列表 --> | 118 | + <!-- 待入库明细列表 --> |
| 122 | <CollectionView Grid.Row="2" | 119 | <CollectionView Grid.Row="2" |
| 123 | ItemsSource="{Binding PendingList}" | 120 | ItemsSource="{Binding PendingList}" |
| 124 | - IsVisible="{Binding IsPendingVisible}"> | 121 | + IsVisible="{Binding IsPendingVisible}" |
| 122 | + SelectionMode="Single"> | ||
| 125 | <CollectionView.ItemTemplate> | 123 | <CollectionView.ItemTemplate> |
| 126 | <DataTemplate> | 124 | <DataTemplate> |
| 127 | - <Grid ColumnDefinitions="2*,2*,1.5*,2*,2*,1.5*,1.5*" | ||
| 128 | - Padding="4" | ||
| 129 | - BackgroundColor="White"> | ||
| 130 | - <Label Text="{Binding Name}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 131 | - <Label Grid.Column="1" Text="{Binding Code}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 132 | - <Label Grid.Column="2" Text="{Binding Spec}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 133 | - <Label Grid.Column="3" Text="{Binding Bin}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 134 | - <Label Grid.Column="4" Text="{Binding BatchNo}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 135 | - <Label Grid.Column="5" Text="{Binding OutQty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 136 | - <Label Grid.Column="6" Text="{Binding ScannedQty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | 125 | + <Grid ColumnDefinitions="*,*,*,*,*,*,*" Padding="8" BackgroundColor="White"> |
| 126 | + <VisualStateManager.VisualStateGroups> | ||
| 127 | + <VisualStateGroup Name="CommonStates"> | ||
| 128 | + <VisualState Name="Normal"> | ||
| 129 | + <VisualState.Setters> | ||
| 130 | + <Setter Property="BackgroundColor" Value="White"/> | ||
| 131 | + </VisualState.Setters> | ||
| 132 | + </VisualState> | ||
| 133 | + <VisualState Name="Selected"> | ||
| 134 | + <VisualState.Setters> | ||
| 135 | + <Setter Property="BackgroundColor" Value="#CCFFCC"/> | ||
| 136 | + </VisualState.Setters> | ||
| 137 | + </VisualState> | ||
| 138 | + </VisualStateGroup> | ||
| 139 | + </VisualStateManager.VisualStateGroups> | ||
| 140 | + <Label Text="{Binding Name}" /> | ||
| 141 | + <Label Grid.Column="1" Text="{Binding MaterialCode}" /> | ||
| 142 | + <Label Grid.Column="2" Text="{Binding Spec}" /> | ||
| 143 | + <Label Grid.Column="3" Text="{Binding Location}" /> | ||
| 144 | + <Label Grid.Column="4" Text="{Binding ProductionBatch}" /> | ||
| 145 | + <Label Grid.Column="5" Text="{Binding OutstockQty}" /> | ||
| 146 | + <Label Grid.Column="6" Text="{Binding Qty}" /> | ||
| 137 | </Grid> | 147 | </Grid> |
| 138 | </DataTemplate> | 148 | </DataTemplate> |
| 139 | </CollectionView.ItemTemplate> | 149 | </CollectionView.ItemTemplate> |
| 140 | </CollectionView> | 150 | </CollectionView> |
| 141 | 151 | ||
| 142 | - | ||
| 143 | <!-- 扫描明细表头 --> | 152 | <!-- 扫描明细表头 --> |
| 144 | - <Grid Grid.Row="1" | ||
| 145 | - ColumnDefinitions="40,*,*,*" | ||
| 146 | - BackgroundColor="#F2F2F2" | ||
| 147 | - IsVisible="{Binding IsScannedVisible}" | ||
| 148 | - Padding="4" | ||
| 149 | - HeightRequest="30"> | ||
| 150 | - <Label Text="选择" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" LineBreakMode="NoWrap"/> | ||
| 151 | - <Label Grid.Column="1" Text="条码" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" LineBreakMode="NoWrap"/> | ||
| 152 | - <Label Grid.Column="2" Text="产品名称" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" LineBreakMode="NoWrap"/> | ||
| 153 | - <Label Grid.Column="3" Text="数量" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" LineBreakMode="NoWrap"/> | 153 | + <Grid Grid.Row="1" ColumnDefinitions="40,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsScannedVisible}" Padding="8"> |
| 154 | + <Label Text="选择" FontAttributes="Bold" /> | ||
| 155 | + <Label Grid.Column="1" Text="物料名称" FontAttributes="Bold" /> | ||
| 156 | + <Label Grid.Column="2" Text="条码" FontAttributes="Bold" /> | ||
| 157 | + <Label Grid.Column="3" Text="数量" FontAttributes="Bold" /> | ||
| 154 | </Grid> | 158 | </Grid> |
| 155 | 159 | ||
| 156 | <!-- 扫描明细列表 --> | 160 | <!-- 扫描明细列表 --> |
| 157 | <CollectionView Grid.Row="2" | 161 | <CollectionView Grid.Row="2" |
| 158 | ItemsSource="{Binding ScannedList}" | 162 | ItemsSource="{Binding ScannedList}" |
| 159 | - IsVisible="{Binding IsScannedVisible}"> | 163 | + IsVisible="{Binding IsScannedVisible}" |
| 164 | + SelectionMode="Single" | ||
| 165 | + SelectedItem="{Binding SelectedScanItem, Mode=TwoWay}"> | ||
| 160 | <CollectionView.ItemTemplate> | 166 | <CollectionView.ItemTemplate> |
| 161 | <DataTemplate> | 167 | <DataTemplate> |
| 162 | - <Grid ColumnDefinitions="40,*,*,*" Padding="4" BackgroundColor="White"> | ||
| 163 | - <CheckBox IsChecked="{Binding IsSelected}" HorizontalOptions="Center" VerticalOptions="Center" /> | ||
| 164 | - <Label Grid.Column="1" Text="{Binding Barcode}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" /> | ||
| 165 | - <Label Grid.Column="2" Text="{Binding Name}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" /> | ||
| 166 | - <Label Grid.Column="3" Text="{Binding Qty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" /> | 168 | + <Grid ColumnDefinitions="40,*,*,*" Padding="8" BackgroundColor="White"> |
| 169 | + <Grid.Triggers> | ||
| 170 | + <!-- ScanStatus = true → 绿色 --> | ||
| 171 | + <DataTrigger TargetType="Grid" Binding="{Binding ScanStatus}" Value="True"> | ||
| 172 | + <Setter Property="BackgroundColor" Value="#E6FFE6" /> | ||
| 173 | + </DataTrigger> | ||
| 174 | + </Grid.Triggers> | ||
| 175 | + | ||
| 176 | + <CheckBox IsChecked="{Binding IsSelected}" /> | ||
| 177 | + <Label Grid.Column="1" Text="{Binding Name}" /> | ||
| 178 | + <Label Grid.Column="2" Text="{Binding Barcode}" /> | ||
| 179 | + <Entry Grid.Column="3" | ||
| 180 | + Keyboard="Numeric" | ||
| 181 | + HorizontalTextAlignment="Center" | ||
| 182 | + WidthRequest="64" | ||
| 183 | + Completed="OnQtyCompleted" | ||
| 184 | + Text="{Binding Qty, Mode=TwoWay, Converter={StaticResource IntConverter}}" /> | ||
| 185 | + | ||
| 167 | </Grid> | 186 | </Grid> |
| 168 | </DataTemplate> | 187 | </DataTemplate> |
| 169 | </CollectionView.ItemTemplate> | 188 | </CollectionView.ItemTemplate> |
| 170 | </CollectionView> | 189 | </CollectionView> |
| 171 | - </Grid> | ||
| 172 | 190 | ||
| 191 | + </Grid> | ||
| 173 | 192 | ||
| 174 | <!-- 底部按钮 --> | 193 | <!-- 底部按钮 --> |
| 175 | <Grid Grid.Row="4" ColumnDefinitions="*,*,*" Padding="16,8" ColumnSpacing="10"> | 194 | <Grid Grid.Row="4" ColumnDefinitions="*,*,*" Padding="16,8" ColumnSpacing="10"> |
| 176 | 195 | ||
| 177 | <!-- 扫描通过 --> | 196 | <!-- 扫描通过 --> |
| 178 | - <HorizontalStackLayout BackgroundColor="#4CAF50" | ||
| 179 | - Padding="10" | ||
| 180 | - HorizontalOptions="FillAndExpand" | ||
| 181 | - VerticalOptions="Center" | ||
| 182 | - Grid.Column="0"> | ||
| 183 | - <Image Source="pass.png" WidthRequest="20" HeightRequest="20" /> | 197 | + <Grid BackgroundColor="#4CAF50" |
| 198 | + HorizontalOptions="Fill" | ||
| 199 | + VerticalOptions="Fill" | ||
| 200 | + HeightRequest="50"> | ||
| 201 | + <Grid.GestureRecognizers> | ||
| 202 | + <TapGestureRecognizer Command="{Binding PassScanCommand}" /> | ||
| 203 | + </Grid.GestureRecognizers> | ||
| 204 | + <StackLayout Orientation="Horizontal" | ||
| 205 | + HorizontalOptions="Center" | ||
| 206 | + VerticalOptions="Center"> | ||
| 207 | + <Image Source="pass.png" HeightRequest="20" WidthRequest="20" /> | ||
| 184 | <Label Text="扫描通过" | 208 | <Label Text="扫描通过" |
| 209 | + Margin="5,0,0,0" | ||
| 185 | VerticalOptions="Center" | 210 | VerticalOptions="Center" |
| 186 | - HorizontalOptions="Center" | ||
| 187 | - TextColor="White" | ||
| 188 | - Margin="5,0,0,0" /> | ||
| 189 | - <HorizontalStackLayout.GestureRecognizers> | ||
| 190 | - <TapGestureRecognizer Command="{Binding PassScanCommand}" /> | ||
| 191 | - </HorizontalStackLayout.GestureRecognizers> | ||
| 192 | - </HorizontalStackLayout> | 211 | + TextColor="White" /> |
| 212 | + </StackLayout> | ||
| 213 | + </Grid> | ||
| 193 | 214 | ||
| 194 | <!-- 取消扫描 --> | 215 | <!-- 取消扫描 --> |
| 195 | - <HorizontalStackLayout BackgroundColor="#F44336" | ||
| 196 | - Padding="10" | ||
| 197 | - HorizontalOptions="FillAndExpand" | ||
| 198 | - VerticalOptions="Center" | ||
| 199 | - Grid.Column="1"> | ||
| 200 | - <Image Source="cancel.png" WidthRequest="20" HeightRequest="20" /> | 216 | + <Grid Grid.Column="1" |
| 217 | + BackgroundColor="#F44336" | ||
| 218 | + HorizontalOptions="Fill" | ||
| 219 | + VerticalOptions="Fill" | ||
| 220 | + HeightRequest="50"> | ||
| 221 | + <Grid.GestureRecognizers> | ||
| 222 | + <TapGestureRecognizer Command="{Binding CancelScanCommand}" /> | ||
| 223 | + </Grid.GestureRecognizers> | ||
| 224 | + <StackLayout Orientation="Horizontal" | ||
| 225 | + HorizontalOptions="Center" | ||
| 226 | + VerticalOptions="Center"> | ||
| 227 | + <Image Source="cancel.png" HeightRequest="20" WidthRequest="20" /> | ||
| 201 | <Label Text="取消扫描" | 228 | <Label Text="取消扫描" |
| 229 | + Margin="5,0,0,0" | ||
| 202 | VerticalOptions="Center" | 230 | VerticalOptions="Center" |
| 203 | - HorizontalOptions="Center" | ||
| 204 | - TextColor="White" | ||
| 205 | - Margin="5,0,0,0" /> | ||
| 206 | - <HorizontalStackLayout.GestureRecognizers> | ||
| 207 | - <TapGestureRecognizer Command="{Binding CancelScanCommand}" /> | ||
| 208 | - </HorizontalStackLayout.GestureRecognizers> | ||
| 209 | - </HorizontalStackLayout> | 231 | + TextColor="White" /> |
| 232 | + </StackLayout> | ||
| 233 | + </Grid> | ||
| 210 | 234 | ||
| 211 | - <!-- 确认出库 --> | ||
| 212 | - <HorizontalStackLayout BackgroundColor="#2196F3" | ||
| 213 | - Padding="10" | ||
| 214 | - HorizontalOptions="FillAndExpand" | ||
| 215 | - VerticalOptions="Center" | ||
| 216 | - Grid.Column="2"> | ||
| 217 | - <Image Source="confirm.png" WidthRequest="20" HeightRequest="20" /> | 235 | + <!-- 确认入库 --> |
| 236 | + <Grid Grid.Column="2" | ||
| 237 | + BackgroundColor="#2196F3" | ||
| 238 | + HorizontalOptions="Fill" | ||
| 239 | + VerticalOptions="Fill" | ||
| 240 | + HeightRequest="50"> | ||
| 241 | + <Grid.GestureRecognizers> | ||
| 242 | + <TapGestureRecognizer Tapped="OnConfirmClicked" /> | ||
| 243 | + </Grid.GestureRecognizers> | ||
| 244 | + <StackLayout Orientation="Horizontal" | ||
| 245 | + HorizontalOptions="Center" | ||
| 246 | + VerticalOptions="Center"> | ||
| 247 | + <Image Source="confirm.png" HeightRequest="20" WidthRequest="20" /> | ||
| 218 | <Label Text="确认出库" | 248 | <Label Text="确认出库" |
| 249 | + Margin="5,0,0,0" | ||
| 219 | VerticalOptions="Center" | 250 | VerticalOptions="Center" |
| 220 | - HorizontalOptions="Center" | ||
| 221 | - TextColor="White" | ||
| 222 | - Margin="5,0,0,0" /> | ||
| 223 | - <HorizontalStackLayout.GestureRecognizers> | ||
| 224 | - <TapGestureRecognizer Command="{Binding ConfirmCommand}" /> | ||
| 225 | - </HorizontalStackLayout.GestureRecognizers> | ||
| 226 | - </HorizontalStackLayout> | 251 | + TextColor="White" /> |
| 252 | + </StackLayout> | ||
| 253 | + </Grid> | ||
| 227 | 254 | ||
| 228 | </Grid> | 255 | </Grid> |
| 229 | 256 |
| 1 | using IndustrialControl.Services; | 1 | using IndustrialControl.Services; |
| 2 | using IndustrialControl.ViewModels; | 2 | using IndustrialControl.ViewModels; |
| 3 | 3 | ||
| 4 | -namespace IndustrialControl.Pages | 4 | +namespace IndustrialControl.Pages; |
| 5 | + | ||
| 6 | +[QueryProperty(nameof(OutstockId), "outstockId")] | ||
| 7 | +[QueryProperty(nameof(OutstockNo), "outstockNo")] | ||
| 8 | +[QueryProperty(nameof(OrderType), "orderType")] | ||
| 9 | +[QueryProperty(nameof(OrderTypeName), "orderTypeName")] | ||
| 10 | +[QueryProperty(nameof(RequisitionMaterialNo), "requisitionMaterialNo")] | ||
| 11 | +[QueryProperty(nameof(ReturnNo), "returnNo")] | ||
| 12 | +[QueryProperty(nameof(DeliveryNo), "deliveryNo")] | ||
| 13 | +[QueryProperty(nameof(CreatedTime), "createdTime")] | ||
| 14 | +public partial class OutboundFinishedPage : ContentPage | ||
| 5 | { | 15 | { |
| 6 | - [QueryProperty(nameof(OutstockId), "outstockId")] | ||
| 7 | - [QueryProperty(nameof(OutstockNo), "outstockNo")] | ||
| 8 | - [QueryProperty(nameof(OrderType), "orderType")] | ||
| 9 | - [QueryProperty(nameof(OrderTypeName), "orderTypeName")] | ||
| 10 | - [QueryProperty(nameof(PurchaseNo), "purchaseNo")] | ||
| 11 | - [QueryProperty(nameof(SupplierName), "supplierName")] | ||
| 12 | - [QueryProperty(nameof(CreatedTime), "createdTime")] | ||
| 13 | - public partial class OutboundFinishedPage : ContentPage | ||
| 14 | - { | ||
| 15 | private readonly ScanService _scanSvc; | 16 | private readonly ScanService _scanSvc; |
| 16 | private readonly OutboundFinishedViewModel _vm; | 17 | private readonly OutboundFinishedViewModel _vm; |
| 17 | public string? OutstockId { get; set; } | 18 | public string? OutstockId { get; set; } |
| 18 | public string? OutstockNo { get; set; } | 19 | public string? OutstockNo { get; set; } |
| 19 | public string? OrderType { get; set; } | 20 | public string? OrderType { get; set; } |
| 20 | public string? OrderTypeName { get; set; } | 21 | public string? OrderTypeName { get; set; } |
| 21 | - public string? PurchaseNo { get; set; } | ||
| 22 | - public string? SupplierName { get; set; } | 22 | + public string? RequisitionMaterialNo { get; set; } |
| 23 | + public string? ReturnNo { get; set; } | ||
| 24 | + public string? DeliveryNo { get; set; } | ||
| 23 | public string? CreatedTime { get; set; } | 25 | public string? CreatedTime { get; set; } |
| 26 | + private readonly IDialogService _dialogs; | ||
| 24 | 27 | ||
| 25 | - | ||
| 26 | - public OutboundFinishedPage(OutboundFinishedViewModel vm, ScanService scanSvc) | 28 | + public OutboundFinishedPage(OutboundFinishedViewModel vm, ScanService scanSvc, IDialogService dialogs) |
| 27 | { | 29 | { |
| 28 | InitializeComponent(); | 30 | InitializeComponent(); |
| 29 | BindingContext = vm; | 31 | BindingContext = vm; |
| 30 | _scanSvc = scanSvc; | 32 | _scanSvc = scanSvc; |
| 31 | _vm = vm; | 33 | _vm = vm; |
| 34 | + _dialogs = dialogs; | ||
| 35 | + // 可选:配置前后缀与防抖 | ||
| 32 | _scanSvc.Prefix = null; // 例如 "}q" 之类的前缀;没有就留 null | 36 | _scanSvc.Prefix = null; // 例如 "}q" 之类的前缀;没有就留 null |
| 33 | // _scanSvc.Suffix = "\n"; // 如果设备会附带换行,可去掉;没有就设 null | 37 | // _scanSvc.Suffix = "\n"; // 如果设备会附带换行,可去掉;没有就设 null |
| 34 | //_scanSvc.DebounceMs = 250; | 38 | //_scanSvc.DebounceMs = 250; |
| 35 | _scanSvc.Suffix = null; // 先关掉 | 39 | _scanSvc.Suffix = null; // 先关掉 |
| 36 | _scanSvc.DebounceMs = 0; // 先关掉 | 40 | _scanSvc.DebounceMs = 0; // 先关掉 |
| 41 | + | ||
| 37 | } | 42 | } |
| 38 | 43 | ||
| 39 | protected override async void OnAppearing() | 44 | protected override async void OnAppearing() |
| @@ -48,8 +53,9 @@ namespace IndustrialControl.Pages | @@ -48,8 +53,9 @@ namespace IndustrialControl.Pages | ||
| 48 | outstockNo: OutstockNo ?? "", | 53 | outstockNo: OutstockNo ?? "", |
| 49 | orderType: OrderType ?? "", | 54 | orderType: OrderType ?? "", |
| 50 | orderTypeName: OrderTypeName ?? "", | 55 | orderTypeName: OrderTypeName ?? "", |
| 51 | - purchaseNo: PurchaseNo ?? "", | ||
| 52 | - supplierName: SupplierName ?? "", | 56 | + requisitionMaterialNo: RequisitionMaterialNo ?? "", |
| 57 | + returnNo: ReturnNo ?? "", | ||
| 58 | + deliveryNo: DeliveryNo ?? "", | ||
| 53 | createdTime: CreatedTime ?? "" | 59 | createdTime: CreatedTime ?? "" |
| 54 | ); | 60 | ); |
| 55 | } | 61 | } |
| @@ -60,6 +66,17 @@ namespace IndustrialControl.Pages | @@ -60,6 +66,17 @@ namespace IndustrialControl.Pages | ||
| 60 | ScanEntry.Focus(); | 66 | ScanEntry.Focus(); |
| 61 | } | 67 | } |
| 62 | 68 | ||
| 69 | + | ||
| 70 | + /// <summary> | ||
| 71 | + /// 清空扫描记录 | ||
| 72 | + /// </summary> | ||
| 73 | + void OnClearClicked(object sender, EventArgs e) | ||
| 74 | + { | ||
| 75 | + _vm.ClearScan(); | ||
| 76 | + ScanEntry.Text = string.Empty; | ||
| 77 | + ScanEntry.Focus(); | ||
| 78 | + } | ||
| 79 | + | ||
| 63 | protected override void OnDisappearing() | 80 | protected override void OnDisappearing() |
| 64 | { | 81 | { |
| 65 | // 退出页面即注销(防止多个程序/页面抢处理) | 82 | // 退出页面即注销(防止多个程序/页面抢处理) |
| @@ -81,62 +98,58 @@ namespace IndustrialControl.Pages | @@ -81,62 +98,58 @@ namespace IndustrialControl.Pages | ||
| 81 | }); | 98 | }); |
| 82 | } | 99 | } |
| 83 | 100 | ||
| 84 | - /// <summary> | ||
| 85 | - /// 清空扫描记录 | ||
| 86 | - /// </summary> | ||
| 87 | - void OnClearClicked(object sender, EventArgs e) | ||
| 88 | - { | ||
| 89 | - _vm.ClearScan(); | ||
| 90 | - ScanEntry.Text = string.Empty; | ||
| 91 | - ScanEntry.Focus(); | ||
| 92 | - } | ||
| 93 | 101 | ||
| 94 | /// <summary> | 102 | /// <summary> |
| 95 | - /// 预留摄像头扫码 | 103 | + /// 确认入库按钮点击 |
| 96 | /// </summary> | 104 | /// </summary> |
| 97 | - async void OnScanClicked(object sender, EventArgs e) | 105 | + async void OnConfirmClicked(object sender, EventArgs e) |
| 98 | { | 106 | { |
| 99 | - await DisplayAlert("提示", "此按钮预留摄像头扫码;硬件扫描直接扣扳机。", "确定"); | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - /// <summary> | ||
| 103 | - /// 点击“出库单明细”Tab | ||
| 104 | - /// </summary> | ||
| 105 | - void OnPendingTabClicked(object sender, EventArgs e) | 107 | + var ok = await _vm.ConfirmOutboundAsync(); |
| 108 | + if (ok) | ||
| 106 | { | 109 | { |
| 107 | - _vm.ShowPendingCommand.Execute(null); | ||
| 108 | - } | 110 | + await DisplayAlert("提示", "入库成功", "确定"); |
| 111 | + _vm.ClearAll(); | ||
| 109 | 112 | ||
| 110 | - /// <summary> | ||
| 111 | - /// 点击“扫描明细”Tab | ||
| 112 | - /// </summary> | ||
| 113 | - void OnScannedTabClicked(object sender, EventArgs e) | 113 | + // ✅ 返回到工单查询页面(InboundFinishedSearchPage) |
| 114 | + await Shell.Current.GoToAsync($"//{nameof(OutboundFinishedSearchPage)}"); | ||
| 115 | + } | ||
| 116 | + else | ||
| 114 | { | 117 | { |
| 115 | - _vm.ShowScannedCommand.Execute(null); | 118 | + await DisplayAlert("提示", "入库失败,请检查数据", "确定"); |
| 119 | + } | ||
| 116 | } | 120 | } |
| 117 | 121 | ||
| 118 | - /// <summary> | ||
| 119 | - /// 扫描通过 | ||
| 120 | - /// </summary> | ||
| 121 | - void OnPassScanClicked(object sender, EventArgs e) | 122 | + |
| 123 | + private async void OnBinTapped(object? sender, TappedEventArgs e) | ||
| 122 | { | 124 | { |
| 123 | - _vm.PassScanCommand.Execute(null); | 125 | + var bindable = sender as BindableObject; |
| 126 | + var row = bindable?.BindingContext; | ||
| 127 | + if (row == null) return; | ||
| 128 | + | ||
| 129 | + var type = row.GetType(); | ||
| 130 | + var currentBin = type.GetProperty("Location")?.GetValue(row)?.ToString(); | ||
| 131 | + | ||
| 132 | + // 1) 打开公共组件选择库位(你已完成的组件) | ||
| 133 | + var picked = await BinPickerPage.ShowAsync(currentBin); | ||
| 134 | + if (picked == null) return; | ||
| 135 | + | ||
| 136 | + // 2) 调用 VM:带上行对象 + 选中的 BinInfo,内部会调接口 & 回填行 | ||
| 137 | + await _vm.UpdateRowLocationAsync(row, picked); | ||
| 124 | } | 138 | } |
| 125 | 139 | ||
| 126 | - /// <summary> | ||
| 127 | - /// 取消扫描 | ||
| 128 | - /// </summary> | ||
| 129 | - void OnCancelScanClicked(object sender, EventArgs e) | 140 | + private async void OnQtyCompleted(object sender, EventArgs e) |
| 130 | { | 141 | { |
| 131 | - _vm.CancelScanCommand.Execute(null); | ||
| 132 | - } | 142 | + if (sender is not Entry entry) return; |
| 143 | + if (entry.BindingContext is not IndustrialControl.ViewModels.OutScannedItem row) return; | ||
| 133 | 144 | ||
| 134 | - /// <summary> | ||
| 135 | - /// 确认出库 | ||
| 136 | - /// </summary> | ||
| 137 | - async void OnConfirmClicked(object sender, EventArgs e) | 145 | + // 只看 ScanStatus:未通过则不提交 |
| 146 | + if (!row.ScanStatus) | ||
| 138 | { | 147 | { |
| 139 | - _vm.ConfirmCommand.Execute(null); | 148 | + await DisplayAlert("提示", "该行尚未扫描通过,不能修改数量。", "确定"); |
| 149 | + return; | ||
| 140 | } | 150 | } |
| 151 | + | ||
| 152 | + await _vm.UpdateQuantityForRowAsync(row); | ||
| 141 | } | 153 | } |
| 154 | + | ||
| 142 | } | 155 | } |
| 1 | <?xml version="1.0" encoding="utf-8" ?> | 1 | <?xml version="1.0" encoding="utf-8" ?> |
| 2 | -<ContentPage x:Name="Page" | 2 | +<ContentPage |
| 3 | + x:Class="IndustrialControl.Pages.OutboundFinishedSearchPage" | ||
| 3 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 4 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 4 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 5 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 5 | - x:Class="IndustrialControl.Pages.OutboundFinishedSearchPage" | ||
| 6 | - xmlns:conv="clr-namespace:IndustrialControl.Converters" | ||
| 7 | - Title="仓储管理系统"> | ||
| 8 | - <ContentPage.Resources> | ||
| 9 | - <ResourceDictionary> | ||
| 10 | - <!-- 空/非空转布尔:非空 => true(按钮可用) --> | ||
| 11 | - <conv:NullToBoolConverter x:Key="NullToBoolConverter" /> | ||
| 12 | - </ResourceDictionary> | ||
| 13 | - </ContentPage.Resources> | 6 | + x:Name="Page" |
| 7 | + Shell.NavBarIsVisible="True"> | ||
| 14 | 8 | ||
| 15 | - <Grid RowDefinitions="Auto,*,Auto" Padding="16" BackgroundColor="#F6F7FB"> | 9 | + <ScrollView> |
| 10 | + <VerticalStackLayout Spacing="12" Padding="12"> | ||
| 16 | 11 | ||
| 17 | - <!-- 顶部:输入条件(第0行) --> | ||
| 18 | - <VerticalStackLayout Grid.Row="0" Spacing="10"> | 12 | + <!-- 查询区域 --> |
| 19 | <Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="8"> | 13 | <Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="8"> |
| 20 | <Entry x:Name="OrderEntry" | 14 | <Entry x:Name="OrderEntry" |
| 21 | Grid.Row="0" Grid.Column="0" | 15 | Grid.Row="0" Grid.Column="0" |
| 22 | - Placeholder="请输入出库单条码" | ||
| 23 | - VerticalOptions="Center" | ||
| 24 | - BackgroundColor="White" | 16 | + Placeholder="请扫描输入出库单条码" |
| 25 | Text="{Binding SearchOrderNo}" /> | 17 | Text="{Binding SearchOrderNo}" /> |
| 26 | 18 | ||
| 27 | - | ||
| 28 | - <!-- 开始日期 --> | ||
| 29 | - <DatePicker Grid.Row="1" Grid.Column="0" | ||
| 30 | - Date="{Binding StartDate}" | ||
| 31 | - MinimumDate="2000-01-01" | ||
| 32 | - MaximumDate="{Binding EndDate}" /> | ||
| 33 | - | ||
| 34 | - <!-- 结束日期 --> | ||
| 35 | - <DatePicker Grid.Row="1" Grid.Column="1" | ||
| 36 | - Date="{Binding EndDate}" | ||
| 37 | - MinimumDate="{Binding StartDate}" /> | ||
| 38 | - <Button Grid.Row="1" Grid.Column="1" | 19 | + <Button Grid.Row="0" Grid.Column="1" |
| 39 | Text="查询" | 20 | Text="查询" |
| 40 | Command="{Binding SearchCommand}" /> | 21 | Command="{Binding SearchCommand}" /> |
| 22 | + | ||
| 23 | + <Grid Grid.Row="1" Grid.ColumnSpan="2" ColumnDefinitions="Auto,*,Auto,*" ColumnSpacing="8"> | ||
| 24 | + <Label Grid.Column="0" Text="开始:" VerticalTextAlignment="Center"/> | ||
| 25 | + <DatePicker Grid.Column="1" Date="{Binding StartDate}" /> | ||
| 26 | + <Label Grid.Column="2" Text="结束:" VerticalTextAlignment="Center"/> | ||
| 27 | + <DatePicker Grid.Column="3" Date="{Binding EndDate}" /> | ||
| 28 | + </Grid> | ||
| 41 | </Grid> | 29 | </Grid> |
| 42 | - </VerticalStackLayout> | ||
| 43 | 30 | ||
| 44 | - <!-- 中部:结果列表(第1行) --> | 31 | + <!-- 列表区域 --> |
| 45 | <CollectionView Grid.Row="1" | 32 | <CollectionView Grid.Row="1" |
| 46 | ItemsSource="{Binding Orders}" | 33 | ItemsSource="{Binding Orders}" |
| 47 | SelectionMode="Single" | 34 | SelectionMode="Single" |
| @@ -49,13 +36,15 @@ | @@ -49,13 +36,15 @@ | ||
| 49 | <CollectionView.ItemTemplate> | 36 | <CollectionView.ItemTemplate> |
| 50 | <DataTemplate> | 37 | <DataTemplate> |
| 51 | <Frame Margin="0,8,0,0" Padding="12" HasShadow="True" CornerRadius="10"> | 38 | <Frame Margin="0,8,0,0" Padding="12" HasShadow="True" CornerRadius="10"> |
| 52 | - <!-- ⭐ 点击整卡片触发命令 --> | 39 | + <!-- 点击整卡片:直接调用 VM 的 GoOutboundCommand,并把当前项作为参数 --> |
| 53 | <Frame.GestureRecognizers> | 40 | <Frame.GestureRecognizers> |
| 54 | <TapGestureRecognizer | 41 | <TapGestureRecognizer |
| 55 | Command="{Binding BindingContext.GoOutboundCommand, Source={x:Reference Page}}" | 42 | Command="{Binding BindingContext.GoOutboundCommand, Source={x:Reference Page}}" |
| 56 | CommandParameter="{Binding .}" /> | 43 | CommandParameter="{Binding .}" /> |
| 57 | </Frame.GestureRecognizers> | 44 | </Frame.GestureRecognizers> |
| 58 | - <Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="Auto,*" ColumnSpacing="8"> | 45 | + |
| 46 | + <Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto" | ||
| 47 | + ColumnDefinitions="Auto,*" ColumnSpacing="8"> | ||
| 59 | <Label Grid.Row="0" Grid.Column="0" Text="出库单号:" FontAttributes="Bold"/> | 48 | <Label Grid.Row="0" Grid.Column="0" Text="出库单号:" FontAttributes="Bold"/> |
| 60 | <Label Grid.Row="0" Grid.Column="1" Text="{Binding outstockNo}"/> | 49 | <Label Grid.Row="0" Grid.Column="1" Text="{Binding outstockNo}"/> |
| 61 | 50 | ||
| @@ -76,10 +65,6 @@ | @@ -76,10 +65,6 @@ | ||
| 76 | </CollectionView.ItemTemplate> | 65 | </CollectionView.ItemTemplate> |
| 77 | </CollectionView> | 66 | </CollectionView> |
| 78 | 67 | ||
| 79 | - <!-- 底部:操作(第2行) --> | ||
| 80 | - <Grid Grid.Row="2" ColumnDefinitions="*,Auto" Padding="0,8,0,0"> | ||
| 81 | - <Label Text="{Binding Orders.Count, StringFormat='共 {0} 条'}" | ||
| 82 | - VerticalTextAlignment="Center" /> | ||
| 83 | - </Grid> | ||
| 84 | - </Grid> | 68 | + </VerticalStackLayout> |
| 69 | + </ScrollView> | ||
| 85 | </ContentPage> | 70 | </ContentPage> |
| 1 | -using System.Threading; | ||
| 2 | using IndustrialControl.Services; | 1 | using IndustrialControl.Services; |
| 3 | using IndustrialControl.ViewModels; | 2 | using IndustrialControl.ViewModels; |
| 4 | namespace IndustrialControl.Pages; | 3 | namespace IndustrialControl.Pages; |
| @@ -30,6 +29,8 @@ public partial class OutboundFinishedSearchPage : ContentPage | @@ -30,6 +29,8 @@ public partial class OutboundFinishedSearchPage : ContentPage | ||
| 30 | //键盘输入 | 29 | //键盘输入 |
| 31 | _scanSvc.Attach(OrderEntry); | 30 | _scanSvc.Attach(OrderEntry); |
| 32 | OrderEntry.Focus(); | 31 | OrderEntry.Focus(); |
| 32 | + | ||
| 33 | + | ||
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | /// <summary> | 36 | /// <summary> |
| @@ -59,4 +60,5 @@ public partial class OutboundFinishedSearchPage : ContentPage | @@ -59,4 +60,5 @@ public partial class OutboundFinishedSearchPage : ContentPage | ||
| 59 | }); | 60 | }); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 63 | + | ||
| 62 | } | 64 | } |
| 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 1 | <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 2 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 3 | x:Class="IndustrialControl.Pages.OutboundMaterialPage" | 3 | x:Class="IndustrialControl.Pages.OutboundMaterialPage" |
| 4 | - BackgroundColor="White"> | 4 | + BackgroundColor="White" Shell.NavBarIsVisible="True"> |
| 5 | 5 | ||
| 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> | 6 | <Grid RowDefinitions="Auto,Auto,Auto,*,Auto"> |
| 7 | 7 | ||
| @@ -14,128 +14,166 @@ | @@ -14,128 +14,166 @@ | ||
| 14 | FontAttributes="Bold"/> | 14 | FontAttributes="Bold"/> |
| 15 | </Grid> | 15 | </Grid> |
| 16 | 16 | ||
| 17 | - <!-- 出库单/条码扫描 --> | 17 | + <!-- 入库单/条码扫描 --> |
| 18 | <Grid Grid.Row="1" ColumnDefinitions="*,60" Padding="16,8"> | 18 | <Grid Grid.Row="1" ColumnDefinitions="*,60" Padding="16,8"> |
| 19 | <Entry x:Name="ScanEntry" | 19 | <Entry x:Name="ScanEntry" |
| 20 | - Placeholder="请扫描出库单/产品/包装条码" | 20 | + Placeholder="请扫描输入出库单条码" |
| 21 | FontSize="14" | 21 | FontSize="14" |
| 22 | VerticalOptions="Center" | 22 | VerticalOptions="Center" |
| 23 | BackgroundColor="White" | 23 | BackgroundColor="White" |
| 24 | HeightRequest="40" | 24 | HeightRequest="40" |
| 25 | Text="{Binding ScanCode}" /> | 25 | Text="{Binding ScanCode}" /> |
| 26 | - <ImageButton Grid.Column="1" | ||
| 27 | - Source="scan.png" | ||
| 28 | - BackgroundColor="#E6F2FF" | ||
| 29 | - CornerRadius="4" | ||
| 30 | - Padding="10" | ||
| 31 | - Clicked="OnScanClicked"/> | 26 | + |
| 32 | </Grid> | 27 | </Grid> |
| 33 | 28 | ||
| 34 | <!-- 基础信息 --> | 29 | <!-- 基础信息 --> |
| 35 | - <Frame Grid.Row="2" Margin="16,0" Padding="8" BorderColor="#CCCCCC" BackgroundColor="White"> | ||
| 36 | - <Grid RowDefinitions="Auto,Auto,Auto,Auto" ColumnDefinitions="Auto,*" ColumnSpacing="8" RowSpacing="6"> | ||
| 37 | - | ||
| 38 | - <!-- 出库单号 --> | ||
| 39 | - <Label Grid.Row="0" Grid.Column="0" Text="出库单号:" FontAttributes="Bold"/> | ||
| 40 | - <Label Grid.Row="0" Grid.Column="1" Text="{Binding OrderNo}" LineBreakMode="CharacterWrap"/> | ||
| 41 | - | ||
| 42 | - <!-- 发货单号 + 客户 --> | ||
| 43 | - <Label Grid.Row="1" Grid.Column="0" Text="发货单号:" FontAttributes="Bold"/> | ||
| 44 | - <Label Grid.Row="1" Grid.Column="1" Text="{Binding DeliveryNo}" LineBreakMode="CharacterWrap"/> | ||
| 45 | - <Label Grid.Row="1" Grid.Column="2" Text="客户:" FontAttributes="Bold"/> | ||
| 46 | - <Label Grid.Row="1" Grid.Column="3" Text="{Binding CustomerName}" LineBreakMode="CharacterWrap"/> | ||
| 47 | - | ||
| 48 | - <!-- 发货时间 + 关联销售单 --> | ||
| 49 | - <Label Grid.Row="2" Grid.Column="0" Text="发货时间:" FontAttributes="Bold"/> | ||
| 50 | - <Label Grid.Row="2" Grid.Column="1" Text="{Binding DeliveryTime}" LineBreakMode="CharacterWrap"/> | ||
| 51 | - <Label Grid.Row="2" Grid.Column="2" Text="关联销售单:" FontAttributes="Bold"/> | ||
| 52 | - <Label Grid.Row="2" Grid.Column="3" Text="{Binding LinkedOrderNo}" LineBreakMode="CharacterWrap"/> | ||
| 53 | - | ||
| 54 | - <!-- 发货单备注 --> | ||
| 55 | - <Label Grid.Row="3" Grid.Column="0" Text="发货单备注:" FontAttributes="Bold"/> | ||
| 56 | - <Label Grid.Row="3" Grid.Column="1" Text="{Binding Remark}" LineBreakMode="WordWrap"/> | 30 | + <Frame Grid.Row="2" Margin="8,0" Padding="8" BorderColor="#CCCCCC" BackgroundColor="White"> |
| 31 | + <Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,*" ColumnSpacing="16" RowSpacing="6"> | ||
| 32 | + | ||
| 33 | + <!-- 第一行左:出库单号 --> | ||
| 34 | + <Label Grid.Row="0" Grid.Column="0" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 35 | + <Label.FormattedText> | ||
| 36 | + <FormattedString> | ||
| 37 | + <Span Text="出库单号:" FontAttributes="Bold"/> | ||
| 38 | + <Span Text="{Binding OutstockNo}"/> | ||
| 39 | + </FormattedString> | ||
| 40 | + </Label.FormattedText> | ||
| 41 | + </Label> | ||
| 42 | + | ||
| 43 | + <!-- 第一行右:领料单号 --> | ||
| 44 | + <Label Grid.Row="1" Grid.Column="0" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 45 | + <Label.FormattedText> | ||
| 46 | + <FormattedString> | ||
| 47 | + <Span Text="领料单号:" FontAttributes="Bold"/> | ||
| 48 | + <Span Text="{Binding RequisitionMaterialNo}"/> | ||
| 49 | + </FormattedString> | ||
| 50 | + </Label.FormattedText> | ||
| 51 | + </Label> | ||
| 52 | + | ||
| 53 | + <!-- 第二行左:工单号 --> | ||
| 54 | + <Label Grid.Row="1" Grid.Column="1" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 55 | + <Label.FormattedText> | ||
| 56 | + <FormattedString> | ||
| 57 | + <Span Text="工单号:" FontAttributes="Bold"/> | ||
| 58 | + <Span Text="{Binding WorkOrderNo}"/> | ||
| 59 | + </FormattedString> | ||
| 60 | + </Label.FormattedText> | ||
| 61 | + </Label> | ||
| 62 | + | ||
| 63 | + <!-- 第二行右:出库单备注 --> | ||
| 64 | + <Label Grid.Row="2" Grid.Column="0" FontSize="13" LineBreakMode="TailTruncation" MaxLines="1"> | ||
| 65 | + <Label.FormattedText> | ||
| 66 | + <FormattedString> | ||
| 67 | + <Span Text="出库单备注:" FontAttributes="Bold"/> | ||
| 68 | + <Span Text="{Binding SupplierName}"/> | ||
| 69 | + </FormattedString> | ||
| 70 | + </Label.FormattedText> | ||
| 71 | + </Label> | ||
| 72 | + | ||
| 57 | </Grid> | 73 | </Grid> |
| 58 | </Frame> | 74 | </Frame> |
| 59 | 75 | ||
| 60 | - <!-- Tab + 列表 --> | ||
| 61 | - <Grid Grid.Row="3" RowDefinitions="Auto,Auto,*"> | ||
| 62 | - | ||
| 63 | - <!-- Tab --> | 76 | + <!-- Tab切换 --> |
| 77 | + <Grid Grid.Row="3" RowDefinitions="Auto,Auto,*" Margin="0"> | ||
| 64 | <Grid ColumnDefinitions="*,*" BackgroundColor="White"> | 78 | <Grid ColumnDefinitions="*,*" BackgroundColor="White"> |
| 65 | <Button Text="出库单明细" | 79 | <Button Text="出库单明细" |
| 80 | + Command="{Binding ShowPendingCommand}" | ||
| 66 | BackgroundColor="{Binding PendingTabColor}" | 81 | BackgroundColor="{Binding PendingTabColor}" |
| 67 | - TextColor="{Binding PendingTextColor}" | ||
| 68 | - Clicked="OnPendingTabClicked"/> | 82 | + TextColor="{Binding PendingTextColor}" /> |
| 69 | <Button Text="扫描明细" | 83 | <Button Text="扫描明细" |
| 70 | Grid.Column="1" | 84 | Grid.Column="1" |
| 85 | + Command="{Binding ShowScannedCommand}" | ||
| 71 | BackgroundColor="{Binding ScannedTabColor}" | 86 | BackgroundColor="{Binding ScannedTabColor}" |
| 72 | - TextColor="{Binding ScannedTextColor}" | ||
| 73 | - Clicked="OnScannedTabClicked"/> | 87 | + TextColor="{Binding ScannedTextColor}" /> |
| 74 | </Grid> | 88 | </Grid> |
| 75 | 89 | ||
| 76 | - <!-- 出库单明细表头 --> | ||
| 77 | - <Grid Grid.Row="1" | ||
| 78 | - ColumnDefinitions="2*,2*,1.5*,1.5*,1.5*,1.5*,1.5*" | ||
| 79 | - BackgroundColor="#F2F2F2" | ||
| 80 | - IsVisible="{Binding IsPendingVisible}" | ||
| 81 | - Padding="4"> | ||
| 82 | - <Label Text="产品名称" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 83 | - <Label Grid.Column="1" Text="产品编码" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 84 | - <Label Grid.Column="2" Text="规格" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 85 | - <Label Grid.Column="3" Text="出库库位" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 86 | - <Label Grid.Column="4" Text="批次号" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 87 | - <Label Grid.Column="5" Text="出库数量" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 88 | - <Label Grid.Column="6" Text="已扫描数" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | 90 | + <!-- 待入库明细表头 --> |
| 91 | + <Grid Grid.Row="1" ColumnDefinitions="*,*,*,*,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsPendingVisible}" Padding="8"> | ||
| 92 | + <Label Text="物料名称" FontAttributes="Bold" /> | ||
| 93 | + <Label Grid.Column="1" Text="物料编码" FontAttributes="Bold" /> | ||
| 94 | + <Label Grid.Column="2" Text="规格" FontAttributes="Bold" /> | ||
| 95 | + <Label Grid.Column="3" Text="出库库位" FontAttributes="Bold" /> | ||
| 96 | + <Label Grid.Column="4" Text="批次号" FontAttributes="Bold" /> | ||
| 97 | + <Label Grid.Column="5" Text="出库数量" FontAttributes="Bold" /> | ||
| 98 | + <Label Grid.Column="6" Text="已扫描数量" FontAttributes="Bold" /> | ||
| 89 | </Grid> | 99 | </Grid> |
| 90 | 100 | ||
| 91 | - <!-- 出库单明细列表 --> | 101 | + <!-- 待入库明细列表 --> |
| 92 | <CollectionView Grid.Row="2" | 102 | <CollectionView Grid.Row="2" |
| 93 | ItemsSource="{Binding PendingList}" | 103 | ItemsSource="{Binding PendingList}" |
| 94 | - IsVisible="{Binding IsPendingVisible}"> | 104 | + IsVisible="{Binding IsPendingVisible}" |
| 105 | + SelectionMode="Single"> | ||
| 95 | <CollectionView.ItemTemplate> | 106 | <CollectionView.ItemTemplate> |
| 96 | <DataTemplate> | 107 | <DataTemplate> |
| 97 | - <Grid ColumnDefinitions="2*,2*,1.5*,1.5*,1.5*,1.5*,1.5*" Padding="4" BackgroundColor="White"> | ||
| 98 | - <Label Text="{Binding Name}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 99 | - <Label Grid.Column="1" Text="{Binding Code}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 100 | - <Label Grid.Column="2" Text="{Binding Spec}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 101 | - <Label Grid.Column="3" Text="{Binding Bin}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 102 | - <Label Grid.Column="4" Text="{Binding BatchNo}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 103 | - <Label Grid.Column="5" Text="{Binding OutQty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 104 | - <Label Grid.Column="6" Text="{Binding ScannedQty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | 108 | + <Grid ColumnDefinitions="*,*,*,*,*,*,*" Padding="8" BackgroundColor="White"> |
| 109 | + <VisualStateManager.VisualStateGroups> | ||
| 110 | + <VisualStateGroup Name="CommonStates"> | ||
| 111 | + <VisualState Name="Normal"> | ||
| 112 | + <VisualState.Setters> | ||
| 113 | + <Setter Property="BackgroundColor" Value="White"/> | ||
| 114 | + </VisualState.Setters> | ||
| 115 | + </VisualState> | ||
| 116 | + <VisualState Name="Selected"> | ||
| 117 | + <VisualState.Setters> | ||
| 118 | + <Setter Property="BackgroundColor" Value="#CCFFCC"/> | ||
| 119 | + </VisualState.Setters> | ||
| 120 | + </VisualState> | ||
| 121 | + </VisualStateGroup> | ||
| 122 | + </VisualStateManager.VisualStateGroups> | ||
| 123 | + <Label Text="{Binding Name}" /> | ||
| 124 | + <Label Grid.Column="1" Text="{Binding MaterialCode}" /> | ||
| 125 | + <Label Grid.Column="2" Text="{Binding Spec}" /> | ||
| 126 | + <Label Grid.Column="3" Text="{Binding Location}" /> | ||
| 127 | + <Label Grid.Column="4" Text="{Binding StockBatch}" /> | ||
| 128 | + <Label Grid.Column="5" Text="{Binding OutstockQty}" /> | ||
| 129 | + <Label Grid.Column="6" Text="{Binding Qty}" /> | ||
| 105 | </Grid> | 130 | </Grid> |
| 106 | </DataTemplate> | 131 | </DataTemplate> |
| 107 | </CollectionView.ItemTemplate> | 132 | </CollectionView.ItemTemplate> |
| 108 | </CollectionView> | 133 | </CollectionView> |
| 109 | 134 | ||
| 110 | <!-- 扫描明细表头 --> | 135 | <!-- 扫描明细表头 --> |
| 111 | - <Grid Grid.Row="1" | ||
| 112 | - ColumnDefinitions="40,*,*,*" | ||
| 113 | - BackgroundColor="#F2F2F2" | ||
| 114 | - IsVisible="{Binding IsScannedVisible}" | ||
| 115 | - Padding="4"> | ||
| 116 | - <Label Text="选择" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 117 | - <Label Grid.Column="1" Text="条码" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 118 | - <Label Grid.Column="2" Text="物料名称" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 119 | - <Label Grid.Column="3" Text="数量" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | 136 | + <Grid Grid.Row="1" ColumnDefinitions="40,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsScannedVisible}" Padding="8"> |
| 137 | + <Label Text="选择" FontAttributes="Bold" /> | ||
| 138 | + <Label Grid.Column="1" Text="物料名称" FontAttributes="Bold" /> | ||
| 139 | + <Label Grid.Column="2" Text="条码" FontAttributes="Bold" /> | ||
| 140 | + <Label Grid.Column="3" Text="数量" FontAttributes="Bold" /> | ||
| 120 | </Grid> | 141 | </Grid> |
| 121 | 142 | ||
| 122 | <!-- 扫描明细列表 --> | 143 | <!-- 扫描明细列表 --> |
| 123 | <CollectionView Grid.Row="2" | 144 | <CollectionView Grid.Row="2" |
| 124 | ItemsSource="{Binding ScannedList}" | 145 | ItemsSource="{Binding ScannedList}" |
| 125 | - IsVisible="{Binding IsScannedVisible}"> | 146 | + IsVisible="{Binding IsScannedVisible}" |
| 147 | + SelectionMode="Single" | ||
| 148 | + SelectedItem="{Binding SelectedScanItem, Mode=TwoWay}"> | ||
| 126 | <CollectionView.ItemTemplate> | 149 | <CollectionView.ItemTemplate> |
| 127 | <DataTemplate> | 150 | <DataTemplate> |
| 128 | - <Grid ColumnDefinitions="40,*,*,*" Padding="4" BackgroundColor="White"> | ||
| 129 | - <CheckBox IsChecked="{Binding IsSelected}" HorizontalOptions="Center" VerticalOptions="Center"/> | ||
| 130 | - <Label Grid.Column="1" Text="{Binding Barcode}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 131 | - <Label Grid.Column="2" Text="{Binding Name}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | ||
| 132 | - <Label Grid.Column="3" Text="{Binding Qty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/> | 151 | + <Grid ColumnDefinitions="40,*,*,*" Padding="8" BackgroundColor="White"> |
| 152 | + <Grid.Triggers> | ||
| 153 | + <!-- ScanStatus = true → 绿色 --> | ||
| 154 | + <DataTrigger TargetType="Grid" Binding="{Binding ScanStatus}" Value="True"> | ||
| 155 | + <Setter Property="BackgroundColor" Value="#E6FFE6" /> | ||
| 156 | + </DataTrigger> | ||
| 157 | + </Grid.Triggers> | ||
| 158 | + | ||
| 159 | + <CheckBox IsChecked="{Binding IsSelected}" /> | ||
| 160 | + <Label Grid.Column="1" Text="{Binding Name}" /> | ||
| 161 | + <Label Grid.Column="2" Text="{Binding Barcode}" /> | ||
| 162 | + <Entry Grid.Column="3" | ||
| 163 | + Keyboard="Numeric" | ||
| 164 | + HorizontalTextAlignment="Center" | ||
| 165 | + WidthRequest="64" | ||
| 166 | + Completed="OnQtyCompleted" | ||
| 167 | + Text="{Binding Qty, Mode=TwoWay, Converter={StaticResource IntConverter}}" /> | ||
| 168 | + | ||
| 133 | </Grid> | 169 | </Grid> |
| 134 | </DataTemplate> | 170 | </DataTemplate> |
| 135 | </CollectionView.ItemTemplate> | 171 | </CollectionView.ItemTemplate> |
| 136 | </CollectionView> | 172 | </CollectionView> |
| 173 | + | ||
| 137 | </Grid> | 174 | </Grid> |
| 138 | 175 | ||
| 176 | + <!-- 底部按钮 --> | ||
| 139 | <Grid Grid.Row="4" ColumnDefinitions="*,*,*" Padding="16,8" ColumnSpacing="10"> | 177 | <Grid Grid.Row="4" ColumnDefinitions="*,*,*" Padding="16,8" ColumnSpacing="10"> |
| 140 | 178 | ||
| 141 | <!-- 扫描通过 --> | 179 | <!-- 扫描通过 --> |
| @@ -177,20 +215,20 @@ | @@ -177,20 +215,20 @@ | ||
| 177 | </StackLayout> | 215 | </StackLayout> |
| 178 | </Grid> | 216 | </Grid> |
| 179 | 217 | ||
| 180 | - <!-- 确认入库 --> | 218 | + <!-- 确认出库 --> |
| 181 | <Grid Grid.Column="2" | 219 | <Grid Grid.Column="2" |
| 182 | BackgroundColor="#2196F3" | 220 | BackgroundColor="#2196F3" |
| 183 | HorizontalOptions="Fill" | 221 | HorizontalOptions="Fill" |
| 184 | VerticalOptions="Fill" | 222 | VerticalOptions="Fill" |
| 185 | HeightRequest="50"> | 223 | HeightRequest="50"> |
| 186 | <Grid.GestureRecognizers> | 224 | <Grid.GestureRecognizers> |
| 187 | - <TapGestureRecognizer Command="{Binding ConfirmCommand}" /> | 225 | + <TapGestureRecognizer Tapped="OnConfirmClicked" /> |
| 188 | </Grid.GestureRecognizers> | 226 | </Grid.GestureRecognizers> |
| 189 | <StackLayout Orientation="Horizontal" | 227 | <StackLayout Orientation="Horizontal" |
| 190 | HorizontalOptions="Center" | 228 | HorizontalOptions="Center" |
| 191 | VerticalOptions="Center"> | 229 | VerticalOptions="Center"> |
| 192 | <Image Source="confirm.png" HeightRequest="20" WidthRequest="20" /> | 230 | <Image Source="confirm.png" HeightRequest="20" WidthRequest="20" /> |
| 193 | - <Label Text="确认入库" | 231 | + <Label Text="确认出库" |
| 194 | Margin="5,0,0,0" | 232 | Margin="5,0,0,0" |
| 195 | VerticalOptions="Center" | 233 | VerticalOptions="Center" |
| 196 | TextColor="White" /> | 234 | TextColor="White" /> |
| 1 | using IndustrialControl.Services; | 1 | using IndustrialControl.Services; |
| 2 | using IndustrialControl.ViewModels; | 2 | using IndustrialControl.ViewModels; |
| 3 | 3 | ||
| 4 | -namespace IndustrialControl.Pages | 4 | +namespace IndustrialControl.Pages; |
| 5 | + | ||
| 6 | +[QueryProperty(nameof(OutstockId), "outstockId")] | ||
| 7 | +[QueryProperty(nameof(OutstockNo), "outstockNo")] | ||
| 8 | +[QueryProperty(nameof(OrderType), "orderType")] | ||
| 9 | +[QueryProperty(nameof(OrderTypeName), "orderTypeName")] | ||
| 10 | +[QueryProperty(nameof(RequisitionMaterialNo), "requisitionMaterialNo")] | ||
| 11 | +[QueryProperty(nameof(ReturnNo), "returnNo")] | ||
| 12 | +[QueryProperty(nameof(DeliveryNo), "deliveryNo")] | ||
| 13 | +[QueryProperty(nameof(CreatedTime), "createdTime")] | ||
| 14 | +public partial class OutboundMaterialPage : ContentPage | ||
| 5 | { | 15 | { |
| 6 | - [QueryProperty(nameof(OutstockId), "outstockId")] | ||
| 7 | - [QueryProperty(nameof(OutstockNo), "outstockNo")] | ||
| 8 | - [QueryProperty(nameof(OrderType), "orderType")] | ||
| 9 | - [QueryProperty(nameof(OrderTypeName), "orderTypeName")] | ||
| 10 | - [QueryProperty(nameof(PurchaseNo), "purchaseNo")] | ||
| 11 | - [QueryProperty(nameof(SupplierName), "supplierName")] | ||
| 12 | - [QueryProperty(nameof(CreatedTime), "createdTime")] | ||
| 13 | - public partial class OutboundMaterialPage : ContentPage | ||
| 14 | - { | ||
| 15 | private readonly ScanService _scanSvc; | 16 | private readonly ScanService _scanSvc; |
| 16 | private readonly OutboundMaterialViewModel _vm; | 17 | private readonly OutboundMaterialViewModel _vm; |
| 17 | public string? OutstockId { get; set; } | 18 | public string? OutstockId { get; set; } |
| 18 | public string? OutstockNo { get; set; } | 19 | public string? OutstockNo { get; set; } |
| 19 | public string? OrderType { get; set; } | 20 | public string? OrderType { get; set; } |
| 20 | public string? OrderTypeName { get; set; } | 21 | public string? OrderTypeName { get; set; } |
| 21 | - public string? PurchaseNo { get; set; } | ||
| 22 | - public string? SupplierName { get; set; } | 22 | + public string? RequisitionMaterialNo { get; set; } |
| 23 | + public string? ReturnNo { get; set; } | ||
| 24 | + public string? DeliveryNo { get; set; } | ||
| 23 | public string? CreatedTime { get; set; } | 25 | public string? CreatedTime { get; set; } |
| 26 | + private readonly IDialogService _dialogs; | ||
| 24 | 27 | ||
| 25 | - | ||
| 26 | - public OutboundMaterialPage(OutboundMaterialViewModel vm, ScanService scanSvc) | 28 | + public OutboundMaterialPage(OutboundMaterialViewModel vm, ScanService scanSvc, IDialogService dialogs) |
| 27 | { | 29 | { |
| 28 | InitializeComponent(); | 30 | InitializeComponent(); |
| 29 | BindingContext = vm; | 31 | BindingContext = vm; |
| 30 | _scanSvc = scanSvc; | 32 | _scanSvc = scanSvc; |
| 31 | _vm = vm; | 33 | _vm = vm; |
| 34 | + _dialogs = dialogs; | ||
| 35 | + // 可选:配置前后缀与防抖 | ||
| 32 | _scanSvc.Prefix = null; // 例如 "}q" 之类的前缀;没有就留 null | 36 | _scanSvc.Prefix = null; // 例如 "}q" 之类的前缀;没有就留 null |
| 33 | // _scanSvc.Suffix = "\n"; // 如果设备会附带换行,可去掉;没有就设 null | 37 | // _scanSvc.Suffix = "\n"; // 如果设备会附带换行,可去掉;没有就设 null |
| 34 | //_scanSvc.DebounceMs = 250; | 38 | //_scanSvc.DebounceMs = 250; |
| 35 | _scanSvc.Suffix = null; // 先关掉 | 39 | _scanSvc.Suffix = null; // 先关掉 |
| 36 | _scanSvc.DebounceMs = 0; // 先关掉 | 40 | _scanSvc.DebounceMs = 0; // 先关掉 |
| 41 | + | ||
| 37 | } | 42 | } |
| 38 | 43 | ||
| 39 | protected override async void OnAppearing() | 44 | protected override async void OnAppearing() |
| @@ -48,8 +53,9 @@ namespace IndustrialControl.Pages | @@ -48,8 +53,9 @@ namespace IndustrialControl.Pages | ||
| 48 | outstockNo: OutstockNo ?? "", | 53 | outstockNo: OutstockNo ?? "", |
| 49 | orderType: OrderType ?? "", | 54 | orderType: OrderType ?? "", |
| 50 | orderTypeName: OrderTypeName ?? "", | 55 | orderTypeName: OrderTypeName ?? "", |
| 51 | - purchaseNo: PurchaseNo ?? "", | ||
| 52 | - supplierName: SupplierName ?? "", | 56 | + requisitionMaterialNo: RequisitionMaterialNo ?? "", |
| 57 | + returnNo: ReturnNo ?? "", | ||
| 58 | + deliveryNo: DeliveryNo ?? "", | ||
| 53 | createdTime: CreatedTime ?? "" | 59 | createdTime: CreatedTime ?? "" |
| 54 | ); | 60 | ); |
| 55 | } | 61 | } |
| @@ -59,6 +65,18 @@ namespace IndustrialControl.Pages | @@ -59,6 +65,18 @@ namespace IndustrialControl.Pages | ||
| 59 | _scanSvc.Attach(ScanEntry); | 65 | _scanSvc.Attach(ScanEntry); |
| 60 | ScanEntry.Focus(); | 66 | ScanEntry.Focus(); |
| 61 | } | 67 | } |
| 68 | + | ||
| 69 | + | ||
| 70 | + /// <summary> | ||
| 71 | + /// 清空扫描记录 | ||
| 72 | + /// </summary> | ||
| 73 | + void OnClearClicked(object sender, EventArgs e) | ||
| 74 | + { | ||
| 75 | + _vm.ClearScan(); | ||
| 76 | + ScanEntry.Text = string.Empty; | ||
| 77 | + ScanEntry.Focus(); | ||
| 78 | + } | ||
| 79 | + | ||
| 62 | protected override void OnDisappearing() | 80 | protected override void OnDisappearing() |
| 63 | { | 81 | { |
| 64 | // 退出页面即注销(防止多个程序/页面抢处理) | 82 | // 退出页面即注销(防止多个程序/页面抢处理) |
| @@ -79,35 +97,59 @@ namespace IndustrialControl.Pages | @@ -79,35 +97,59 @@ namespace IndustrialControl.Pages | ||
| 79 | await _vm.HandleScannedAsync(data, type); | 97 | await _vm.HandleScannedAsync(data, type); |
| 80 | }); | 98 | }); |
| 81 | } | 99 | } |
| 82 | - // Tab切换 | ||
| 83 | - void OnPendingTabClicked(object sender, EventArgs e) | ||
| 84 | - { | ||
| 85 | - _vm.IsPendingVisible = true; | ||
| 86 | - _vm.IsScannedVisible = false; | ||
| 87 | - } | ||
| 88 | 100 | ||
| 89 | - void OnScannedTabClicked(object sender, EventArgs e) | 101 | + |
| 102 | + /// <summary> | ||
| 103 | + /// 确认入库按钮点击 | ||
| 104 | + /// </summary> | ||
| 105 | + async void OnConfirmClicked(object sender, EventArgs e) | ||
| 90 | { | 106 | { |
| 91 | - _vm.IsPendingVisible = false; | ||
| 92 | - _vm.IsScannedVisible = true; | ||
| 93 | - } | 107 | + var ok = await _vm.ConfirmOutboundAsync(); |
| 108 | + if (ok) | ||
| 109 | + { | ||
| 110 | + await DisplayAlert("提示", "入库成功", "确定"); | ||
| 111 | + _vm.ClearAll(); | ||
| 94 | 112 | ||
| 95 | - // 清空扫描记录 | ||
| 96 | - void OnClearClicked(object sender, EventArgs e) | 113 | + // ✅ 返回到工单查询页面(InboundMaterialSearchPage) |
| 114 | + await Shell.Current.GoToAsync($"//{nameof(OutboundMaterialSearchPage)}"); | ||
| 115 | + } | ||
| 116 | + else | ||
| 97 | { | 117 | { |
| 98 | - _vm.ClearScan(); | ||
| 99 | - ScanEntry.Text = string.Empty; | ||
| 100 | - ScanEntry.Focus(); | 118 | + await DisplayAlert("提示", "入库失败,请检查数据", "确定"); |
| 101 | } | 119 | } |
| 120 | + } | ||
| 121 | + | ||
| 102 | 122 | ||
| 103 | - // 摄像头扫码按钮 | ||
| 104 | - async void OnScanClicked(object sender, EventArgs e) | 123 | + private async void OnBinTapped(object? sender, TappedEventArgs e) |
| 105 | { | 124 | { |
| 106 | - await DisplayAlert("提示", "此按钮预留摄像头扫码;硬件扫描直接扣扳机。", "确定"); | ||
| 107 | - } | 125 | + var bindable = sender as BindableObject; |
| 126 | + var row = bindable?.BindingContext; | ||
| 127 | + if (row == null) return; | ||
| 128 | + | ||
| 129 | + var type = row.GetType(); | ||
| 130 | + var currentBin = type.GetProperty("Location")?.GetValue(row)?.ToString(); | ||
| 108 | 131 | ||
| 132 | + // 1) 打开公共组件选择库位(你已完成的组件) | ||
| 133 | + var picked = await BinPickerPage.ShowAsync(currentBin); | ||
| 134 | + if (picked == null) return; | ||
| 135 | + | ||
| 136 | + // 2) 调用 VM:带上行对象 + 选中的 BinInfo,内部会调接口 & 回填行 | ||
| 137 | + await _vm.UpdateRowLocationAsync(row, picked); | ||
| 138 | + } | ||
| 109 | 139 | ||
| 140 | + private async void OnQtyCompleted(object sender, EventArgs e) | ||
| 141 | + { | ||
| 142 | + if (sender is not Entry entry) return; | ||
| 143 | + if (entry.BindingContext is not IndustrialControl.ViewModels.OutScannedItem row) return; | ||
| 110 | 144 | ||
| 145 | + // 只看 ScanStatus:未通过则不提交 | ||
| 146 | + if (!row.ScanStatus) | ||
| 147 | + { | ||
| 148 | + await DisplayAlert("提示", "该行尚未扫描通过,不能修改数量。", "确定"); | ||
| 149 | + return; | ||
| 150 | + } | ||
| 111 | 151 | ||
| 152 | + await _vm.UpdateQuantityForRowAsync(row); | ||
| 112 | } | 153 | } |
| 154 | + | ||
| 113 | } | 155 | } |
| 1 | <?xml version="1.0" encoding="utf-8" ?> | 1 | <?xml version="1.0" encoding="utf-8" ?> |
| 2 | -<ContentPage x:Name="Page" | 2 | +<ContentPage |
| 3 | + x:Class="IndustrialControl.Pages.OutboundMaterialSearchPage" | ||
| 3 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | 4 | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
| 4 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | 5 | xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
| 5 | - x:Class="IndustrialControl.Pages.OutboundMaterialSearchPage" | ||
| 6 | - xmlns:conv="clr-namespace:IndustrialControl.Converters" | ||
| 7 | - Title="仓储管理系统"> | ||
| 8 | - <ContentPage.Resources> | ||
| 9 | - <ResourceDictionary> | ||
| 10 | - <!-- 空/非空转布尔:非空 => true(按钮可用) --> | ||
| 11 | - <conv:NullToBoolConverter x:Key="NullToBoolConverter" /> | ||
| 12 | - </ResourceDictionary> | ||
| 13 | - </ContentPage.Resources> | 6 | + x:Name="Page" |
| 7 | + Shell.NavBarIsVisible="True"> | ||
| 14 | 8 | ||
| 15 | - <Grid RowDefinitions="Auto,*,Auto" Padding="16" BackgroundColor="#F6F7FB"> | 9 | + <ScrollView> |
| 10 | + <VerticalStackLayout Spacing="12" Padding="12"> | ||
| 16 | 11 | ||
| 17 | - <!-- 顶部:输入条件(第0行) --> | ||
| 18 | - <VerticalStackLayout Grid.Row="0" Spacing="10"> | 12 | + <!-- 查询区域 --> |
| 19 | <Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="8"> | 13 | <Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="8"> |
| 20 | <Entry x:Name="OrderEntry" | 14 | <Entry x:Name="OrderEntry" |
| 21 | Grid.Row="0" Grid.Column="0" | 15 | Grid.Row="0" Grid.Column="0" |
| 22 | - Placeholder="请输入出库单条码" | ||
| 23 | - VerticalOptions="Center" | ||
| 24 | - BackgroundColor="White" | 16 | + Placeholder="请扫描输入出库单条码" |
| 25 | Text="{Binding SearchOrderNo}" /> | 17 | Text="{Binding SearchOrderNo}" /> |
| 26 | 18 | ||
| 27 | - | ||
| 28 | - <!-- 开始日期 --> | ||
| 29 | - <DatePicker Grid.Row="1" Grid.Column="0" | ||
| 30 | - Date="{Binding StartDate}" | ||
| 31 | - MinimumDate="2000-01-01" | ||
| 32 | - MaximumDate="{Binding EndDate}" /> | ||
| 33 | - | ||
| 34 | - <!-- 结束日期 --> | ||
| 35 | - <DatePicker Grid.Row="1" Grid.Column="1" | ||
| 36 | - Date="{Binding EndDate}" | ||
| 37 | - MinimumDate="{Binding StartDate}" /> | ||
| 38 | - <Button Grid.Row="1" Grid.Column="1" | 19 | + <Button Grid.Row="0" Grid.Column="1" |
| 39 | Text="查询" | 20 | Text="查询" |
| 40 | Command="{Binding SearchCommand}" /> | 21 | Command="{Binding SearchCommand}" /> |
| 22 | + | ||
| 23 | + <Grid Grid.Row="1" Grid.ColumnSpan="2" ColumnDefinitions="Auto,*,Auto,*" ColumnSpacing="8"> | ||
| 24 | + <Label Grid.Column="0" Text="开始:" VerticalTextAlignment="Center"/> | ||
| 25 | + <DatePicker Grid.Column="1" Date="{Binding StartDate}" /> | ||
| 26 | + <Label Grid.Column="2" Text="结束:" VerticalTextAlignment="Center"/> | ||
| 27 | + <DatePicker Grid.Column="3" Date="{Binding EndDate}" /> | ||
| 28 | + </Grid> | ||
| 41 | </Grid> | 29 | </Grid> |
| 42 | - </VerticalStackLayout> | ||
| 43 | 30 | ||
| 44 | - <!-- 中部:结果列表(第1行) --> | 31 | + <!-- 列表区域 --> |
| 45 | <CollectionView Grid.Row="1" | 32 | <CollectionView Grid.Row="1" |
| 46 | ItemsSource="{Binding Orders}" | 33 | ItemsSource="{Binding Orders}" |
| 47 | SelectionMode="Single" | 34 | SelectionMode="Single" |
| @@ -49,13 +36,15 @@ | @@ -49,13 +36,15 @@ | ||
| 49 | <CollectionView.ItemTemplate> | 36 | <CollectionView.ItemTemplate> |
| 50 | <DataTemplate> | 37 | <DataTemplate> |
| 51 | <Frame Margin="0,8,0,0" Padding="12" HasShadow="True" CornerRadius="10"> | 38 | <Frame Margin="0,8,0,0" Padding="12" HasShadow="True" CornerRadius="10"> |
| 52 | - <!-- ⭐ 点击整卡片触发命令 --> | 39 | + <!-- 点击整卡片:直接调用 VM 的 GoOutboundCommand,并把当前项作为参数 --> |
| 53 | <Frame.GestureRecognizers> | 40 | <Frame.GestureRecognizers> |
| 54 | <TapGestureRecognizer | 41 | <TapGestureRecognizer |
| 55 | Command="{Binding BindingContext.GoOutboundCommand, Source={x:Reference Page}}" | 42 | Command="{Binding BindingContext.GoOutboundCommand, Source={x:Reference Page}}" |
| 56 | CommandParameter="{Binding .}" /> | 43 | CommandParameter="{Binding .}" /> |
| 57 | </Frame.GestureRecognizers> | 44 | </Frame.GestureRecognizers> |
| 58 | - <Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="Auto,*" ColumnSpacing="8"> | 45 | + |
| 46 | + <Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto" | ||
| 47 | + ColumnDefinitions="Auto,*" ColumnSpacing="8"> | ||
| 59 | <Label Grid.Row="0" Grid.Column="0" Text="出库单号:" FontAttributes="Bold"/> | 48 | <Label Grid.Row="0" Grid.Column="0" Text="出库单号:" FontAttributes="Bold"/> |
| 60 | <Label Grid.Row="0" Grid.Column="1" Text="{Binding outstockNo}"/> | 49 | <Label Grid.Row="0" Grid.Column="1" Text="{Binding outstockNo}"/> |
| 61 | 50 | ||
| @@ -76,10 +65,6 @@ | @@ -76,10 +65,6 @@ | ||
| 76 | </CollectionView.ItemTemplate> | 65 | </CollectionView.ItemTemplate> |
| 77 | </CollectionView> | 66 | </CollectionView> |
| 78 | 67 | ||
| 79 | - <!-- 底部:操作(第2行) --> | ||
| 80 | - <Grid Grid.Row="2" ColumnDefinitions="*,Auto" Padding="0,8,0,0"> | ||
| 81 | - <Label Text="{Binding Orders.Count, StringFormat='共 {0} 条'}" | ||
| 82 | - VerticalTextAlignment="Center" /> | ||
| 83 | - </Grid> | ||
| 84 | - </Grid> | 68 | + </VerticalStackLayout> |
| 69 | + </ScrollView> | ||
| 85 | </ContentPage> | 70 | </ContentPage> |
| 1 | -using System.Threading; | ||
| 2 | using IndustrialControl.Services; | 1 | using IndustrialControl.Services; |
| 3 | using IndustrialControl.ViewModels; | 2 | using IndustrialControl.ViewModels; |
| 4 | namespace IndustrialControl.Pages; | 3 | namespace IndustrialControl.Pages; |
| @@ -30,6 +29,8 @@ public partial class OutboundMaterialSearchPage : ContentPage | @@ -30,6 +29,8 @@ public partial class OutboundMaterialSearchPage : ContentPage | ||
| 30 | //键盘输入 | 29 | //键盘输入 |
| 31 | _scanSvc.Attach(OrderEntry); | 30 | _scanSvc.Attach(OrderEntry); |
| 32 | OrderEntry.Focus(); | 31 | OrderEntry.Focus(); |
| 32 | + | ||
| 33 | + | ||
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | /// <summary> | 36 | /// <summary> |
| @@ -59,4 +60,5 @@ public partial class OutboundMaterialSearchPage : ContentPage | @@ -59,4 +60,5 @@ public partial class OutboundMaterialSearchPage : ContentPage | ||
| 59 | }); | 60 | }); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 63 | + | ||
| 62 | } | 64 | } |
| 1 | using IndustrialControl.Models; | 1 | using IndustrialControl.Models; |
| 2 | using IndustrialControl.ViewModels; | 2 | using IndustrialControl.ViewModels; |
| 3 | 3 | ||
| 4 | -namespace IndustrialControl.Services | 4 | +namespace IndustrialControl.Services; |
| 5 | + | ||
| 6 | +public interface IOutboundMaterialService | ||
| 5 | { | 7 | { |
| 6 | - public interface IOutboundMaterialService | ||
| 7 | - { | 8 | + |
| 8 | // NEW: 查询列表(图1) | 9 | // NEW: 查询列表(图1) |
| 9 | Task<IEnumerable<OutboundOrderSummary>> ListOutboundOrdersAsync( | 10 | Task<IEnumerable<OutboundOrderSummary>> ListOutboundOrdersAsync( |
| 10 | string? orderNoOrBarcode, | 11 | string? orderNoOrBarcode, |
| @@ -19,11 +20,28 @@ namespace IndustrialControl.Services | @@ -19,11 +20,28 @@ namespace IndustrialControl.Services | ||
| 19 | /// <summary>扫描条码入库</summary> | 20 | /// <summary>扫描条码入库</summary> |
| 20 | Task<SimpleOk> OutStockByBarcodeAsync(string outstockId, string barcode, CancellationToken ct = default); | 21 | Task<SimpleOk> OutStockByBarcodeAsync(string outstockId, string barcode, CancellationToken ct = default); |
| 21 | /// <summary>PDA 扫描通过(确认当前入库单已扫描项)</summary> | 22 | /// <summary>PDA 扫描通过(确认当前入库单已扫描项)</summary> |
| 22 | - Task<SimpleOk> ScanConfirmAsync(string outstockId, CancellationToken ct = default); | 23 | + // IOutboundMaterialService.cs |
| 24 | + Task<SimpleOk> ScanConfirmAsync(IEnumerable<(string barcode, string id)> items, CancellationToken ct = default); | ||
| 25 | + Task<SimpleOk> CancelScanAsync(IEnumerable<(string barcode, string id)> items, CancellationToken ct = default); | ||
| 23 | 26 | ||
| 24 | - Task<SimpleOk> CancelScanAsync(string outstockId, CancellationToken ct = default); | ||
| 25 | Task<SimpleOk> ConfirmOutstockAsync(string outstockId, CancellationToken ct = default); | 27 | Task<SimpleOk> ConfirmOutstockAsync(string outstockId, CancellationToken ct = default); |
| 26 | /// <summary>判断入库单明细是否全部扫码确认</summary> | 28 | /// <summary>判断入库单明细是否全部扫码确认</summary> |
| 27 | Task<bool> JudgeOutstockDetailScanAllAsync(string outstockId, CancellationToken ct = default); | 29 | Task<bool> JudgeOutstockDetailScanAllAsync(string outstockId, CancellationToken ct = default); |
| 28 | - } | 30 | + |
| 31 | + | ||
| 32 | + Task<SimpleOk> UpdateOutstockLocationAsync( | ||
| 33 | + string detailId, | ||
| 34 | + string id, | ||
| 35 | + string outstockWarehouse, | ||
| 36 | + string outstockWarehouseCode, | ||
| 37 | + string location, | ||
| 38 | + CancellationToken ct = default); | ||
| 39 | + | ||
| 40 | + Task<SimpleOk> UpdateQuantityAsync( | ||
| 41 | + string barcode, string detailId, string id, int quantity, CancellationToken ct = default); | ||
| 42 | + | ||
| 29 | } | 43 | } |
| 44 | + | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + |
| @@ -155,11 +155,13 @@ public sealed class InboundMaterialService : IInboundMaterialService | @@ -155,11 +155,13 @@ public sealed class InboundMaterialService : IInboundMaterialService | ||
| 155 | purchaseNo: x.purchaseNo ?? "", | 155 | purchaseNo: x.purchaseNo ?? "", |
| 156 | arrivalNo: x.arrivalNo ?? "", | 156 | arrivalNo: x.arrivalNo ?? "", |
| 157 | supplierName: x.supplierName ?? "", | 157 | supplierName: x.supplierName ?? "", |
| 158 | + workOrderNo: x.workOrderNo ?? "", | ||
| 159 | + materialName: x.materialName ?? "", | ||
| 160 | + instockQty: ToInt(x.instockQty), | ||
| 158 | createdTime: x.createdTime ?? "" | 161 | createdTime: x.createdTime ?? "" |
| 159 | )); | 162 | )); |
| 160 | } | 163 | } |
| 161 | 164 | ||
| 162 | - | ||
| 163 | public async Task<IReadOnlyList<InboundPendingRow>> GetInStockDetailAsync( | 165 | public async Task<IReadOnlyList<InboundPendingRow>> GetInStockDetailAsync( |
| 164 | string instockId, CancellationToken ct = default) | 166 | string instockId, CancellationToken ct = default) |
| 165 | { | 167 | { |
| @@ -281,7 +283,7 @@ public sealed class InboundMaterialService : IInboundMaterialService | @@ -281,7 +283,7 @@ public sealed class InboundMaterialService : IInboundMaterialService | ||
| 281 | 283 | ||
| 282 | public async Task<SimpleOk> ConfirmInstockAsync(string instockId, CancellationToken ct = default) | 284 | public async Task<SimpleOk> ConfirmInstockAsync(string instockId, CancellationToken ct = default) |
| 283 | { | 285 | { |
| 284 | - var bodyJson = JsonSerializer.Serialize(new { instockId }); | 286 | + var bodyJson = JsonSerializer.Serialize(new { id = instockId }); |
| 285 | using var req = new HttpRequestMessage(HttpMethod.Post, _confirmInstockEndpoint) | 287 | using var req = new HttpRequestMessage(HttpMethod.Post, _confirmInstockEndpoint) |
| 286 | { | 288 | { |
| 287 | Content = new StringContent(bodyJson, Encoding.UTF8, "application/json") | 289 | Content = new StringContent(bodyJson, Encoding.UTF8, "application/json") |
| @@ -577,6 +579,9 @@ public class GetInStockRecord | @@ -577,6 +579,9 @@ public class GetInStockRecord | ||
| 577 | public string? supplierName { get; set; } | 579 | public string? supplierName { get; set; } |
| 578 | public string? arrivalNo { get; set; } | 580 | public string? arrivalNo { get; set; } |
| 579 | public string? purchaseNo { get; set; } | 581 | public string? purchaseNo { get; set; } |
| 582 | + public string? workOrderNo { get; set; } | ||
| 583 | + public string? materialName { get; set; } | ||
| 584 | + public decimal? instockQty { get; set; } | ||
| 580 | public string? createdTime { get; set; } | 585 | public string? createdTime { get; set; } |
| 581 | } | 586 | } |
| 582 | public sealed class GetInStockScanDetailResp | 587 | public sealed class GetInStockScanDetailResp |
| 1 | -// 文件:Services/WarehouseDataApiService.cs | 1 | +using IndustrialControl.Models; |
| 2 | +using IndustrialControl.ViewModels; | ||
| 2 | using System.Net.Http; | 3 | using System.Net.Http; |
| 3 | using System.Text; | 4 | using System.Text; |
| 4 | using System.Text.Json; | 5 | using System.Text.Json; |
| 5 | using System.Text.Json.Nodes; | 6 | using System.Text.Json.Nodes; |
| 6 | -using IndustrialControl.Models; | ||
| 7 | -using IndustrialControl.ViewModels; | 7 | +using System.Text.Json.Serialization; |
| 8 | 8 | ||
| 9 | namespace IndustrialControl.Services; | 9 | namespace IndustrialControl.Services; |
| 10 | 10 | ||
| @@ -13,23 +13,25 @@ namespace IndustrialControl.Services; | @@ -13,23 +13,25 @@ namespace IndustrialControl.Services; | ||
| 13 | /// </summary> | 13 | /// </summary> |
| 14 | public sealed class OutboundMaterialService : IOutboundMaterialService | 14 | public sealed class OutboundMaterialService : IOutboundMaterialService |
| 15 | { | 15 | { |
| 16 | - private readonly HttpClient _http; | ||
| 17 | - private readonly string _outboundListEndpoint; | ||
| 18 | - private readonly string _detailEndpoint; | ||
| 19 | - private readonly string _scanDetailEndpoint; | 16 | + public readonly HttpClient _http; |
| 17 | + public readonly string _outboundListEndpoint; | ||
| 18 | + public readonly string _detailEndpoint; | ||
| 19 | + public readonly string _scanDetailEndpoint; | ||
| 20 | // 新增:扫码入库端点 | 20 | // 新增:扫码入库端点 |
| 21 | - private readonly string _scanByBarcodeEndpoint; | ||
| 22 | - private readonly string _scanConfirmEndpoint; | ||
| 23 | - private readonly string _cancelScanEndpoint; | ||
| 24 | - private readonly string _confirmOutstockEndpoint; | ||
| 25 | - private readonly string _judgeScanAllEndpoint; | 21 | + public readonly string _scanByBarcodeEndpoint; |
| 22 | + public readonly string _scanConfirmEndpoint; | ||
| 23 | + public readonly string _cancelScanEndpoint; | ||
| 24 | + public readonly string _confirmOutstockEndpoint; | ||
| 25 | + public readonly string _judgeScanAllEndpoint; | ||
| 26 | + private readonly JsonSerializerOptions _opt; | ||
| 27 | + private readonly JsonSerializerOptions _json = new() { PropertyNameCaseInsensitive = true }; | ||
| 26 | 28 | ||
| 27 | public OutboundMaterialService(HttpClient http, IConfigLoader configLoader) | 29 | public OutboundMaterialService(HttpClient http, IConfigLoader configLoader) |
| 28 | { | 30 | { |
| 29 | _http = http; | 31 | _http = http; |
| 30 | - | ||
| 31 | - // 和 WorkOrderApi 一样从 appconfig.json 读取端点,留兼容键名 + 兜底硬编码 | 32 | + _opt = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; |
| 32 | JsonNode cfg = configLoader.Load(); | 33 | JsonNode cfg = configLoader.Load(); |
| 34 | + | ||
| 33 | // ⭐ 新增:读取 baseUrl 或 ip+port | 35 | // ⭐ 新增:读取 baseUrl 或 ip+port |
| 34 | var baseUrl = | 36 | var baseUrl = |
| 35 | (string?)cfg?["server"]?["baseUrl"] | 37 | (string?)cfg?["server"]?["baseUrl"] |
| @@ -40,32 +42,43 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -40,32 +42,43 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 40 | 42 | ||
| 41 | if (_http.BaseAddress is null) | 43 | if (_http.BaseAddress is null) |
| 42 | _http.BaseAddress = new Uri(baseUrl, UriKind.Absolute); | 44 | _http.BaseAddress = new Uri(baseUrl, UriKind.Absolute); |
| 45 | + | ||
| 46 | + // 下面保持原来的相对路径读取(不变) | ||
| 43 | _outboundListEndpoint = | 47 | _outboundListEndpoint = |
| 44 | (string?)cfg?["apiEndpoints"]?["outbound"]?["list"] ?? | 48 | (string?)cfg?["apiEndpoints"]?["outbound"]?["list"] ?? |
| 45 | (string?)cfg?["apiEndpoints"]?["getOutStock"] ?? | 49 | (string?)cfg?["apiEndpoints"]?["getOutStock"] ?? |
| 46 | "/normalService/pda/wmsMaterialOutstock/getOutStock"; | 50 | "/normalService/pda/wmsMaterialOutstock/getOutStock"; |
| 47 | - _detailEndpoint = (string?)cfg?["apiEndpoints"]?["outbound"]?["detail"] | ||
| 48 | - ?? "/normalService/pda/wmsMaterialOutstock/getOutStockDetail"; | ||
| 49 | - _scanDetailEndpoint = (string?)cfg?["apiEndpoints"]?["outbound"]?["scanDetail"] | ||
| 50 | - ?? "/normalService/pda/wmsMaterialOutstock/getOutStockScanDetail"; | 51 | + |
| 52 | + _detailEndpoint = | ||
| 53 | + (string?)cfg?["apiEndpoints"]?["outbound"]?["detail"] ?? | ||
| 54 | + "/normalService/pda/wmsMaterialOutstock/getOutStockDetail"; | ||
| 55 | + | ||
| 56 | + _scanDetailEndpoint = | ||
| 57 | + (string?)cfg?["apiEndpoints"]?["outbound"]?["scanDetail"] ?? | ||
| 58 | + "/normalService/pda/wmsMaterialOutstock/getOutStockScanDetail"; | ||
| 59 | + | ||
| 51 | _scanByBarcodeEndpoint = | 60 | _scanByBarcodeEndpoint = |
| 52 | - (string?)cfg?["apiEndpoints"]?["outbound"]?["scanByBarcode"] | ||
| 53 | - ?? "/normalService/pda/wmsMaterialOutstock/getOutStockByBarcode"; | 61 | + (string?)cfg?["apiEndpoints"]?["outbound"]?["scanByBarcode"] ?? |
| 62 | + "/normalService/pda/wmsMaterialOutstock/getOutStockByBarcode"; | ||
| 63 | + | ||
| 54 | _scanConfirmEndpoint = | 64 | _scanConfirmEndpoint = |
| 55 | - (string?)cfg?["apiEndpoints"]?["outbound"]?["scanConfirm"] | ||
| 56 | - ?? "/normalService/pda/wmsMaterialOutstock/scanConfirm"; | 65 | + (string?)cfg?["apiEndpoints"]?["outbound"]?["scanConfirm"] ?? |
| 66 | + "/normalService/pda/wmsMaterialOutstock/scanOutConfirm"; | ||
| 67 | + | ||
| 57 | _cancelScanEndpoint = | 68 | _cancelScanEndpoint = |
| 58 | - (string?)cfg?["apiEndpoints"]?["outbound"]?["cancelScan"] | ||
| 59 | - ?? "/normalService/pda/wmsMaterialOutstock/cancelScan"; | 69 | + (string?)cfg?["apiEndpoints"]?["outbound"]?["cancelScan"] ?? |
| 70 | + "/normalService/pda/wmsMaterialOutstock/cancelOutScan"; | ||
| 71 | + | ||
| 60 | _confirmOutstockEndpoint = | 72 | _confirmOutstockEndpoint = |
| 61 | - (string?)cfg?["apiEndpoints"]?["outbound"]?["confirm"] | ||
| 62 | - ?? "/normalService/pda/wmsMaterialOutstock/confirm"; | 73 | + (string?)cfg?["apiEndpoints"]?["outbound"]?["confirm"] ?? |
| 74 | + "/normalService/pda/wmsMaterialOutstock/confirm"; | ||
| 75 | + | ||
| 63 | _judgeScanAllEndpoint = | 76 | _judgeScanAllEndpoint = |
| 64 | - (string?)cfg?["apiEndpoints"]?["outbound"]?["judgeScanAll"] | ||
| 65 | - ?? "/normalService/pda/wmsMaterialOutstock/judgeOutstockDetailScanAll"; | 77 | + (string?)cfg?["apiEndpoints"]?["outbound"]?["judgeScanAll"] ?? |
| 78 | + "/normalService/pda/wmsMaterialOutstock/judgeOutstockDetailScanAll"; | ||
| 66 | } | 79 | } |
| 67 | 80 | ||
| 68 | - // ====== 你当前页面会调用的方法 ====== | 81 | + // ⭐ 新增:拼接 ip + port → baseUrl |
| 69 | private static string? BuildBaseUrl(JsonNode? ipNode, JsonNode? portNode) | 82 | private static string? BuildBaseUrl(JsonNode? ipNode, JsonNode? portNode) |
| 70 | { | 83 | { |
| 71 | string? ip = ipNode?.ToString().Trim(); | 84 | string? ip = ipNode?.ToString().Trim(); |
| @@ -134,25 +147,21 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -134,25 +147,21 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 134 | 147 | ||
| 135 | return records.Select(x => new OutboundOrderSummary( | 148 | return records.Select(x => new OutboundOrderSummary( |
| 136 | outstockId: x.id ?? "", | 149 | outstockId: x.id ?? "", |
| 150 | + outstockNo: x.outstockNo ?? "", | ||
| 137 | orderType: x.orderType ?? "", | 151 | orderType: x.orderType ?? "", |
| 138 | orderTypeName: x.orderTypeName ?? "", | 152 | orderTypeName: x.orderTypeName ?? "", |
| 139 | - purchaseNo: x.purchaseNo ?? "", | ||
| 140 | - supplierName: x.supplierName ?? "", | ||
| 141 | - arrivalNo: x.arrivalNo ?? "", | ||
| 142 | - createdTime: x.createdTime ?? "", | ||
| 143 | - deliveryNo: x.deliveryNo ?? "", | ||
| 144 | - requisitionMaterialNo:x.requisitionMaterialNo ?? "", | ||
| 145 | - returnNo: x.returnNo ?? "", | ||
| 146 | - workOrderNo:x.workOrderNo ?? "" | ||
| 147 | - | ||
| 148 | - | 153 | + workOrderNo: x.workOrderNo ?? "", |
| 154 | + requisitionMaterialNo: x.requisitionMaterialNo ?? "", | ||
| 155 | + returnNo:x.returnNo ?? "", | ||
| 156 | + deliveryNo:x.deliveryNo ?? "", | ||
| 157 | + createdTime: x.createdTime ?? "" | ||
| 149 | )); | 158 | )); |
| 150 | } | 159 | } |
| 151 | 160 | ||
| 152 | - | ||
| 153 | public async Task<IReadOnlyList<OutboundPendingRow>> GetOutStockDetailAsync( | 161 | public async Task<IReadOnlyList<OutboundPendingRow>> GetOutStockDetailAsync( |
| 154 | string outstockId, CancellationToken ct = default) | 162 | string outstockId, CancellationToken ct = default) |
| 155 | { | 163 | { |
| 164 | + // ✅ 文档为 GET + x-www-form-urlencoded,参数名是小写 outstockId | ||
| 156 | var url = $"{_detailEndpoint}?outstockId={Uri.EscapeDataString(outstockId)}"; | 165 | var url = $"{_detailEndpoint}?outstockId={Uri.EscapeDataString(outstockId)}"; |
| 157 | using var req = new HttpRequestMessage(HttpMethod.Get, url); | 166 | using var req = new HttpRequestMessage(HttpMethod.Get, url); |
| 158 | 167 | ||
| @@ -166,27 +175,22 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -166,27 +175,22 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 166 | if (dto?.success != true || dto.result is null || dto.result.Count == 0) | 175 | if (dto?.success != true || dto.result is null || dto.result.Count == 0) |
| 167 | return Array.Empty<OutboundPendingRow>(); | 176 | return Array.Empty<OutboundPendingRow>(); |
| 168 | 177 | ||
| 169 | - static int ToIntSafe(string? s) | ||
| 170 | - { | ||
| 171 | - if (string.IsNullOrWhiteSpace(s)) return 0; | ||
| 172 | - s = s.Trim().Replace(",", ""); | ||
| 173 | - return int.TryParse(s, out var v) ? v : 0; | ||
| 174 | - } | ||
| 175 | - | ||
| 176 | // ⚠️ 接口没有 barcode,这里先用空串;如需展示可以改成 x.materialCode 或 x.stockBatch | 178 | // ⚠️ 接口没有 barcode,这里先用空串;如需展示可以改成 x.materialCode 或 x.stockBatch |
| 177 | var list = dto.result.Select(x => new OutboundPendingRow( | 179 | var list = dto.result.Select(x => new OutboundPendingRow( |
| 178 | - Barcode: string.Empty, // 或 $"{x.materialCode}" / $"{x.stockBatch}" | ||
| 179 | - DetailId: x.id ?? string.Empty, // ← 改为接口的 id | ||
| 180 | - Location: x.location ?? string.Empty, | ||
| 181 | MaterialName: x.materialName ?? string.Empty, | 180 | MaterialName: x.materialName ?? string.Empty, |
| 182 | - PendingQty: ToIntSafe(x.outstockQty), // ← 预计数量 | ||
| 183 | - ScannedQty: ToIntSafe(x.qty), // ← 已扫描量 | ||
| 184 | - Spec: x.spec ?? string.Empty | 181 | + MaterialCode: x.materialCode ?? string.Empty, |
| 182 | + Spec: x.spec ?? string.Empty, | ||
| 183 | + Location: x.location ?? string.Empty, | ||
| 184 | + ProductionBatch: x.productionBatch ?? string.Empty, | ||
| 185 | + StockBatch: x.stockBatch ?? string.Empty, | ||
| 186 | + OutstockQty: ToInt(x.outstockQty), // 此处再转 int | ||
| 187 | + Qty: ToInt(x.qty) // ← 已扫描量 | ||
| 185 | )).ToList(); | 188 | )).ToList(); |
| 186 | 189 | ||
| 187 | return list; | 190 | return list; |
| 188 | } | 191 | } |
| 189 | 192 | ||
| 193 | + static int ToInt(decimal? v) => v.HasValue ? (int)Math.Round(v.Value, MidpointRounding.AwayFromZero) : 0; | ||
| 190 | public async Task<IReadOnlyList<OutboundScannedRow>> GetOutStockScanDetailAsync( | 194 | public async Task<IReadOnlyList<OutboundScannedRow>> GetOutStockScanDetailAsync( |
| 191 | string outstockId, | 195 | string outstockId, |
| 192 | CancellationToken ct = default) | 196 | CancellationToken ct = default) |
| @@ -206,21 +210,13 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -206,21 +210,13 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 206 | if (dto?.success != true || dto.result is null || dto.result.Count == 0) | 210 | if (dto?.success != true || dto.result is null || dto.result.Count == 0) |
| 207 | return Array.Empty<OutboundScannedRow>(); | 211 | return Array.Empty<OutboundScannedRow>(); |
| 208 | 212 | ||
| 209 | - static int ToIntSafe(string? s) | ||
| 210 | - { | ||
| 211 | - if (string.IsNullOrWhiteSpace(s)) return 0; | ||
| 212 | - // 去除千分位、空格 | ||
| 213 | - s = s.Trim().Replace(",", ""); | ||
| 214 | - return int.TryParse(s, out var v) ? v : 0; | ||
| 215 | - } | ||
| 216 | - | ||
| 217 | // 映射:OutstockId <- id(截图注释“入库单明细主键id”) | 213 | // 映射:OutstockId <- id(截图注释“入库单明细主键id”) |
| 218 | var list = dto.result.Select(x => new OutboundScannedRow( | 214 | var list = dto.result.Select(x => new OutboundScannedRow( |
| 219 | Barcode: (x.barcode ?? string.Empty).Trim(), | 215 | Barcode: (x.barcode ?? string.Empty).Trim(), |
| 220 | DetailId: (x.id ?? string.Empty).Trim(), | 216 | DetailId: (x.id ?? string.Empty).Trim(), |
| 221 | Location: (x.location ?? string.Empty).Trim(), | 217 | Location: (x.location ?? string.Empty).Trim(), |
| 222 | MaterialName: (x.materialName ?? string.Empty).Trim(), | 218 | MaterialName: (x.materialName ?? string.Empty).Trim(), |
| 223 | - Qty: ToIntSafe(x.qty), | 219 | + Qty: ToInt(x.qty), |
| 224 | Spec: (x.spec ?? string.Empty).Trim(), | 220 | Spec: (x.spec ?? string.Empty).Trim(), |
| 225 | ScanStatus: x.scanStatus ?? false, | 221 | ScanStatus: x.scanStatus ?? false, |
| 226 | WarehouseCode: x.warehouseCode?.Trim() | 222 | WarehouseCode: x.warehouseCode?.Trim() |
| @@ -228,10 +224,13 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -228,10 +224,13 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 228 | 224 | ||
| 229 | return list; | 225 | return list; |
| 230 | } | 226 | } |
| 227 | + | ||
| 231 | // ========= 扫码入库实现 ========= | 228 | // ========= 扫码入库实现 ========= |
| 232 | public async Task<SimpleOk> OutStockByBarcodeAsync(string outstockId, string barcode, CancellationToken ct = default) | 229 | public async Task<SimpleOk> OutStockByBarcodeAsync(string outstockId, string barcode, CancellationToken ct = default) |
| 233 | { | 230 | { |
| 234 | - var body = JsonSerializer.Serialize(new { barcode, outstockId }); | 231 | + // 注意:接口要的是 id 不是 outstockId |
| 232 | + var body = JsonSerializer.Serialize(new { barcode, id = outstockId }); | ||
| 233 | + | ||
| 235 | using var req = new HttpRequestMessage(HttpMethod.Post, _scanByBarcodeEndpoint) | 234 | using var req = new HttpRequestMessage(HttpMethod.Post, _scanByBarcodeEndpoint) |
| 236 | { | 235 | { |
| 237 | Content = new StringContent(body, Encoding.UTF8, "application/json") | 236 | Content = new StringContent(body, Encoding.UTF8, "application/json") |
| @@ -239,16 +238,16 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -239,16 +238,16 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 239 | 238 | ||
| 240 | using var res = await _http.SendAsync(req, ct); | 239 | using var res = await _http.SendAsync(req, ct); |
| 241 | var json = await res.Content.ReadAsStringAsync(ct); | 240 | var json = await res.Content.ReadAsStringAsync(ct); |
| 242 | - var opt = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; | ||
| 243 | - var dto = JsonSerializer.Deserialize<ScanByBarcodeResp>(json, opt); | ||
| 244 | 241 | ||
| 245 | - // 按文档:以 success 判断;message 作为失败提示 | ||
| 246 | - var ok = dto?.success == true; | 242 | + var dto = JsonSerializer.Deserialize<ScanByBarcodeResp>(json, _opt); |
| 243 | + var ok = dto?.success == true || dto?.result?.ToString() == "true"; | ||
| 247 | return new SimpleOk(ok, dto?.message); | 244 | return new SimpleOk(ok, dto?.message); |
| 248 | } | 245 | } |
| 249 | - public async Task<SimpleOk> ScanConfirmAsync(string outstockId, CancellationToken ct = default) | 246 | + |
| 247 | + public async Task<SimpleOk> ScanConfirmAsync(IEnumerable<(string barcode, string id)> items, CancellationToken ct = default) | ||
| 250 | { | 248 | { |
| 251 | - var bodyJson = JsonSerializer.Serialize(new { outstockId }); | 249 | + var payload = items.Select(x => new { barcode = x.barcode, id = x.id }); |
| 250 | + var bodyJson = JsonSerializer.Serialize(payload); | ||
| 252 | using var req = new HttpRequestMessage(HttpMethod.Post, _scanConfirmEndpoint) | 251 | using var req = new HttpRequestMessage(HttpMethod.Post, _scanConfirmEndpoint) |
| 253 | { | 252 | { |
| 254 | Content = new StringContent(bodyJson, Encoding.UTF8, "application/json") | 253 | Content = new StringContent(bodyJson, Encoding.UTF8, "application/json") |
| @@ -256,17 +255,16 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -256,17 +255,16 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 256 | 255 | ||
| 257 | using var res = await _http.SendAsync(req, ct); | 256 | using var res = await _http.SendAsync(req, ct); |
| 258 | var json = await res.Content.ReadAsStringAsync(ct); | 257 | var json = await res.Content.ReadAsStringAsync(ct); |
| 258 | + var dto = JsonSerializer.Deserialize<ScanConfirmResp>(json, _opt); | ||
| 259 | 259 | ||
| 260 | - var opt = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; | ||
| 261 | - var dto = JsonSerializer.Deserialize<ScanConfirmResp>(json, opt); | ||
| 262 | - | ||
| 263 | - var ok = dto?.success == true; | 260 | + var ok = dto?.success == true; // 你的接口:success=true 且 result=true |
| 264 | return new SimpleOk(ok, dto?.message); | 261 | return new SimpleOk(ok, dto?.message); |
| 265 | } | 262 | } |
| 266 | 263 | ||
| 267 | - public async Task<SimpleOk> CancelScanAsync(string outstockId, CancellationToken ct = default) | 264 | + public async Task<SimpleOk> CancelScanAsync(IEnumerable<(string barcode, string id)> items, CancellationToken ct = default) |
| 268 | { | 265 | { |
| 269 | - var bodyJson = JsonSerializer.Serialize(new { outstockId }); | 266 | + var payload = items.Select(x => new { barcode = x.barcode, id = x.id }); |
| 267 | + var bodyJson = JsonSerializer.Serialize(payload); | ||
| 270 | using var req = new HttpRequestMessage(HttpMethod.Post, _cancelScanEndpoint) | 268 | using var req = new HttpRequestMessage(HttpMethod.Post, _cancelScanEndpoint) |
| 271 | { | 269 | { |
| 272 | Content = new StringContent(bodyJson, Encoding.UTF8, "application/json") | 270 | Content = new StringContent(bodyJson, Encoding.UTF8, "application/json") |
| @@ -274,17 +272,16 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -274,17 +272,16 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 274 | 272 | ||
| 275 | using var res = await _http.SendAsync(req, ct); | 273 | using var res = await _http.SendAsync(req, ct); |
| 276 | var json = await res.Content.ReadAsStringAsync(ct); | 274 | var json = await res.Content.ReadAsStringAsync(ct); |
| 277 | - | ||
| 278 | - var opt = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; | ||
| 279 | - var dto = JsonSerializer.Deserialize<CancelScanResp>(json, opt); | 275 | + var dto = JsonSerializer.Deserialize<CancelScanResp>(json, _opt); |
| 280 | 276 | ||
| 281 | var ok = dto?.success == true; | 277 | var ok = dto?.success == true; |
| 282 | return new SimpleOk(ok, dto?.message); | 278 | return new SimpleOk(ok, dto?.message); |
| 283 | } | 279 | } |
| 284 | 280 | ||
| 281 | + | ||
| 285 | public async Task<SimpleOk> ConfirmOutstockAsync(string outstockId, CancellationToken ct = default) | 282 | public async Task<SimpleOk> ConfirmOutstockAsync(string outstockId, CancellationToken ct = default) |
| 286 | { | 283 | { |
| 287 | - var bodyJson = JsonSerializer.Serialize(new { outstockId }); | 284 | + var bodyJson = JsonSerializer.Serialize(new { id = outstockId }); |
| 288 | using var req = new HttpRequestMessage(HttpMethod.Post, _confirmOutstockEndpoint) | 285 | using var req = new HttpRequestMessage(HttpMethod.Post, _confirmOutstockEndpoint) |
| 289 | { | 286 | { |
| 290 | Content = new StringContent(bodyJson, Encoding.UTF8, "application/json") | 287 | Content = new StringContent(bodyJson, Encoding.UTF8, "application/json") |
| @@ -299,6 +296,12 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -299,6 +296,12 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 299 | var ok = dto?.success == true; | 296 | var ok = dto?.success == true; |
| 300 | return new SimpleOk(ok, dto?.message); | 297 | return new SimpleOk(ok, dto?.message); |
| 301 | } | 298 | } |
| 299 | + /// <summary> | ||
| 300 | + /// 判断入库单明细是否已全部扫描确认 | ||
| 301 | + /// </summary> | ||
| 302 | + /// <param name="outstockId"></param> | ||
| 303 | + /// <param name="ct"></param> | ||
| 304 | + /// <returns></returns> | ||
| 302 | public async Task<bool> JudgeOutstockDetailScanAllAsync(string outstockId, CancellationToken ct = default) | 305 | public async Task<bool> JudgeOutstockDetailScanAllAsync(string outstockId, CancellationToken ct = default) |
| 303 | { | 306 | { |
| 304 | var url = $"{_judgeScanAllEndpoint}?id={Uri.EscapeDataString(outstockId)}"; | 307 | var url = $"{_judgeScanAllEndpoint}?id={Uri.EscapeDataString(outstockId)}"; |
| @@ -313,28 +316,58 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | @@ -313,28 +316,58 @@ public sealed class OutboundMaterialService : IOutboundMaterialService | ||
| 313 | return dto?.result == true; | 316 | return dto?.result == true; |
| 314 | } | 317 | } |
| 315 | 318 | ||
| 316 | -} | ||
| 317 | 319 | ||
| 318 | -public class GetOutStockReq | ||
| 319 | -{ | ||
| 320 | - public string? createdTime { get; set; } | ||
| 321 | - public string? endTime { get; set; } | ||
| 322 | - public string? outstockNo { get; set; } | ||
| 323 | - public string? orderType { get; set; } | ||
| 324 | - public string? startTime { get; set; } | ||
| 325 | -} | 320 | + public async Task<SimpleOk> UpdateOutstockLocationAsync( |
| 321 | + string detailId, string id, string outstockWarehouse, string outstockWarehouseCode, string location, CancellationToken ct = default) | ||
| 322 | + { | ||
| 323 | + var url = "/normalService/pda/wmsMaterialOutstock/updateLocation"; | ||
| 324 | + var payload = new | ||
| 325 | + { | ||
| 326 | + detailId, | ||
| 327 | + id, | ||
| 328 | + outstockWarehouse, | ||
| 329 | + outstockWarehouseCode, | ||
| 330 | + location | ||
| 331 | + }; | ||
| 326 | 332 | ||
| 327 | -public class GetOutStockResp | ||
| 328 | -{ | ||
| 329 | - public int code { get; set; } | ||
| 330 | - public long costTime { get; set; } | ||
| 331 | - public string? message { get; set; } | ||
| 332 | - public bool success { get; set; } | ||
| 333 | - public List<GetOutStockItem>? result { get; set; } | ||
| 334 | -} | 333 | + var json = JsonSerializer.Serialize(payload); |
| 334 | + using var req = new HttpRequestMessage(HttpMethod.Post, url) | ||
| 335 | + { | ||
| 336 | + Content = new StringContent(json, Encoding.UTF8, "application/json") | ||
| 337 | + }; | ||
| 335 | 338 | ||
| 336 | -public class GetOutStockItem | ||
| 337 | -{ | 339 | + using var res = await _http.SendAsync(req, ct); |
| 340 | + var body = await res.Content.ReadAsStringAsync(ct); | ||
| 341 | + | ||
| 342 | + // 假设响应:{ code, message, result: true/false, success: true/false } | ||
| 343 | + var dto = JsonSerializer.Deserialize<UpdateLocationResp>(body, _json); | ||
| 344 | + var ok = dto?.success == true || dto?.result == true; | ||
| 345 | + | ||
| 346 | + return new SimpleOk(ok, dto?.message); | ||
| 347 | + } | ||
| 348 | + | ||
| 349 | + public async Task<SimpleOk> UpdateQuantityAsync( | ||
| 350 | + string barcode, string detailId, string id, int quantity, CancellationToken ct = default) | ||
| 351 | + { | ||
| 352 | + var url = "/normalService/pda/wmsMaterialOutstock/updateQuantity"; | ||
| 353 | + var payload = new { barcode, detailId, id, quantity }; | ||
| 354 | + var json = JsonSerializer.Serialize(payload); | ||
| 355 | + using var req = new HttpRequestMessage(HttpMethod.Post, url) | ||
| 356 | + { | ||
| 357 | + Content = new StringContent(json, Encoding.UTF8, "application/json") | ||
| 358 | + }; | ||
| 359 | + | ||
| 360 | + using var res = await _http.SendAsync(req, ct); | ||
| 361 | + var body = await res.Content.ReadAsStringAsync(ct); | ||
| 362 | + | ||
| 363 | + // 响应格式:{ success, message, code, result, ... }(与截图一致) | ||
| 364 | + var dto = JsonSerializer.Deserialize<ConfirmResp>(body, _json); | ||
| 365 | + var ok = dto?.success == true || dto?.result == true; | ||
| 366 | + return new SimpleOk(ok, dto?.message); | ||
| 367 | + } | ||
| 368 | + | ||
| 369 | + public class GetOutStockItem | ||
| 370 | + { | ||
| 338 | public string? arrivalNo { get; set; } | 371 | public string? arrivalNo { get; set; } |
| 339 | public string? createdTime { get; set; } | 372 | public string? createdTime { get; set; } |
| 340 | public string? outstockId { get; set; } | 373 | public string? outstockId { get; set; } |
| @@ -342,87 +375,88 @@ public class GetOutStockItem | @@ -342,87 +375,88 @@ public class GetOutStockItem | ||
| 342 | public string? orderType { get; set; } | 375 | public string? orderType { get; set; } |
| 343 | public string? purchaseNo { get; set; } | 376 | public string? purchaseNo { get; set; } |
| 344 | public string? supplierName { get; set; } | 377 | public string? supplierName { get; set; } |
| 345 | -} | ||
| 346 | -public sealed class GetOutStockDetailResp | ||
| 347 | -{ | 378 | + } |
| 379 | + public sealed class GetOutStockDetailResp | ||
| 380 | + { | ||
| 348 | public bool success { get; set; } | 381 | public bool success { get; set; } |
| 349 | public string? message { get; set; } | 382 | public string? message { get; set; } |
| 350 | public int? code { get; set; } | 383 | public int? code { get; set; } |
| 351 | public List<GetOutStockDetailItem>? result { get; set; } | 384 | public List<GetOutStockDetailItem>? result { get; set; } |
| 352 | public int? costTime { get; set; } | 385 | public int? costTime { get; set; } |
| 353 | -} | ||
| 354 | -public sealed class GetOutStockDetailItem | ||
| 355 | -{ | 386 | + } |
| 387 | + public sealed class GetOutStockDetailItem | ||
| 388 | + { | ||
| 356 | public string? id { get; set; } // 入库单明细主键id | 389 | public string? id { get; set; } // 入库单明细主键id |
| 357 | public string? outstockNo { get; set; } // 入库单号 | 390 | public string? outstockNo { get; set; } // 入库单号 |
| 358 | - public string? materialCode { get; set; } | ||
| 359 | public string? materialName { get; set; } | 391 | public string? materialName { get; set; } |
| 360 | - public string? spec { get; set; } | ||
| 361 | - public string? stockBatch { get; set; } | ||
| 362 | - public string? outstockQty { get; set; } // 预计数量(字符串/可能为空) | ||
| 363 | public string? outstockWarehouseCode { get; set; } // 入库仓库编码 | 392 | public string? outstockWarehouseCode { get; set; } // 入库仓库编码 |
| 364 | - public string? location { get; set; } // 内点库位 | ||
| 365 | - public string? qty { get; set; } // 已扫描量(字符串/可能为空) | ||
| 366 | -} | ||
| 367 | - | ||
| 368 | - | ||
| 369 | - | ||
| 370 | - | 393 | + public string? materialCode { get; set; } //产品编码 |
| 394 | + public string? spec { get; set; } //规格 | ||
| 395 | + public string? location { get; set; } //出库库位 | ||
| 396 | + public string? productionBatch { get; set; } //生产批号 | ||
| 397 | + | ||
| 398 | + public string? stockBatch { get; set; } //批次号 | ||
| 399 | + public int outstockQty { get; set; } //出库数量 | ||
| 400 | + public int qty { get; set; } //已扫描数 | ||
| 401 | + } | ||
| 371 | 402 | ||
| 403 | + private sealed class UpdateLocationResp | ||
| 404 | + { | ||
| 405 | + public int code { get; set; } | ||
| 406 | + public string? message { get; set; } | ||
| 407 | + public bool? result { get; set; } | ||
| 408 | + public bool? success { get; set; } | ||
| 409 | + } | ||
| 372 | 410 | ||
| 373 | -public class GetOutStockPageResp | ||
| 374 | -{ | 411 | + public class GetOutStockPageResp |
| 412 | + { | ||
| 375 | public int code { get; set; } | 413 | public int code { get; set; } |
| 376 | public long costTime { get; set; } | 414 | public long costTime { get; set; } |
| 377 | public string? message { get; set; } | 415 | public string? message { get; set; } |
| 378 | public bool success { get; set; } | 416 | public bool success { get; set; } |
| 379 | public GetOutStockPageData? result { get; set; } | 417 | public GetOutStockPageData? result { get; set; } |
| 380 | -} | 418 | + } |
| 381 | 419 | ||
| 382 | -public class GetOutStockPageData | ||
| 383 | -{ | 420 | + public class GetOutStockPageData |
| 421 | + { | ||
| 384 | public int pageNo { get; set; } | 422 | public int pageNo { get; set; } |
| 385 | public int pageSize { get; set; } | 423 | public int pageSize { get; set; } |
| 386 | public long total { get; set; } | 424 | public long total { get; set; } |
| 387 | public List<GetOutStockRecord> records { get; set; } = new(); | 425 | public List<GetOutStockRecord> records { get; set; } = new(); |
| 388 | -} | 426 | + } |
| 389 | 427 | ||
| 390 | -public class GetOutStockRecord | ||
| 391 | -{ | 428 | + public class GetOutStockRecord |
| 429 | + { | ||
| 392 | public string? id { get; set; } | 430 | public string? id { get; set; } |
| 393 | public string? outstockNo { get; set; } | 431 | public string? outstockNo { get; set; } |
| 394 | public string? orderType { get; set; } | 432 | public string? orderType { get; set; } |
| 395 | public string? orderTypeName { get; set; } | 433 | public string? orderTypeName { get; set; } |
| 396 | - public string? supplierName { get; set; } | ||
| 397 | - public string? arrivalNo { get; set; } | ||
| 398 | - public string? purchaseNo { get; set; } | ||
| 399 | - public string? createdTime { get; set; } | ||
| 400 | - public string? deliveryNo { get; set; } | 434 | + public string? workOrderNo { get; set; } |
| 435 | + public string? materialName { get; set; } | ||
| 401 | public string? requisitionMaterialNo { get; set; } | 436 | public string? requisitionMaterialNo { get; set; } |
| 402 | public string? returnNo { get; set; } | 437 | public string? returnNo { get; set; } |
| 403 | - public string? workOrderNo { get; set; } | ||
| 404 | -} | ||
| 405 | -public sealed class GetOutStockScanDetailResp | ||
| 406 | -{ | 438 | + public string? deliveryNo { get; set; } |
| 439 | + public string? createdTime { get; set; } | ||
| 440 | + } | ||
| 441 | + public sealed class GetOutStockScanDetailResp | ||
| 442 | + { | ||
| 407 | public bool success { get; set; } | 443 | public bool success { get; set; } |
| 408 | public string? message { get; set; } | 444 | public string? message { get; set; } |
| 409 | public int? code { get; set; } | 445 | public int? code { get; set; } |
| 410 | public List<GetOutStockScanDetailItem>? result { get; set; } | 446 | public List<GetOutStockScanDetailItem>? result { get; set; } |
| 411 | public int? costTime { get; set; } | 447 | public int? costTime { get; set; } |
| 412 | -} | 448 | + } |
| 413 | 449 | ||
| 414 | -public sealed class GetOutStockScanDetailItem | ||
| 415 | -{ | 450 | + public class GetOutStockScanDetailItem |
| 451 | + { | ||
| 416 | public string? id { get; set; } // 入库单明细主键 id | 452 | public string? id { get; set; } // 入库单明细主键 id |
| 417 | public string? barcode { get; set; } | 453 | public string? barcode { get; set; } |
| 418 | public string? materialName { get; set; } | 454 | public string? materialName { get; set; } |
| 419 | public string? spec { get; set; } | 455 | public string? spec { get; set; } |
| 420 | - public string? qty { get; set; } // 可能是 null 或 “数字字符串” | 456 | + public decimal? qty { get; set; } // 可能是 null 或 “数字字符串” |
| 421 | public string? warehouseCode { get; set; } | 457 | public string? warehouseCode { get; set; } |
| 422 | public string? location { get; set; } | 458 | public string? location { get; set; } |
| 423 | public bool? scanStatus { get; set; } // 可能为 null,按 false 处理 | 459 | public bool? scanStatus { get; set; } // 可能为 null,按 false 处理 |
| 424 | -} | ||
| 425 | - | ||
| 426 | - | ||
| 427 | - | 460 | + } |
| 428 | 461 | ||
| 462 | +} |
| @@ -22,7 +22,7 @@ namespace IndustrialControl.ViewModels | @@ -22,7 +22,7 @@ namespace IndustrialControl.ViewModels | ||
| 22 | // 列表数据源 | 22 | // 列表数据源 |
| 23 | public ObservableCollection<string> AvailableBins { get; } = new(); | 23 | public ObservableCollection<string> AvailableBins { get; } = new(); |
| 24 | public ObservableCollection<OutScannedItem> ScannedList { get; } = new(); | 24 | public ObservableCollection<OutScannedItem> ScannedList { get; } = new(); |
| 25 | - public ObservableCollection<PendingItem> PendingList { get; } = new(); | 25 | + public ObservableCollection<OutPendingItem> PendingList { get; } = new(); |
| 26 | 26 | ||
| 27 | [ObservableProperty] private OutScannedItem? selectedScanItem; | 27 | [ObservableProperty] private OutScannedItem? selectedScanItem; |
| 28 | 28 |
| @@ -100,5 +100,8 @@ public record InboundOrderSummary( | @@ -100,5 +100,8 @@ public record InboundOrderSummary( | ||
| 100 | string purchaseNo, | 100 | string purchaseNo, |
| 101 | string supplierName, | 101 | string supplierName, |
| 102 | string arrivalNo, | 102 | string arrivalNo, |
| 103 | + string workOrderNo, | ||
| 104 | + string materialName, | ||
| 105 | + int instockQty, | ||
| 103 | string createdTime | 106 | string createdTime |
| 104 | ); | 107 | ); |
| @@ -362,8 +362,11 @@ namespace IndustrialControl.ViewModels | @@ -362,8 +362,11 @@ namespace IndustrialControl.ViewModels | ||
| 362 | // === 列表行模型 === | 362 | // === 列表行模型 === |
| 363 | public class PendingItem | 363 | public class PendingItem |
| 364 | { | 364 | { |
| 365 | - public string Name { get; set; } = ""; | ||
| 366 | - public string Spec { get; set; } = ""; | 365 | + public string Name { get; set; } = ""; //产品名称 |
| 366 | + public string MaterialCode { get; set; } = ""; //产品编码 | ||
| 367 | + public string Spec { get; set; } = "";//规格 | ||
| 368 | + public string Location { get; set; } = "";//出库库位 | ||
| 369 | + public string ProductionBatch { get; set; } = "";//生产批号 | ||
| 367 | public int PendingQty { get; set; } | 370 | public int PendingQty { get; set; } |
| 368 | public string Bin { get; set; } = "请选择"; | 371 | public string Bin { get; set; } = "请选择"; |
| 369 | public int ScannedQty { get; set; } | 372 | public int ScannedQty { get; set; } |
| @@ -81,6 +81,9 @@ public partial class InboundProductionSearchViewModel : ObservableObject | @@ -81,6 +81,9 @@ public partial class InboundProductionSearchViewModel : ObservableObject | ||
| 81 | ["purchaseNo"] = o.purchaseNo, | 81 | ["purchaseNo"] = o.purchaseNo, |
| 82 | ["supplierName"] = o.supplierName, | 82 | ["supplierName"] = o.supplierName, |
| 83 | ["arrivalNo"] = o.arrivalNo, | 83 | ["arrivalNo"] = o.arrivalNo, |
| 84 | + ["instockQty"] = o.instockQty, | ||
| 85 | + ["materialName"] = o.materialName, | ||
| 86 | + ["workOrderNo"] = o.workOrderNo, | ||
| 84 | ["createdTime"] = o.createdTime | 87 | ["createdTime"] = o.createdTime |
| 85 | }); | 88 | }); |
| 86 | 89 |
| @@ -18,6 +18,9 @@ namespace IndustrialControl.ViewModels | @@ -18,6 +18,9 @@ namespace IndustrialControl.ViewModels | ||
| 18 | [ObservableProperty] private string? orderTypeName; | 18 | [ObservableProperty] private string? orderTypeName; |
| 19 | [ObservableProperty] private string? purchaseNo; | 19 | [ObservableProperty] private string? purchaseNo; |
| 20 | [ObservableProperty] private string? supplierName; | 20 | [ObservableProperty] private string? supplierName; |
| 21 | + [ObservableProperty] private string? workOrderNo; | ||
| 22 | + [ObservableProperty] private string? materialName; | ||
| 23 | + [ObservableProperty] private int instockQty; | ||
| 21 | [ObservableProperty] private string? createdTime; | 24 | [ObservableProperty] private string? createdTime; |
| 22 | 25 | ||
| 23 | // 列表数据源 | 26 | // 列表数据源 |
| @@ -41,7 +44,7 @@ namespace IndustrialControl.ViewModels | @@ -41,7 +44,7 @@ namespace IndustrialControl.ViewModels | ||
| 41 | // ================ 初始化入口(页面 OnAppearing 调用) ================ | 44 | // ================ 初始化入口(页面 OnAppearing 调用) ================ |
| 42 | public async Task InitializeFromSearchAsync( | 45 | public async Task InitializeFromSearchAsync( |
| 43 | string instockId, string instockNo, string orderType, string orderTypeName, | 46 | string instockId, string instockNo, string orderType, string orderTypeName, |
| 44 | - string purchaseNo, string supplierName, string createdTime) | 47 | + string purchaseNo, string supplierName, string createdTime,string workOrderNo,string materialName,int instockQty) |
| 45 | { | 48 | { |
| 46 | // 1) 基础信息 | 49 | // 1) 基础信息 |
| 47 | InstockId = instockId; | 50 | InstockId = instockId; |
| @@ -51,7 +54,9 @@ namespace IndustrialControl.ViewModels | @@ -51,7 +54,9 @@ namespace IndustrialControl.ViewModels | ||
| 51 | PurchaseNo = purchaseNo; | 54 | PurchaseNo = purchaseNo; |
| 52 | SupplierName = supplierName; | 55 | SupplierName = supplierName; |
| 53 | CreatedTime = createdTime; | 56 | CreatedTime = createdTime; |
| 54 | - | 57 | + WorkOrderNo = workOrderNo; |
| 58 | + MaterialName = materialName; | ||
| 59 | + InstockQty = instockQty; | ||
| 55 | // 2) 下拉库位(如无接口可留空或使用后端返回的 location 聚合) | 60 | // 2) 下拉库位(如无接口可留空或使用后端返回的 location 聚合) |
| 56 | AvailableBins.Clear(); | 61 | AvailableBins.Clear(); |
| 57 | 62 |
| @@ -8,19 +8,20 @@ namespace IndustrialControl.ViewModels; | @@ -8,19 +8,20 @@ namespace IndustrialControl.ViewModels; | ||
| 8 | public partial class OutboundFinishedSearchViewModel : ObservableObject | 8 | public partial class OutboundFinishedSearchViewModel : ObservableObject |
| 9 | { | 9 | { |
| 10 | private readonly IOutboundMaterialService _dataSvc; | 10 | private readonly IOutboundMaterialService _dataSvc; |
| 11 | - [ObservableProperty] private string? searchOrderNo; | 11 | + |
| 12 | + [ObservableProperty] private string searchOrderNo; | ||
| 12 | [ObservableProperty] private DateTime startDate = DateTime.Today; | 13 | [ObservableProperty] private DateTime startDate = DateTime.Today; |
| 13 | [ObservableProperty] private DateTime endDate = DateTime.Today; | 14 | [ObservableProperty] private DateTime endDate = DateTime.Today; |
| 14 | - [ObservableProperty] private OutboundOrderSummary? selectedOrder; | ||
| 15 | private CancellationTokenSource? _searchCts; | 15 | private CancellationTokenSource? _searchCts; |
| 16 | + // 仅用于“高亮选中” | ||
| 17 | + [ObservableProperty] private OutboundOrderSummary? selectedOrder; | ||
| 18 | + | ||
| 16 | public OutboundFinishedSearchViewModel(IOutboundMaterialService dataSvc) | 19 | public OutboundFinishedSearchViewModel(IOutboundMaterialService dataSvc) |
| 17 | { | 20 | { |
| 18 | _dataSvc = dataSvc; | 21 | _dataSvc = dataSvc; |
| 19 | Orders = new ObservableCollection<OutboundOrderSummary>(); | 22 | Orders = new ObservableCollection<OutboundOrderSummary>(); |
| 20 | } | 23 | } |
| 21 | 24 | ||
| 22 | - | ||
| 23 | - | ||
| 24 | public ObservableCollection<OutboundOrderSummary> Orders { get; } | 25 | public ObservableCollection<OutboundOrderSummary> Orders { get; } |
| 25 | 26 | ||
| 26 | [RelayCommand] | 27 | [RelayCommand] |
| @@ -31,9 +32,15 @@ public partial class OutboundFinishedSearchViewModel : ObservableObject | @@ -31,9 +32,15 @@ public partial class OutboundFinishedSearchViewModel : ObservableObject | ||
| 31 | var ct = _searchCts.Token; | 32 | var ct = _searchCts.Token; |
| 32 | try | 33 | try |
| 33 | { | 34 | { |
| 34 | - var list = await _dataSvc.ListOutboundOrdersAsync(SearchOrderNo, startDate, endDate, "out_delivery", null,ct); | 35 | + var list = await _dataSvc.ListOutboundOrdersAsync( |
| 36 | + searchOrderNo, // 单号/条码 | ||
| 37 | + startDate, // 开始日期 | ||
| 38 | + endDate, // 结束日期(Service 内会扩到 23:59:59) | ||
| 39 | + "out_delivery", // 不传单值 orderType,用 null 更清晰 | ||
| 40 | + null, // 多类型数组 | ||
| 41 | + ct // ← 新增:取消令牌 | ||
| 42 | + ); | ||
| 35 | 43 | ||
| 36 | - // ★ 在主线程更新 ObservableCollection,避免看起来“没刷新” | ||
| 37 | await MainThread.InvokeOnMainThreadAsync(() => | 44 | await MainThread.InvokeOnMainThreadAsync(() => |
| 38 | { | 45 | { |
| 39 | Orders.Clear(); | 46 | Orders.Clear(); |
| @@ -44,7 +51,6 @@ public partial class OutboundFinishedSearchViewModel : ObservableObject | @@ -44,7 +51,6 @@ public partial class OutboundFinishedSearchViewModel : ObservableObject | ||
| 44 | } | 51 | } |
| 45 | }); | 52 | }); |
| 46 | 53 | ||
| 47 | - // (排查辅助)无数据时提示一下,确认命令确实执行了 | ||
| 48 | if (list == null || !list.Any()) | 54 | if (list == null || !list.Any()) |
| 49 | await Shell.Current.DisplayAlert("提示", "未查询到任何入库单", "确定"); | 55 | await Shell.Current.DisplayAlert("提示", "未查询到任何入库单", "确定"); |
| 50 | } | 56 | } |
| @@ -54,38 +60,34 @@ public partial class OutboundFinishedSearchViewModel : ObservableObject | @@ -54,38 +60,34 @@ public partial class OutboundFinishedSearchViewModel : ObservableObject | ||
| 54 | } | 60 | } |
| 55 | } | 61 | } |
| 56 | 62 | ||
| 57 | - | 63 | + // === 方案A:命令接收“当前项”作为参数,不依赖 SelectedOrder === |
| 58 | [RelayCommand(CanExecute = nameof(CanGoOutbound))] | 64 | [RelayCommand(CanExecute = nameof(CanGoOutbound))] |
| 59 | private async Task GoOutboundAsync(OutboundOrderSummary? item) | 65 | private async Task GoOutboundAsync(OutboundOrderSummary? item) |
| 60 | { | 66 | { |
| 61 | - if (SelectedOrder is null) return; | 67 | + if (item is null) return; |
| 62 | 68 | ||
| 63 | static string E(string? v) => Uri.EscapeDataString(v ?? ""); | 69 | static string E(string? v) => Uri.EscapeDataString(v ?? ""); |
| 64 | 70 | ||
| 65 | - var o = SelectedOrder; // 确保 SelectedOrder 包含以下字段 | ||
| 66 | - var url = | ||
| 67 | - $"//OutboundFinished" + | ||
| 68 | - $"?outstockId={E(o.outstockId)}" + | ||
| 69 | - $"&outstockNo={E(searchOrderNo)}" + | ||
| 70 | - $"&orderType={E(o.orderType)}" + | ||
| 71 | - $"&orderTypeName={E(o.orderTypeName)}" + | ||
| 72 | - $"&purchaseNo={E(o.purchaseNo)}" + | ||
| 73 | - $"&supplierName={E(o.supplierName)}" + | ||
| 74 | - $"&createdTime={E(o.createdTime)}"; | ||
| 75 | - | ||
| 76 | - await Shell.Current.GoToAsync(url); | 71 | + var o = item; |
| 72 | + | ||
| 73 | + await Shell.Current.GoToAsync( | ||
| 74 | + nameof(Pages.OutboundFinishedPage), | ||
| 75 | + new Dictionary<string, object> | ||
| 76 | + { | ||
| 77 | + ["outstockId"] = o.outstockId, | ||
| 78 | + ["outstockNo"] = o.outstockNo, | ||
| 79 | + ["orderType"] = o.orderType, | ||
| 80 | + ["orderTypeName"] = o.orderTypeName, | ||
| 81 | + ["requisitionMaterialNo"] = o.requisitionMaterialNo, | ||
| 82 | + ["returnNo"] = o.returnNo, | ||
| 83 | + ["deliveryNo"] = o.deliveryNo, | ||
| 84 | + ["createdTime"] = o.createdTime | ||
| 85 | + }); | ||
| 86 | + | ||
| 77 | } | 87 | } |
| 78 | 88 | ||
| 89 | + // 与命令同签名的 CanExecute | ||
| 79 | private bool CanGoOutbound(OutboundOrderSummary? item) => item != null; | 90 | private bool CanGoOutbound(OutboundOrderSummary? item) => item != null; |
| 80 | - | ||
| 81 | } | 91 | } |
| 82 | -public record OutboundFinishedSummary( | ||
| 83 | - string outstockId, | ||
| 84 | - string outstockNo, | ||
| 85 | - string orderType, | ||
| 86 | - string orderTypeName, | ||
| 87 | - string purchaseNo, | ||
| 88 | - string supplierName, | ||
| 89 | - string createdTime | ||
| 90 | -); | 92 | + |
| 91 | 93 |
| 1 | using CommunityToolkit.Mvvm.ComponentModel; | 1 | using CommunityToolkit.Mvvm.ComponentModel; |
| 2 | using CommunityToolkit.Mvvm.Input; | 2 | using CommunityToolkit.Mvvm.Input; |
| 3 | -using IndustrialControl.Services; | ||
| 4 | using System.Collections.ObjectModel; | 3 | using System.Collections.ObjectModel; |
| 5 | -using System.Linq; | ||
| 6 | -using System.Threading.Tasks; | 4 | +using IndustrialControl.Services; |
| 5 | +using IndustrialControl.Models; | ||
| 7 | 6 | ||
| 8 | namespace IndustrialControl.ViewModels | 7 | namespace IndustrialControl.ViewModels |
| 9 | { | 8 | { |
| @@ -17,16 +16,15 @@ namespace IndustrialControl.ViewModels | @@ -17,16 +16,15 @@ namespace IndustrialControl.ViewModels | ||
| 17 | [ObservableProperty] private string? outstockNo; | 16 | [ObservableProperty] private string? outstockNo; |
| 18 | [ObservableProperty] private string? orderType; | 17 | [ObservableProperty] private string? orderType; |
| 19 | [ObservableProperty] private string? orderTypeName; | 18 | [ObservableProperty] private string? orderTypeName; |
| 20 | - [ObservableProperty] private string? purchaseNo; | ||
| 21 | - [ObservableProperty] private string? supplierName; | ||
| 22 | - [ObservableProperty] private string? createdTime; | ||
| 23 | [ObservableProperty] private string? requisitionMaterialNo; | 19 | [ObservableProperty] private string? requisitionMaterialNo; |
| 24 | [ObservableProperty] private string? returnNo; | 20 | [ObservableProperty] private string? returnNo; |
| 25 | - [ObservableProperty] private string? workOrderNo; | 21 | + [ObservableProperty] private string? deliveryNo; |
| 22 | + [ObservableProperty] private string? createdTime; | ||
| 23 | + | ||
| 26 | // 列表数据源 | 24 | // 列表数据源 |
| 27 | public ObservableCollection<string> AvailableBins { get; } = new(); | 25 | public ObservableCollection<string> AvailableBins { get; } = new(); |
| 28 | public ObservableCollection<OutScannedItem> ScannedList { get; } = new(); | 26 | public ObservableCollection<OutScannedItem> ScannedList { get; } = new(); |
| 29 | - public ObservableCollection<PendingItem> PendingList { get; } = new(); | 27 | + public ObservableCollection<OutPendingItem> PendingList { get; } = new(); |
| 30 | 28 | ||
| 31 | [ObservableProperty] private OutScannedItem? selectedScanItem; | 29 | [ObservableProperty] private OutScannedItem? selectedScanItem; |
| 32 | 30 | ||
| @@ -43,28 +41,27 @@ namespace IndustrialControl.ViewModels | @@ -43,28 +41,27 @@ namespace IndustrialControl.ViewModels | ||
| 43 | // 命令 | 41 | // 命令 |
| 44 | public IRelayCommand ShowPendingCommand { get; } | 42 | public IRelayCommand ShowPendingCommand { get; } |
| 45 | public IRelayCommand ShowScannedCommand { get; } | 43 | public IRelayCommand ShowScannedCommand { get; } |
| 46 | - public IAsyncRelayCommand ConfirmCommand { get; } | ||
| 47 | 44 | ||
| 48 | - public OutboundFinishedViewModel(IOutboundMaterialService warehouseSvc) | 45 | + public OutboundFinishedViewModel(IOutboundMaterialService api) |
| 49 | { | 46 | { |
| 50 | - _api = warehouseSvc; | 47 | + _api = api; |
| 51 | ShowPendingCommand = new RelayCommand(() => SwitchTab(true)); | 48 | ShowPendingCommand = new RelayCommand(() => SwitchTab(true)); |
| 52 | ShowScannedCommand = new RelayCommand(() => SwitchTab(false)); | 49 | ShowScannedCommand = new RelayCommand(() => SwitchTab(false)); |
| 53 | - //ConfirmCommand = new AsyncRelayCommand(ConfirmInboundAsync); | ||
| 54 | } | 50 | } |
| 55 | 51 | ||
| 56 | // ================ 初始化入口(页面 OnAppearing 调用) ================ | 52 | // ================ 初始化入口(页面 OnAppearing 调用) ================ |
| 57 | public async Task InitializeFromSearchAsync( | 53 | public async Task InitializeFromSearchAsync( |
| 58 | string outstockId, string outstockNo, string orderType, string orderTypeName, | 54 | string outstockId, string outstockNo, string orderType, string orderTypeName, |
| 59 | - string purchaseNo, string supplierName, string createdTime) | 55 | + string requisitionMaterialNo, string returnNo, string deliveryNo, string createdTime) |
| 60 | { | 56 | { |
| 61 | // 1) 基础信息 | 57 | // 1) 基础信息 |
| 62 | OutstockId = outstockId; | 58 | OutstockId = outstockId; |
| 63 | OutstockNo = outstockNo; | 59 | OutstockNo = outstockNo; |
| 64 | OrderType = orderType; | 60 | OrderType = orderType; |
| 65 | OrderTypeName = orderTypeName; | 61 | OrderTypeName = orderTypeName; |
| 66 | - PurchaseNo = purchaseNo; | ||
| 67 | - SupplierName = supplierName; | 62 | + RequisitionMaterialNo = requisitionMaterialNo; |
| 63 | + ReturnNo = returnNo; | ||
| 64 | + DeliveryNo = deliveryNo; | ||
| 68 | CreatedTime = createdTime; | 65 | CreatedTime = createdTime; |
| 69 | 66 | ||
| 70 | // 2) 下拉库位(如无接口可留空或使用后端返回的 location 聚合) | 67 | // 2) 下拉库位(如无接口可留空或使用后端返回的 location 聚合) |
| @@ -102,15 +99,19 @@ namespace IndustrialControl.ViewModels | @@ -102,15 +99,19 @@ namespace IndustrialControl.ViewModels | ||
| 102 | var rows = await _api.GetOutStockDetailAsync(OutstockId!); | 99 | var rows = await _api.GetOutStockDetailAsync(OutstockId!); |
| 103 | foreach (var r in rows) | 100 | foreach (var r in rows) |
| 104 | { | 101 | { |
| 105 | - PendingList.Add(new PendingItem | 102 | + PendingList.Add(new OutPendingItem |
| 106 | { | 103 | { |
| 107 | Name = r.MaterialName ?? "", | 104 | Name = r.MaterialName ?? "", |
| 105 | + MaterialCode = r.MaterialCode ?? "", | ||
| 108 | Spec = r.Spec ?? "", | 106 | Spec = r.Spec ?? "", |
| 109 | - PendingQty = r.PendingQty, | ||
| 110 | - Bin = string.IsNullOrWhiteSpace(r.Location) ? "请选择" : r.Location!, | ||
| 111 | - ScannedQty = r.ScannedQty | 107 | + Location = r.Location ?? "", |
| 108 | + ProductionBatch = r.ProductionBatch ?? "", | ||
| 109 | + StockBatch = r.StockBatch ?? "", | ||
| 110 | + OutstockQty = r.OutstockQty, | ||
| 111 | + Qty = r.Qty | ||
| 112 | }); | 112 | }); |
| 113 | 113 | ||
| 114 | + | ||
| 114 | // 聚合可选库位 | 115 | // 聚合可选库位 |
| 115 | if (!string.IsNullOrWhiteSpace(r.Location) && !AvailableBins.Contains(r.Location)) | 116 | if (!string.IsNullOrWhiteSpace(r.Location) && !AvailableBins.Contains(r.Location)) |
| 116 | AvailableBins.Add(r.Location); | 117 | AvailableBins.Add(r.Location); |
| @@ -132,7 +133,11 @@ namespace IndustrialControl.ViewModels | @@ -132,7 +133,11 @@ namespace IndustrialControl.ViewModels | ||
| 132 | Name = r.MaterialName ?? "", | 133 | Name = r.MaterialName ?? "", |
| 133 | Spec = r.Spec ?? "", | 134 | Spec = r.Spec ?? "", |
| 134 | Location = string.IsNullOrWhiteSpace(r.Location) ? "请选择" : r.Location!, | 135 | Location = string.IsNullOrWhiteSpace(r.Location) ? "请选择" : r.Location!, |
| 135 | - Qty = r.Qty | 136 | + Qty = r.Qty, |
| 137 | + ScanStatus = r.ScanStatus, | ||
| 138 | + WarehouseCode = r.WarehouseCode ?? "", | ||
| 139 | + DetailId = r.DetailId, | ||
| 140 | + Id = OutstockId | ||
| 136 | }); | 141 | }); |
| 137 | 142 | ||
| 138 | if (!string.IsNullOrWhiteSpace(r.Location) && !AvailableBins.Contains(r.Location)) | 143 | if (!string.IsNullOrWhiteSpace(r.Location) && !AvailableBins.Contains(r.Location)) |
| @@ -140,83 +145,48 @@ namespace IndustrialControl.ViewModels | @@ -140,83 +145,48 @@ namespace IndustrialControl.ViewModels | ||
| 140 | } | 145 | } |
| 141 | } | 146 | } |
| 142 | 147 | ||
| 148 | + // OutboundFinishedViewModel.cs | ||
| 149 | + | ||
| 143 | [RelayCommand] | 150 | [RelayCommand] |
| 144 | private async Task PassScan() | 151 | private async Task PassScan() |
| 145 | { | 152 | { |
| 146 | - if (string.IsNullOrWhiteSpace(OutstockId)) | ||
| 147 | - { | ||
| 148 | - await ShowTip("缺少 OutstockId,无法确认。请从查询页进入。"); | ||
| 149 | - return; | ||
| 150 | - } | ||
| 151 | - | ||
| 152 | - // 依旧要求只能选中一行,作为“操作目标”的 UI 约束(接口本身仅需 instockId) | ||
| 153 | - var selected = ScannedList.Where(x => x.IsSelected).ToList(); | ||
| 154 | - if (selected.Count != 1) | ||
| 155 | - { | ||
| 156 | - await ShowTip(selected.Count == 0 ? "请先勾选一条已扫描记录。" : "一次只能操作一条记录。"); | ||
| 157 | - return; | ||
| 158 | - } | ||
| 159 | - var selectedBarcode = selected[0].Barcode; | 153 | + var picks = ScannedList.Where(x => x.IsSelected).ToList(); |
| 154 | + if (picks.Count == 0) { await ShowTip("请先勾选至少一条已扫描记录。"); return; } | ||
| 160 | 155 | ||
| 161 | - // 调用确认接口 | ||
| 162 | - var resp = await _api.ScanConfirmAsync(OutstockId!); | 156 | + // 组装 [{ barcode, id }] |
| 157 | + var items = picks.Select(x => (barcode: x.Barcode, id: x.Id)).ToList(); | ||
| 158 | + var resp = await _api.ScanConfirmAsync(items); | ||
| 163 | if (!resp.Succeeded) | 159 | if (!resp.Succeeded) |
| 164 | { | 160 | { |
| 165 | await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "扫描通过失败,请重试。" : resp.Message!); | 161 | await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "扫描通过失败,请重试。" : resp.Message!); |
| 166 | return; | 162 | return; |
| 167 | } | 163 | } |
| 168 | 164 | ||
| 169 | - // 成功:刷新两张表 | ||
| 170 | await LoadPendingAsync(); | 165 | await LoadPendingAsync(); |
| 171 | await LoadScannedAsync(); | 166 | await LoadScannedAsync(); |
| 172 | - | ||
| 173 | - // 友好:恢复选中刚才那条(如果还在列表里) | ||
| 174 | - var hit = ScannedList.FirstOrDefault(x => | ||
| 175 | - string.Equals(x.Barcode, selectedBarcode, StringComparison.OrdinalIgnoreCase)); | ||
| 176 | - if (hit != null) { hit.IsSelected = true; SelectedScanItem = hit; } | ||
| 177 | - | ||
| 178 | await ShowTip("已确认通过。"); | 167 | await ShowTip("已确认通过。"); |
| 179 | } | 168 | } |
| 180 | 169 | ||
| 181 | - | ||
| 182 | [RelayCommand] | 170 | [RelayCommand] |
| 183 | private async Task CancelScan() | 171 | private async Task CancelScan() |
| 184 | { | 172 | { |
| 185 | - if (string.IsNullOrWhiteSpace(OutstockId)) | ||
| 186 | - { | ||
| 187 | - await ShowTip("缺少 OutstockId,无法取消。请从查询页进入。"); | ||
| 188 | - return; | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - // 依旧限制一次只操作一条(接口本身只要 instockId,这里是 UI 规范) | ||
| 192 | - var selected = ScannedList.Where(x => x.IsSelected).ToList(); | ||
| 193 | - if (selected.Count != 1) | ||
| 194 | - { | ||
| 195 | - await ShowTip(selected.Count == 0 ? "请先勾选一条已扫描记录。" : "一次只能操作一条记录。"); | ||
| 196 | - return; | ||
| 197 | - } | ||
| 198 | - var selectedBarcode = selected[0].Barcode; | ||
| 199 | - | ||
| 200 | - var resp = await _api.CancelScanAsync(OutstockId!); | 173 | + var picks = ScannedList.Where(x => x.IsSelected).ToList(); |
| 174 | + if (picks.Count == 0) { await ShowTip("请先勾选至少一条记录。"); return; } | ||
| 175 | + var items = picks.Select(x => (barcode: x.Barcode, id: x.Id)).ToList(); | ||
| 176 | + var resp = await _api.CancelScanAsync(items); | ||
| 201 | if (!resp.Succeeded) | 177 | if (!resp.Succeeded) |
| 202 | { | 178 | { |
| 203 | await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "取消扫描失败,请重试。" : resp.Message!); | 179 | await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "取消扫描失败,请重试。" : resp.Message!); |
| 204 | return; | 180 | return; |
| 205 | } | 181 | } |
| 206 | 182 | ||
| 207 | - // 成功后以服务端为准刷新两张表 | ||
| 208 | await LoadPendingAsync(); | 183 | await LoadPendingAsync(); |
| 209 | await LoadScannedAsync(); | 184 | await LoadScannedAsync(); |
| 210 | - | ||
| 211 | - // 友好:若那条还在列表里,恢复选中 | ||
| 212 | - var hit = ScannedList.FirstOrDefault(x => | ||
| 213 | - string.Equals(x.Barcode, selectedBarcode, StringComparison.OrdinalIgnoreCase)); | ||
| 214 | - if (hit != null) { hit.IsSelected = true; SelectedScanItem = hit; } | ||
| 215 | - | ||
| 216 | await ShowTip("已取消扫描。"); | 185 | await ShowTip("已取消扫描。"); |
| 217 | } | 186 | } |
| 218 | 187 | ||
| 219 | 188 | ||
| 189 | + | ||
| 220 | public async Task HandleScannedAsync(string data, string symbology) | 190 | public async Task HandleScannedAsync(string data, string symbology) |
| 221 | { | 191 | { |
| 222 | var barcode = (data ?? string.Empty).Trim(); | 192 | var barcode = (data ?? string.Empty).Trim(); |
| @@ -279,7 +249,12 @@ namespace IndustrialControl.ViewModels | @@ -279,7 +249,12 @@ namespace IndustrialControl.ViewModels | ||
| 279 | } | 249 | } |
| 280 | } | 250 | } |
| 281 | 251 | ||
| 282 | - public async Task<bool> ConfirmInboundAsync() | 252 | + // OutboundFinishedViewModel.cs |
| 253 | + | ||
| 254 | + private Task<bool> AskAsync(string title, string message, string ok = "是", string cancel = "否") => | ||
| 255 | + Shell.Current?.DisplayAlert(title, message, ok, cancel) ?? Task.FromResult(false); | ||
| 256 | + | ||
| 257 | + public async Task<bool> ConfirmOutboundAsync() | ||
| 283 | { | 258 | { |
| 284 | if (string.IsNullOrWhiteSpace(OutstockId)) | 259 | if (string.IsNullOrWhiteSpace(OutstockId)) |
| 285 | { | 260 | { |
| @@ -287,6 +262,17 @@ namespace IndustrialControl.ViewModels | @@ -287,6 +262,17 @@ namespace IndustrialControl.ViewModels | ||
| 287 | return false; | 262 | return false; |
| 288 | } | 263 | } |
| 289 | 264 | ||
| 265 | + // ② 服务端权威校验:是否全部扫码确认,后端接口 | ||
| 266 | + bool serverAllOk = await _api.JudgeOutstockDetailScanAllAsync(OutstockId!); | ||
| 267 | + | ||
| 268 | + // 任意一处不一致 → 提示是否继续 | ||
| 269 | + if (!serverAllOk) | ||
| 270 | + { | ||
| 271 | + bool goOn = await AskAsync("提示", "已扫描列表与待入库数量不一致,是否继续入库?"); | ||
| 272 | + if (!goOn) return false; | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + // ③ 调用确认入库接口 | ||
| 290 | var r = await _api.ConfirmOutstockAsync(OutstockId!); | 276 | var r = await _api.ConfirmOutstockAsync(OutstockId!); |
| 291 | if (!r.Succeeded) | 277 | if (!r.Succeeded) |
| 292 | { | 278 | { |
| @@ -294,12 +280,87 @@ namespace IndustrialControl.ViewModels | @@ -294,12 +280,87 @@ namespace IndustrialControl.ViewModels | ||
| 294 | return false; | 280 | return false; |
| 295 | } | 281 | } |
| 296 | 282 | ||
| 297 | - // 成功:可选刷新一次,以服务端为准;随后按钮事件里会 ClearAll() | ||
| 298 | - await LoadPendingAsync(); | ||
| 299 | - await LoadScannedAsync(); | ||
| 300 | return true; | 283 | return true; |
| 301 | } | 284 | } |
| 302 | 285 | ||
| 286 | + public async Task<bool> UpdateRowLocationAsync(object row, BinInfo bin, CancellationToken ct = default) | ||
| 287 | + { | ||
| 288 | + if (row is null || bin is null) return false; | ||
| 289 | + | ||
| 290 | + // 通过反射从行对象里取必要字段(兼容不同命名) | ||
| 291 | + var t = row.GetType(); | ||
| 292 | + string detailId = | ||
| 293 | + t.GetProperty("DetailId")?.GetValue(row)?.ToString() | ||
| 294 | + ?? t.GetProperty("detailId")?.GetValue(row)?.ToString() | ||
| 295 | + ?? string.Empty; | ||
| 296 | + | ||
| 297 | + string id = | ||
| 298 | + t.GetProperty("Id")?.GetValue(row)?.ToString() | ||
| 299 | + ?? t.GetProperty("id")?.GetValue(row)?.ToString() | ||
| 300 | + ?? string.Empty; | ||
| 301 | + | ||
| 302 | + if (string.IsNullOrWhiteSpace(detailId) || string.IsNullOrWhiteSpace(id)) | ||
| 303 | + { | ||
| 304 | + await MainThread.InvokeOnMainThreadAsync(async () => | ||
| 305 | + await Application.Current.MainPage.DisplayAlert("提示", "缺少必要字段:detailId 或 id。", "确定")); | ||
| 306 | + return false; | ||
| 307 | + } | ||
| 308 | + | ||
| 309 | + // 组织参数 | ||
| 310 | + var outstockWarehouse = bin.WarehouseName ?? ""; | ||
| 311 | + var outstockWarehouseCode = bin.WarehouseCode ?? ""; | ||
| 312 | + var location = bin.Location ?? ""; | ||
| 313 | + | ||
| 314 | + // 调用接口 | ||
| 315 | + var ok = await _api.UpdateOutstockLocationAsync( | ||
| 316 | + detailId, id, outstockWarehouse, outstockWarehouseCode, location, ct); | ||
| 317 | + | ||
| 318 | + if (!ok.Succeeded) | ||
| 319 | + { | ||
| 320 | + await MainThread.InvokeOnMainThreadAsync(async () => | ||
| 321 | + await Application.Current.MainPage.DisplayAlert("提示", ok.Message ?? "更新库位失败", "确定")); | ||
| 322 | + return false; | ||
| 323 | + } | ||
| 324 | + | ||
| 325 | + return true; | ||
| 326 | + } | ||
| 327 | + public async Task<bool> UpdateQuantityForRowAsync(OutScannedItem row, CancellationToken ct = default) | ||
| 328 | + { | ||
| 329 | + if (row is null) return false; | ||
| 330 | + if (!row.ScanStatus) | ||
| 331 | + { | ||
| 332 | + await ShowTip("该行尚未扫描通过,不能修改数量。"); | ||
| 333 | + return false; | ||
| 334 | + } | ||
| 335 | + if (row.Qty < 0) | ||
| 336 | + { | ||
| 337 | + await ShowTip("数量不能为负数。"); | ||
| 338 | + return false; | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + var resp = await _api.UpdateQuantityAsync(row.Barcode, row.DetailId, row.Id, row.Qty, ct); | ||
| 342 | + if (!resp.Succeeded) | ||
| 343 | + { | ||
| 344 | + await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "更新数量失败" : resp.Message!); | ||
| 345 | + return false; | ||
| 346 | + } | ||
| 347 | + | ||
| 348 | + // ✅ 成功:先提示,再刷新两张表 | ||
| 349 | + await ShowTip("数量修改成功"); | ||
| 350 | + | ||
| 351 | + // 记录一下当前行用于刷新后恢复选中 | ||
| 352 | + var keepBarcode = row.Barcode; | ||
| 353 | + | ||
| 354 | + await LoadPendingAsync(); // 刷新“待入库明细” | ||
| 355 | + await LoadScannedAsync(); // 刷新“已扫描明细” | ||
| 356 | + | ||
| 357 | + var hit = ScannedList.FirstOrDefault(x => string.Equals(x.Barcode, keepBarcode, StringComparison.OrdinalIgnoreCase)); | ||
| 358 | + if (hit != null) { hit.IsSelected = true; SelectedScanItem = hit; } | ||
| 359 | + | ||
| 360 | + return true; | ||
| 361 | + } | ||
| 362 | + | ||
| 363 | + | ||
| 303 | } | 364 | } |
| 304 | 365 | ||
| 305 | 366 |
| 1 | using CommunityToolkit.Mvvm.ComponentModel; | 1 | using CommunityToolkit.Mvvm.ComponentModel; |
| 2 | using CommunityToolkit.Mvvm.Input; | 2 | using CommunityToolkit.Mvvm.Input; |
| 3 | -using IndustrialControl.Pages; | ||
| 4 | using IndustrialControl.Services; | 3 | using IndustrialControl.Services; |
| 5 | -using System; | ||
| 6 | using System.Collections.ObjectModel; | 4 | using System.Collections.ObjectModel; |
| 7 | 5 | ||
| 8 | namespace IndustrialControl.ViewModels; | 6 | namespace IndustrialControl.ViewModels; |
| @@ -10,19 +8,20 @@ namespace IndustrialControl.ViewModels; | @@ -10,19 +8,20 @@ namespace IndustrialControl.ViewModels; | ||
| 10 | public partial class OutboundMaterialSearchViewModel : ObservableObject | 8 | public partial class OutboundMaterialSearchViewModel : ObservableObject |
| 11 | { | 9 | { |
| 12 | private readonly IOutboundMaterialService _dataSvc; | 10 | private readonly IOutboundMaterialService _dataSvc; |
| 13 | - [ObservableProperty] private string? searchOrderNo; | 11 | + |
| 12 | + [ObservableProperty] private string searchOrderNo; | ||
| 14 | [ObservableProperty] private DateTime startDate = DateTime.Today; | 13 | [ObservableProperty] private DateTime startDate = DateTime.Today; |
| 15 | [ObservableProperty] private DateTime endDate = DateTime.Today; | 14 | [ObservableProperty] private DateTime endDate = DateTime.Today; |
| 16 | - [ObservableProperty] private OutboundOrderSummary? selectedOrder; | ||
| 17 | private CancellationTokenSource? _searchCts; | 15 | private CancellationTokenSource? _searchCts; |
| 16 | + // 仅用于“高亮选中” | ||
| 17 | + [ObservableProperty] private OutboundOrderSummary? selectedOrder; | ||
| 18 | + | ||
| 18 | public OutboundMaterialSearchViewModel(IOutboundMaterialService dataSvc) | 19 | public OutboundMaterialSearchViewModel(IOutboundMaterialService dataSvc) |
| 19 | { | 20 | { |
| 20 | _dataSvc = dataSvc; | 21 | _dataSvc = dataSvc; |
| 21 | Orders = new ObservableCollection<OutboundOrderSummary>(); | 22 | Orders = new ObservableCollection<OutboundOrderSummary>(); |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | - | ||
| 25 | - | ||
| 26 | public ObservableCollection<OutboundOrderSummary> Orders { get; } | 25 | public ObservableCollection<OutboundOrderSummary> Orders { get; } |
| 27 | 26 | ||
| 28 | [RelayCommand] | 27 | [RelayCommand] |
| @@ -33,10 +32,16 @@ public partial class OutboundMaterialSearchViewModel : ObservableObject | @@ -33,10 +32,16 @@ public partial class OutboundMaterialSearchViewModel : ObservableObject | ||
| 33 | var ct = _searchCts.Token; | 32 | var ct = _searchCts.Token; |
| 34 | try | 33 | try |
| 35 | { | 34 | { |
| 36 | - var orderTypeList = new[] { "out_return", "out_return", "out_other" }; | ||
| 37 | - var list = await _dataSvc.ListOutboundOrdersAsync(SearchOrderNo, startDate, endDate, null, orderTypeList,ct); | 35 | + var orderTypeList = new[] { "out_return", "out_requisition", "out_other" }; |
| 36 | + var list = await _dataSvc.ListOutboundOrdersAsync( | ||
| 37 | + searchOrderNo, // 单号/条码 | ||
| 38 | + startDate, // 开始日期 | ||
| 39 | + endDate, // 结束日期(Service 内会扩到 23:59:59) | ||
| 40 | + null, // 不传单值 orderType,用 null 更清晰 | ||
| 41 | + orderTypeList, // 多类型数组 | ||
| 42 | + ct // ← 新增:取消令牌 | ||
| 43 | + ); | ||
| 38 | 44 | ||
| 39 | - // ★ 在主线程更新 ObservableCollection,避免看起来“没刷新” | ||
| 40 | await MainThread.InvokeOnMainThreadAsync(() => | 45 | await MainThread.InvokeOnMainThreadAsync(() => |
| 41 | { | 46 | { |
| 42 | Orders.Clear(); | 47 | Orders.Clear(); |
| @@ -47,7 +52,6 @@ public partial class OutboundMaterialSearchViewModel : ObservableObject | @@ -47,7 +52,6 @@ public partial class OutboundMaterialSearchViewModel : ObservableObject | ||
| 47 | } | 52 | } |
| 48 | }); | 53 | }); |
| 49 | 54 | ||
| 50 | - // (排查辅助)无数据时提示一下,确认命令确实执行了 | ||
| 51 | if (list == null || !list.Any()) | 55 | if (list == null || !list.Any()) |
| 52 | await Shell.Current.DisplayAlert("提示", "未查询到任何入库单", "确定"); | 56 | await Shell.Current.DisplayAlert("提示", "未查询到任何入库单", "确定"); |
| 53 | } | 57 | } |
| @@ -57,40 +61,45 @@ public partial class OutboundMaterialSearchViewModel : ObservableObject | @@ -57,40 +61,45 @@ public partial class OutboundMaterialSearchViewModel : ObservableObject | ||
| 57 | } | 61 | } |
| 58 | } | 62 | } |
| 59 | 63 | ||
| 60 | - | 64 | + // === 方案A:命令接收“当前项”作为参数,不依赖 SelectedOrder === |
| 61 | [RelayCommand(CanExecute = nameof(CanGoOutbound))] | 65 | [RelayCommand(CanExecute = nameof(CanGoOutbound))] |
| 62 | private async Task GoOutboundAsync(OutboundOrderSummary? item) | 66 | private async Task GoOutboundAsync(OutboundOrderSummary? item) |
| 63 | { | 67 | { |
| 64 | - if (SelectedOrder is null) return; | 68 | + if (item is null) return; |
| 65 | 69 | ||
| 66 | static string E(string? v) => Uri.EscapeDataString(v ?? ""); | 70 | static string E(string? v) => Uri.EscapeDataString(v ?? ""); |
| 67 | 71 | ||
| 68 | - var o = SelectedOrder; // 确保 SelectedOrder 包含以下字段 | ||
| 69 | - var url = | ||
| 70 | - $"//OutboundMaterial" + | ||
| 71 | - $"?outstockId={E(o.outstockId)}" + | ||
| 72 | - $"&outstockNo={E(searchOrderNo)}" + | ||
| 73 | - $"&orderType={E(o.orderType)}" + | ||
| 74 | - $"&orderTypeName={E(o.orderTypeName)}" + | ||
| 75 | - $"&purchaseNo={E(o.purchaseNo)}" + | ||
| 76 | - $"&supplierName={E(o.supplierName)}" + | ||
| 77 | - $"&createdTime={E(o.createdTime)}"; | 72 | + var o = item; |
| 73 | + | ||
| 74 | + await Shell.Current.GoToAsync( | ||
| 75 | + nameof(Pages.OutboundMaterialPage), | ||
| 76 | + new Dictionary<string, object> | ||
| 77 | + { | ||
| 78 | + ["outstockId"] = o.outstockId, | ||
| 79 | + ["outstockNo"] = o.outstockNo, | ||
| 80 | + ["orderType"] = o.orderType, | ||
| 81 | + ["orderTypeName"] = o.orderTypeName, | ||
| 82 | + ["requisitionMaterialNo"] = o.requisitionMaterialNo, | ||
| 83 | + ["returnNo"] = o.returnNo, | ||
| 84 | + ["deliveryNo"] = o.deliveryNo, | ||
| 85 | + ["createdTime"] = o.createdTime | ||
| 86 | + }); | ||
| 78 | 87 | ||
| 79 | - await Shell.Current.GoToAsync(url); | ||
| 80 | } | 88 | } |
| 89 | + | ||
| 90 | + // 与命令同签名的 CanExecute | ||
| 81 | private bool CanGoOutbound(OutboundOrderSummary? item) => item != null; | 91 | private bool CanGoOutbound(OutboundOrderSummary? item) => item != null; |
| 82 | } | 92 | } |
| 93 | + | ||
| 94 | +// 用于列表显示的精简 DTO | ||
| 83 | public record OutboundOrderSummary( | 95 | public record OutboundOrderSummary( |
| 84 | string outstockId, | 96 | string outstockId, |
| 97 | + string outstockNo, | ||
| 85 | string orderType, | 98 | string orderType, |
| 86 | string orderTypeName, | 99 | string orderTypeName, |
| 87 | - string purchaseNo, | ||
| 88 | - string supplierName, | ||
| 89 | - string arrivalNo, | ||
| 90 | - string createdTime, | 100 | + string workOrderNo, |
| 101 | + string returnNo, | ||
| 91 | string deliveryNo, | 102 | string deliveryNo, |
| 92 | string requisitionMaterialNo, | 103 | string requisitionMaterialNo, |
| 93 | - string returnNo, | ||
| 94 | - string workOrderNo | 104 | + string createdTime |
| 95 | ); | 105 | ); |
| 96 | - |
| 1 | using CommunityToolkit.Mvvm.ComponentModel; | 1 | using CommunityToolkit.Mvvm.ComponentModel; |
| 2 | using CommunityToolkit.Mvvm.Input; | 2 | using CommunityToolkit.Mvvm.Input; |
| 3 | -using IndustrialControl.Services; | ||
| 4 | using System.Collections.ObjectModel; | 3 | using System.Collections.ObjectModel; |
| 4 | +using IndustrialControl.Services; | ||
| 5 | +using IndustrialControl.Models; | ||
| 5 | 6 | ||
| 6 | namespace IndustrialControl.ViewModels | 7 | namespace IndustrialControl.ViewModels |
| 7 | { | 8 | { |
| @@ -15,17 +16,15 @@ namespace IndustrialControl.ViewModels | @@ -15,17 +16,15 @@ namespace IndustrialControl.ViewModels | ||
| 15 | [ObservableProperty] private string? outstockNo; | 16 | [ObservableProperty] private string? outstockNo; |
| 16 | [ObservableProperty] private string? orderType; | 17 | [ObservableProperty] private string? orderType; |
| 17 | [ObservableProperty] private string? orderTypeName; | 18 | [ObservableProperty] private string? orderTypeName; |
| 18 | - [ObservableProperty] private string? purchaseNo; | ||
| 19 | - [ObservableProperty] private string? supplierName; | ||
| 20 | - [ObservableProperty] private string? createdTime; | ||
| 21 | [ObservableProperty] private string? requisitionMaterialNo; | 19 | [ObservableProperty] private string? requisitionMaterialNo; |
| 22 | [ObservableProperty] private string? returnNo; | 20 | [ObservableProperty] private string? returnNo; |
| 23 | - [ObservableProperty] private string? workOrderNo; | 21 | + [ObservableProperty] private string? deliveryNo; |
| 22 | + [ObservableProperty] private string? createdTime; | ||
| 24 | 23 | ||
| 25 | // 列表数据源 | 24 | // 列表数据源 |
| 26 | public ObservableCollection<string> AvailableBins { get; } = new(); | 25 | public ObservableCollection<string> AvailableBins { get; } = new(); |
| 27 | public ObservableCollection<OutScannedItem> ScannedList { get; } = new(); | 26 | public ObservableCollection<OutScannedItem> ScannedList { get; } = new(); |
| 28 | - public ObservableCollection<PendingItem> PendingList { get; } = new(); | 27 | + public ObservableCollection<OutPendingItem> PendingList { get; } = new(); |
| 29 | 28 | ||
| 30 | [ObservableProperty] private OutScannedItem? selectedScanItem; | 29 | [ObservableProperty] private OutScannedItem? selectedScanItem; |
| 31 | 30 | ||
| @@ -42,28 +41,27 @@ namespace IndustrialControl.ViewModels | @@ -42,28 +41,27 @@ namespace IndustrialControl.ViewModels | ||
| 42 | // 命令 | 41 | // 命令 |
| 43 | public IRelayCommand ShowPendingCommand { get; } | 42 | public IRelayCommand ShowPendingCommand { get; } |
| 44 | public IRelayCommand ShowScannedCommand { get; } | 43 | public IRelayCommand ShowScannedCommand { get; } |
| 45 | - public IAsyncRelayCommand ConfirmCommand { get; } | ||
| 46 | 44 | ||
| 47 | - public OutboundMaterialViewModel(IOutboundMaterialService warehouseSvc) | 45 | + public OutboundMaterialViewModel(IOutboundMaterialService api) |
| 48 | { | 46 | { |
| 49 | - _api = warehouseSvc; | 47 | + _api = api; |
| 50 | ShowPendingCommand = new RelayCommand(() => SwitchTab(true)); | 48 | ShowPendingCommand = new RelayCommand(() => SwitchTab(true)); |
| 51 | ShowScannedCommand = new RelayCommand(() => SwitchTab(false)); | 49 | ShowScannedCommand = new RelayCommand(() => SwitchTab(false)); |
| 52 | - //ConfirmCommand = new AsyncRelayCommand(ConfirmInboundAsync); | ||
| 53 | } | 50 | } |
| 54 | 51 | ||
| 55 | // ================ 初始化入口(页面 OnAppearing 调用) ================ | 52 | // ================ 初始化入口(页面 OnAppearing 调用) ================ |
| 56 | public async Task InitializeFromSearchAsync( | 53 | public async Task InitializeFromSearchAsync( |
| 57 | string outstockId, string outstockNo, string orderType, string orderTypeName, | 54 | string outstockId, string outstockNo, string orderType, string orderTypeName, |
| 58 | - string purchaseNo, string supplierName, string createdTime) | 55 | + string requisitionMaterialNo, string returnNo,string deliveryNo, string createdTime) |
| 59 | { | 56 | { |
| 60 | // 1) 基础信息 | 57 | // 1) 基础信息 |
| 61 | OutstockId = outstockId; | 58 | OutstockId = outstockId; |
| 62 | OutstockNo = outstockNo; | 59 | OutstockNo = outstockNo; |
| 63 | OrderType = orderType; | 60 | OrderType = orderType; |
| 64 | OrderTypeName = orderTypeName; | 61 | OrderTypeName = orderTypeName; |
| 65 | - PurchaseNo = purchaseNo; | ||
| 66 | - SupplierName = supplierName; | 62 | + RequisitionMaterialNo = requisitionMaterialNo; |
| 63 | + ReturnNo = returnNo; | ||
| 64 | + DeliveryNo = deliveryNo; | ||
| 67 | CreatedTime = createdTime; | 65 | CreatedTime = createdTime; |
| 68 | 66 | ||
| 69 | // 2) 下拉库位(如无接口可留空或使用后端返回的 location 聚合) | 67 | // 2) 下拉库位(如无接口可留空或使用后端返回的 location 聚合) |
| @@ -101,13 +99,16 @@ namespace IndustrialControl.ViewModels | @@ -101,13 +99,16 @@ namespace IndustrialControl.ViewModels | ||
| 101 | var rows = await _api.GetOutStockDetailAsync(OutstockId!); | 99 | var rows = await _api.GetOutStockDetailAsync(OutstockId!); |
| 102 | foreach (var r in rows) | 100 | foreach (var r in rows) |
| 103 | { | 101 | { |
| 104 | - PendingList.Add(new PendingItem | 102 | + PendingList.Add(new OutPendingItem |
| 105 | { | 103 | { |
| 106 | Name = r.MaterialName ?? "", | 104 | Name = r.MaterialName ?? "", |
| 105 | + MaterialCode = r.MaterialCode ?? "", | ||
| 107 | Spec = r.Spec ?? "", | 106 | Spec = r.Spec ?? "", |
| 108 | - PendingQty = r.PendingQty, | ||
| 109 | - Bin = string.IsNullOrWhiteSpace(r.Location) ? "请选择" : r.Location!, | ||
| 110 | - ScannedQty = r.ScannedQty | 107 | + Location = r.Location, |
| 108 | + ProductionBatch = r.ProductionBatch, | ||
| 109 | + StockBatch = r.StockBatch, | ||
| 110 | + OutstockQty = r.OutstockQty, | ||
| 111 | + Qty = r.Qty | ||
| 111 | }); | 112 | }); |
| 112 | 113 | ||
| 113 | // 聚合可选库位 | 114 | // 聚合可选库位 |
| @@ -132,6 +133,8 @@ namespace IndustrialControl.ViewModels | @@ -132,6 +133,8 @@ namespace IndustrialControl.ViewModels | ||
| 132 | Spec = r.Spec ?? "", | 133 | Spec = r.Spec ?? "", |
| 133 | Location = string.IsNullOrWhiteSpace(r.Location) ? "请选择" : r.Location!, | 134 | Location = string.IsNullOrWhiteSpace(r.Location) ? "请选择" : r.Location!, |
| 134 | Qty = r.Qty, | 135 | Qty = r.Qty, |
| 136 | + ScanStatus = r.ScanStatus, | ||
| 137 | + WarehouseCode = r.WarehouseCode ?? "", | ||
| 135 | DetailId = r.DetailId, | 138 | DetailId = r.DetailId, |
| 136 | Id = OutstockId | 139 | Id = OutstockId |
| 137 | }); | 140 | }); |
| @@ -141,83 +144,48 @@ namespace IndustrialControl.ViewModels | @@ -141,83 +144,48 @@ namespace IndustrialControl.ViewModels | ||
| 141 | } | 144 | } |
| 142 | } | 145 | } |
| 143 | 146 | ||
| 147 | + // OutboundMaterialViewModel.cs | ||
| 148 | + | ||
| 144 | [RelayCommand] | 149 | [RelayCommand] |
| 145 | private async Task PassScan() | 150 | private async Task PassScan() |
| 146 | { | 151 | { |
| 147 | - if (string.IsNullOrWhiteSpace(OutstockId)) | ||
| 148 | - { | ||
| 149 | - await ShowTip("缺少 OutstockId,无法确认。请从查询页进入。"); | ||
| 150 | - return; | ||
| 151 | - } | ||
| 152 | - | ||
| 153 | - // 依旧要求只能选中一行,作为“操作目标”的 UI 约束(接口本身仅需 outstockId) | ||
| 154 | - var selected = ScannedList.Where(x => x.IsSelected).ToList(); | ||
| 155 | - if (selected.Count != 1) | ||
| 156 | - { | ||
| 157 | - await ShowTip(selected.Count == 0 ? "请先勾选一条已扫描记录。" : "一次只能操作一条记录。"); | ||
| 158 | - return; | ||
| 159 | - } | ||
| 160 | - var selectedBarcode = selected[0].Barcode; | 152 | + var picks = ScannedList.Where(x => x.IsSelected).ToList(); |
| 153 | + if (picks.Count == 0) { await ShowTip("请先勾选至少一条已扫描记录。"); return; } | ||
| 161 | 154 | ||
| 162 | - // 调用确认接口 | ||
| 163 | - var resp = await _api.ScanConfirmAsync(OutstockId!); | 155 | + // 组装 [{ barcode, id }] |
| 156 | + var items = picks.Select(x => (barcode: x.Barcode, id: x.Id)).ToList(); | ||
| 157 | + var resp = await _api.ScanConfirmAsync(items); | ||
| 164 | if (!resp.Succeeded) | 158 | if (!resp.Succeeded) |
| 165 | { | 159 | { |
| 166 | await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "扫描通过失败,请重试。" : resp.Message!); | 160 | await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "扫描通过失败,请重试。" : resp.Message!); |
| 167 | return; | 161 | return; |
| 168 | } | 162 | } |
| 169 | 163 | ||
| 170 | - // 成功:刷新两张表 | ||
| 171 | await LoadPendingAsync(); | 164 | await LoadPendingAsync(); |
| 172 | await LoadScannedAsync(); | 165 | await LoadScannedAsync(); |
| 173 | - | ||
| 174 | - // 友好:恢复选中刚才那条(如果还在列表里) | ||
| 175 | - var hit = ScannedList.FirstOrDefault(x => | ||
| 176 | - string.Equals(x.Barcode, selectedBarcode, StringComparison.OrdinalIgnoreCase)); | ||
| 177 | - if (hit != null) { hit.IsSelected = true; SelectedScanItem = hit; } | ||
| 178 | - | ||
| 179 | await ShowTip("已确认通过。"); | 166 | await ShowTip("已确认通过。"); |
| 180 | } | 167 | } |
| 181 | 168 | ||
| 182 | - | ||
| 183 | [RelayCommand] | 169 | [RelayCommand] |
| 184 | private async Task CancelScan() | 170 | private async Task CancelScan() |
| 185 | { | 171 | { |
| 186 | - if (string.IsNullOrWhiteSpace(OutstockId)) | ||
| 187 | - { | ||
| 188 | - await ShowTip("缺少 OutstockId,无法取消。请从查询页进入。"); | ||
| 189 | - return; | ||
| 190 | - } | ||
| 191 | - | ||
| 192 | - // 依旧限制一次只操作一条(接口本身只要 outstockId,这里是 UI 规范) | ||
| 193 | - var selected = ScannedList.Where(x => x.IsSelected).ToList(); | ||
| 194 | - if (selected.Count != 1) | ||
| 195 | - { | ||
| 196 | - await ShowTip(selected.Count == 0 ? "请先勾选一条已扫描记录。" : "一次只能操作一条记录。"); | ||
| 197 | - return; | ||
| 198 | - } | ||
| 199 | - var selectedBarcode = selected[0].Barcode; | ||
| 200 | - | ||
| 201 | - var resp = await _api.CancelScanAsync(OutstockId!); | 172 | + var picks = ScannedList.Where(x => x.IsSelected).ToList(); |
| 173 | + if (picks.Count == 0) { await ShowTip("请先勾选至少一条记录。"); return; } | ||
| 174 | + var items = picks.Select(x => (barcode: x.Barcode, id: x.Id)).ToList(); | ||
| 175 | + var resp = await _api.CancelScanAsync(items); | ||
| 202 | if (!resp.Succeeded) | 176 | if (!resp.Succeeded) |
| 203 | { | 177 | { |
| 204 | await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "取消扫描失败,请重试。" : resp.Message!); | 178 | await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "取消扫描失败,请重试。" : resp.Message!); |
| 205 | return; | 179 | return; |
| 206 | } | 180 | } |
| 207 | 181 | ||
| 208 | - // 成功后以服务端为准刷新两张表 | ||
| 209 | await LoadPendingAsync(); | 182 | await LoadPendingAsync(); |
| 210 | await LoadScannedAsync(); | 183 | await LoadScannedAsync(); |
| 211 | - | ||
| 212 | - // 友好:若那条还在列表里,恢复选中 | ||
| 213 | - var hit = ScannedList.FirstOrDefault(x => | ||
| 214 | - string.Equals(x.Barcode, selectedBarcode, StringComparison.OrdinalIgnoreCase)); | ||
| 215 | - if (hit != null) { hit.IsSelected = true; SelectedScanItem = hit; } | ||
| 216 | - | ||
| 217 | await ShowTip("已取消扫描。"); | 184 | await ShowTip("已取消扫描。"); |
| 218 | } | 185 | } |
| 219 | 186 | ||
| 220 | 187 | ||
| 188 | + | ||
| 221 | public async Task HandleScannedAsync(string data, string symbology) | 189 | public async Task HandleScannedAsync(string data, string symbology) |
| 222 | { | 190 | { |
| 223 | var barcode = (data ?? string.Empty).Trim(); | 191 | var barcode = (data ?? string.Empty).Trim(); |
| @@ -280,7 +248,12 @@ namespace IndustrialControl.ViewModels | @@ -280,7 +248,12 @@ namespace IndustrialControl.ViewModels | ||
| 280 | } | 248 | } |
| 281 | } | 249 | } |
| 282 | 250 | ||
| 283 | - public async Task<bool> ConfirmInboundAsync() | 251 | + // OutboundMaterialViewModel.cs |
| 252 | + | ||
| 253 | + private Task<bool> AskAsync(string title, string message, string ok = "是", string cancel = "否") => | ||
| 254 | + Shell.Current?.DisplayAlert(title, message, ok, cancel) ?? Task.FromResult(false); | ||
| 255 | + | ||
| 256 | + public async Task<bool> ConfirmOutboundAsync() | ||
| 284 | { | 257 | { |
| 285 | if (string.IsNullOrWhiteSpace(OutstockId)) | 258 | if (string.IsNullOrWhiteSpace(OutstockId)) |
| 286 | { | 259 | { |
| @@ -288,6 +261,17 @@ namespace IndustrialControl.ViewModels | @@ -288,6 +261,17 @@ namespace IndustrialControl.ViewModels | ||
| 288 | return false; | 261 | return false; |
| 289 | } | 262 | } |
| 290 | 263 | ||
| 264 | + // ② 服务端权威校验:是否全部扫码确认,后端接口 | ||
| 265 | + bool serverAllOk = await _api.JudgeOutstockDetailScanAllAsync(OutstockId!); | ||
| 266 | + | ||
| 267 | + // 任意一处不一致 → 提示是否继续 | ||
| 268 | + if (!serverAllOk) | ||
| 269 | + { | ||
| 270 | + bool goOn = await AskAsync("提示", "已扫描列表与待入库数量不一致,是否继续入库?"); | ||
| 271 | + if (!goOn) return false; | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + // ③ 调用确认入库接口 | ||
| 291 | var r = await _api.ConfirmOutstockAsync(OutstockId!); | 275 | var r = await _api.ConfirmOutstockAsync(OutstockId!); |
| 292 | if (!r.Succeeded) | 276 | if (!r.Succeeded) |
| 293 | { | 277 | { |
| @@ -295,13 +279,101 @@ namespace IndustrialControl.ViewModels | @@ -295,13 +279,101 @@ namespace IndustrialControl.ViewModels | ||
| 295 | return false; | 279 | return false; |
| 296 | } | 280 | } |
| 297 | 281 | ||
| 298 | - // 成功:可选刷新一次,以服务端为准;随后按钮事件里会 ClearAll() | ||
| 299 | - await LoadPendingAsync(); | ||
| 300 | - await LoadScannedAsync(); | ||
| 301 | return true; | 282 | return true; |
| 302 | } | 283 | } |
| 303 | 284 | ||
| 285 | + public async Task<bool> UpdateRowLocationAsync(object row, BinInfo bin, CancellationToken ct = default) | ||
| 286 | + { | ||
| 287 | + if (row is null || bin is null) return false; | ||
| 288 | + | ||
| 289 | + // 通过反射从行对象里取必要字段(兼容不同命名) | ||
| 290 | + var t = row.GetType(); | ||
| 291 | + string detailId = | ||
| 292 | + t.GetProperty("DetailId")?.GetValue(row)?.ToString() | ||
| 293 | + ?? t.GetProperty("detailId")?.GetValue(row)?.ToString() | ||
| 294 | + ?? string.Empty; | ||
| 295 | + | ||
| 296 | + string id = | ||
| 297 | + t.GetProperty("Id")?.GetValue(row)?.ToString() | ||
| 298 | + ?? t.GetProperty("id")?.GetValue(row)?.ToString() | ||
| 299 | + ?? string.Empty; | ||
| 300 | + | ||
| 301 | + if (string.IsNullOrWhiteSpace(detailId) || string.IsNullOrWhiteSpace(id)) | ||
| 302 | + { | ||
| 303 | + await MainThread.InvokeOnMainThreadAsync(async () => | ||
| 304 | + await Application.Current.MainPage.DisplayAlert("提示", "缺少必要字段:detailId 或 id。", "确定")); | ||
| 305 | + return false; | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + // 组织参数 | ||
| 309 | + var outstockWarehouse = bin.WarehouseName ?? ""; | ||
| 310 | + var outstockWarehouseCode = bin.WarehouseCode ?? ""; | ||
| 311 | + var location = bin.Location ?? ""; | ||
| 312 | + | ||
| 313 | + // 调用接口 | ||
| 314 | + var ok = await _api.UpdateOutstockLocationAsync( | ||
| 315 | + detailId, id, outstockWarehouse, outstockWarehouseCode, location, ct); | ||
| 316 | + | ||
| 317 | + if (!ok.Succeeded) | ||
| 318 | + { | ||
| 319 | + await MainThread.InvokeOnMainThreadAsync(async () => | ||
| 320 | + await Application.Current.MainPage.DisplayAlert("提示", ok.Message ?? "更新库位失败", "确定")); | ||
| 321 | + return false; | ||
| 304 | } | 322 | } |
| 305 | 323 | ||
| 324 | + return true; | ||
| 325 | + } | ||
| 326 | + public async Task<bool> UpdateQuantityForRowAsync(OutScannedItem row, CancellationToken ct = default) | ||
| 327 | + { | ||
| 328 | + if (row is null) return false; | ||
| 329 | + if (!row.ScanStatus) | ||
| 330 | + { | ||
| 331 | + await ShowTip("该行尚未扫描通过,不能修改数量。"); | ||
| 332 | + return false; | ||
| 333 | + } | ||
| 334 | + if (row.Qty < 0) | ||
| 335 | + { | ||
| 336 | + await ShowTip("数量不能为负数。"); | ||
| 337 | + return false; | ||
| 338 | + } | ||
| 306 | 339 | ||
| 340 | + var resp = await _api.UpdateQuantityAsync(row.Barcode, row.DetailId, row.Id, row.Qty, ct); | ||
| 341 | + if (!resp.Succeeded) | ||
| 342 | + { | ||
| 343 | + await ShowTip(string.IsNullOrWhiteSpace(resp.Message) ? "更新数量失败" : resp.Message!); | ||
| 344 | + return false; | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + // ✅ 成功:先提示,再刷新两张表 | ||
| 348 | + await ShowTip("数量修改成功"); | ||
| 349 | + | ||
| 350 | + // 记录一下当前行用于刷新后恢复选中 | ||
| 351 | + var keepBarcode = row.Barcode; | ||
| 352 | + | ||
| 353 | + await LoadPendingAsync(); // 刷新“待入库明细” | ||
| 354 | + await LoadScannedAsync(); // 刷新“已扫描明细” | ||
| 355 | + | ||
| 356 | + var hit = ScannedList.FirstOrDefault(x => string.Equals(x.Barcode, keepBarcode, StringComparison.OrdinalIgnoreCase)); | ||
| 357 | + if (hit != null) { hit.IsSelected = true; SelectedScanItem = hit; } | ||
| 358 | + | ||
| 359 | + return true; | ||
| 360 | + } | ||
| 361 | + | ||
| 362 | + | ||
| 363 | + } | ||
| 364 | + | ||
| 365 | + public class OutPendingItem | ||
| 366 | + { | ||
| 367 | + public string Name { get; set; } = ""; //产品名称 | ||
| 368 | + public string MaterialCode { get; set; } = ""; //产品编码 | ||
| 369 | + public string Spec { get; set; } = "";//规格 | ||
| 370 | + public string Location { get; set; } = "";//出库库位 | ||
| 371 | + public string ProductionBatch { get; set; } = "";//生产批号 | ||
| 372 | + | ||
| 373 | + public string StockBatch { get; set; } = "";//批次号 | ||
| 374 | + public int OutstockQty { get; set; } //出库数量 | ||
| 375 | + public int Qty { get; set; } //已扫描数 | ||
| 376 | + public string Bin { get; set; } = "请选择"; | ||
| 377 | + | ||
| 378 | + } | ||
| 307 | } | 379 | } |
-
请 注册 或 登录 后发表评论