作者 李壮

Initial commit

正在显示 55 个修改的文件 包含 3083 行增加0 行删除
## 忽略编译输出
bin/
obj/
[Bb]uild/
[Bb]in/
[Oo]bj/
## Visual Studio 相关
.vs/
*.user
*.suo
*.userosscache
*.sln.docstates
## Rider / JetBrains
.idea/
*.sln.iml
## MAUI 平台输出
# Android
*.apk
*.aab
platforms/android/
**/bin/Debug/
**/bin/Release/
# iOS / MacCatalyst
*.app
*.ipa
*.dSYM
platforms/ios/
platforms/maccatalyst/
# Windows
*.appx
*.appxbundle
*.msix
*.msixbundle
AppPackages/
## 日志文件
*.log
## 临时文件
~*
*.tmp
*.temp
## 备份文件
*.bak
*.backup
## NuGet
*.nupkg
*.snupkg
# 包缓存目录
packages/
# NuGet 本地源
.nuget/
## 发布/打包文件
publish/
artifacts/
## 配置文件(用户环境相关)
appsettings.Development.json
appsettings.Local.json
local.settings.json
## 其他
# macOS
.DS_Store
# Windows 缩略图
Thumbs.db
... ...
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="IndustrialControl.App">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="BlueCardStyle" TargetType="Frame">
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="15"/>
<Setter Property="HasShadow" Value="True"/>
<Setter Property="BackgroundColor" Value="#1976D2"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<Setter.Value>
<VisualStateGroupList>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#1976D2"/>
</VisualState.Setters>
</VisualState>
<VisualState Name="Pressed">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#2196F3"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
... ...
namespace IndustrialControl;
public partial class App : Application
{
public App(AppShell shell)
{
InitializeComponent();
MainPage = shell;
}
}
... ...
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="IndustrialControl.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Shell.FlyoutBehavior="Disabled"
Title="IndustrialControl" />
... ...
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Controls;
namespace IndustrialControl
{
public partial class AppShell : Shell
{
public AppShell(IServiceProvider sp)
{
InitializeComponent();
// Tab: 登录
var login = new ShellContent
{
Title = "登录",
Route = "Login",
ContentTemplate = new DataTemplate(() => sp.GetRequiredService<Pages.LoginPage>())
};
// Tab: 主页
var home = new ShellContent
{
Title = "主页",
Route = "Home",
ContentTemplate = new DataTemplate(() => sp.GetRequiredService<Pages.HomePage>())
};
// Tab: 管理员
var admin = new ShellContent
{
Title = "管理员",
Route = "Admin",
ContentTemplate = new DataTemplate(() => sp.GetRequiredService<Pages.AdminPage>())
};
// Tab: 日志
var logs = new ShellContent
{
Title = "日志",
Route = "Logs",
ContentTemplate = new DataTemplate(() => sp.GetRequiredService<Pages.LogsPage>())
};
var tabBar = new TabBar();
tabBar.Items.Add(login);
tabBar.Items.Add(home);
tabBar.Items.Add(admin);
tabBar.Items.Add(logs);
Items.Add(tabBar);
Routing.RegisterRoute(nameof(Pages.InboundMaterialPage), typeof(Pages.InboundMaterialPage));
Routing.RegisterRoute(nameof(Pages.InboundProductionPage), typeof(Pages.InboundProductionPage));
Routing.RegisterRoute(nameof(Pages.OutboundMaterialPage), typeof(Pages.OutboundMaterialPage));
Routing.RegisterRoute(nameof(Pages.OutboundFinishedPage), typeof(Pages.OutboundFinishedPage));
}
}
}
... ...
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</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> -->
<!-- Note for MacCatalyst:
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>.
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->
<OutputType>Exe</OutputType>
<RootNamespace>IndustrialControl</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Display name -->
<ApplicationTitle>IndustrialControl</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.companyname.industrialcontrol</ApplicationId>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<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>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.0"/>
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<MauiXaml Update="App.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Pages\AdminPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Pages\HomePage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Pages\InboundMaterialPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Pages\InboundProductionPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Pages\LoginPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Pages\LogsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Pages\OutboundFinishedPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Pages\OutboundMaterialPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>
</Project>
... ...

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IndustrialControl", "IndustrialControl.csproj", "{A54387CA-553D-4CE0-B86C-08A45497D703}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A54387CA-553D-4CE0-B86C-08A45497D703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Debug|x64.ActiveCfg = Debug|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Debug|x64.Build.0 = Debug|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Debug|x86.ActiveCfg = Debug|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Debug|x86.Build.0 = Debug|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Release|Any CPU.Build.0 = Release|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Release|x64.ActiveCfg = Release|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Release|x64.Build.0 = Release|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Release|x86.ActiveCfg = Release|Any CPU
{A54387CA-553D-4CE0-B86C-08A45497D703}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
... ...
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using IndustrialControl.Services;
namespace IndustrialControl
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services.AddSingleton<AppShell>();
// 注册 ConfigLoader
builder.Services.AddSingleton<Services.ConfigLoader>();
builder.Services.AddSingleton<Services.LogService>();
builder.Services.AddSingleton<IWarehouseDataService, MockWarehouseDataService>();
// 扫码服务
builder.Services.AddSingleton<ScanService>();
// ===== 注册 ViewModels =====
builder.Services.AddTransient<ViewModels.LoginViewModel>();
builder.Services.AddTransient<ViewModels.HomeViewModel>();
builder.Services.AddTransient<ViewModels.AdminViewModel>();
builder.Services.AddTransient<ViewModels.LogsViewModel>();
builder.Services.AddTransient<ViewModels.InboundMaterialViewModel>();
builder.Services.AddTransient<ViewModels.InboundProductionViewModel>();
builder.Services.AddTransient<ViewModels.OutboundMaterialViewModel>();
builder.Services.AddTransient<ViewModels.OutboundFinishedViewModel>();
// ===== 注册 Pages(DI 创建)=====
builder.Services.AddTransient<Pages.LoginPage>();
builder.Services.AddTransient<Pages.HomePage>();
builder.Services.AddTransient<Pages.AdminPage>();
builder.Services.AddTransient<Pages.LogsPage>();
// 注册需要路由的页面
builder.Services.AddTransient<Pages.InboundMaterialPage>();
builder.Services.AddTransient<Pages.InboundProductionPage>();
builder.Services.AddTransient<Pages.OutboundMaterialPage>();
builder.Services.AddTransient<Pages.OutboundFinishedPage>();
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
}
... ...
namespace IndustrialControl.Models;
public class ServerSettings
{
public string IpAddress { get; set; } = "192.168.1.100";
public int Port { get; set; } = 8080;
}
public class ApiEndpoints
{
public string Login { get; set; } = "/sso/login";
}
public class LoggingSettings
{
public string Level { get; set; } = "Information";
}
public class AppConfig
{
public int SchemaVersion { get; set; } = 3;
public ServerSettings Server { get; set; } = new();
public ApiEndpoints ApiEndpoints { get; set; } = new();
public LoggingSettings Logging { get; set; } = new();
}
... ...
namespace IndustrialControl.Models;
public record ScanLine(int Index, string Barcode, string? Bin, int Qty);
... ...
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="IndustrialControl.Pages.AdminPage"
Title="管理员设置">
<ScrollView>
<VerticalStackLayout Padding="24" Spacing="12">
<Label Text="配置版本(只读)" />
<Entry Text="{Binding SchemaVersion}" IsReadOnly="True"/>
<Label Text="后端 IP 地址" />
<Entry Text="{Binding IpAddress}" Keyboard="Numeric"/>
<Label Text="端口号" />
<Entry Text="{Binding Port}" Keyboard="Numeric"/>
<Frame BackgroundColor="#eef" Padding="8">
<Label Text="{Binding BaseUrl}" />
</Frame>
<HorizontalStackLayout Spacing="12" Margin="0,12,0,0">
<Button Text="保存" Command="{Binding SaveCommand}"/>
<Button Text="从包内默认重载" Command="{Binding ResetToPackageCommand}"/>
</HorizontalStackLayout>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
... ...
namespace IndustrialControl.Pages;
public partial class AdminPage : ContentPage
{ public AdminPage(ViewModels.AdminViewModel vm)
{ InitializeComponent(); BindingContext = vm; }
}
... ...
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="IndustrialControl.Pages.HomePage"
Title="仓储作业">
<VerticalStackLayout Padding="24" Spacing="16">
<Frame Style="{StaticResource BlueCardStyle}">
<Label Text="物料入库" FontSize="18" TextColor="White" VerticalTextAlignment="Center"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnInMat" NumberOfTapsRequired="1" />
</Frame.GestureRecognizers>
</Frame>
<Frame Style="{StaticResource BlueCardStyle}">
<Label Text="生产入库" FontSize="18" TextColor="White" VerticalTextAlignment="Center"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnInProd" NumberOfTapsRequired="1" />
</Frame.GestureRecognizers>
</Frame>
<Frame Style="{StaticResource BlueCardStyle}">
<Label Text="物料出库" FontSize="18" TextColor="White" VerticalTextAlignment="Center"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnOutMat" NumberOfTapsRequired="1" />
</Frame.GestureRecognizers>
</Frame>
<Frame Style="{StaticResource BlueCardStyle}">
<Label Text="成品出库/装货" FontSize="18" TextColor="White" VerticalTextAlignment="Center"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnOutFinished" NumberOfTapsRequired="1" />
</Frame.GestureRecognizers>
</Frame>
</VerticalStackLayout>
</ContentPage>
... ...
namespace IndustrialControl.Pages
{
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
}
private async void OnInMat(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(InboundMaterialPage));
}
private async void OnInProd(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(InboundProductionPage));
}
private async void OnOutMat(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(OutboundMaterialPage));
}
private async void OnOutFinished(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(OutboundFinishedPage));
}
}
}
... ...
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="IndustrialControl.Pages.InboundMaterialPage"
BackgroundColor="White">
<Grid RowDefinitions="Auto,Auto,Auto,*,Auto">
<!-- 顶部蓝色标题栏 -->
<Grid BackgroundColor="#007BFF" HeightRequest="60" Padding="16,0">
<Label Text="仓储管理系统"
VerticalOptions="Center"
TextColor="White"
FontSize="18"
FontAttributes="Bold"/>
</Grid>
<!-- 入库单/条码扫描 -->
<Grid Grid.Row="1" ColumnDefinitions="*,60" Padding="16,8">
<Entry x:Name="ScanEntry"
Placeholder="请输入/扫描单号/物料/包装条码"
FontSize="14"
VerticalOptions="Center"
BackgroundColor="White"
HeightRequest="40"
Text="{Binding ScanCode}" />
<ImageButton Grid.Column="1"
Source="scan.png"
BackgroundColor="#E6F2FF"
CornerRadius="4"
Padding="10"
Clicked="OnScanClicked"/>
</Grid>
<!-- 基础信息 -->
<Frame Grid.Row="2" Margin="16,0" Padding="8" BorderColor="#CCCCCC" BackgroundColor="White">
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,*,Auto,*" ColumnSpacing="8" RowSpacing="6">
<!-- 第一行 -->
<Label Grid.Row="0" Grid.Column="0" Text="入库单号:" FontAttributes="Bold" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding OrderNo}" LineBreakMode="TailTruncation" />
<Label Grid.Row="0" Grid.Column="2" Text="关联到货单号:" FontAttributes="Bold" />
<Label Grid.Row="0" Grid.Column="3" Text="{Binding LinkedDeliveryNo}" LineBreakMode="TailTruncation" />
<!-- 第二行 -->
<Label Grid.Row="1" Grid.Column="0" Text="关联采购单:" FontAttributes="Bold" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding LinkedPurchaseNo}" LineBreakMode="TailTruncation" />
<Label Grid.Row="1" Grid.Column="2" Text="供应商:" FontAttributes="Bold" />
<Label Grid.Row="1" Grid.Column="3" Text="{Binding Supplier}" LineBreakMode="TailTruncation" />
</Grid>
</Frame>
<!-- Tab切换 -->
<Grid Grid.Row="3" RowDefinitions="Auto,Auto,*" Margin="0">
<Grid ColumnDefinitions="*,*" BackgroundColor="White">
<Button Text="待入库明细"
Command="{Binding ShowPendingCommand}"
BackgroundColor="{Binding PendingTabColor}"
TextColor="{Binding PendingTextColor}" />
<Button Text="扫描明细"
Grid.Column="1"
Command="{Binding ShowScannedCommand}"
BackgroundColor="{Binding ScannedTabColor}"
TextColor="{Binding ScannedTextColor}" />
</Grid>
<!-- 待入库明细表头 -->
<Grid Grid.Row="1" ColumnDefinitions="*,*,*,*,*" BackgroundColor="#F2F2F2" IsVisible="{Binding IsPendingVisible}" 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="4" Text="已扫描数量" FontAttributes="Bold" />
</Grid>
<!-- 待入库明细列表 -->
<CollectionView Grid.Row="2"
ItemsSource="{Binding PendingList}"
IsVisible="{Binding IsPendingVisible}"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="*,*,*,*,*" Padding="8" BackgroundColor="White">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="White"/>
</VisualState.Setters>
</VisualState>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#CCFFCC"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Label Text="{Binding Name}" />
<Label Grid.Column="1" Text="{Binding Spec}" />
<Label Grid.Column="2" Text="{Binding PendingQty}" />
<Label Grid.Column="3" Text="{Binding Bin}" />
<Label Grid.Column="4" Text="{Binding ScannedQty}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- 扫描明细表头 -->
<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="4" Text="入库库位" FontAttributes="Bold" />
<Label Grid.Column="5" Text="数量" FontAttributes="Bold" />
</Grid>
<!-- 扫描明细列表 -->
<!-- 扫描明细列表 -->
<CollectionView Grid.Row="2"
ItemsSource="{Binding ScannedList}"
IsVisible="{Binding IsScannedVisible}"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="40,*,*,*,*,*" Padding="8" BackgroundColor="White">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="White"/>
</VisualState.Setters>
</VisualState>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#CCFFCC"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<CheckBox IsChecked="{Binding IsSelected}" />
<Label Grid.Column="1" Text="{Binding Barcode}" />
<Label Grid.Column="2" Text="{Binding Name}" />
<Label Grid.Column="3" Text="{Binding Spec}" />
<!-- 改成 Picker -->
<Picker Grid.Column="4"
ItemsSource="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.AvailableBins}"
SelectedItem="{Binding Bin, Mode=TwoWay}"
FontSize="14"
Title="请选择"
HorizontalOptions="FillAndExpand" />
<Label Grid.Column="5" Text="{Binding Qty}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
<!-- 底部按钮 -->
<!-- 底部按钮 -->
<Grid Grid.Row="4" ColumnDefinitions="*,*,*" Padding="16,8" ColumnSpacing="10">
<!-- 扫描通过 -->
<Grid BackgroundColor="#4CAF50"
HorizontalOptions="Fill"
VerticalOptions="Fill">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PassScanCommand}" />
</Grid.GestureRecognizers>
<StackLayout Orientation="Horizontal"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="pass.png" HeightRequest="20" WidthRequest="20" />
<Label Text="扫描通过"
Margin="5,0,0,0"
VerticalOptions="Center"
TextColor="White" />
</StackLayout>
</Grid>
<!-- 取消扫描 -->
<Grid Grid.Column="1"
BackgroundColor="#F44336"
HorizontalOptions="Fill"
VerticalOptions="Fill">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CancelScanCommand}" />
</Grid.GestureRecognizers>
<StackLayout Orientation="Horizontal"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="cancel.png" HeightRequest="20" WidthRequest="20" />
<Label Text="取消扫描"
Margin="5,0,0,0"
VerticalOptions="Center"
TextColor="White" />
</StackLayout>
</Grid>
<!-- 确认入库 -->
<Grid Grid.Column="2"
BackgroundColor="#2196F3"
HorizontalOptions="Fill"
VerticalOptions="Fill">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ConfirmCommand}" />
</Grid.GestureRecognizers>
<StackLayout Orientation="Horizontal"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="confirm.png" HeightRequest="20" WidthRequest="20" />
<Label Text="确认入库"
Margin="5,0,0,0"
VerticalOptions="Center"
TextColor="White" />
</StackLayout>
</Grid>
</Grid>
</Grid>
</ContentPage>
... ...
using IndustrialControl.Services;
using IndustrialControl.ViewModels;
namespace IndustrialControl.Pages;
public partial class InboundMaterialPage : ContentPage
{
private readonly ScanService _scanSvc;
private readonly InboundMaterialViewModel _vm;
public InboundMaterialPage(InboundMaterialViewModel vm, ScanService scanSvc)
{
InitializeComponent();
BindingContext = vm;
_scanSvc = scanSvc;
_vm = vm;
}
protected override void OnAppearing()
{
base.OnAppearing();
_scanSvc.Attach(ScanEntry);
ScanEntry.Focus();
}
/// <summary>
/// 清空扫描记录
/// </summary>
void OnClearClicked(object sender, EventArgs e)
{
_vm.ClearScan();
ScanEntry.Text = string.Empty;
ScanEntry.Focus();
}
/// <summary>
/// 预留摄像头扫码
/// </summary>
async void OnScanClicked(object sender, EventArgs e)
{
await DisplayAlert("提示", "此按钮预留摄像头扫码;硬件扫描直接扣扳机。", "确定");
}
/// <summary>
/// 扫描通过按钮点击
/// </summary>
void OnPassScanClicked(object sender, EventArgs e)
{
_vm.PassSelectedScan();
}
/// <summary>
/// 取消扫描按钮点击
/// </summary>
void OnCancelScanClicked(object sender, EventArgs e)
{
_vm.CancelSelectedScan();
}
/// <summary>
/// 确认入库按钮点击
/// </summary>
async void OnConfirmClicked(object sender, EventArgs e)
{
var ok = await _vm.ConfirmInboundAsync();
if (ok)
{
await DisplayAlert("提示", "入库成功", "确定");
_vm.ClearAll();
}
else
{
await DisplayAlert("提示", "入库失败,请检查数据", "确定");
}
}
}
... ...
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="IndustrialControl.Pages.InboundProductionPage"
BackgroundColor="White">
<Grid RowDefinitions="Auto,Auto,Auto,*,Auto">
<!-- 顶部蓝色标题栏 -->
<Grid BackgroundColor="#007BFF" HeightRequest="60" Padding="16,0">
<Label Text="仓储管理系统"
VerticalOptions="Center"
TextColor="White"
FontSize="18"
FontAttributes="Bold"/>
</Grid>
<!-- 入库单/条码扫描 -->
<Grid Grid.Row="1" ColumnDefinitions="*,60" Padding="16,8">
<Entry x:Name="ScanEntry"
Placeholder="请扫描入库单/产品/包装条码"
FontSize="14"
VerticalOptions="Center"
BackgroundColor="White"
HeightRequest="40"
Text="{Binding ScanCode}" />
<ImageButton Grid.Column="1"
Source="scan.png"
BackgroundColor="#E6F2FF"
CornerRadius="4"
Padding="10"
Clicked="OnScanClicked"/>
</Grid>
<!-- 基础信息 -->
<Frame Grid.Row="2" Margin="16,0" Padding="8" BorderColor="#CCCCCC" BackgroundColor="White">
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,*,Auto,*" ColumnSpacing="8" RowSpacing="6">
<Label Grid.Row="0" Grid.Column="0" Text="入库单号:" FontAttributes="Bold" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding OrderNo}" LineBreakMode="TailTruncation" />
<Label Grid.Row="0" Grid.Column="2" Text="工单号:" FontAttributes="Bold" />
<Label Grid.Row="0" Grid.Column="3" Text="{Binding WorkOrderNo}" LineBreakMode="TailTruncation" />
<Label Grid.Row="1" Grid.Column="0" Text="产品名称:" FontAttributes="Bold" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding ProductName}" LineBreakMode="TailTruncation" />
<Label Grid.Row="1" Grid.Column="2" Text="待入库数:" FontAttributes="Bold" />
<Label Grid.Row="1" Grid.Column="3" Text="{Binding PendingQty}" LineBreakMode="TailTruncation" />
</Grid>
</Frame>
<!-- 扫描明细 -->
<Grid Grid.Row="3" RowDefinitions="Auto,*" Margin="0">
<!-- 表头 -->
<Grid ColumnDefinitions="40,*,*,*" BackgroundColor="#F2F2F2" 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" />
</Grid>
<!-- 数据列表 -->
<CollectionView Grid.Row="1"
ItemsSource="{Binding Lines}"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="40,*,*,*" Padding="8" BackgroundColor="White">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="White"/>
</VisualState.Setters>
</VisualState>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#CCFFCC"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<CheckBox IsChecked="{Binding IsSelected}" />
<Label Grid.Column="1" Text="{Binding Barcode}" />
<!-- 库位改成 Picker -->
<Picker Grid.Column="2"
ItemsSource="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.AvailableBins}"
SelectedItem="{Binding Bin, Mode=TwoWay}"
FontSize="14"
Title="请选择"
HorizontalOptions="FillAndExpand" />
<Label Grid.Column="3" Text="{Binding Qty}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
<!-- 底部按钮 -->
<Grid Grid.Row="4" ColumnDefinitions="*,*,*" Padding="16,8" ColumnSpacing="10">
<!-- 扫描通过 -->
<Grid BackgroundColor="#4CAF50"
HorizontalOptions="Fill"
VerticalOptions="Fill">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PassScanCommand}" />
</Grid.GestureRecognizers>
<StackLayout Orientation="Horizontal"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="pass.png" HeightRequest="20" WidthRequest="20" />
<Label Text="扫描通过"
Margin="5,0,0,0"
VerticalOptions="Center"
TextColor="White" />
</StackLayout>
</Grid>
<!-- 取消扫描 -->
<Grid Grid.Column="1"
BackgroundColor="#F44336"
HorizontalOptions="Fill"
VerticalOptions="Fill">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CancelScanCommand}" />
</Grid.GestureRecognizers>
<StackLayout Orientation="Horizontal"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="cancel.png" HeightRequest="20" WidthRequest="20" />
<Label Text="取消扫描"
Margin="5,0,0,0"
VerticalOptions="Center"
TextColor="White" />
</StackLayout>
</Grid>
<!-- 确认入库 -->
<Grid Grid.Column="2"
BackgroundColor="#2196F3"
HorizontalOptions="Fill"
VerticalOptions="Fill">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ConfirmCommand}" />
</Grid.GestureRecognizers>
<StackLayout Orientation="Horizontal"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image Source="confirm.png" HeightRequest="20" WidthRequest="20" />
<Label Text="确认入库"
Margin="5,0,0,0"
VerticalOptions="Center"
TextColor="White" />
</StackLayout>
</Grid>
</Grid>
</Grid>
</ContentPage>
... ...
using IndustrialControl.Services;
using IndustrialControl.ViewModels;
namespace IndustrialControl.Pages
{
public partial class InboundProductionPage : ContentPage
{
private readonly ScanService _scanSvc;
private readonly InboundProductionViewModel _vm;
public InboundProductionPage(InboundProductionViewModel vm, ScanService scanSvc)
{
InitializeComponent();
BindingContext = vm;
_scanSvc = scanSvc;
_vm = vm;
}
protected override void OnAppearing()
{
base.OnAppearing();
_scanSvc.Attach(ScanEntry);
ScanEntry.Focus();
}
/// <summary>
/// 清空扫描记录
/// </summary>
void OnClearClicked(object sender, EventArgs e)
{
_vm.ClearScan();
ScanEntry.Text = string.Empty;
ScanEntry.Focus();
}
/// <summary>
/// 预留摄像头扫码
/// </summary>
async void OnScanClicked(object sender, EventArgs e)
{
await DisplayAlert("提示", "此按钮预留摄像头扫码;硬件扫描直接扣扳机。", "确定");
}
/// <summary>
/// 扫描通过
/// </summary>
void OnPassScanClicked(object sender, EventArgs e)
{
_vm.PassSelectedScan();
}
/// <summary>
/// 取消扫描
/// </summary>
void OnCancelScanClicked(object sender, EventArgs e)
{
_vm.CancelSelectedScan();
}
/// <summary>
/// 确认入库
/// </summary>
async void OnConfirmClicked(object sender, EventArgs e)
{
var ok = await _vm.ConfirmInboundAsync();
if (ok)
{
await DisplayAlert("提示", "入库成功", "确定");
_vm.ClearAll();
}
else
{
await DisplayAlert("提示", "入库失败,请检查数据", "确定");
}
}
}
}
... ...
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:IndustrialControl.Pages"
x:Class="IndustrialControl.Pages.LoginPage"
Title="登录">
<Grid Padding="24" RowDefinitions="Auto,*">
<VerticalStackLayout Spacing="12">
<Label Text="联创数智管家" FontAttributes="Bold" FontSize="28"/>
<Entry Placeholder="请输入用户名" Text="{Binding UserName}"/>
<Entry Placeholder="请输入登录密码" IsPassword="True" Text="{Binding Password}"/>
<Button Text="登录" Command="{Binding LoginCommand}"/>
</VerticalStackLayout>
</Grid>
</ContentPage>
... ...
namespace IndustrialControl.Pages;
public partial class LoginPage : ContentPage
{ public LoginPage(ViewModels.LoginViewModel vm)
{ InitializeComponent(); BindingContext = vm; }
}
... ...
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="IndustrialControl.Pages.LogsPage"
Title="实时日志">
<Grid RowDefinitions="Auto,*" Padding="12">
<Label Text="当日日志(自动刷新)" FontAttributes="Bold"/>
<ScrollView Grid.Row="1">
<Label Text="{Binding LogText}" FontFamily="monospace"/>
</ScrollView>
</Grid>
</ContentPage>
... ...
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(); }
}
... ...
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="IndustrialControl.Pages.OutboundFinishedPage"
BackgroundColor="White">
<Grid RowDefinitions="Auto,Auto,Auto,*,Auto">
<!-- 顶部蓝色标题栏 -->
<Grid BackgroundColor="#007BFF" HeightRequest="60" Padding="16,0">
<Label Text="仓储管理系统"
VerticalOptions="Center"
TextColor="White"
FontSize="18"
FontAttributes="Bold"/>
</Grid>
<!-- 出库单/发货单扫描 -->
<Grid Grid.Row="1" ColumnDefinitions="*,60" Padding="16,8">
<Entry x:Name="ScanEntry"
Placeholder="请输入/扫描出库单号/产品条码"
FontSize="14"
VerticalOptions="Center"
BackgroundColor="White"
HeightRequest="40"
Text="{Binding OrderNo}" />
<ImageButton Grid.Column="1"
Source="scan.png"
BackgroundColor="#E6F2FF"
CornerRadius="4"
Padding="10"
Clicked="OnScanClicked"/>
</Grid>
<!-- 基础信息 -->
<Frame Grid.Row="2" Margin="16,0" Padding="8" BorderColor="#CCCCCC" BackgroundColor="White">
<Grid RowDefinitions="Auto,Auto,Auto,Auto"
ColumnDefinitions="Auto,* ,Auto,*"
ColumnSpacing="8"
RowSpacing="6">
<!-- 出库单号(独占一行) -->
<Label Grid.Row="0" Grid.Column="0" Text="出库单号:" FontAttributes="Bold" VerticalOptions="Center" />
<Label Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3"
Text="{Binding OrderNo}" VerticalOptions="Center" />
<!-- 发货单号 + 客户 -->
<Label Grid.Row="1" Grid.Column="0" Text="发货单号:" FontAttributes="Bold" VerticalOptions="Center" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding DeliveryNo}" VerticalOptions="Center" />
<Label Grid.Row="1" Grid.Column="2" Text="客户:" FontAttributes="Bold" VerticalOptions="Center" />
<Label Grid.Row="1" Grid.Column="3" Text="{Binding Customer}" VerticalOptions="Center" />
<!-- 要求发货时间 + 关联销售单 -->
<Label Grid.Row="2" Grid.Column="0" Text="要求发货时间:" FontAttributes="Bold" VerticalOptions="Center" />
<Label Grid.Row="2" Grid.Column="1" Text="{Binding DeliveryTime}" VerticalOptions="Center" />
<Label Grid.Row="2" Grid.Column="2" Text="关联销售单:" FontAttributes="Bold" VerticalOptions="Center" />
<Label Grid.Row="2" Grid.Column="3" Text="{Binding SalesOrder}" VerticalOptions="Center" />
<!-- 发货单备注(独占一行) -->
<Label Grid.Row="3" Grid.Column="0" Text="发货单备注:" FontAttributes="Bold" VerticalOptions="Center" />
<Label Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3"
Text="{Binding Remark}" VerticalOptions="Center" />
</Grid>
</Frame>
<!-- Tab切换 + 表格 -->
<Grid Grid.Row="3" RowDefinitions="Auto,Auto,*" Margin="0">
<!-- Tab -->
<Grid ColumnDefinitions="*,*" BackgroundColor="White">
<Button Text="出库单明细"
BackgroundColor="{Binding PendingTabColor}"
TextColor="{Binding PendingTextColor}"
Clicked="OnPendingTabClicked"/>
<Button Text="扫描明细"
Grid.Column="1"
BackgroundColor="{Binding ScannedTabColor}"
TextColor="{Binding ScannedTextColor}"
Clicked="OnScannedTabClicked"/>
</Grid>
<!-- 出库单明细表头 -->
<!-- 出库单明细表头 -->
<Grid Grid.Row="1"
ColumnDefinitions="2*,2*,1.5*,2*,2*,1.5*,1.5*"
BackgroundColor="#F2F2F2"
IsVisible="{Binding IsPendingVisible}"
Padding="4"
HeightRequest="50">
<Label Text="产品名称" FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineBreakMode="WordWrap"/>
<Label Grid.Column="1" Text="产品编码" FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineBreakMode="WordWrap"/>
<Label Grid.Column="2" Text="规格" FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineBreakMode="WordWrap"/>
<Label Grid.Column="3" Text="出库库位" FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineBreakMode="WordWrap"/>
<Label Grid.Column="4" Text="生产批号" FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineBreakMode="WordWrap"/>
<Label Grid.Column="5" Text="出库数量" FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineBreakMode="WordWrap"/>
<Label Grid.Column="6" Text="已扫描数" FontAttributes="Bold"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
LineBreakMode="WordWrap"/>
</Grid>
<!-- 出库单明细列表 -->
<CollectionView Grid.Row="2"
ItemsSource="{Binding PendingList}"
IsVisible="{Binding IsPendingVisible}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="2*,2*,1.5*,2*,2*,1.5*,1.5*"
Padding="4"
BackgroundColor="White">
<Label Text="{Binding Name}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="1" Text="{Binding Code}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="2" Text="{Binding Spec}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="3" Text="{Binding Bin}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="4" Text="{Binding BatchNo}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="5" Text="{Binding OutQty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="6" Text="{Binding ScannedQty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- 扫描明细表头 -->
<Grid Grid.Row="1"
ColumnDefinitions="40,*,*,*"
BackgroundColor="#F2F2F2"
IsVisible="{Binding IsScannedVisible}"
Padding="4"
HeightRequest="30">
<Label Text="选择" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" LineBreakMode="NoWrap"/>
<Label Grid.Column="1" Text="条码" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" LineBreakMode="NoWrap"/>
<Label Grid.Column="2" Text="产品名称" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" LineBreakMode="NoWrap"/>
<Label Grid.Column="3" Text="数量" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" LineBreakMode="NoWrap"/>
</Grid>
<!-- 扫描明细列表 -->
<CollectionView Grid.Row="2"
ItemsSource="{Binding ScannedList}"
IsVisible="{Binding IsScannedVisible}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="40,*,*,*" Padding="4" BackgroundColor="White">
<CheckBox IsChecked="{Binding IsSelected}" HorizontalOptions="Center" VerticalOptions="Center" />
<Label Grid.Column="1" Text="{Binding Barcode}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
<Label Grid.Column="2" Text="{Binding Name}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
<Label Grid.Column="3" Text="{Binding Qty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
<!-- 底部按钮 -->
<Grid Grid.Row="4" ColumnDefinitions="*,*,*" Padding="16,8" ColumnSpacing="10">
<!-- 扫描通过 -->
<HorizontalStackLayout BackgroundColor="#4CAF50"
Padding="10"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
Grid.Column="0">
<Image Source="pass.png" WidthRequest="20" HeightRequest="20" />
<Label Text="扫描通过"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="White"
Margin="5,0,0,0" />
<HorizontalStackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding PassScanCommand}" />
</HorizontalStackLayout.GestureRecognizers>
</HorizontalStackLayout>
<!-- 取消扫描 -->
<HorizontalStackLayout BackgroundColor="#F44336"
Padding="10"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
Grid.Column="1">
<Image Source="cancel.png" WidthRequest="20" HeightRequest="20" />
<Label Text="取消扫描"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="White"
Margin="5,0,0,0" />
<HorizontalStackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CancelScanCommand}" />
</HorizontalStackLayout.GestureRecognizers>
</HorizontalStackLayout>
<!-- 确认出库 -->
<HorizontalStackLayout BackgroundColor="#2196F3"
Padding="10"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
Grid.Column="2">
<Image Source="confirm.png" WidthRequest="20" HeightRequest="20" />
<Label Text="确认出库"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="White"
Margin="5,0,0,0" />
<HorizontalStackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ConfirmCommand}" />
</HorizontalStackLayout.GestureRecognizers>
</HorizontalStackLayout>
</Grid>
</Grid>
</ContentPage>
... ...
using IndustrialControl.Services;
using IndustrialControl.ViewModels;
namespace IndustrialControl.Pages
{
public partial class OutboundFinishedPage : ContentPage
{
private readonly ScanService _scanSvc;
private readonly OutboundFinishedViewModel _vm;
public OutboundFinishedPage(OutboundFinishedViewModel vm, ScanService scanSvc)
{
InitializeComponent();
BindingContext = vm;
_scanSvc = scanSvc;
_vm = vm;
}
protected override void OnAppearing()
{
base.OnAppearing();
_scanSvc.Attach(ScanEntry);
ScanEntry.Focus();
}
/// <summary>
/// 清空扫描记录
/// </summary>
void OnClearClicked(object sender, EventArgs e)
{
_vm.ClearScan();
ScanEntry.Text = string.Empty;
ScanEntry.Focus();
}
/// <summary>
/// 预留摄像头扫码
/// </summary>
async void OnScanClicked(object sender, EventArgs e)
{
await DisplayAlert("提示", "此按钮预留摄像头扫码;硬件扫描直接扣扳机。", "确定");
}
/// <summary>
/// 点击“出库单明细”Tab
/// </summary>
void OnPendingTabClicked(object sender, EventArgs e)
{
_vm.ShowPendingCommand.Execute(null);
}
/// <summary>
/// 点击“扫描明细”Tab
/// </summary>
void OnScannedTabClicked(object sender, EventArgs e)
{
_vm.ShowScannedCommand.Execute(null);
}
/// <summary>
/// 扫描通过
/// </summary>
void OnPassScanClicked(object sender, EventArgs e)
{
_vm.PassScanCommand.Execute(null);
}
/// <summary>
/// 取消扫描
/// </summary>
void OnCancelScanClicked(object sender, EventArgs e)
{
_vm.CancelScanCommand.Execute(null);
}
/// <summary>
/// 确认出库
/// </summary>
async void OnConfirmClicked(object sender, EventArgs e)
{
_vm.ConfirmCommand.Execute(null);
}
}
}
... ...
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="IndustrialControl.Pages.OutboundMaterialPage"
BackgroundColor="White">
<Grid RowDefinitions="Auto,Auto,Auto,*,Auto">
<!-- 顶部蓝色标题栏 -->
<Grid BackgroundColor="#007BFF" HeightRequest="60" Padding="16,0">
<Label Text="仓储管理系统"
VerticalOptions="Center"
TextColor="White"
FontSize="18"
FontAttributes="Bold"/>
</Grid>
<!-- 出库单/条码扫描 -->
<Grid Grid.Row="1" ColumnDefinitions="*,60" Padding="16,8">
<Entry x:Name="ScanEntry"
Placeholder="请扫描出库单/产品/包装条码"
FontSize="14"
VerticalOptions="Center"
BackgroundColor="White"
HeightRequest="40"
Text="{Binding ScanCode}" />
<ImageButton Grid.Column="1"
Source="scan.png"
BackgroundColor="#E6F2FF"
CornerRadius="4"
Padding="10"
Clicked="OnScanClicked"/>
</Grid>
<!-- 基础信息 -->
<Frame Grid.Row="2" Margin="16,0" Padding="8" BorderColor="#CCCCCC" BackgroundColor="White">
<Grid RowDefinitions="Auto,Auto,Auto,Auto" ColumnDefinitions="Auto,*" ColumnSpacing="8" RowSpacing="6">
<!-- 出库单号 -->
<Label Grid.Row="0" Grid.Column="0" Text="出库单号:" FontAttributes="Bold"/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding OrderNo}" LineBreakMode="CharacterWrap"/>
<!-- 发货单号 + 客户 -->
<Label Grid.Row="1" Grid.Column="0" Text="发货单号:" FontAttributes="Bold"/>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding DeliveryNo}" LineBreakMode="CharacterWrap"/>
<Label Grid.Row="1" Grid.Column="2" Text="客户:" FontAttributes="Bold"/>
<Label Grid.Row="1" Grid.Column="3" Text="{Binding CustomerName}" LineBreakMode="CharacterWrap"/>
<!-- 发货时间 + 关联销售单 -->
<Label Grid.Row="2" Grid.Column="0" Text="发货时间:" FontAttributes="Bold"/>
<Label Grid.Row="2" Grid.Column="1" Text="{Binding DeliveryTime}" LineBreakMode="CharacterWrap"/>
<Label Grid.Row="2" Grid.Column="2" Text="关联销售单:" FontAttributes="Bold"/>
<Label Grid.Row="2" Grid.Column="3" Text="{Binding LinkedOrderNo}" LineBreakMode="CharacterWrap"/>
<!-- 发货单备注 -->
<Label Grid.Row="3" Grid.Column="0" Text="发货单备注:" FontAttributes="Bold"/>
<Label Grid.Row="3" Grid.Column="1" Text="{Binding Remark}" LineBreakMode="WordWrap"/>
</Grid>
</Frame>
<!-- Tab + 列表 -->
<Grid Grid.Row="3" RowDefinitions="Auto,Auto,*">
<!-- Tab -->
<Grid ColumnDefinitions="*,*" BackgroundColor="White">
<Button Text="出库单明细"
BackgroundColor="{Binding PendingTabColor}"
TextColor="{Binding PendingTextColor}"
Clicked="OnPendingTabClicked"/>
<Button Text="扫描明细"
Grid.Column="1"
BackgroundColor="{Binding ScannedTabColor}"
TextColor="{Binding ScannedTextColor}"
Clicked="OnScannedTabClicked"/>
</Grid>
<!-- 出库单明细表头 -->
<Grid Grid.Row="1"
ColumnDefinitions="2*,2*,1.5*,1.5*,1.5*,1.5*,1.5*"
BackgroundColor="#F2F2F2"
IsVisible="{Binding IsPendingVisible}"
Padding="4">
<Label Text="产品名称" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="1" Text="产品编码" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="2" Text="规格" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="3" Text="出库库位" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="4" Text="批次号" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="5" Text="出库数量" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="6" Text="已扫描数" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
</Grid>
<!-- 出库单明细列表 -->
<CollectionView Grid.Row="2"
ItemsSource="{Binding PendingList}"
IsVisible="{Binding IsPendingVisible}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="2*,2*,1.5*,1.5*,1.5*,1.5*,1.5*" Padding="4" BackgroundColor="White">
<Label Text="{Binding Name}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="1" Text="{Binding Code}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="2" Text="{Binding Spec}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="3" Text="{Binding Bin}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="4" Text="{Binding BatchNo}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="5" Text="{Binding OutQty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="6" Text="{Binding ScannedQty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- 扫描明细表头 -->
<Grid Grid.Row="1"
ColumnDefinitions="40,*,*,*"
BackgroundColor="#F2F2F2"
IsVisible="{Binding IsScannedVisible}"
Padding="4">
<Label Text="选择" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="1" Text="条码" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="2" Text="物料名称" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="3" Text="数量" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
</Grid>
<!-- 扫描明细列表 -->
<CollectionView Grid.Row="2"
ItemsSource="{Binding ScannedList}"
IsVisible="{Binding IsScannedVisible}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="40,*,*,*" Padding="4" BackgroundColor="White">
<CheckBox IsChecked="{Binding IsSelected}" HorizontalOptions="Center" VerticalOptions="Center"/>
<Label Grid.Column="1" Text="{Binding Barcode}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="2" Text="{Binding Name}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
<Label Grid.Column="3" Text="{Binding Qty}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
<!-- 底部按钮 -->
<Grid Grid.Row="4" ColumnDefinitions="*,*,*" Padding="16,8" ColumnSpacing="10">
<Button Text="扫描通过" BackgroundColor="#4CAF50" TextColor="White" Clicked="OnPassScanClicked"/>
<Button Grid.Column="1" Text="取消扫描" BackgroundColor="#F44336" TextColor="White" Clicked="OnCancelScanClicked"/>
<Button Grid.Column="2" Text="确认出库" BackgroundColor="#2196F3" TextColor="White" Clicked="OnConfirmClicked"/>
</Grid>
</Grid>
</ContentPage>
... ...
using IndustrialControl.Services;
using IndustrialControl.ViewModels;
namespace IndustrialControl.Pages
{
public partial class OutboundMaterialPage : ContentPage
{
private readonly ScanService _scanSvc;
private readonly OutboundMaterialViewModel _vm;
public OutboundMaterialPage(OutboundMaterialViewModel vm, ScanService scanSvc)
{
InitializeComponent();
BindingContext = vm;
_scanSvc = scanSvc;
_vm = vm;
}
protected override void OnAppearing()
{
base.OnAppearing();
_scanSvc.Attach(ScanEntry);
ScanEntry.Focus();
}
// Tab切换
void OnPendingTabClicked(object sender, EventArgs e)
{
_vm.IsPendingVisible = true;
_vm.IsScannedVisible = false;
}
void OnScannedTabClicked(object sender, EventArgs e)
{
_vm.IsPendingVisible = false;
_vm.IsScannedVisible = true;
}
// 清空扫描记录
void OnClearClicked(object sender, EventArgs e)
{
_vm.ClearScan();
ScanEntry.Text = string.Empty;
ScanEntry.Focus();
}
// 摄像头扫码按钮
async void OnScanClicked(object sender, EventArgs e)
{
await DisplayAlert("提示", "此按钮预留摄像头扫码;硬件扫描直接扣扳机。", "确定");
}
// 扫描通过
void OnPassScanClicked(object sender, EventArgs e)
{
_vm.PassSelectedScan();
}
// 取消扫描
void OnCancelScanClicked(object sender, EventArgs e)
{
_vm.CancelSelectedScan();
}
// 确认出库
async void OnConfirmClicked(object sender, EventArgs e)
{
var ok = await _vm.ConfirmOutboundAsync();
if (ok)
{
await DisplayAlert("提示", "出库成功", "确定");
_vm.ClearAll();
}
else
{
await DisplayAlert("提示", "出库失败,请检查数据", "确定");
}
}
}
}
... ...
using System;
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
namespace IndustrialControl
{
internal class Program : MauiApplication
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
static void Main(string[] args)
{
var app = new Program();
app.Run(args);
}
}
}
... ...
<?xml version="1.0" encoding="utf-8"?>
<manifest package="maui-application-id-placeholder" version="0.0.0" api-version="8" xmlns="http://tizen.org/ns/packages">
<profile name="common" />
<ui-application appid="maui-application-id-placeholder" exec="IndustrialControl.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
<label>maui-application-title-placeholder</label>
<icon>maui-appicon-placeholder</icon>
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
</ui-application>
<shortcut-list />
<privileges>
<privilege>http://tizen.org/privilege/internet</privilege>
</privileges>
<dependencies />
<provides-appdefined-privileges />
</manifest>
\ No newline at end of file
... ...
<maui:MauiWinUIApplication
x:Class="IndustrialControl.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:maui="using:Microsoft.Maui"
xmlns:local="using:IndustrialControl.WinUI">
</maui:MauiWinUIApplication>
... ...
using Microsoft.UI.Xaml;
// 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
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : MauiWinUIApplication
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}
... ...
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
<mp:PhoneIdentity PhoneProductId="51796DCC-E64D-4816-833F-CF9CE3D4BEEA" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>$placeholder$</DisplayName>
<PublisherDisplayName>User Name</PublisherDisplayName>
<Logo>$placeholder$.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="$placeholder$"
Description="$placeholder$"
Square150x150Logo="$placeholder$.png"
Square44x44Logo="$placeholder$.png"
BackgroundColor="transparent">
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
<uap:SplashScreen Image="$placeholder$.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>
... ...
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="IndustrialControl.WinUI.app"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>
... ...
{
"profiles": {
"Windows Machine": {
"commandName": "MsixPackage",
"nativeDebugging": false
}
}
}
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
</svg>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>
\ No newline at end of file
... ...
不能预览此文件类型
不能预览此文件类型
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories). Deployment of the asset to your application
is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
These files will be deployed with your package and will be accessible using Essentials:
async Task LoadMauiAsset()
{
using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
using var reader = new StreamReader(stream);
var contents = reader.ReadToEnd();
}
... ...
{
"schemaVersion": 3,
"server": {
"ipAddress": "192.168.1.100",
"port": 8080
},
"apiEndpoints": {
"login": "/sso/login"
},
"logging": {
"level": "Information"
}
}
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<!-- Note: For Android please see also Platforms\Android\Resources\values\colors.xml -->
<Color x:Key="Primary">#512BD4</Color>
<Color x:Key="PrimaryDark">#ac99ea</Color>
<Color x:Key="PrimaryDarkText">#242424</Color>
<Color x:Key="Secondary">#DFD8F7</Color>
<Color x:Key="SecondaryDarkText">#9880e5</Color>
<Color x:Key="Tertiary">#2B0B98</Color>
<Color x:Key="White">White</Color>
<Color x:Key="Black">Black</Color>
<Color x:Key="Magenta">#D600AA</Color>
<Color x:Key="MidnightBlue">#190649</Color>
<Color x:Key="OffBlack">#1f1f1f</Color>
<Color x:Key="Gray100">#E1E1E1</Color>
<Color x:Key="Gray200">#C8C8C8</Color>
<Color x:Key="Gray300">#ACACAC</Color>
<Color x:Key="Gray400">#919191</Color>
<Color x:Key="Gray500">#6E6E6E</Color>
<Color x:Key="Gray600">#404040</Color>
<Color x:Key="Gray900">#212121</Color>
<Color x:Key="Gray950">#141414</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}"/>
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}"/>
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}"/>
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}"/>
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}"/>
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}"/>
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}"/>
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}"/>
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}"/>
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}"/>
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}"/>
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}"/>
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}"/>
</ResourceDictionary>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Style TargetType="ActivityIndicator">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="IndicatorView">
<Setter Property="IndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}"/>
<Setter Property="SelectedIndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}"/>
</Style>
<Style TargetType="Border">
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="StrokeShape" Value="Rectangle"/>
<Setter Property="StrokeThickness" Value="1"/>
</Style>
<Style TargetType="BoxView">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Button">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource PrimaryDarkText}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="14,10"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="DatePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Editor">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Entry">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Frame">
<Setter Property="HasShadow" Value="False" />
<Setter Property="BorderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="ImageButton">
<Setter Property="Opacity" Value="1" />
<Setter Property="BorderColor" Value="Transparent"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Opacity" Value="0.5" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Span">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="Label" x:Key="Headline">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
<Setter Property="FontSize" Value="32" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
<Style TargetType="Label" x:Key="SubHeadline">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
<Setter Property="FontSize" Value="24" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
<Style TargetType="ListView">
<Setter Property="SeparatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="RefreshControlColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Picker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RadioButton">
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RefreshView">
<Setter Property="RefreshColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="SearchBar">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="CancelButtonColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SearchHandler">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Shadow">
<Setter Property="Radius" Value="15" />
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Brush" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Offset" Value="10,10" />
</Style>
<Style TargetType="Slider">
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SwipeItem">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Switch">
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="ThumbColor" Value="{StaticResource White}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="TimePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Page" ApplyToDerivedTypes="True">
<Setter Property="Padding" Value="0"/>
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
</Style>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
<Setter Property="Shell.ForegroundColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
<Setter Property="Shell.TitleColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
<Setter Property="Shell.DisabledColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.UnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
<Setter Property="Shell.NavBarHasShadow" Value="False" />
<Setter Property="Shell.TabBarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="Shell.TabBarForegroundColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarTitleColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarUnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
<Setter Property="IconColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="TabbedPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
<Setter Property="UnselectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="SelectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
</ResourceDictionary>
... ...
using System.Text.Json;
using IndustrialControl.Models;
namespace IndustrialControl.Services;
public class ConfigLoader
{
public const string FileName = "appconfig.json";
private readonly string _configPath = Path.Combine(FileSystem.AppDataDirectory, FileName);
private readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
public event Action? ConfigChanged;
public AppConfig Current { get; private set; } = new AppConfig();
public string BaseUrl => $"http://{Current.Server.IpAddress}:{Current.Server.Port}";
public ConfigLoader()
{
Task.Run(EnsureConfigIsLatestAsync).Wait();
}
public async Task EnsureConfigIsLatestAsync()
{
using var pkgStream = await FileSystem.OpenAppPackageFileAsync(FileName);
using var pkgReader = new StreamReader(pkgStream);
var pkgJson = await pkgReader.ReadToEndAsync();
var pkgCfg = JsonSerializer.Deserialize<AppConfig>(pkgJson, _jsonOptions) ?? new AppConfig();
if (!File.Exists(_configPath))
{
Directory.CreateDirectory(Path.GetDirectoryName(_configPath)!);
await File.WriteAllTextAsync(_configPath, pkgJson);
Current = pkgCfg;
return;
}
var currJson = await File.ReadAllTextAsync(_configPath);
var currCfg = JsonSerializer.Deserialize<AppConfig>(currJson, _jsonOptions) ?? new AppConfig();
if (currCfg.SchemaVersion < pkgCfg.SchemaVersion)
{
pkgCfg.Server = currCfg.Server;
Current = pkgCfg;
await SaveAsync(Current, fireChanged: false);
}
else
{
currCfg.ApiEndpoints ??= pkgCfg.ApiEndpoints;
currCfg.Logging ??= pkgCfg.Logging;
currCfg.Server ??= currCfg.Server ?? new ServerSettings();
Current = currCfg;
}
}
public async Task SaveAsync(AppConfig cfg, bool fireChanged = true)
{
var json = JsonSerializer.Serialize(cfg, _jsonOptions);
await File.WriteAllTextAsync(_configPath, json);
Current = cfg;
if (fireChanged) ConfigChanged?.Invoke();
}
public async Task<AppConfig> ReloadAsync()
{
var json = await File.ReadAllTextAsync(_configPath);
Current = JsonSerializer.Deserialize<AppConfig>(json, _jsonOptions) ?? new AppConfig();
ConfigChanged?.Invoke();
return Current;
}
public string GetConfigPath() => _configPath;
}
... ...
namespace IndustrialControl.Services;
public class LogService : IDisposable
{
private readonly string _logsDir = Path.Combine(FileSystem.AppDataDirectory, "logs");
private readonly TimeSpan _interval = TimeSpan.FromMilliseconds(800);
private CancellationTokenSource? _cts;
public event Action<string>? LogTextUpdated;
public string TodayLogPath => Path.Combine(_logsDir, $"gr-{DateTime.Now:yyyy-MM-dd}.txt");
public void Start()
{
Stop();
_cts = new CancellationTokenSource();
_ = Task.Run(() => LoopAsync(_cts.Token));
}
public void Stop()
{
_cts?.Cancel();
_cts?.Dispose();
_cts = null;
}
private async Task LoopAsync(CancellationToken token)
{
Directory.CreateDirectory(_logsDir);
string last = string.Empty;
while (!token.IsCancellationRequested)
{
try
{
if (File.Exists(TodayLogPath))
{
var text = await File.ReadAllTextAsync(TodayLogPath, token);
if (!string.Equals(text, last, StringComparison.Ordinal))
{
last = text;
LogTextUpdated?.Invoke(text);
}
}
}
catch { }
await Task.Delay(_interval, token);
}
}
public void Dispose() => Stop();
}
... ...
namespace IndustrialControl.Services;
public interface IWarehouseDataService
{
Task<InboundOrder> GetInboundOrderAsync(string orderNo);
Task<SimpleOk> ConfirmInboundAsync(string orderNo, IEnumerable<ScanItem> items);
Task<SimpleOk> ConfirmInboundProductionAsync(string orderNo, IEnumerable<ScanItem> items);
Task<SimpleOk> ConfirmOutboundMaterialAsync(string orderNo, IEnumerable<ScanItem> items);
Task<SimpleOk> ConfirmOutboundFinishedAsync(string orderNo, IEnumerable<ScanItem> items);
}
public record InboundOrder(string OrderNo, string Supplier, string LinkedNo, int ExpectedQty);
public record ScanItem(int Index, string Barcode, string? Bin, int Qty);
public record SimpleOk(bool Succeeded, string? Message = null);
public class MockWarehouseDataService : IWarehouseDataService
{
private readonly Random _rand = new();
public Task<InboundOrder> GetInboundOrderAsync(string orderNo)
=> Task.FromResult(new InboundOrder(orderNo, "供应商/客户:XXXX", "关联单:CGD2736273", _rand.Next(10, 80)));
public Task<SimpleOk> ConfirmInboundAsync(string orderNo, IEnumerable<ScanItem> items)
=> Task.FromResult(new SimpleOk(true, $"入库成功:{items.Count()} 条"));
public Task<SimpleOk> ConfirmInboundProductionAsync(string orderNo, IEnumerable<ScanItem> items)
=> Task.FromResult(new SimpleOk(true, $"生产入库成功:{items.Count()} 条"));
public Task<SimpleOk> ConfirmOutboundMaterialAsync(string orderNo, IEnumerable<ScanItem> items)
=> Task.FromResult(new SimpleOk(true, $"物料出库成功:{items.Count()} 条"));
public Task<SimpleOk> ConfirmOutboundFinishedAsync(string orderNo, IEnumerable<ScanItem> items)
=> Task.FromResult(new SimpleOk(true, $"成品出库成功:{items.Count()} 条"));
}
... ...
using CommunityToolkit.Mvvm.Messaging;
namespace IndustrialControl.Services;
public record ScanMessage(string Data);
public class ScanService
{
public void Publish(string data)
{
if (string.IsNullOrWhiteSpace(data)) return;
MainThread.BeginInvokeOnMainThread(() =>
WeakReferenceMessenger.Default.Send(new ScanMessage(data.Trim())));
}
public void Attach(Entry entry)
{
entry.Completed += (s, e) => Publish(((Entry)s!).Text ?? string.Empty);
}
}
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using IndustrialControl.Models;
using IndustrialControl.Services;
namespace IndustrialControl.ViewModels;
public partial class AdminViewModel : ObservableObject
{
private readonly ConfigLoader _cfg;
[ObservableProperty] private int schemaVersion;
[ObservableProperty] private string ipAddress = "";
[ObservableProperty] private int port;
[ObservableProperty] private string baseUrl = "";
public AdminViewModel(ConfigLoader cfg)
{
_cfg = cfg;
LoadFromConfig();
_cfg.ConfigChanged += () => LoadFromConfig();
}
private void LoadFromConfig()
{
var c = _cfg.Current;
SchemaVersion = c.SchemaVersion;
IpAddress = c.Server.IpAddress;
Port = c.Server.Port;
BaseUrl = _cfg.BaseUrl;
}
[RelayCommand]
public async Task SaveAsync()
{
var c = _cfg.Current;
c.Server.IpAddress = IpAddress.Trim();
c.Server.Port = Port;
await _cfg.SaveAsync(c);
BaseUrl = _cfg.BaseUrl;
await Shell.Current.DisplayAlert("已保存", "配置已保存,可立即生效。", "确定");
}
[RelayCommand]
public async Task ResetToPackageAsync()
{
await _cfg.EnsureConfigIsLatestAsync();
await _cfg.ReloadAsync();
await Shell.Current.DisplayAlert("已重载", "已从包内默认配置重载/合并。", "确定");
}
}
... ...
namespace IndustrialControl.ViewModels; public partial class HomeViewModel { }
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using IndustrialControl.Services;
using System.Threading.Tasks;
namespace IndustrialControl.ViewModels
{
public partial class InboundMaterialViewModel : ObservableObject
{
private readonly IWarehouseDataService _warehouseSvc;
public ObservableCollection<string> AvailableBins { get; set; } = new ObservableCollection<string>
{
"CK1_A201",
"CK1_A202",
"CK1_A203",
"CK1_A204"
};
public InboundMaterialViewModel(IWarehouseDataService warehouseSvc)
{
_warehouseSvc = warehouseSvc;
// 初始化命令
ShowPendingCommand = new RelayCommand(() => SwitchTab(true));
ShowScannedCommand = new RelayCommand(() => SwitchTab(false));
ConfirmCommand = new AsyncRelayCommand(ConfirmInboundAsync);
// 默认显示待入库明细
IsPendingVisible = true;
IsScannedVisible = false;
// 测试数据(上线可去掉)
PendingList = new ObservableCollection<PendingItem>
{
new PendingItem { Name="物料1", Spec="XXX", PendingQty=100, Bin="CK1_A201", ScannedQty=80 },
new PendingItem { Name="物料2", Spec="YYY", PendingQty=50, Bin="CK1_A202", ScannedQty=0 }
};
ScannedList = new ObservableCollection<OutScannedItem>
{
new OutScannedItem { IsSelected=false, Barcode="FSC2025060300001", Name="物料1", Spec="XXX", Bin="CK1_A201", Qty=1 },
new OutScannedItem { IsSelected=false, Barcode="FSC2025060300002", Name="物料1", Spec="XXX", Bin="请选择", Qty=1 }
};
}
// 基础信息
[ObservableProperty] private string orderNo;
[ObservableProperty] private string linkedDeliveryNo;
[ObservableProperty] private string linkedPurchaseNo;
[ObservableProperty] private string supplier;
// Tab 显示控制
[ObservableProperty] private bool isPendingVisible;
[ObservableProperty] private bool isScannedVisible;
// Tab 按钮颜色
[ObservableProperty] private string pendingTabColor = "#E6F2FF";
[ObservableProperty] private string scannedTabColor = "White";
[ObservableProperty] private string pendingTextColor = "#007BFF";
[ObservableProperty] private string scannedTextColor = "#333333";
// 列表数据
public ObservableCollection<PendingItem> PendingList { get; set; }
public ObservableCollection<OutScannedItem> ScannedList { get; set; }
// 命令
public IRelayCommand ShowPendingCommand { get; }
public IRelayCommand ShowScannedCommand { get; }
public IAsyncRelayCommand ConfirmCommand { get; }
/// <summary>
/// 切换 Tab
/// </summary>
private void SwitchTab(bool showPending)
{
IsPendingVisible = showPending;
IsScannedVisible = !showPending;
if (showPending)
{
PendingTabColor = "#E6F2FF";
ScannedTabColor = "White";
PendingTextColor = "#007BFF";
ScannedTextColor = "#333333";
}
else
{
PendingTabColor = "White";
ScannedTabColor = "#E6F2FF";
PendingTextColor = "#333333";
ScannedTextColor = "#007BFF";
}
}
/// <summary>
/// 清空扫描记录
/// </summary>
public void ClearScan()
{
ScannedList.Clear();
}
/// <summary>
/// 清空所有数据
/// </summary>
public void ClearAll()
{
OrderNo = string.Empty;
LinkedDeliveryNo = string.Empty;
LinkedPurchaseNo = string.Empty;
Supplier = string.Empty;
PendingList.Clear();
ScannedList.Clear();
}
/// <summary>
/// 扫描通过逻辑
/// </summary>
public void PassSelectedScan()
{
foreach (var item in ScannedList)
{
if (item.IsSelected)
{
// 这里可以做业务处理,比如更新状态
}
}
}
/// <summary>
/// 取消扫描逻辑
/// </summary>
public void CancelSelectedScan()
{
for (int i = ScannedList.Count - 1; i >= 0; i--)
{
if (ScannedList[i].IsSelected)
{
ScannedList.RemoveAt(i);
}
}
}
/// <summary>
/// 确认入库逻辑
/// </summary>
public async Task<bool> ConfirmInboundAsync()
{
if (string.IsNullOrWhiteSpace(OrderNo))
return false;
var result = await _warehouseSvc.ConfirmInboundAsync(OrderNo,
ScannedList.Select(x => new ScanItem(0, x.Barcode, x.Bin, x.Qty)));
return result.Succeeded;
}
}
// 待入库明细模型
public class PendingItem
{
public string Name { get; set; }
public string Spec { get; set; }
public int PendingQty { get; set; }
public string Bin { get; set; }
public int ScannedQty { get; set; }
}
// 扫描明细模型
public class OutScannedItem
{
public bool IsSelected { get; set; }
public string Barcode { get; set; }
public string Name { get; set; }
public string Spec { get; set; }
public string Bin { get; set; }
public int Qty { get; set; }
}
}
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using IndustrialControl.Services;
using System.Threading.Tasks;
namespace IndustrialControl.ViewModels
{
public partial class InboundProductionViewModel : ObservableObject
{
private readonly IWarehouseDataService _warehouseSvc;
public InboundProductionViewModel(IWarehouseDataService warehouseSvc)
{
_warehouseSvc = warehouseSvc;
ConfirmCommand = new AsyncRelayCommand(ConfirmInboundAsync);
// 测试数据
Lines = new ObservableCollection<ProductionScanItem>
{
new ProductionScanItem { IsSelected = false, Barcode="FSC2025060300001", Bin="CK1_A201", Qty=1 },
new ProductionScanItem { IsSelected = false, Barcode="FSC2025060300002", Bin="CK1_A201", Qty=1 }
};
AvailableBins = new ObservableCollection<string>
{
"CK1_A201", "CK1_A202", "CK1_A203"
};
}
// 基础信息
[ObservableProperty] private string orderNo;
[ObservableProperty] private string workOrderNo;
[ObservableProperty] private string productName;
[ObservableProperty] private int pendingQty;
public ObservableCollection<ProductionScanItem> Lines { get; set; }
public ObservableCollection<string> AvailableBins { get; set; }
public IAsyncRelayCommand ConfirmCommand { get; }
public void ClearScan()
{
Lines.Clear();
}
public void ClearAll()
{
OrderNo = string.Empty;
WorkOrderNo = string.Empty;
ProductName = string.Empty;
PendingQty = 0;
Lines.Clear();
}
public void PassSelectedScan()
{
foreach (var item in Lines)
{
if (item.IsSelected)
{
// 扫描通过处理
}
}
}
public void CancelSelectedScan()
{
for (int i = Lines.Count - 1; i >= 0; i--)
{
if (Lines[i].IsSelected)
Lines.RemoveAt(i);
}
}
public async Task<bool> ConfirmInboundAsync()
{
if (string.IsNullOrWhiteSpace(OrderNo))
return false;
var result = await _warehouseSvc.ConfirmInboundProductionAsync(OrderNo,
Lines.Select(x => new ScanItem(0, x.Barcode, x.Bin, x.Qty)));
return result.Succeeded;
}
}
public class ProductionScanItem
{
public bool IsSelected { get; set; }
public string Barcode { get; set; }
public string Bin { get; set; }
public int Qty { get; set; }
}
}
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace IndustrialControl.ViewModels;
public partial class LoginViewModel : ObservableObject
{
[ObservableProperty] private string userName = string.Empty;
[ObservableProperty] private string password = string.Empty;
[ObservableProperty] private bool isBusy;
[RelayCommand]
public async Task LoginAsync()
{
if (IsBusy) return; IsBusy = true;
try
{
await Task.Delay(200); // mock
await Shell.Current.GoToAsync("//Home");
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("登录失败", ex.Message, "确定");
}
finally { IsBusy = false; }
}
}
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using IndustrialControl.Services;
namespace IndustrialControl.ViewModels;
public partial class LogsViewModel : ObservableObject
{
private readonly LogService _logSvc;
[ObservableProperty] private string logText = "日志初始化中...";
public string TodayPath => _logSvc.TodayLogPath;
public LogsViewModel(LogService logSvc)
{
_logSvc = logSvc;
_logSvc.LogTextUpdated += text => LogText = text;
}
public void OnAppearing() => _logSvc.Start();
public void OnDisappearing() => _logSvc.Stop();
}
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace IndustrialControl.ViewModels
{
public partial class OutboundFinishedViewModel : ObservableObject
{
#region 基础信息
[ObservableProperty] private string orderNo;
[ObservableProperty] private string deliveryNo;
[ObservableProperty] private string deliveryTime;
[ObservableProperty] private string remark;
#endregion
#region Tab 状态
[ObservableProperty] private bool isPendingVisible = true;
[ObservableProperty] private bool isScannedVisible = false;
[ObservableProperty] private string pendingTabColor = "#2196F3";
[ObservableProperty] private string pendingTextColor = "White";
[ObservableProperty] private string scannedTabColor = "White";
[ObservableProperty] private string scannedTextColor = "Black";
#endregion
#region 数据集合
public ObservableCollection<OutPendingItem> PendingList { get; } = new();
public ObservableCollection<OutboundScannedItem> ScannedList { get; } = new();
#endregion
public OutboundFinishedViewModel()
{
// 测试数据
OrderNo = "CK20250001";
DeliveryNo = "FH20250088";
DeliveryTime = "2025-08-15";
Remark = "优先出库";
PendingList.Add(new OutPendingItem { Name = "成品A", Code = "CP001", Spec = "规格1", Bin = "A01", BatchNo = "B20250815", OutQty = 10 });
PendingList.Add(new OutPendingItem { Name = "成品B", Code = "CP002", Spec = "规格2", Bin = "A02", BatchNo = "B20250815", OutQty = 5 });
ScannedList.Add(new OutboundScannedItem { Barcode = "BC0001", Name = "成品A", Qty = 2 });
ScannedList.Add(new OutboundScannedItem { Barcode = "BC0002", Name = "成品B", Qty = 1 });
}
#region Tab 切换命令
[RelayCommand]
private void ShowPending()
{
IsPendingVisible = true;
IsScannedVisible = false;
PendingTabColor = "#2196F3";
PendingTextColor = "White";
ScannedTabColor = "White";
ScannedTextColor = "Black";
}
[RelayCommand]
private void ShowScanned()
{
IsPendingVisible = false;
IsScannedVisible = true;
PendingTabColor = "White";
PendingTextColor = "Black";
ScannedTabColor = "#2196F3";
ScannedTextColor = "White";
}
#endregion
#region 底部按钮命令
[RelayCommand]
private void PassScan()
{
var selected = ScannedList.Where(s => s.IsSelected).ToList();
if (!selected.Any()) return;
foreach (var item in selected)
{
// 模拟通过逻辑
item.IsSelected = false;
}
}
[RelayCommand]
private void CancelScan()
{
var selected = ScannedList.Where(s => s.IsSelected).ToList();
if (!selected.Any()) return;
foreach (var item in selected)
{
ScannedList.Remove(item);
}
}
[RelayCommand]
private async Task Confirm()
{
// 模拟提交
await Task.Delay(300);
// 这里你可以加出库成功提示逻辑
ClearAll();
}
#endregion
#region 其他方法
public void ClearAll()
{
PendingList.Clear();
ScannedList.Clear();
OrderNo = string.Empty;
DeliveryNo = string.Empty;
DeliveryTime = string.Empty;
Remark = string.Empty;
}
public void ClearScan()
{
ScannedList.Clear();
}
#endregion
}
public class OutPendingItem
{
public string Name { get; set; }
public string Code { get; set; }
public string Spec { get; set; }
public string Bin { get; set; }
public string BatchNo { get; set; }
public int OutQty { get; set; }
}
public class OutboundScannedItem
{
public bool IsSelected { get; set; }
public string Barcode { get; set; }
public string Name { get; set; }
public int Qty { get; set; }
}
}
... ...
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Linq;
using System.Windows.Input;
using Microsoft.Maui.Graphics;
namespace IndustrialControl.ViewModels
{
public partial class OutboundMaterialViewModel : ObservableObject
{
// ===== 基础信息 =====
[ObservableProperty] private string orderNo;
[ObservableProperty] private string deliveryNo;
[ObservableProperty] private string customerName;
[ObservableProperty] private string deliveryTime;
[ObservableProperty] private string linkedOrderNo;
[ObservableProperty] private string remark;
// ===== Tab 切换状态 =====
[ObservableProperty] private bool isPendingVisible = true;
[ObservableProperty] private bool isScannedVisible = false;
[ObservableProperty] private Color pendingTabColor = Colors.LightBlue;
[ObservableProperty] private Color pendingTextColor = Colors.White;
[ObservableProperty] private Color scannedTabColor = Colors.White;
[ObservableProperty] private Color scannedTextColor = Colors.Black;
// ===== 列表数据 =====
public ObservableCollection<OutboundPendingItem> PendingList { get; set; } = new();
public ObservableCollection<ScannedItem> ScannedList { get; set; } = new();
// ===== 扫码输入 =====
[ObservableProperty] private string scanCode;
public OutboundMaterialViewModel()
{
LoadSampleData();
}
private void LoadSampleData()
{
// 基础信息示例
OrderNo = "XXXXX";
DeliveryNo = "SCGD20250601002";
CustomerName = "CGD2736273";
DeliveryTime = "2025-08-15";
LinkedOrderNo = "CGD2736273";
Remark = "优先出库";
// 出库单明细
PendingList.Add(new OutboundPendingItem { Name = "产品1", Code = "XXX", Spec = "A101", Bin = "A101", BatchNo = "B20250815", OutQty = 10, ScannedQty = 10 });
PendingList.Add(new OutboundPendingItem { Name = "产品2", Code = "YYY", Spec = "A102", Bin = "A102", BatchNo = "B20250815", OutQty = 5, ScannedQty = 2 });
PendingList.Add(new OutboundPendingItem { Name = "产品3", Code = "ZZZ", Spec = "A103", Bin = "A103", BatchNo = "B20250815", OutQty = 8, ScannedQty = 0 });
// 扫描明细
ScannedList.Add(new ScannedItem { Barcode = "FSC2025060300001", Name = "XXXX", Qty = 1, IsSelected = true });
ScannedList.Add(new ScannedItem { Barcode = "FSC2025060300002", Name = "XXXX", Qty = 1, IsSelected = true });
ScannedList.Add(new ScannedItem { Barcode = "FSC2025060300003", Name = "XXXX", Qty = 1, IsSelected = false });
ScannedList.Add(new ScannedItem { Barcode = "FSC2025060300004", Name = "XXXX", Qty = 1, IsSelected = false });
}
// ===== 操作方法 =====
public void ClearScan()
{
ScanCode = string.Empty;
}
public void ClearAll()
{
PendingList.Clear();
ScannedList.Clear();
ScanCode = string.Empty;
}
public void PassSelectedScan()
{
foreach (var item in ScannedList.Where(s => s.IsSelected))
{
// 逻辑示例:标记为已通过(这里可加业务处理)
}
}
public void CancelSelectedScan()
{
var toRemove = ScannedList.Where(s => s.IsSelected).ToList();
foreach (var item in toRemove)
ScannedList.Remove(item);
}
public async Task<bool> ConfirmOutboundAsync()
{
// 模拟出库确认逻辑
await Task.Delay(500); // 模拟网络延迟
return true;
}
}
// 出库单明细数据模型
public class OutboundPendingItem
{
public string Name { get; set; }
public string Code { get; set; }
public string Spec { get; set; }
public string Bin { get; set; }
public string BatchNo { get; set; }
public int OutQty { get; set; }
public int ScannedQty { get; set; }
}
// 扫描明细数据模型
public class ScannedItem
{
public bool IsSelected { get; set; }
public string Barcode { get; set; }
public string Name { get; set; }
public int Qty { get; set; }
}
}
... ...