作者 李壮

outbound

正在显示 45 个修改的文件 包含 211 行增加192 行删除
using Microsoft.Extensions.DependencyInjection;
namespace IndustrialControl;
public partial class App : Application
... ...
using Microsoft.Extensions.DependencyInjection;
namespace IndustrialControl;
namespace IndustrialControl;
public partial class AppShell : Shell
{
... ...
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks>net8.0-android</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
... ... @@ -36,6 +36,7 @@
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
<SkipValidateMauiImplicitPackageReferences>true</SkipValidateMauiImplicitPackageReferences>
</PropertyGroup>
<!-- Debug 下:关裁剪、关AOT、关链接器;仅安卓生效 -->
<PropertyGroup Condition="'$(Configuration)'=='Debug' and $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
... ...
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using CommunityToolkit.Maui;
using IndustrialControl.Services;
using CommunityToolkit.Maui;
using IndustrialControl.ViewModels;
using Microsoft.Extensions.Logging;
namespace IndustrialControl
{
... ...
namespace IndustrialControl.Pages;
public partial class AdminPage : ContentPage
{ public AdminPage(ViewModels.AdminViewModel vm)
{
public AdminPage(ViewModels.AdminViewModel vm)
{ InitializeComponent(); BindingContext = vm; }
}
... ...
namespace IndustrialControl.Pages;
public partial class LoginPage : ContentPage
{ public LoginPage(ViewModels.LoginViewModel vm)
{
public LoginPage(ViewModels.LoginViewModel vm)
{ InitializeComponent(); BindingContext = vm; }
}
... ...
... ... @@ -2,7 +2,7 @@ namespace IndustrialControl.Pages;
public partial class LogsPage : ContentPage
{
public LogsPage(ViewModels.LogsViewModel vm){ InitializeComponent(); BindingContext = vm; }
protected override void OnAppearing(){ base.OnAppearing(); if(BindingContext is ViewModels.LogsViewModel vm) vm.OnAppearing(); }
protected override void OnDisappearing(){ base.OnDisappearing(); if(BindingContext is ViewModels.LogsViewModel vm) vm.OnDisappearing(); }
public LogsPage(ViewModels.LogsViewModel vm) { InitializeComponent(); BindingContext = vm; }
protected override void OnAppearing() { base.OnAppearing(); if (BindingContext is ViewModels.LogsViewModel vm) vm.OnAppearing(); }
protected override void OnDisappearing() { base.OnDisappearing(); if (BindingContext is ViewModels.LogsViewModel vm) vm.OnDisappearing(); }
}
... ...
... ... @@ -50,8 +50,8 @@ public partial class OutboundMoldSearchPage : ContentPage
// 点整张卡片跳转(推荐)
private async void OnOrderTapped(object sender, TappedEventArgs e)
{
private async void OnOrderTapped(object sender, TappedEventArgs e)
{
try
{
if (e.Parameter is not MoldDto item) return;
... ... @@ -70,9 +70,9 @@ private async void OnOrderTapped(object sender, TappedEventArgs e)
{
await DisplayAlert("导航失败", ex.Message, "确定");
}
}
}
private async void OnScanHintClicked(object sender, EventArgs e)
private async void OnScanHintClicked(object sender, EventArgs e)
=> await DisplayAlert("提示", "此按钮预留摄像头扫码;硬件扫描直接扣扳机。", "确定");
}
... ...
using System.Text.Json;
using IndustrialControl.Models;
using IndustrialControl.Models;
using IndustrialControl.ViewModels;
using System.Text.Json;
namespace IndustrialControl.Pages;
... ...
... ... @@ -50,8 +50,8 @@ public partial class WorkOrderSearchPage : ContentPage
// 点整张卡片跳转(推荐)
private async void OnOrderTapped(object sender, TappedEventArgs e)
{
private async void OnOrderTapped(object sender, TappedEventArgs e)
{
try
{
if (e.Parameter is not WorkOrderDto item) return;
... ... @@ -70,9 +70,9 @@ private async void OnOrderTapped(object sender, TappedEventArgs e)
{
await DisplayAlert("导航失败", ex.Message, "确定");
}
}
}
private async void OnScanHintClicked(object sender, EventArgs e)
private async void OnScanHintClicked(object sender, EventArgs e)
=> await DisplayAlert("提示", "此按钮预留摄像头扫码;硬件扫描直接扣扳机。", "确定");
}
... ...
using System.Collections.ObjectModel;
using IndustrialControl.Models;
using IndustrialControl.Services;
using IndustrialControl.Models;
using System.Collections.ObjectModel;
namespace IndustrialControl.Pages;
... ...
using IndustrialControl.Models;
using IndustrialControl.Services;
using IndustrialControl.ViewModels;
namespace IndustrialControl.Pages;
... ...
... ... @@ -55,7 +55,7 @@
<Label.FormattedText>
<FormattedString>
<Span Text="客户:" FontAttributes="Bold"/>
<Span Text="{Binding PurchaseNo}"/>
<Span Text="{Binding Customer}"/>
</FormattedString>
</Label.FormattedText>
</Label>
... ... @@ -65,7 +65,7 @@
<Label.FormattedText>
<FormattedString>
<Span Text="要求发货时间:" FontAttributes="Bold"/>
<Span Text="{Binding SupplierName}"/>
<Span Text="{Binding ExpectedDeliveryTime}"/>
</FormattedString>
</Label.FormattedText>
</Label>
... ... @@ -74,7 +74,7 @@
<Label.FormattedText>
<FormattedString>
<Span Text="关联销售单:" FontAttributes="Bold"/>
<Span Text="{Binding SupplierName}"/>
<Span Text="{Binding saleNo}"/>
</FormattedString>
</Label.FormattedText>
</Label>
... ... @@ -83,7 +83,7 @@
<Label.FormattedText>
<FormattedString>
<Span Text="发货单备注:" FontAttributes="Bold"/>
<Span Text="{Binding SupplierName}"/>
<Span Text="{Binding DeliveryMemo}"/>
</FormattedString>
</Label.FormattedText>
</Label>
... ... @@ -150,11 +150,12 @@
</CollectionView>
<!-- 扫描明细表头 -->
<Grid Grid.Row="1" ColumnDefinitions="40,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsScannedVisible}" Padding="8">
<Grid Grid.Row="1" ColumnDefinitions="40,*,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsScannedVisible}" Padding="8">
<Label Text="选择" FontAttributes="Bold" />
<Label Grid.Column="1" Text="物料名称" FontAttributes="Bold" />
<Label Grid.Column="2" Text="条码" FontAttributes="Bold" />
<Label Grid.Column="3" Text="数量" FontAttributes="Bold" />
<Label Grid.Column="3" Text="出库数量" FontAttributes="Bold" />
<Label Grid.Column="4" Text="数量" FontAttributes="Bold" />
</Grid>
<!-- 扫描明细列表 -->
... ... @@ -165,7 +166,7 @@
SelectedItem="{Binding SelectedScanItem, Mode=TwoWay}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="40,*,*,*" Padding="8" BackgroundColor="White">
<Grid ColumnDefinitions="40,*,*,*,*" Padding="8" BackgroundColor="White">
<Grid.Triggers>
<!-- ScanStatus = true → 绿色 -->
<DataTrigger TargetType="Grid" Binding="{Binding ScanStatus}" Value="True">
... ... @@ -176,7 +177,8 @@
<CheckBox IsChecked="{Binding IsSelected}" />
<Label Grid.Column="1" Text="{Binding Name}" />
<Label Grid.Column="2" Text="{Binding Barcode}" />
<Entry Grid.Column="3"
<Label Grid.Column="3" Text="{Binding OutstockQty}" />
<Entry Grid.Column="4"
Keyboard="Numeric"
HorizontalTextAlignment="Center"
WidthRequest="64"
... ...
... ... @@ -5,24 +5,22 @@ namespace IndustrialControl.Pages;
[QueryProperty(nameof(OutstockId), "outstockId")]
[QueryProperty(nameof(OutstockNo), "outstockNo")]
[QueryProperty(nameof(OrderType), "orderType")]
[QueryProperty(nameof(OrderTypeName), "orderTypeName")]
[QueryProperty(nameof(RequisitionMaterialNo), "requisitionMaterialNo")]
[QueryProperty(nameof(ReturnNo), "returnNo")]
[QueryProperty(nameof(DeliveryNo), "deliveryNo")]
[QueryProperty(nameof(CreatedTime), "createdTime")]
[QueryProperty(nameof(Customer), "customer")]
[QueryProperty(nameof(ExpectedDeliveryTime), "expectedDeliveryTime")]
[QueryProperty(nameof(SaleNo), "saleNo")]
[QueryProperty(nameof(DeliveryMemo), "deliveryMemo")]
public partial class OutboundFinishedPage : ContentPage
{
private readonly ScanService _scanSvc;
private readonly OutboundFinishedViewModel _vm;
public string? OutstockId { get; set; }
public string? OutstockNo { get; set; }
public string? OrderType { get; set; }
public string? OrderTypeName { get; set; }
public string? RequisitionMaterialNo { get; set; }
public string? ReturnNo { get; set; }
public string? Customer { get; set; }
public string? ExpectedDeliveryTime { get; set; }
public string? DeliveryNo { get; set; }
public string? CreatedTime { get; set; }
public string? SaleNo { get; set; }
public string? DeliveryMemo { get; set; }
private readonly IDialogService _dialogs;
public OutboundFinishedPage(OutboundFinishedViewModel vm, ScanService scanSvc, IDialogService dialogs)
... ... @@ -51,12 +49,11 @@ public partial class OutboundFinishedPage : ContentPage
await _vm.InitializeFromSearchAsync(
outstockId: OutstockId ?? "",
outstockNo: OutstockNo ?? "",
orderType: OrderType ?? "",
orderTypeName: OrderTypeName ?? "",
requisitionMaterialNo: RequisitionMaterialNo ?? "",
returnNo: ReturnNo ?? "",
deliveryNo: DeliveryNo ?? "",
createdTime: CreatedTime ?? ""
customer: Customer ?? "",
expectedDeliveryTime: ExpectedDeliveryTime ?? "",
saleNo: SaleNo ?? "",
deliveryMemo: DeliveryMemo ?? ""
);
}
... ...
... ... @@ -55,7 +55,7 @@
<Label Grid.Row="2" Grid.Column="1" Text="{Binding deliveryNo}" />
<Label Grid.Row="3" Grid.Column="0" Text="关联销售号:" FontAttributes="Bold"/>
<Label Grid.Row="3" Grid.Column="1" Text="{Binding arrivalNo}" />
<Label Grid.Row="3" Grid.Column="1" Text="{Binding saleNo}" />
<Label Grid.Row="4" Grid.Column="0" Text="创建日期:" FontAttributes="Bold"/>
<Label Grid.Row="4" Grid.Column="1" Text="{Binding createdTime}" />
... ...
... ... @@ -65,7 +65,7 @@
<Label.FormattedText>
<FormattedString>
<Span Text="出库单备注:" FontAttributes="Bold"/>
<Span Text="{Binding SupplierName}"/>
<Span Text="{Binding Memo}"/>
</FormattedString>
</Label.FormattedText>
</Label>
... ... @@ -133,11 +133,12 @@
</CollectionView>
<!-- 扫描明细表头 -->
<Grid Grid.Row="1" ColumnDefinitions="40,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsScannedVisible}" Padding="8">
<Grid Grid.Row="1" ColumnDefinitions="40,*,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsScannedVisible}" Padding="8">
<Label Text="选择" FontAttributes="Bold" />
<Label Grid.Column="1" Text="物料名称" FontAttributes="Bold" />
<Label Grid.Column="2" Text="条码" FontAttributes="Bold" />
<Label Grid.Column="3" Text="数量" FontAttributes="Bold" />
<Label Grid.Column="3" Text="出库数量" FontAttributes="Bold" />
<Label Grid.Column="4" Text="数量" FontAttributes="Bold" />
</Grid>
<!-- 扫描明细列表 -->
... ... @@ -148,7 +149,7 @@
SelectedItem="{Binding SelectedScanItem, Mode=TwoWay}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="40,*,*,*" Padding="8" BackgroundColor="White">
<Grid ColumnDefinitions="40,*,*,*,*" Padding="8" BackgroundColor="White">
<Grid.Triggers>
<!-- ScanStatus = true → 绿色 -->
<DataTrigger TargetType="Grid" Binding="{Binding ScanStatus}" Value="True">
... ... @@ -159,7 +160,8 @@
<CheckBox IsChecked="{Binding IsSelected}" />
<Label Grid.Column="1" Text="{Binding Name}" />
<Label Grid.Column="2" Text="{Binding Barcode}" />
<Entry Grid.Column="3"
<Label Grid.Column="3" Text="{Binding OutstockQty}" />
<Entry Grid.Column="4"
Keyboard="Numeric"
HorizontalTextAlignment="Center"
WidthRequest="64"
... ...
... ... @@ -5,24 +5,18 @@ namespace IndustrialControl.Pages;
[QueryProperty(nameof(OutstockId), "outstockId")]
[QueryProperty(nameof(OutstockNo), "outstockNo")]
[QueryProperty(nameof(OrderType), "orderType")]
[QueryProperty(nameof(OrderTypeName), "orderTypeName")]
[QueryProperty(nameof(RequisitionMaterialNo), "requisitionMaterialNo")]
[QueryProperty(nameof(ReturnNo), "returnNo")]
[QueryProperty(nameof(DeliveryNo), "deliveryNo")]
[QueryProperty(nameof(CreatedTime), "createdTime")]
[QueryProperty(nameof(WorkOrderNo), "workOrderNo")]
[QueryProperty(nameof(Memo), "memo")]
public partial class OutboundMaterialPage : ContentPage
{
private readonly ScanService _scanSvc;
private readonly OutboundMaterialViewModel _vm;
public string? OutstockId { get; set; }
public string? OutstockNo { get; set; }
public string? OrderType { get; set; }
public string? OrderTypeName { get; set; }
public string? RequisitionMaterialNo { get; set; }
public string? ReturnNo { get; set; }
public string? DeliveryNo { get; set; }
public string? CreatedTime { get; set; }
public string? WorkOrderNo { get; set; }
public string? Memo { get; set; }
private readonly IDialogService _dialogs;
public OutboundMaterialPage(OutboundMaterialViewModel vm, ScanService scanSvc, IDialogService dialogs)
... ... @@ -51,12 +45,9 @@ public partial class OutboundMaterialPage : ContentPage
await _vm.InitializeFromSearchAsync(
outstockId: OutstockId ?? "",
outstockNo: OutstockNo ?? "",
orderType: OrderType ?? "",
orderTypeName: OrderTypeName ?? "",
requisitionMaterialNo: RequisitionMaterialNo ?? "",
returnNo: ReturnNo ?? "",
deliveryNo: DeliveryNo ?? "",
createdTime: CreatedTime ?? ""
workOrderNo: WorkOrderNo ?? "",
memo: Memo ?? ""
);
}
... ...
using Microsoft.UI.Xaml;
// To learn more about WinUI, the WinUI project structure,
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace IndustrialControl.WinUI
... ...
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net8.0-android\publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
</PropertyGroup>
</Project>
\ No newline at end of file
... ...
// Services/AuthHeaderHandler.cs
using System.Net;
using System.Net.Http.Headers;
namespace IndustrialControl.Services
... ...
using System.Text.Json;
using IndustrialControl.Models;
using System.Text.Json;
namespace IndustrialControl.Services;
... ...
using Microsoft.Maui.Dispatching;
using IndustrialControl.Models;
using IndustrialControl.Pages;
using IndustrialControl.Models;
namespace IndustrialControl.Services;
... ...
... ... @@ -33,9 +33,9 @@ public interface IInboundMaterialService
Task<List<LocationNodeDto>> GetLocationTreeAsync(CancellationToken ct = default);
Task<List<BinInfo>> GetBinsByLayerAsync(
string warehouseCode, string layer,
int pageNo = 1, int pageSize = 50, int status = 1,
CancellationToken ct = default);
string warehouseCode, string layer,
int pageNo = 1, int pageSize = 50, int status = 1,
CancellationToken ct = default);
/// <summary>更新扫描明细的库位(/normalService/pda/wmsMaterialInstock/updateLocation)</summary>
Task<SimpleOk> UpdateInstockLocationAsync(
... ...
using IndustrialControl.Models;
using IndustrialControl.ViewModels;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
... ... @@ -210,7 +208,7 @@ namespace IndustrialControl.Services
return resp;
}
public async Task<DictBundle> GetMoldDictsAsync(CancellationToken ct = default)
public async Task<DictBundle> GetMoldDictsAsync(CancellationToken ct = default)
{
using var req = new HttpRequestMessage(HttpMethod.Get, _dictEndpoint);
using var res = await _http.SendAsync(req, ct);
... ...
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace IndustrialControl.Services
... ... @@ -172,7 +171,7 @@ namespace IndustrialControl.Services
return resp;
}
public async Task<DictBundle> GetWorkOrderDictsAsync(CancellationToken ct = default)
public async Task<DictBundle> GetWorkOrderDictsAsync(CancellationToken ct = default)
{
using var req = new HttpRequestMessage(HttpMethod.Get, _dictEndpoint);
using var res = await _http.SendAsync(req, ct);
... ... @@ -337,14 +336,14 @@ public async Task<DictBundle> GetWorkOrderDictsAsync(CancellationToken ct = defa
public List<DictItem> AuditStatus { get; set; } = new();
public List<DictItem> Urgent { get; set; } = new();
}
public sealed class WorkflowResp { public bool success { get; set; } public string? message { get; set; } public int code { get; set; } public List<WorkflowItem>? result { get; set; } }
public sealed class WorkflowItem { public string? statusValue { get; set; } public string? statusName { get; set; } public string? statusTime { get; set; } }
public sealed class WorkflowResp { public bool success { get; set; } public string? message { get; set; } public int code { get; set; } public List<WorkflowItem>? result { get; set; } }
public sealed class WorkflowItem { public string? statusValue { get; set; } public string? statusName { get; set; } public string? statusTime { get; set; } }
public sealed class PageResp<T> { public bool success { get; set; } public string? message { get; set; } public int code { get; set; } public PageResult<T>? result { get; set; } }
public sealed class PageResult<T> { public int pageNo { get; set; } public int pageSize { get; set; } public int total { get; set; } public List<T>? records { get; set; } }
public sealed class PageResp<T> { public bool success { get; set; } public string? message { get; set; } public int code { get; set; } public PageResult<T>? result { get; set; } }
public sealed class PageResult<T> { public int pageNo { get; set; } public int pageSize { get; set; } public int total { get; set; } public List<T>? records { get; set; } }
public sealed class ProcessTask
{
public sealed class ProcessTask
{
public string? id { get; set; }
public string? processCode { get; set; }
public string? processName { get; set; }
... ... @@ -354,5 +353,5 @@ public sealed class ProcessTask
public string? endDate { get; set; }
public int? sortNumber { get; set; }
public string? auditStatus { get; set; }
}
}
}
... ...
using IndustrialControl.Models;
using IndustrialControl.ViewModels;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace IndustrialControl.Services;
... ... @@ -153,8 +151,8 @@ public sealed class InboundMaterialService : IInboundMaterialService
orderType: x.orderType ?? "",
orderTypeName: x.orderTypeName ?? "",
purchaseNo: x.purchaseNo ?? "",
arrivalNo: x.arrivalNo ?? "",
supplierName: x.supplierName ?? "",
arrivalNo: x.arrivalNo ?? "",
workOrderNo: x.workOrderNo ?? "",
materialName: x.materialName ?? "",
instockQty: ToInt(x.instockQty),
... ... @@ -220,8 +218,8 @@ public sealed class InboundMaterialService : IInboundMaterialService
MaterialName: (x.materialName ?? string.Empty).Trim(),
Qty: ToInt(x.qty),
Spec: (x.spec ?? string.Empty).Trim(),
ScanStatus :x.scanStatus ?? false,
WarehouseCode :x.warehouseCode?.Trim()
ScanStatus: x.scanStatus ?? false,
WarehouseCode: x.warehouseCode?.Trim()
)).ToList();
return list;
... ... @@ -452,25 +450,25 @@ public sealed class InboundMaterialService : IInboundMaterialService
// ====== DTO(按接口示例字段) ======
public class GetInStockReq
{
{
public string? createdTime { get; set; }
public string? endTime { get; set; }
public string? instockNo { get; set; }
public string? orderType { get; set; }
public string? startTime { get; set; }
}
}
public class GetInStockResp
{
public class GetInStockResp
{
public int code { get; set; }
public long costTime { get; set; }
public string? message { get; set; }
public bool success { get; set; }
public List<GetInStockItem>? result { get; set; }
}
}
public class GetInStockItem
{
public class GetInStockItem
{
public string? arrivalNo { get; set; }
public string? createdTime { get; set; }
public string? instockId { get; set; }
... ... @@ -478,7 +476,7 @@ public class GetInStockReq
public string? orderType { get; set; }
public string? purchaseNo { get; set; }
public string? supplierName { get; set; }
}
}
public sealed class GetInStockDetailResp
{
public bool success { get; set; }
... ... @@ -504,55 +502,55 @@ public sealed class GetInStockDetailItem
public class ScanRow
{
{
public string? barcode { get; set; }
public string? instockId { get; set; }
public string? location { get; set; }
public string? materialName { get; set; }
public string? qty { get; set; }
public string? spec { get; set; }
}
}
public class ScanByBarcodeResp
{
public class ScanByBarcodeResp
{
public int code { get; set; }
public long costTime { get; set; }
public string? message { get; set; }
public object? result { get; set; } // 文档里 result 只是 bool/无结构,这里占位
public bool success { get; set; }
}
public class ScanConfirmResp
{
}
public class ScanConfirmResp
{
public int code { get; set; }
public long costTime { get; set; }
public string? message { get; set; }
public object? result { get; set; }
public bool success { get; set; }
}
public class CancelScanResp
{
}
public class CancelScanResp
{
public int code { get; set; }
public long costTime { get; set; }
public string? message { get; set; }
public object? result { get; set; }
public bool success { get; set; }
}
public class ConfirmResp
{
}
public class ConfirmResp
{
public int code { get; set; }
public long costTime { get; set; }
public string? message { get; set; }
public bool? result { get; set; }
public bool? success { get; set; }
}
public class JudgeScanAllResp
{
}
public class JudgeScanAllResp
{
public int code { get; set; }
public long costTime { get; set; }
public string? message { get; set; }
public bool success { get; set; }
public bool? result { get; set; } // 文档中为布尔
}
}
public class GetInStockPageResp
{
public int code { get; set; }
... ...
using IndustrialControl.Models;
using IndustrialControl.ViewModels;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace IndustrialControl.Services;
... ... @@ -151,9 +149,14 @@ public sealed class OutboundMaterialService : IOutboundMaterialService
orderType: x.orderType ?? "",
orderTypeName: x.orderTypeName ?? "",
workOrderNo: x.workOrderNo ?? "",
returnNo: x.returnNo ?? "",
deliveryNo: x.deliveryNo ?? "",
requisitionMaterialNo: x.requisitionMaterialNo ?? "",
returnNo:x.returnNo ?? "",
deliveryNo:x.deliveryNo ?? "",
customer: x.customer ?? "",
deliveryMemo: x.deliveryMemo ?? "",
expectedDeliveryTime: x.expectedDeliveryTime ?? "",
memo: x.memo ?? "",
saleNo: x.saleNo ?? "",
createdTime: x.createdTime ?? ""
));
}
... ... @@ -436,6 +439,11 @@ public sealed class OutboundMaterialService : IOutboundMaterialService
public string? requisitionMaterialNo { get; set; }
public string? returnNo { get; set; }
public string? deliveryNo { get; set; }
public string? customer { get; set; }
public string? deliveryMemo { get; set; }
public string? expectedDeliveryTime { get; set; }
public string? memo { get; set; }
public string? saleNo { get; set; }
public string? createdTime { get; set; }
}
public sealed class GetOutStockScanDetailResp
... ...
using System;
// File: Services/ScanService.cs
using System;
using Microsoft.Maui.Controls;
#if ANDROID
using Android.Content;
using Android.Util; // ✅ 用于 Log
using IndustrialControl.Droid; // 需要 DynamicScanReceiver
using Android.Util;
using IndustrialControl.Droid; // 引用 DynamicScanReceiver
#endif
namespace IndustrialControl.Services
{
/// <summary>
/// 统一的扫码服务:支持软键盘回车、手动发布、以及 Android 广播动态接收
/// </summary>
public class ScanService
{
public event Action<string, string>? Scanned;
public event Action<string, string?>? Scanned;
/// <summary>可选:前缀过滤(匹配到则裁剪)</summary>
public string? Prefix { get; set; }
/// <summary>可选:后缀过滤(匹配到则裁剪)</summary>
public string? Suffix { get; set; }
/// <summary>去抖间隔(毫秒):相同码在该间隔内只触发一次</summary>
public int DebounceMs { get; set; } = 250;
private string? _lastData;
private DateTime _lastAt = DateTime.MinValue;
// 约定的广播/键名(与你的设备或发送方对齐)
public const string BroadcastAction = "lc";
public const string DataKey = "data";
public const string TypeKey = "SCAN_BARCODE_TYPE_NAME";
/// <summary>
/// 绑定一个 Entry:回车或换行时触发扫码
/// </summary>
public void Attach(Entry entry)
{
// 回车(Completed)
entry.Completed += (s, e) =>
{
var data = entry.Text?.Trim();
... ... @@ -34,11 +48,12 @@ namespace IndustrialControl.Services
#if ANDROID
Log.Info("ScanService", $"[Attach] Entry.Completed -> {data}");
#endif
Scanned?.Invoke(data, "kbd");
FilterAndRaise(data, "kbd");
entry.Text = string.Empty;
}
};
// 文本变化:遇到 \n/\r 也触发
entry.TextChanged += (s, e) =>
{
if (string.IsNullOrEmpty(e.NewTextValue)) return;
... ... @@ -49,24 +64,30 @@ namespace IndustrialControl.Services
#if ANDROID
Log.Info("ScanService", $"[Attach] Entry.TextChanged -> {data}");
#endif
Scanned?.Invoke(data, "kbd");
FilterAndRaise(data, "kbd");
entry.Text = string.Empty;
}
};
}
public void Publish(string code, string type = "")
/// <summary>
/// 代码侧模拟一次扫码(用于调试/联调)
/// </summary>
public void Publish(string code, string? type = null)
{
#if ANDROID
Log.Info("ScanService", $"[Publish] 模拟扫码 -> {code}, type={type}");
#endif
FilterAndRaise(code, type);
FilterAndRaise(code, type ?? string.Empty);
}
/// <summary>
/// 开始监听 Android 广播(动态注册)
/// </summary>
public void StartListening()
{
#if ANDROID
Android.Util.Log.Info("ScanService", "[StartListening] ENTER");
Log.Info("ScanService", "[StartListening] ENTER");
if (_receiver != null) return;
_receiver = new DynamicScanReceiver();
... ... @@ -79,6 +100,9 @@ namespace IndustrialControl.Services
#endif
}
/// <summary>
/// 停止监听 Android 广播(反注册)
/// </summary>
public void StopListening()
{
#if ANDROID
... ... @@ -100,19 +124,22 @@ namespace IndustrialControl.Services
#endif
}
private bool FilterAndRaise(string data, string type)
/// <summary>
/// 统一过滤 + 去抖 + 触发事件
/// </summary>
private bool FilterAndRaise(string data, string? type)
{
if (!string.IsNullOrEmpty(Prefix) && data.StartsWith(Prefix))
if (!string.IsNullOrEmpty(Prefix) && data.StartsWith(Prefix, StringComparison.Ordinal))
data = data.Substring(Prefix.Length);
if (!string.IsNullOrEmpty(Suffix) && data.EndsWith(Suffix))
if (!string.IsNullOrEmpty(Suffix) && data.EndsWith(Suffix, StringComparison.Ordinal))
data = data.Substring(0, data.Length - Suffix.Length);
var now = DateTime.UtcNow;
if (_lastData == data && (now - _lastAt).TotalMilliseconds < DebounceMs)
{
#if ANDROID
Log.Info("ScanService", $"[FilterAndRaise] 数据去抖: {data}");
Log.Info("ScanService", $"[FilterAndRaise] 去抖丢弃: {data}");
#endif
return false;
}
... ... @@ -121,7 +148,7 @@ namespace IndustrialControl.Services
_lastAt = now;
#if ANDROID
Log.Info("ScanService", $"[FilterAndRaise] 最终触发 -> {data}, type={type}");
Log.Info("ScanService", $"[FilterAndRaise] 触发 -> {data}, type={type}");
#endif
Scanned?.Invoke(data, type);
return true;
... ... @@ -131,9 +158,9 @@ namespace IndustrialControl.Services
private DynamicScanReceiver? _receiver;
private IntentFilter? _filter;
private void OnScannedFromPlatform(string data, string type)
private void OnScannedFromPlatform(string data, string? type)
{
Log.Info("ScanService", $"[OnScannedFromPlatform] 原始数据 -> {data}, type={type}");
Log.Info("ScanService", $"[OnScannedFromPlatform] 原始 -> {data}, type={type}");
FilterAndRaise(data, type);
}
#endif
... ...
// Utils/TokenStorage.cs
using Microsoft.Maui.Storage;
namespace IndustrialControl;
public static class TokenStorage
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using IndustrialControl.Services;
using IndustrialControl.Pages; // 用于弹出 BinPickerPage / BinListPage
using IndustrialControl.Services;
using System.Collections.ObjectModel;
using ConfirmDetail = IndustrialControl.Services.InStockDetail;
// 使用服务层 DTO,避免 VM 内重复定义
using ConfirmReq = IndustrialControl.Services.InStockConfirmReq;
using ConfirmDetail = IndustrialControl.Services.InStockDetail;
namespace IndustrialControl.ViewModels
{
... ...
... ... @@ -130,7 +130,7 @@ namespace IndustrialControl.ViewModels
LineName = r.lineName ?? "",
Status = statusName,
Urgent = urgentName,
CurQty =(int?)r.curQty,
CurQty = (int?)r.curQty,
CreateDate = createdAt?.ToString("yyyy-MM-dd") ?? (r.createdTime ?? ""),
BomCode = r.bomCode, // e.g. "BOM00000006"
RouteName = r.routeName, // e.g. "午餐肉罐头测试工序调整"
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using IndustrialControl.Services;
using System.Collections.ObjectModel;
namespace IndustrialControl.ViewModels
{
... ...
... ... @@ -129,7 +129,7 @@ namespace IndustrialControl.ViewModels
LineName = r.lineName ?? "",
Status = statusName,
Urgent = urgentName,
CurQty =(int?)r.curQty,
CurQty = (int?)r.curQty,
CreateDate = createdAt?.ToString("yyyy-MM-dd") ?? (r.createdTime ?? ""),
BomCode = r.bomCode, // e.g. "BOM00000006"
RouteName = r.routeName, // e.g. "午餐肉罐头测试工序调整"
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using IndustrialControl.Services;
using IndustrialControl.Models;
using IndustrialControl.Services;
using System.Collections.ObjectModel;
namespace IndustrialControl.ViewModels
{
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using IndustrialControl.Services;
using IndustrialControl.Models;
using IndustrialControl.Services;
using System.Collections.ObjectModel;
namespace IndustrialControl.ViewModels
{
... ... @@ -44,7 +44,7 @@ namespace IndustrialControl.ViewModels
// ================ 初始化入口(页面 OnAppearing 调用) ================
public async Task InitializeFromSearchAsync(
string instockId, string instockNo, string orderType, string orderTypeName,
string purchaseNo, string supplierName, string createdTime,string workOrderNo,string materialName,int instockQty)
string purchaseNo, string supplierName, string createdTime, string workOrderNo, string materialName, int instockQty)
{
// 1) 基础信息
InstockId = instockId;
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using IndustrialControl.Services;
using IndustrialControl.Models;
using IndustrialControl.Services;
using System.Collections.ObjectModel;
namespace IndustrialControl.ViewModels
{
... ... @@ -14,12 +14,11 @@ namespace IndustrialControl.ViewModels
// === 基础信息(由搜索页带入) ===
[ObservableProperty] private string? outstockId;
[ObservableProperty] private string? outstockNo;
[ObservableProperty] private string? orderType;
[ObservableProperty] private string? orderTypeName;
[ObservableProperty] private string? requisitionMaterialNo;
[ObservableProperty] private string? returnNo;
[ObservableProperty] private string? deliveryNo;
[ObservableProperty] private string? createdTime;
[ObservableProperty] private string? customer;
[ObservableProperty] private string? expectedDeliveryTime;
[ObservableProperty] private string? saleNo;
[ObservableProperty] private string? deliveryMemo;
// 列表数据源
public ObservableCollection<string> AvailableBins { get; } = new();
... ... @@ -51,18 +50,17 @@ namespace IndustrialControl.ViewModels
// ================ 初始化入口(页面 OnAppearing 调用) ================
public async Task InitializeFromSearchAsync(
string outstockId, string outstockNo, string orderType, string orderTypeName,
string requisitionMaterialNo, string returnNo, string deliveryNo, string createdTime)
string outstockId, string outstockNo, string deliveryNo, string customer,
string expectedDeliveryTime, string saleNo, string deliveryMemo)
{
// 1) 基础信息
OutstockId = outstockId;
OutstockNo = outstockNo;
OrderType = orderType;
OrderTypeName = orderTypeName;
RequisitionMaterialNo = requisitionMaterialNo;
ReturnNo = returnNo;
DeliveryNo = deliveryNo;
CreatedTime = createdTime;
Customer = customer;
ExpectedDeliveryTime = expectedDeliveryTime;
SaleNo = saleNo;
DeliveryMemo = deliveryMemo;
// 2) 下拉库位(如无接口可留空或使用后端返回的 location 聚合)
AvailableBins.Clear();
... ...
... ... @@ -77,12 +77,9 @@ public partial class OutboundMaterialSearchViewModel : ObservableObject
{
["outstockId"] = o.outstockId,
["outstockNo"] = o.outstockNo,
["orderType"] = o.orderType,
["orderTypeName"] = o.orderTypeName,
["requisitionMaterialNo"] = o.requisitionMaterialNo,
["returnNo"] = o.returnNo,
["deliveryNo"] = o.deliveryNo,
["createdTime"] = o.createdTime
["workOrderNo"] = o.workOrderNo,
["memo"] = o.memo
});
}
... ... @@ -101,5 +98,10 @@ public record OutboundOrderSummary(
string returnNo,
string deliveryNo,
string requisitionMaterialNo,
string customer,
string deliveryMemo,
string expectedDeliveryTime,
string memo,
string saleNo,
string createdTime
);
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using IndustrialControl.Services;
using IndustrialControl.Models;
using IndustrialControl.Services;
using System.Collections.ObjectModel;
namespace IndustrialControl.ViewModels
{
... ... @@ -14,12 +14,9 @@ namespace IndustrialControl.ViewModels
// === 基础信息(由搜索页带入) ===
[ObservableProperty] private string? outstockId;
[ObservableProperty] private string? outstockNo;
[ObservableProperty] private string? orderType;
[ObservableProperty] private string? orderTypeName;
[ObservableProperty] private string? requisitionMaterialNo;
[ObservableProperty] private string? returnNo;
[ObservableProperty] private string? deliveryNo;
[ObservableProperty] private string? createdTime;
[ObservableProperty] private string? workOrderNo;
[ObservableProperty] private string? memo;
// 列表数据源
public ObservableCollection<string> AvailableBins { get; } = new();
... ... @@ -51,18 +48,17 @@ namespace IndustrialControl.ViewModels
// ================ 初始化入口(页面 OnAppearing 调用) ================
public async Task InitializeFromSearchAsync(
string outstockId, string outstockNo, string orderType, string orderTypeName,
string requisitionMaterialNo, string returnNo,string deliveryNo, string createdTime)
string outstockId, string outstockNo,
string requisitionMaterialNo, string workOrderNo, string memo)
{
// 1) 基础信息
OutstockId = outstockId;
OutstockNo = outstockNo;
OrderType = orderType;
OrderTypeName = orderTypeName;
RequisitionMaterialNo = requisitionMaterialNo;
ReturnNo = returnNo;
DeliveryNo = deliveryNo;
CreatedTime = createdTime;
WorkOrderNo = workOrderNo;
Memo = memo;
// 2) 下拉库位(如无接口可留空或使用后端返回的 location 聚合)
AvailableBins.Clear();
... ...
{ "sdk": { "version": "8.0.414" } }
... ...