feat: 完善登录验证码、订单同步和打包功能
- 添加验证码登录支持(图形验证码显示和输入) - 修复订单同步,正确解析收件人信息(从logisticses合并) - 修复API端点配置(user.api.it120.cc) - 添加Costura.Fody实现单文件EXE打包 - 添加应用图标(app.ico) - 添加Inno Setup安装脚本(支持Win7+) - 暂时禁用导入发货功能 - 添加.gitignore文件 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
75
.gitignore
vendored
Normal file
75
.gitignore
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Rr]elease/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio
|
||||||
|
.vs/
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# NuGet
|
||||||
|
*.nupkg
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
publish/
|
||||||
|
installer/
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.pdb
|
||||||
|
*.cache
|
||||||
|
|
||||||
|
# Installer files
|
||||||
|
*.7z
|
||||||
|
*.zip
|
||||||
|
*.msi
|
||||||
|
|
||||||
|
# Test results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
*.trx
|
||||||
|
|
||||||
|
# Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Temp files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
*.log
|
||||||
|
*~
|
||||||
|
nul
|
||||||
|
|
||||||
|
# Local data
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
|
||||||
|
# Config with secrets (keep App.config template)
|
||||||
|
# App.config
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# Backup files
|
||||||
|
*.bak
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# API test files
|
||||||
|
*_response.json
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
</startup>
|
</startup>
|
||||||
<appSettings>
|
<appSettings>
|
||||||
<!-- API工厂配置 -->
|
<!-- API工厂配置 -->
|
||||||
<add key="ApiBaseUrl" value="https://user.api.it120.cc" />
|
<add key="ApiBaseUrl" value="https://api.it120.cc" />
|
||||||
<add key="SubDomain" value="vv125s" />
|
<add key="SubDomain" value="let5see" />
|
||||||
|
|
||||||
<!-- 同步配置 -->
|
<!-- 同步配置 -->
|
||||||
<add key="SyncPageSize" value="50" />
|
<add key="SyncPageSize" value="50" />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using PackagingMallShipper.Helpers;
|
using PackagingMallShipper.Helpers;
|
||||||
|
|
||||||
@@ -19,13 +20,21 @@ namespace PackagingMallShipper.Data
|
|||||||
);
|
);
|
||||||
|
|
||||||
var dir = Path.GetDirectoryName(_dbPath);
|
var dir = Path.GetDirectoryName(_dbPath);
|
||||||
|
Debug.WriteLine($"[数据库初始化] 数据库目录: {dir}");
|
||||||
|
|
||||||
if (!Directory.Exists(dir))
|
if (!Directory.Exists(dir))
|
||||||
|
{
|
||||||
Directory.CreateDirectory(dir);
|
Directory.CreateDirectory(dir);
|
||||||
|
Debug.WriteLine($"[数据库初始化] 创建数据库目录");
|
||||||
|
}
|
||||||
|
|
||||||
_connectionString = $"Data Source={_dbPath};Version=3;";
|
_connectionString = $"Data Source={_dbPath};Version=3;";
|
||||||
|
Debug.WriteLine($"[数据库初始化] 数据库路径: {_dbPath}");
|
||||||
|
Debug.WriteLine($"[数据库初始化] 数据库文件存在: {File.Exists(_dbPath)}");
|
||||||
|
|
||||||
if (!File.Exists(_dbPath))
|
if (!File.Exists(_dbPath))
|
||||||
{
|
{
|
||||||
|
Debug.WriteLine($"[数据库初始化] 创建新数据库文件");
|
||||||
SQLiteConnection.CreateFile(_dbPath);
|
SQLiteConnection.CreateFile(_dbPath);
|
||||||
CreateTables();
|
CreateTables();
|
||||||
}
|
}
|
||||||
@@ -40,15 +49,146 @@ namespace PackagingMallShipper.Data
|
|||||||
|
|
||||||
private static void CreateTables()
|
private static void CreateTables()
|
||||||
{
|
{
|
||||||
using (var conn = GetConnection())
|
try
|
||||||
{
|
{
|
||||||
conn.Open();
|
Debug.WriteLine($"[数据库初始化] 开始创建数据库表");
|
||||||
var sql = ResourceHelper.GetEmbeddedResource("schema.sql");
|
using (var conn = GetConnection())
|
||||||
using (var cmd = new SQLiteCommand(sql, conn))
|
|
||||||
{
|
{
|
||||||
cmd.ExecuteNonQuery();
|
conn.Open();
|
||||||
|
Debug.WriteLine($"[数据库初始化] 数据库连接已打开");
|
||||||
|
|
||||||
|
string sql;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 尝试从嵌入资源读取
|
||||||
|
sql = ResourceHelper.GetEmbeddedResource("schema.sql");
|
||||||
|
Debug.WriteLine($"[数据库初始化] 从嵌入资源读取 schema.sql,长度: {sql?.Length ?? 0} 字符");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 如果嵌入资源读取失败,尝试从文件系统读取
|
||||||
|
Debug.WriteLine($"[数据库初始化] 嵌入资源读取失败: {ex.Message}");
|
||||||
|
var schemaPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data", "Resources", "schema.sql");
|
||||||
|
Debug.WriteLine($"[数据库初始化] 尝试从文件读取: {schemaPath}");
|
||||||
|
|
||||||
|
if (!File.Exists(schemaPath))
|
||||||
|
{
|
||||||
|
// 如果文件也不存在,使用硬编码的SQL
|
||||||
|
Debug.WriteLine($"[数据库初始化] schema.sql 文件不存在,使用硬编码SQL");
|
||||||
|
sql = GetHardcodedSchema();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sql = File.ReadAllText(schemaPath);
|
||||||
|
Debug.WriteLine($"[数据库初始化] 从文件读取成功,长度: {sql?.Length ?? 0} 字符");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var cmd = new SQLiteCommand(sql, conn))
|
||||||
|
{
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
Debug.WriteLine($"[数据库初始化] 数据库表创建成功");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"[数据库初始化] 创建表失败: {ex.Message}");
|
||||||
|
Debug.WriteLine($"[数据库初始化] StackTrace: {ex.StackTrace}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetHardcodedSchema()
|
||||||
|
{
|
||||||
|
return @"
|
||||||
|
-- 1. 本地用户会话
|
||||||
|
CREATE TABLE IF NOT EXISTS local_session (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
mobile TEXT NOT NULL,
|
||||||
|
token TEXT NOT NULL,
|
||||||
|
uid INTEGER,
|
||||||
|
nickname TEXT,
|
||||||
|
enterprise_id INTEGER,
|
||||||
|
last_login_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
token_expires_at DATETIME
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 2. 订单缓存表
|
||||||
|
CREATE TABLE IF NOT EXISTS orders_cache (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
order_number TEXT NOT NULL UNIQUE,
|
||||||
|
status INTEGER NOT NULL,
|
||||||
|
amount REAL,
|
||||||
|
amount_real REAL,
|
||||||
|
uid INTEGER,
|
||||||
|
user_mobile TEXT,
|
||||||
|
logistics_name TEXT,
|
||||||
|
logistics_mobile TEXT,
|
||||||
|
logistics_province TEXT,
|
||||||
|
logistics_city TEXT,
|
||||||
|
logistics_district TEXT,
|
||||||
|
logistics_address TEXT,
|
||||||
|
goods_json TEXT,
|
||||||
|
express_company_id INTEGER,
|
||||||
|
express_company_name TEXT,
|
||||||
|
tracking_number TEXT,
|
||||||
|
date_ship DATETIME,
|
||||||
|
sync_status TEXT DEFAULT 'synced',
|
||||||
|
local_updated_at DATETIME,
|
||||||
|
date_add DATETIME,
|
||||||
|
date_pay DATETIME,
|
||||||
|
date_update DATETIME,
|
||||||
|
synced_at DATETIME
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 3. 发货队列表
|
||||||
|
CREATE TABLE IF NOT EXISTS ship_queue (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
order_id INTEGER NOT NULL,
|
||||||
|
order_number TEXT NOT NULL,
|
||||||
|
express_company_id INTEGER NOT NULL,
|
||||||
|
tracking_number TEXT NOT NULL,
|
||||||
|
status TEXT DEFAULT 'pending',
|
||||||
|
retry_count INTEGER DEFAULT 0,
|
||||||
|
error_message TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 4. 同步日志表
|
||||||
|
CREATE TABLE IF NOT EXISTS sync_logs (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
sync_type TEXT NOT NULL,
|
||||||
|
sync_mode TEXT,
|
||||||
|
sync_start DATETIME NOT NULL,
|
||||||
|
sync_end DATETIME,
|
||||||
|
total_count INTEGER DEFAULT 0,
|
||||||
|
new_count INTEGER DEFAULT 0,
|
||||||
|
updated_count INTEGER DEFAULT 0,
|
||||||
|
failed_count INTEGER DEFAULT 0,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
error_message TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 5. 操作日志表
|
||||||
|
CREATE TABLE IF NOT EXISTS operation_logs (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
operation_type TEXT NOT NULL,
|
||||||
|
target_id INTEGER,
|
||||||
|
target_number TEXT,
|
||||||
|
details TEXT,
|
||||||
|
result TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 索引
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders_cache(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_orders_date_add ON orders_cache(date_add);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_orders_order_number ON orders_cache(order_number);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ship_queue_status ON ship_queue(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_sync_logs_status ON sync_logs(status);
|
||||||
|
";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteNonQuery(string sql, params SQLiteParameter[] parameters)
|
public static void ExecuteNonQuery(string sql, params SQLiteParameter[] parameters)
|
||||||
|
|||||||
11
PackagingMallShipper/FodyWeavers.xml
Normal file
11
PackagingMallShipper/FodyWeavers.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||||
|
<Costura>
|
||||||
|
<!-- 设置为true以包含调试符号 -->
|
||||||
|
<IncludeDebugSymbols>false</IncludeDebugSymbols>
|
||||||
|
<!-- 禁用压缩可提高启动速度 -->
|
||||||
|
<DisableCompression>false</DisableCompression>
|
||||||
|
<!-- 创建临时目录存放原生DLL -->
|
||||||
|
<CreateTemporaryAssemblies>true</CreateTemporaryAssemblies>
|
||||||
|
</Costura>
|
||||||
|
</Weavers>
|
||||||
@@ -25,6 +25,18 @@ namespace PackagingMallShipper.Models
|
|||||||
public int Uid { get; set; }
|
public int Uid { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AdminLoginData
|
||||||
|
{
|
||||||
|
[JsonProperty("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("token")]
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("nickname")]
|
||||||
|
public string Nickname { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class UserInfo
|
public class UserInfo
|
||||||
{
|
{
|
||||||
[JsonProperty("nick")]
|
[JsonProperty("nick")]
|
||||||
@@ -43,18 +55,122 @@ namespace PackagingMallShipper.Models
|
|||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
public UserInfo UserInfo { get; set; }
|
public UserInfo UserInfo { get; set; }
|
||||||
|
public bool RequireCaptcha { get; set; } // 是否需要验证码
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OrderListData
|
public class OrderListData
|
||||||
{
|
{
|
||||||
|
// API可能返回不同的字段名
|
||||||
[JsonProperty("orderList")]
|
[JsonProperty("orderList")]
|
||||||
public List<OrderDto> OrderList { get; set; }
|
public List<OrderDto> OrderList { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("result")]
|
||||||
|
public List<OrderDto> Result { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("list")]
|
||||||
|
public List<OrderDto> List { get; set; }
|
||||||
|
|
||||||
|
// 物流/收件人信息列表(与订单通过id关联)
|
||||||
|
[JsonProperty("logisticses")]
|
||||||
|
public List<LogisticsInfo> Logisticses { get; set; }
|
||||||
|
|
||||||
|
// 商品列表(与订单通过orderId关联)
|
||||||
|
[JsonProperty("goods")]
|
||||||
|
public List<GoodsItem> Goods { get; set; }
|
||||||
|
|
||||||
[JsonProperty("totalRow")]
|
[JsonProperty("totalRow")]
|
||||||
public int TotalRow { get; set; }
|
public int TotalRow { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("total")]
|
||||||
|
public int Total { get; set; }
|
||||||
|
|
||||||
[JsonProperty("totalPage")]
|
[JsonProperty("totalPage")]
|
||||||
public int TotalPage { get; set; }
|
public int TotalPage { get; set; }
|
||||||
|
|
||||||
|
// 辅助方法:获取订单列表(兼容多种返回格式)并合并物流信息
|
||||||
|
public List<OrderDto> GetOrders()
|
||||||
|
{
|
||||||
|
var orders = Result ?? OrderList ?? List ?? new List<OrderDto>();
|
||||||
|
|
||||||
|
// 合并物流信息到订单
|
||||||
|
if (Logisticses != null && Logisticses.Count > 0)
|
||||||
|
{
|
||||||
|
var logisticsMap = new Dictionary<int, LogisticsInfo>();
|
||||||
|
foreach (var log in Logisticses)
|
||||||
|
{
|
||||||
|
logisticsMap[log.Id] = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var order in orders)
|
||||||
|
{
|
||||||
|
if (logisticsMap.TryGetValue(order.Id, out var logistics))
|
||||||
|
{
|
||||||
|
order.LogisticsName = logistics.LinkMan;
|
||||||
|
order.LogisticsMobile = logistics.Mobile;
|
||||||
|
order.LogisticsProvince = logistics.ProvinceStr;
|
||||||
|
order.LogisticsCity = logistics.CityStr;
|
||||||
|
order.LogisticsDistrict = logistics.AreaStr;
|
||||||
|
order.LogisticsAddress = logistics.Address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并商品信息到订单
|
||||||
|
if (Goods != null && Goods.Count > 0)
|
||||||
|
{
|
||||||
|
var goodsMap = new Dictionary<int, List<GoodsItem>>();
|
||||||
|
foreach (var item in Goods)
|
||||||
|
{
|
||||||
|
if (!goodsMap.ContainsKey(item.OrderId))
|
||||||
|
goodsMap[item.OrderId] = new List<GoodsItem>();
|
||||||
|
goodsMap[item.OrderId].Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var order in orders)
|
||||||
|
{
|
||||||
|
if (goodsMap.TryGetValue(order.Id, out var items))
|
||||||
|
{
|
||||||
|
order.Goods = items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助方法:获取总记录数
|
||||||
|
public int GetTotalRow()
|
||||||
|
{
|
||||||
|
return TotalRow > 0 ? TotalRow : Total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 物流/收件人信息
|
||||||
|
public class LogisticsInfo
|
||||||
|
{
|
||||||
|
[JsonProperty("id")]
|
||||||
|
public int Id { get; set; } // 订单ID
|
||||||
|
|
||||||
|
[JsonProperty("linkMan")]
|
||||||
|
public string LinkMan { get; set; } // 收件人
|
||||||
|
|
||||||
|
[JsonProperty("mobile")]
|
||||||
|
public string Mobile { get; set; } // 收件人电话
|
||||||
|
|
||||||
|
[JsonProperty("provinceStr")]
|
||||||
|
public string ProvinceStr { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("cityStr")]
|
||||||
|
public string CityStr { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("areaStr")]
|
||||||
|
public string AreaStr { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("address")]
|
||||||
|
public string Address { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("streetStr")]
|
||||||
|
public string StreetStr { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OrderDto
|
public class OrderDto
|
||||||
|
|||||||
@@ -63,14 +63,29 @@ namespace PackagingMallShipper.Models
|
|||||||
|
|
||||||
public class GoodsItem
|
public class GoodsItem
|
||||||
{
|
{
|
||||||
|
[JsonProperty("orderId")]
|
||||||
|
public int OrderId { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("goodsId")]
|
||||||
|
public int GoodsId { get; set; }
|
||||||
|
|
||||||
[JsonProperty("goodsName")]
|
[JsonProperty("goodsName")]
|
||||||
public string GoodsName { get; set; }
|
public string GoodsName { get; set; }
|
||||||
|
|
||||||
[JsonProperty("number")]
|
[JsonProperty("number")]
|
||||||
public int Number { get; set; }
|
public int Number { get; set; }
|
||||||
|
|
||||||
[JsonProperty("price")]
|
[JsonProperty("amount")]
|
||||||
|
public decimal Amount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("amountSingle")]
|
||||||
public decimal Price { get; set; }
|
public decimal Price { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("pic")]
|
||||||
|
public string Pic { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("unit")]
|
||||||
|
public string Unit { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class OrderStatus
|
public static class OrderStatus
|
||||||
|
|||||||
@@ -1,139 +1,47 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}</ProjectGuid>
|
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
<RootNamespace>PackagingMallShipper</RootNamespace>
|
<RootNamespace>PackagingMallShipper</RootNamespace>
|
||||||
<AssemblyName>PackagingMallShipper</AssemblyName>
|
<AssemblyName>PackagingMallShipper</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
<UseWPF>true</UseWPF>
|
||||||
<FileAlignment>512</FileAlignment>
|
<UseWindowsForms>false</UseWindowsForms>
|
||||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<LangVersion>9.0</LangVersion>
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
|
||||||
<Deterministic>true</Deterministic>
|
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup>
|
|
||||||
<ApplicationIcon>Resources\Icons\app.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\Icons\app.ico</ApplicationIcon>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
|
||||||
|
<!-- 版本信息 -->
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<FileVersion>1.0.0.0</FileVersion>
|
||||||
|
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||||
|
<Company>PackagingMall</Company>
|
||||||
|
<Product>包装商城发货助手</Product>
|
||||||
|
<Copyright>Copyright © 2025</Copyright>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
<Reference Include="System.Xaml">
|
|
||||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="WindowsBase" />
|
|
||||||
<Reference Include="PresentationCore" />
|
|
||||||
<Reference Include="PresentationFramework" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ApplicationDefinition Include="App.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</ApplicationDefinition>
|
|
||||||
<Page Include="Views\MainWindow.xaml">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Include="Views\LoginWindow.xaml">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Include="Views\OrderListView.xaml">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Include="Views\ShippingDialog.xaml">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Include="Resources\Styles.xaml">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Compile Include="App.xaml.cs">
|
|
||||||
<DependentUpon>App.xaml</DependentUpon>
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Converters\BoolToVisibilityConverter.cs" />
|
|
||||||
<Compile Include="Converters\StatusToColorConverter.cs" />
|
|
||||||
<Compile Include="Converters\StatusToTextConverter.cs" />
|
|
||||||
<Compile Include="Data\SqliteHelper.cs" />
|
|
||||||
<Compile Include="Helpers\AppConfig.cs" />
|
|
||||||
<Compile Include="Helpers\ExpressCompanies.cs" />
|
|
||||||
<Compile Include="Helpers\ResourceHelper.cs" />
|
|
||||||
<Compile Include="Models\ApiResponses.cs" />
|
|
||||||
<Compile Include="Models\LocalSession.cs" />
|
|
||||||
<Compile Include="Models\Order.cs" />
|
|
||||||
<Compile Include="Models\ShipModels.cs" />
|
|
||||||
<Compile Include="Models\SyncLog.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
<Compile Include="Services\AuthService.cs" />
|
|
||||||
<Compile Include="Services\ExcelService.cs" />
|
|
||||||
<Compile Include="Services\Interfaces.cs" />
|
|
||||||
<Compile Include="Services\OrderService.cs" />
|
|
||||||
<Compile Include="Services\ShipService.cs" />
|
|
||||||
<Compile Include="Services\SyncService.cs" />
|
|
||||||
<Compile Include="ViewModels\LoginViewModel.cs" />
|
|
||||||
<Compile Include="ViewModels\MainViewModel.cs" />
|
|
||||||
<Compile Include="ViewModels\OrderListViewModel.cs" />
|
|
||||||
<Compile Include="ViewModels\ViewModelBase.cs" />
|
|
||||||
<Compile Include="Views\LoginWindow.xaml.cs">
|
|
||||||
<DependentUpon>LoginWindow.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Views\MainWindow.xaml.cs">
|
|
||||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Views\OrderListView.xaml.cs">
|
|
||||||
<DependentUpon>OrderListView.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Views\ShippingDialog.xaml.cs">
|
|
||||||
<DependentUpon>ShippingDialog.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Include="Data\Resources\schema.sql" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Resource Include="Resources\Icons\app.ico" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="App.config" />
|
|
||||||
<None Include="packages.config" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||||
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
|
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="ClosedXML" Version="0.102.2" />
|
<PackageReference Include="ClosedXML" Version="0.102.2" />
|
||||||
|
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
|
||||||
|
<!-- 将所有DLL打包到EXE中 -->
|
||||||
|
<PackageReference Include="Costura.Fody" Version="5.7.0" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Data\Resources\schema.sql" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="App.config">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
BIN
PackagingMallShipper/Resources/Icons/app.ico
Normal file
BIN
PackagingMallShipper/Resources/Icons/app.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
60
PackagingMallShipper/Resources/Icons/app.svg
Normal file
60
PackagingMallShipper/Resources/Icons/app.svg
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="boxGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#FF9800;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#F57C00;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="boxSide" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
|
<stop offset="0%" style="stop-color:#E65100;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#EF6C00;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="boxTop" x1="0%" y1="100%" x2="0%" y2="0%">
|
||||||
|
<stop offset="0%" style="stop-color:#FFB74D;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#FFA726;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="arrowGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#4CAF50;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#388E3C;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- 背景圆形 -->
|
||||||
|
<circle cx="256" cy="256" r="240" fill="#FFF3E0"/>
|
||||||
|
|
||||||
|
<!-- 包装盒主体 - 正面 -->
|
||||||
|
<path d="M100 200 L256 280 L256 420 L100 340 Z" fill="url(#boxGradient)"/>
|
||||||
|
|
||||||
|
<!-- 包装盒主体 - 侧面 -->
|
||||||
|
<path d="M256 280 L412 200 L412 340 L256 420 Z" fill="url(#boxSide)"/>
|
||||||
|
|
||||||
|
<!-- 包装盒顶部 -->
|
||||||
|
<path d="M100 200 L256 120 L412 200 L256 280 Z" fill="url(#boxTop)"/>
|
||||||
|
|
||||||
|
<!-- 盒子封口线 -->
|
||||||
|
<line x1="256" y1="120" x2="256" y2="280" stroke="#E65100" stroke-width="3"/>
|
||||||
|
<line x1="100" y1="200" x2="412" y2="200" stroke="#E65100" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- 胶带 -->
|
||||||
|
<rect x="220" y="105" width="72" height="25" rx="3" fill="#8D6E63"/>
|
||||||
|
<rect x="224" y="109" width="64" height="17" rx="2" fill="#A1887F"/>
|
||||||
|
|
||||||
|
<!-- 发货箭头 -->
|
||||||
|
<g transform="translate(320, 80)">
|
||||||
|
<!-- 箭头主体 -->
|
||||||
|
<path d="M0 60 L40 60 L40 40 L80 70 L40 100 L40 80 L0 80 Z" fill="url(#arrowGradient)"/>
|
||||||
|
<!-- 箭头高光 -->
|
||||||
|
<path d="M0 60 L40 60 L40 40 L50 50 L10 50 L10 70 L0 70 Z" fill="#66BB6A" opacity="0.5"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- 商城标志 - 购物车图标简化版 -->
|
||||||
|
<g transform="translate(130, 290)">
|
||||||
|
<circle cx="20" cy="60" r="8" fill="#5D4037"/>
|
||||||
|
<circle cx="50" cy="60" r="8" fill="#5D4037"/>
|
||||||
|
<path d="M5 10 L15 10 L25 45 L55 45 L60 25 L20 25" stroke="#5D4037" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- 文字装饰线 -->
|
||||||
|
<rect x="310" y="300" width="60" height="6" rx="3" fill="#FFCC80"/>
|
||||||
|
<rect x="310" y="315" width="45" height="6" rx="3" fill="#FFCC80"/>
|
||||||
|
<rect x="310" y="330" width="55" height="6" rx="3" fill="#FFCC80"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -25,43 +26,74 @@ namespace PackagingMallShipper.Services
|
|||||||
|
|
||||||
public bool IsLoggedIn => !string.IsNullOrEmpty(GetToken());
|
public bool IsLoggedIn => !string.IsNullOrEmpty(GetToken());
|
||||||
|
|
||||||
public async Task<LoginResult> LoginAsync(string mobile, string password)
|
public string GetCaptchaUrl(string key)
|
||||||
|
{
|
||||||
|
return $"https://user.api.it120.cc/code?k={Uri.EscapeDataString(key)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LoginResult> LoginAsync(string userName, string password, string captcha = null, string captchaKey = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var url = AppConfig.GetApiUrl($"/user/m/login?mobile={mobile}&pwd={password}");
|
// 使用子账号登录接口 /login/userName/v2
|
||||||
|
// 注意:该接口会优先判断手机号码登录,如果满足直接登录成功,其次才会尝试子账号登录
|
||||||
|
// 该接口要求必须提供验证码参数
|
||||||
|
var urlBuilder = new System.Text.StringBuilder();
|
||||||
|
urlBuilder.Append("https://user.api.it120.cc/login/userName/v2");
|
||||||
|
urlBuilder.Append($"?userName={Uri.EscapeDataString(userName)}");
|
||||||
|
urlBuilder.Append($"&pwd={Uri.EscapeDataString(password)}");
|
||||||
|
urlBuilder.Append($"&pdomain={Uri.EscapeDataString(AppConfig.SubDomain)}");
|
||||||
|
urlBuilder.Append("&rememberMe=true");
|
||||||
|
|
||||||
|
// 验证码参数是必须的
|
||||||
|
urlBuilder.Append($"&imgcode={Uri.EscapeDataString(captcha ?? "")}");
|
||||||
|
urlBuilder.Append($"&k={Uri.EscapeDataString(captchaKey ?? "")}");
|
||||||
|
|
||||||
|
var url = urlBuilder.ToString();
|
||||||
|
|
||||||
|
// 详细日志:请求URL(隐藏密码)
|
||||||
|
var logUrl = url.Replace($"&pwd={Uri.EscapeDataString(password)}", "&pwd=***");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录请求] URL: {logUrl}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录请求] 用户名: {userName}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录请求] 验证码: {captcha}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录请求] 验证码Key: {captchaKey}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录请求] 子域名: {AppConfig.SubDomain}");
|
||||||
|
|
||||||
var response = await _httpClient.PostAsync(url, null);
|
var response = await _httpClient.PostAsync(url, null);
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
var result = JsonConvert.DeserializeObject<ApiResponse<LoginData>>(json);
|
|
||||||
|
// 详细日志:API响应
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录响应] HTTP状态码: {response.StatusCode}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录响应] JSON原始内容: {json}");
|
||||||
|
|
||||||
|
var result = JsonConvert.DeserializeObject<ApiResponse<AdminLoginData>>(json);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录响应] 解析结果 - Code: {result?.Code}, Msg: {result?.Msg}");
|
||||||
|
|
||||||
if (result?.Code != 0)
|
if (result?.Code != 0)
|
||||||
{
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[登录失败] 错误码: {result?.Code}, 错误信息: {result?.Msg}");
|
||||||
|
|
||||||
return new LoginResult
|
return new LoginResult
|
||||||
{
|
{
|
||||||
Success = false,
|
Success = false,
|
||||||
Message = result?.Msg ?? "登录失败"
|
Message = result?.Msg ?? "登录失败",
|
||||||
|
RequireCaptcha = result?.Code == 700 // 700 表示需要验证码
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = result.Data.Token;
|
var token = result.Data.Token;
|
||||||
var uid = result.Data.Uid;
|
var adminId = result.Data.Id;
|
||||||
|
|
||||||
_httpClient.DefaultRequestHeaders.Clear();
|
Debug.WriteLine($"[登录成功] Token: {token?.Substring(0, Math.Min(20, token?.Length ?? 0))}..., AdminId: {adminId}");
|
||||||
_httpClient.DefaultRequestHeaders.Add("X-Token", token);
|
|
||||||
|
|
||||||
var userInfoUrl = AppConfig.GetApiUrl("/user/detail");
|
|
||||||
var userResponse = await _httpClient.GetAsync(userInfoUrl);
|
|
||||||
var userJson = await userResponse.Content.ReadAsStringAsync();
|
|
||||||
var userResult = JsonConvert.DeserializeObject<ApiResponse<UserInfo>>(userJson);
|
|
||||||
|
|
||||||
_currentSession = new LocalSession
|
_currentSession = new LocalSession
|
||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
Mobile = mobile,
|
Mobile = userName, // 存储用户名/手机号
|
||||||
Token = token,
|
Token = token,
|
||||||
Uid = uid,
|
Uid = adminId,
|
||||||
Nickname = userResult?.Data?.Nick ?? mobile,
|
Nickname = result.Data.Nickname ?? userName,
|
||||||
LastLoginAt = DateTime.Now,
|
LastLoginAt = DateTime.Now,
|
||||||
TokenExpiresAt = DateTime.Now.AddHours(AppConfig.TokenExpireHours)
|
TokenExpiresAt = DateTime.Now.AddHours(AppConfig.TokenExpireHours)
|
||||||
};
|
};
|
||||||
@@ -72,11 +104,14 @@ namespace PackagingMallShipper.Services
|
|||||||
{
|
{
|
||||||
Success = true,
|
Success = true,
|
||||||
Token = token,
|
Token = token,
|
||||||
UserInfo = userResult?.Data
|
UserInfo = null // 子账号登录不需要额外获取用户信息
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Debug.WriteLine($"[登录异常] {ex.GetType().Name}: {ex.Message}");
|
||||||
|
Debug.WriteLine($"[登录异常] StackTrace: {ex.StackTrace}");
|
||||||
|
|
||||||
return new LoginResult
|
return new LoginResult
|
||||||
{
|
{
|
||||||
Success = false,
|
Success = false,
|
||||||
|
|||||||
@@ -49,8 +49,9 @@ namespace PackagingMallShipper.Services
|
|||||||
worksheet.Cell(row, 1).Value = order.OrderNumber;
|
worksheet.Cell(row, 1).Value = order.OrderNumber;
|
||||||
worksheet.Cell(row, 2).Value = order.DateAdd?.ToString("yyyy-MM-dd HH:mm:ss");
|
worksheet.Cell(row, 2).Value = order.DateAdd?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
worksheet.Cell(row, 3).Value = order.LogisticsName;
|
worksheet.Cell(row, 3).Value = order.LogisticsName;
|
||||||
worksheet.Cell(row, 4).Value = order.LogisticsMobile;
|
var mobileCell = worksheet.Cell(row, 4);
|
||||||
worksheet.Cell(row, 4).SetDataType(XLDataType.Text);
|
mobileCell.Value = order.LogisticsMobile;
|
||||||
|
mobileCell.Style.NumberFormat.Format = "@";
|
||||||
worksheet.Cell(row, 5).Value = order.LogisticsProvince;
|
worksheet.Cell(row, 5).Value = order.LogisticsProvince;
|
||||||
worksheet.Cell(row, 6).Value = order.LogisticsCity;
|
worksheet.Cell(row, 6).Value = order.LogisticsCity;
|
||||||
worksheet.Cell(row, 7).Value = order.LogisticsDistrict;
|
worksheet.Cell(row, 7).Value = order.LogisticsDistrict;
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ namespace PackagingMallShipper.Services
|
|||||||
{
|
{
|
||||||
public interface IAuthService
|
public interface IAuthService
|
||||||
{
|
{
|
||||||
Task<LoginResult> LoginAsync(string mobile, string password);
|
Task<LoginResult> LoginAsync(string userName, string password, string captcha = null, string captchaKey = null);
|
||||||
|
string GetCaptchaUrl(string key);
|
||||||
string GetToken();
|
string GetToken();
|
||||||
bool IsLoggedIn { get; }
|
bool IsLoggedIn { get; }
|
||||||
LocalSession CurrentSession { get; }
|
LocalSession CurrentSession { get; }
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -57,22 +58,44 @@ namespace PackagingMallShipper.Services
|
|||||||
{
|
{
|
||||||
OnSyncMessage?.Invoke($"正在同步第 {page}/{totalPages} 页...");
|
OnSyncMessage?.Invoke($"正在同步第 {page}/{totalPages} 页...");
|
||||||
|
|
||||||
var url = AppConfig.GetApiUrl($"/order/list?page={page}&pageSize={pageSize}");
|
// 使用正确的API端点:/user/apiExtOrder/list (POST请求)
|
||||||
|
// 域名:user.api.it120.cc,路径不含subdomain
|
||||||
|
var urlBuilder = new System.Text.StringBuilder();
|
||||||
|
urlBuilder.Append("https://user.api.it120.cc/user/apiExtOrder/list");
|
||||||
|
urlBuilder.Append($"?page={page}&pageSize={pageSize}");
|
||||||
|
urlBuilder.Append("&export=true"); // 获取更丰富的数据
|
||||||
if (lastSyncTime.HasValue)
|
if (lastSyncTime.HasValue)
|
||||||
url += $"&dateUpdateBegin={lastSyncTime:yyyy-MM-dd HH:mm:ss}";
|
urlBuilder.Append($"&dateUpdateBegin={lastSyncTime:yyyy-MM-dd HH:mm:ss}");
|
||||||
|
|
||||||
|
var url = urlBuilder.ToString();
|
||||||
|
|
||||||
|
Debug.WriteLine($"[同步请求] URL: {url}");
|
||||||
|
Debug.WriteLine($"[同步请求] Token: {token?.Substring(0, Math.Min(20, token?.Length ?? 0))}...");
|
||||||
|
|
||||||
_httpClient.DefaultRequestHeaders.Clear();
|
_httpClient.DefaultRequestHeaders.Clear();
|
||||||
_httpClient.DefaultRequestHeaders.Add("X-Token", token);
|
_httpClient.DefaultRequestHeaders.Add("X-Token", token);
|
||||||
|
|
||||||
var response = await _httpClient.GetAsync(url);
|
// 使用 POST 请求
|
||||||
|
var response = await _httpClient.PostAsync(url, null);
|
||||||
var json = await response.Content.ReadAsStringAsync();
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
Debug.WriteLine($"[同步响应] HTTP状态码: {response.StatusCode}");
|
||||||
|
|
||||||
var data = JsonConvert.DeserializeObject<ApiResponse<OrderListData>>(json);
|
var data = JsonConvert.DeserializeObject<ApiResponse<OrderListData>>(json);
|
||||||
|
|
||||||
if (data?.Code != 0)
|
if (data?.Code != 0)
|
||||||
|
{
|
||||||
|
if (data?.Msg?.Contains("token") == true || data?.Msg?.Contains("登录") == true)
|
||||||
|
throw new UnauthorizedAccessException($"当前登录token无效,请重新登录");
|
||||||
throw new Exception(data?.Msg ?? "获取订单失败");
|
throw new Exception(data?.Msg ?? "获取订单失败");
|
||||||
|
}
|
||||||
|
|
||||||
var orders = data.Data?.OrderList ?? new List<OrderDto>();
|
// 使用辅助方法获取订单列表(自动合并物流和商品信息)
|
||||||
|
var orders = data.Data?.GetOrders() ?? new List<OrderDto>();
|
||||||
totalPages = data.Data?.TotalPage ?? 1;
|
totalPages = data.Data?.TotalPage ?? 1;
|
||||||
|
var totalRow = data.Data?.GetTotalRow() ?? 0;
|
||||||
|
|
||||||
|
Debug.WriteLine($"[同步响应] 订单数量: {orders.Count}, 总记录: {totalRow}, 总页数: {totalPages}");
|
||||||
|
|
||||||
if (orders.Count == 0) break;
|
if (orders.Count == 0) break;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using PackagingMallShipper.Services;
|
using PackagingMallShipper.Services;
|
||||||
@@ -10,33 +11,61 @@ namespace PackagingMallShipper.ViewModels
|
|||||||
public partial class LoginViewModel : ViewModelBase
|
public partial class LoginViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
|
private string _captchaKey = "";
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _mobile = "";
|
private string _userName = "";
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _password = "";
|
private string _password = "";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _captcha = "";
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _errorMessage = "";
|
private string _errorMessage = "";
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _rememberMe = true;
|
private bool _rememberMe = true;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _showCaptcha = false;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private BitmapImage _captchaImageSource;
|
||||||
|
|
||||||
public event Action OnLoginSuccess;
|
public event Action OnLoginSuccess;
|
||||||
|
|
||||||
public LoginViewModel(IAuthService authService)
|
public LoginViewModel(IAuthService authService)
|
||||||
{
|
{
|
||||||
_authService = authService;
|
_authService = authService;
|
||||||
LoadSavedCredentials();
|
LoadSavedCredentials();
|
||||||
|
|
||||||
|
// 子账号登录必须提供验证码,默认显示
|
||||||
|
ShowCaptcha = true;
|
||||||
|
RefreshCaptcha();
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
public void RefreshCaptcha()
|
||||||
|
{
|
||||||
|
_captchaKey = Guid.NewGuid().ToString("N");
|
||||||
|
var url = _authService.GetCaptchaUrl(_captchaKey);
|
||||||
|
|
||||||
|
CaptchaImageSource = new BitmapImage();
|
||||||
|
CaptchaImageSource.BeginInit();
|
||||||
|
CaptchaImageSource.UriSource = new Uri(url);
|
||||||
|
CaptchaImageSource.CacheOption = BitmapCacheOption.None;
|
||||||
|
CaptchaImageSource.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
|
||||||
|
CaptchaImageSource.EndInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task LoginAsync()
|
private async Task LoginAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Mobile))
|
if (string.IsNullOrWhiteSpace(UserName))
|
||||||
{
|
{
|
||||||
ErrorMessage = "请输入手机号";
|
ErrorMessage = "请输入用户名或手机号";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,12 +75,23 @@ namespace PackagingMallShipper.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ShowCaptcha && string.IsNullOrWhiteSpace(Captcha))
|
||||||
|
{
|
||||||
|
ErrorMessage = "请输入验证码";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
ErrorMessage = "";
|
ErrorMessage = "";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await _authService.LoginAsync(Mobile, Password);
|
var result = await _authService.LoginAsync(
|
||||||
|
UserName,
|
||||||
|
Password,
|
||||||
|
ShowCaptcha ? Captcha : null,
|
||||||
|
ShowCaptcha ? _captchaKey : null
|
||||||
|
);
|
||||||
|
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
@@ -64,6 +104,19 @@ namespace PackagingMallShipper.ViewModels
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ErrorMessage = result.Message ?? "登录失败";
|
ErrorMessage = result.Message ?? "登录失败";
|
||||||
|
|
||||||
|
// 如果需要验证码,显示验证码输入
|
||||||
|
if (result.RequireCaptcha && !ShowCaptcha)
|
||||||
|
{
|
||||||
|
ShowCaptcha = true;
|
||||||
|
RefreshCaptcha();
|
||||||
|
}
|
||||||
|
else if (ShowCaptcha)
|
||||||
|
{
|
||||||
|
// 验证码错误,刷新验证码
|
||||||
|
Captcha = "";
|
||||||
|
RefreshCaptcha();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -80,10 +133,10 @@ namespace PackagingMallShipper.ViewModels
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var mobile = Properties.Settings.Default.SavedMobile;
|
var userName = Properties.Settings.Default.SavedMobile;
|
||||||
if (!string.IsNullOrEmpty(mobile))
|
if (!string.IsNullOrEmpty(userName))
|
||||||
{
|
{
|
||||||
Mobile = mobile;
|
UserName = userName;
|
||||||
RememberMe = true;
|
RememberMe = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +150,7 @@ namespace PackagingMallShipper.ViewModels
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Properties.Settings.Default.SavedMobile = Mobile;
|
Properties.Settings.Default.SavedMobile = UserName;
|
||||||
Properties.Settings.Default.Save();
|
Properties.Settings.Default.Save();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="包装商城发货助手 - 登录"
|
Title="包装商城发货助手 - 登录"
|
||||||
Height="400" Width="350"
|
Height="480" Width="350"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
ResizeMode="NoResize"
|
ResizeMode="NoResize"
|
||||||
Background="#F5F5F5">
|
Background="#F5F5F5">
|
||||||
@@ -22,19 +22,46 @@
|
|||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
Margin="0,0,0,30"/>
|
Margin="0,0,0,30"/>
|
||||||
|
|
||||||
<TextBlock Text="手机号" Margin="0,0,0,5" FontSize="13"/>
|
<TextBlock Text="用户名/手机号" Margin="0,0,0,5" FontSize="13"/>
|
||||||
<TextBox x:Name="MobileTextBox"
|
<TextBox x:Name="UserNameTextBox"
|
||||||
Text="{Binding Mobile, UpdateSourceTrigger=PropertyChanged}"
|
Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}"
|
||||||
Height="35" FontSize="14" Padding="10,5"
|
Height="35" FontSize="14" Padding="10,5"
|
||||||
Margin="0,0,0,15"/>
|
Margin="0,0,0,15"/>
|
||||||
|
|
||||||
<TextBlock Text="密码" Margin="0,0,0,5" FontSize="13"/>
|
<TextBlock Text="密码" Margin="0,0,0,5" FontSize="13"/>
|
||||||
<PasswordBox x:Name="PasswordBox"
|
<PasswordBox x:Name="PasswordBox"
|
||||||
Height="35" FontSize="14" Padding="10,5"
|
Height="35" FontSize="14" Padding="10,5"
|
||||||
Margin="0,0,0,10"
|
Margin="0,0,0,15"
|
||||||
PasswordChanged="PasswordBox_PasswordChanged"/>
|
PasswordChanged="PasswordBox_PasswordChanged"/>
|
||||||
|
|
||||||
<CheckBox Content="记住手机号"
|
<!-- 验证码区域 -->
|
||||||
|
<StackPanel x:Name="CaptchaPanel"
|
||||||
|
Visibility="{Binding ShowCaptcha, Converter={StaticResource BoolToVisibility}}"
|
||||||
|
Margin="0,0,0,15">
|
||||||
|
<TextBlock Text="验证码" Margin="0,0,0,5" FontSize="13"/>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="120"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBox x:Name="CaptchaTextBox"
|
||||||
|
Text="{Binding Captcha, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Height="35" FontSize="14" Padding="10,5"
|
||||||
|
Grid.Column="0" Margin="0,0,10,0"/>
|
||||||
|
|
||||||
|
<Border Grid.Column="1" BorderBrush="#CCCCCC" BorderThickness="1" CornerRadius="2">
|
||||||
|
<Image x:Name="CaptchaImage"
|
||||||
|
Source="{Binding CaptchaImageSource}"
|
||||||
|
Stretch="Fill"
|
||||||
|
MouseLeftButtonDown="CaptchaImage_Click"
|
||||||
|
Cursor="Hand"
|
||||||
|
ToolTip="点击刷新验证码"/>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<CheckBox Content="记住用户名"
|
||||||
IsChecked="{Binding RememberMe}"
|
IsChecked="{Binding RememberMe}"
|
||||||
Margin="0,0,0,20"/>
|
Margin="0,0,0,20"/>
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ namespace PackagingMallShipper.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CaptchaImage_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
_viewModel.RefreshCaptchaCommand.Execute(null);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnLoginSuccess()
|
private void OnLoginSuccess()
|
||||||
{
|
{
|
||||||
OpenMainWindow();
|
OpenMainWindow();
|
||||||
|
|||||||
@@ -77,10 +77,12 @@
|
|||||||
Width="90" Height="30" Margin="0,0,5,0"
|
Width="90" Height="30" Margin="0,0,5,0"
|
||||||
IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBool}}"/>
|
IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBool}}"/>
|
||||||
|
|
||||||
<Button Content="📥 导入发货" Command="{Binding ImportAndShipCommand}"
|
<!-- 暂时禁用导入发货功能
|
||||||
|
<Button Content="📥 导入发货" Command="{Binding ImportAndShipCommand}"
|
||||||
Width="90" Height="30"
|
Width="90" Height="30"
|
||||||
Background="#52C41A" Foreground="White" BorderThickness="0"
|
Background="#52C41A" Foreground="White" BorderThickness="0"
|
||||||
IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBool}}"/>
|
IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBool}}"/>
|
||||||
|
-->
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- 订单列表 -->
|
<!-- 订单列表 -->
|
||||||
|
|||||||
28
README.md
28
README.md
@@ -31,16 +31,28 @@
|
|||||||
|
|
||||||
## 开发环境
|
## 开发环境
|
||||||
|
|
||||||
- Visual Studio 2019 Community
|
- Visual Studio 2019+ 或 .NET SDK 8.0+
|
||||||
- .NET Framework 4.8 开发工具
|
- .NET Framework 4.8 开发工具
|
||||||
|
|
||||||
## 编译步骤
|
## 编译步骤
|
||||||
|
|
||||||
1. 使用 Visual Studio 2019 打开 `PackagingMallShipper.sln`
|
### 方式一:命令行编译(推荐)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 还原依赖并编译
|
||||||
|
dotnet build PackagingMallShipper.sln -c Release
|
||||||
|
|
||||||
|
# 输出文件位于
|
||||||
|
bin/Release/net48/PackagingMallShipper.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:Visual Studio
|
||||||
|
|
||||||
|
1. 使用 Visual Studio 2019+ 打开 `PackagingMallShipper.sln`
|
||||||
2. 还原 NuGet 包
|
2. 还原 NuGet 包
|
||||||
3. 选择 `Release` 配置
|
3. 选择 `Release` 配置
|
||||||
4. 生成解决方案
|
4. 生成解决方案
|
||||||
5. 输出文件位于 `bin/Release/` 目录
|
5. 输出文件位于 `bin/Release/net48/` 目录
|
||||||
|
|
||||||
## 配置说明
|
## 配置说明
|
||||||
|
|
||||||
@@ -49,8 +61,8 @@
|
|||||||
```xml
|
```xml
|
||||||
<appSettings>
|
<appSettings>
|
||||||
<!-- API工厂配置 -->
|
<!-- API工厂配置 -->
|
||||||
<add key="ApiBaseUrl" value="https://user.api.it120.cc" />
|
<add key="ApiBaseUrl" value="https://api.it120.cc" />
|
||||||
<add key="SubDomain" value="vv125s" />
|
<add key="SubDomain" value="let5see" />
|
||||||
|
|
||||||
<!-- 同步配置 -->
|
<!-- 同步配置 -->
|
||||||
<add key="SyncPageSize" value="50" />
|
<add key="SyncPageSize" value="50" />
|
||||||
@@ -64,7 +76,11 @@
|
|||||||
|
|
||||||
## 使用流程
|
## 使用流程
|
||||||
|
|
||||||
1. **登录** - 使用API工厂账号登录
|
1. **登录** - 使用API工厂子账号登录
|
||||||
|
- 输入用户名或手机号
|
||||||
|
- 输入密码
|
||||||
|
- 输入图形验证码(点击图片可刷新)
|
||||||
|
- 首次登录建议勾选"记住用户名"
|
||||||
2. **同步订单** - 点击"同步订单"获取最新订单
|
2. **同步订单** - 点击"同步订单"获取最新订单
|
||||||
3. **查看订单** - 切换状态筛选,搜索订单
|
3. **查看订单** - 切换状态筛选,搜索订单
|
||||||
4. **发货方式一** - 导出Excel → 填写快递信息 → 导入发货
|
4. **发货方式一** - 导出Excel → 填写快递信息 → 导入发货
|
||||||
|
|||||||
58
build_installer.bat
Normal file
58
build_installer.bat
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 936 >nul
|
||||||
|
echo ========================================
|
||||||
|
echo PackagingMallShipper - Build Installer
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
set "ISCC="
|
||||||
|
if exist "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" (
|
||||||
|
set "ISCC=C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
|
||||||
|
)
|
||||||
|
if exist "C:\Program Files\Inno Setup 6\ISCC.exe" (
|
||||||
|
set "ISCC=C:\Program Files\Inno Setup 6\ISCC.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%ISCC%"=="" (
|
||||||
|
echo [Error] Inno Setup 6 not found
|
||||||
|
echo.
|
||||||
|
echo Please download and install Inno Setup 6:
|
||||||
|
echo https://jrsoftware.org/isdl.php
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [Info] Found Inno Setup: %ISCC%
|
||||||
|
echo.
|
||||||
|
|
||||||
|
if not exist "publish\PackagingMallShipper.exe" (
|
||||||
|
echo [Error] publish\PackagingMallShipper.exe not found
|
||||||
|
echo.
|
||||||
|
echo Please build Release version first:
|
||||||
|
echo dotnet build -c Release
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [Info] Compiling installer...
|
||||||
|
echo.
|
||||||
|
|
||||||
|
"%ISCC%" setup.iss
|
||||||
|
|
||||||
|
if %ERRORLEVEL% EQU 0 (
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo [Success] Installer created!
|
||||||
|
echo.
|
||||||
|
echo Location: installer\PackagingMallShipper_Setup_v1.0.0.exe
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
if exist "installer" explorer "installer"
|
||||||
|
) else (
|
||||||
|
echo.
|
||||||
|
echo [Error] Build failed, error code: %ERRORLEVEL%
|
||||||
|
)
|
||||||
|
|
||||||
|
pause
|
||||||
148
setup.iss
Normal file
148
setup.iss
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
; Inno Setup Script for 包装商城发货助手
|
||||||
|
; Requires Inno Setup 6.x
|
||||||
|
|
||||||
|
#define MyAppName "包装商城发货助手"
|
||||||
|
#define MyAppVersion "1.0.0"
|
||||||
|
#define MyAppPublisher "PackagingMall"
|
||||||
|
#define MyAppExeName "PackagingMallShipper.exe"
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
; 应用程序信息
|
||||||
|
AppId={{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
|
||||||
|
AppName={#MyAppName}
|
||||||
|
AppVersion={#MyAppVersion}
|
||||||
|
AppPublisher={#MyAppPublisher}
|
||||||
|
DefaultDirName={autopf}\{#MyAppName}
|
||||||
|
DefaultGroupName={#MyAppName}
|
||||||
|
AllowNoIcons=yes
|
||||||
|
; 输出设置
|
||||||
|
OutputDir=installer
|
||||||
|
OutputBaseFilename=PackagingMallShipper_Setup_v{#MyAppVersion}
|
||||||
|
; 压缩设置
|
||||||
|
Compression=lzma2/ultra64
|
||||||
|
SolidCompression=yes
|
||||||
|
; 权限设置
|
||||||
|
PrivilegesRequired=admin
|
||||||
|
; 系统要求 - Windows 7 SP1 或更高版本
|
||||||
|
MinVersion=6.1.7601
|
||||||
|
; 安装向导设置
|
||||||
|
WizardStyle=modern
|
||||||
|
DisableProgramGroupPage=yes
|
||||||
|
; 卸载设置
|
||||||
|
UninstallDisplayName={#MyAppName}
|
||||||
|
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
Name: "chinesesimplified"; MessagesFile: "compiler:Languages\ChineseSimplified.isl"
|
||||||
|
|
||||||
|
[Tasks]
|
||||||
|
Name: "desktopicon"; Description: "创建桌面快捷方式"; GroupDescription: "附加选项:"; Flags: checkedonce
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
; 主程序
|
||||||
|
Source: "publish\PackagingMallShipper.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
Source: "publish\PackagingMallShipper.exe.config"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
; SQLite 本地库
|
||||||
|
Source: "publish\x64\SQLite.Interop.dll"; DestDir: "{app}\x64"; Flags: ignoreversion
|
||||||
|
Source: "publish\x86\SQLite.Interop.dll"; DestDir: "{app}\x86"; Flags: ignoreversion
|
||||||
|
; .NET Framework 4.8 离线安装包(可选,取消注释以包含)
|
||||||
|
; Source: "redist\ndp48-x86-x64-allos-enu.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall; Check: not IsDotNetInstalled
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||||
|
Name: "{group}\卸载 {#MyAppName}"; Filename: "{uninstallexe}"
|
||||||
|
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||||
|
|
||||||
|
[Run]
|
||||||
|
; 安装完成后运行程序
|
||||||
|
Filename: "{app}\{#MyAppExeName}"; Description: "运行 {#MyAppName}"; Flags: nowait postinstall skipifsilent
|
||||||
|
|
||||||
|
[Code]
|
||||||
|
// 检查 .NET Framework 4.8 是否已安装
|
||||||
|
function IsDotNetInstalled: Boolean;
|
||||||
|
var
|
||||||
|
Release: Cardinal;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
// .NET Framework 4.8 的 Release 值为 528040 或更高
|
||||||
|
if RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', Release) then
|
||||||
|
begin
|
||||||
|
Result := (Release >= 528040);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// 获取 .NET Framework 版本描述
|
||||||
|
function GetDotNetVersionStr: String;
|
||||||
|
var
|
||||||
|
Release: Cardinal;
|
||||||
|
begin
|
||||||
|
Result := '未安装';
|
||||||
|
if RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', Release) then
|
||||||
|
begin
|
||||||
|
if Release >= 533320 then
|
||||||
|
Result := '4.8.1 或更高'
|
||||||
|
else if Release >= 528040 then
|
||||||
|
Result := '4.8'
|
||||||
|
else if Release >= 461808 then
|
||||||
|
Result := '4.7.2'
|
||||||
|
else if Release >= 461308 then
|
||||||
|
Result := '4.7.1'
|
||||||
|
else if Release >= 460798 then
|
||||||
|
Result := '4.7'
|
||||||
|
else if Release >= 394802 then
|
||||||
|
Result := '4.6.2'
|
||||||
|
else if Release >= 394254 then
|
||||||
|
Result := '4.6.1'
|
||||||
|
else if Release >= 393295 then
|
||||||
|
Result := '4.6'
|
||||||
|
else
|
||||||
|
Result := '4.5.x 或更低';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// 初始化安装向导
|
||||||
|
function InitializeSetup: Boolean;
|
||||||
|
var
|
||||||
|
ErrorCode: Integer;
|
||||||
|
NetVersion: String;
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
|
||||||
|
// 检查 .NET Framework
|
||||||
|
if not IsDotNetInstalled then
|
||||||
|
begin
|
||||||
|
NetVersion := GetDotNetVersionStr;
|
||||||
|
|
||||||
|
if MsgBox('此程序需要 .NET Framework 4.8 才能运行。' + #13#10 + #13#10 +
|
||||||
|
'当前检测到的版本: ' + NetVersion + #13#10 + #13#10 +
|
||||||
|
'是否要打开微软官网下载 .NET Framework 4.8?' + #13#10 +
|
||||||
|
'(下载完成后请先安装运行时,再重新运行此安装程序)',
|
||||||
|
mbConfirmation, MB_YESNO) = IDYES then
|
||||||
|
begin
|
||||||
|
// 打开 .NET Framework 4.8 下载页面
|
||||||
|
ShellExec('open', 'https://dotnet.microsoft.com/download/dotnet-framework/net48', '', '', SW_SHOW, ewNoWait, ErrorCode);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := MsgBox('是否仍要继续安装?' + #13#10 + #13#10 +
|
||||||
|
'注意:如果没有安装 .NET Framework 4.8,程序将无法运行。',
|
||||||
|
mbConfirmation, MB_YESNO) = IDYES;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// 卸载时清理数据目录(可选)
|
||||||
|
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||||
|
var
|
||||||
|
DataDir: String;
|
||||||
|
begin
|
||||||
|
if CurUninstallStep = usPostUninstall then
|
||||||
|
begin
|
||||||
|
DataDir := ExpandConstant('{localappdata}\PackagingMallShipper');
|
||||||
|
if DirExists(DataDir) then
|
||||||
|
begin
|
||||||
|
if MsgBox('是否删除程序数据(包括本地缓存的订单数据)?', mbConfirmation, MB_YESNO) = IDYES then
|
||||||
|
begin
|
||||||
|
DelTree(DataDir, True, True, True);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
Reference in New Issue
Block a user