Browse Source

添加项目文件。

master
忱 沈 1 week ago
parent
commit
54eaa8a836
  1. 14
      App.xaml
  2. 19
      App.xaml.cs
  3. 46
      AppShell.xaml
  4. 34
      AppShell.xaml.cs
  5. 255
      Class/SQLiteConfig.cs
  6. 504
      Class/SqliteHelper.cs
  7. 54
      Class/TcpServer.cs
  8. 1
      Images/adduserfilled.svg
  9. 8
      Images/infofeed.svg
  10. 1
      Images/ixnotifications.svg
  11. BIN
      Images/lucidescanline.png
  12. 8
      Images/search.svg
  13. 9
      InfoPage.xaml
  14. 9
      InfoPage.xaml.cs
  15. 48
      MauiProgram.cs
  16. 89
      Models/AppModels.cs
  17. 40
      Models/DataModel.cs
  18. 58
      Models/DataSource.cs
  19. 60
      NotificationPage.xaml
  20. 93
      NotificationPage.xaml.cs
  21. 39
      NotificationView/MessagePage.xaml
  22. 42
      NotificationView/MessagePage.xaml.cs
  23. 39
      NotificationView/NoticePage.xaml
  24. 43
      NotificationView/NoticePage.xaml.cs
  25. 39
      NotificationView/SystemPage.xaml
  26. 42
      NotificationView/SystemPage.xaml.cs
  27. 7
      Platforms/Android/AndroidManifest.xml
  28. 11
      Platforms/Android/MainActivity.cs
  29. 16
      Platforms/Android/MainApplication.cs
  30. 6
      Platforms/Android/Resources/values/colors.xml
  31. 10
      Platforms/MacCatalyst/AppDelegate.cs
  32. 14
      Platforms/MacCatalyst/Entitlements.plist
  33. 40
      Platforms/MacCatalyst/Info.plist
  34. 16
      Platforms/MacCatalyst/Program.cs
  35. 8
      Platforms/Windows/App.xaml
  36. 25
      Platforms/Windows/App.xaml.cs
  37. 46
      Platforms/Windows/Package.appxmanifest
  38. 17
      Platforms/Windows/app.manifest
  39. 10
      Platforms/iOS/AppDelegate.cs
  40. 32
      Platforms/iOS/Info.plist
  41. 16
      Platforms/iOS/Program.cs
  42. 51
      Platforms/iOS/Resources/PrivacyInfo.xcprivacy
  43. 224
      ProfilePage.xaml
  44. 97
      ProfilePage.xaml.cs
  45. 8
      Properties/launchSettings.json
  46. 43
      QueryPage.xaml
  47. 38
      QueryPage.xaml.cs
  48. 4
      Resources/AppIcon/appicon.svg
  49. 8
      Resources/AppIcon/appiconfg.svg
  50. BIN
      Resources/Fonts/OpenSans-Regular.ttf
  51. BIN
      Resources/Fonts/OpenSans-Semibold.ttf
  52. BIN
      Resources/Images/dotnet_bot.png
  53. 15
      Resources/Raw/AboutAssets.txt
  54. 8
      Resources/Splash/splash.svg
  55. 44
      Resources/Styles/Colors.xaml
  56. 434
      Resources/Styles/Styles.xaml
  57. 19
      ScanPage.xaml
  58. 30
      ScanPage.xaml.cs
  59. 131
      SunlightAggregationTerminal.csproj
  60. 3
      SunlightAggregationTerminal.slnx
  61. 65
      View/LogPage.xaml
  62. 61
      View/LogPage.xaml.cs
  63. 6
      View/LogWindow.xaml
  64. 19
      View/LogWindow.xaml.cs

14
App.xaml

@ -0,0 +1,14 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SunlightAggregationTerminal"
x:Class="SunlightAggregationTerminal.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

19
App.xaml.cs

@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using SunlightAggregationTerminal.Models;
namespace SunlightAggregationTerminal
{
public partial class App : Application
{
public static DataModel GlobalData { get; set;} = new DataModel();
public App()
{
InitializeComponent();
}
protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(new AppShell());
}
}
}

46
AppShell.xaml

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="SunlightAggregationTerminal.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SunlightAggregationTerminal"
Title="SunlightAggregationTerminal"
Loaded="Shell_Loaded"
Shell.FlyoutBehavior="Disabled">
<Shell.Resources>
<ResourceDictionary>
<Style TargetType="TabBar">
<!-- 核心代码:半透明白色 (Alpha值CC约80%不透明度) -->
<!-- 视觉上非常像磨砂玻璃,且兼容所有平台 -->
<Setter Property="Shell.TabBarBackgroundColor" Value="#CCF0F0F0" />
<!-- 设置选中时的标题颜色(建议深色) -->
<Setter Property="Shell.TabBarTitleColor" Value="#333333" />
<!-- 设置未选中时的图标和标题颜色 -->
<Setter Property="Shell.TabBarUnselectedColor" Value="#888888" />
</Style>
</ResourceDictionary>
</Shell.Resources>
<TabBar>
<!-- 1 信息页面 -->
<ShellContent
Title="信息"
Icon="infofeed.png"
ContentTemplate="{DataTemplate local:InfoPage}"/>
<!-- 2 查询页面 -->
<ShellContent
Title="查询"
Icon="search.png"
ContentTemplate="{DataTemplate local:QueryPage}"/>
<!-- 3 通知页面 -->
<ShellContent
Title="通知"
Icon="ixnotifications.png"
ContentTemplate="{DataTemplate local:NotificationPage}"/>
<!-- 4 我的页面 -->
<ShellContent
Title="我"
Icon="adduserfilled.png"
ContentTemplate="{DataTemplate local:ProfilePage}"/>
</TabBar>
</Shell>

34
AppShell.xaml.cs

@ -0,0 +1,34 @@
using SunlightAggregationTerminal.Class;
using SunlightAggregationTerminal.Models;
namespace SunlightAggregationTerminal
{
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
this.BindingContext = new AppModels();
}
private void Shell_Loaded(object sender, EventArgs e)
{
//打开登录
if (!App.GlobalData.LogNo)
{
var logpage = new View.LogPage();
Navigation.PushModalAsync(logpage);
}
else
{//发送登录请求
/* var dat = TcpServer.Query("");
if (dat != null)
{//拒绝登陆时打开登录页面
var logpage = new View.LogPage();
Navigation.PushModalAsync(logpage);
}*/
}
}
}
}

255
Class/SQLiteConfig.cs

@ -0,0 +1,255 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SunlightAggregationTerminal.Class
{
public class SQLiteConfig()
{
public static SqliteHelper SQLitedata = null!; //定义数据库
public static SqliteHelper? Config(string dat)
{
try
{
SQLitedata = new SqliteHelper(dat); //数据库连接路径(获取各平台的应用数据目录)
SQLitedata.Open(); //打开数据库
if (SQLitedata.TableExists("Users"))
{
CheckAndRepairTableSchema();
}
else
{
CreateFreshUserTable();
}
if (SQLitedata.TableExists("Notification"))
{
CheckAndRepairTableSchemaNotification();
}
else
{
CreateFreshUserTableNotification();
}
SQLitedata.Close();
return SQLitedata;
}
catch (Exception)
{
return null;
}
}
// --- 逻辑分支 表存在,检查字段完整性 ---
private static void CheckAndRepairTableSchema()
{
// 获取现有字段列表
// 使用 PRAGMA table_info 获取结构
string pragmaSql = "PRAGMA table_info('Users');";
// 复用现有的 ExecuteReader 方法
// ExecuteReader 返回 null 代表出错,否则返回 DataReader
using (var reader = SQLitedata.ExecuteReader(pragmaSql, null))
{
if (reader == null) return;
var existingColumns = new List<string>();
while (reader.Read())
{
// PRAGMA table_info 返回结果的第2列(Index=1)是列名
string colName = reader.GetString(1);
existingColumns.Add(colName);
}
reader.Close(); // 记得关闭 Reader
// 检查并添加缺失字段 (直接拼接 SQL)
// 检查 User
if (!existingColumns.Contains("User"))
{
string sql = "ALTER TABLE Users ADD COLUMN User TEXT NOT NULL;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 UserId
if (!existingColumns.Contains("UserId"))
{
string sql = "ALTER TABLE Users ADD COLUMN UserId TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 UserName
if (!existingColumns.Contains("UserName"))
{
string sql = "ALTER TABLE Users ADD COLUMN UserName TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 UserPassword
if (!existingColumns.Contains("UserPassword"))
{
string sql = "ALTER TABLE Users ADD COLUMN UserPassword TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 Enterprise
if (!existingColumns.Contains("Enterprise"))
{
string sql = "ALTER TABLE Users ADD COLUMN Enterprise TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 Department
if (!existingColumns.Contains("Department"))
{
string sql = "ALTER TABLE Users ADD COLUMN Department TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 ServerID
if (!existingColumns.Contains("ServerID"))
{
string sql = "ALTER TABLE Users ADD COLUMN ServerID TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 Group
if (!existingColumns.Contains("Groups"))
{
string sql = "ALTER TABLE Users ADD COLUMN Groups TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 ProxyID
if (!existingColumns.Contains("ProxyID"))
{
string sql = "ALTER TABLE Users ADD COLUMN ProxyID TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 LogNo
if (!existingColumns.Contains("LogNo"))
{
string sql = "ALTER TABLE Users ADD COLUMN LogNo BOOL DEFAULT (0);";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 LocalAreaNetworkMode
if (!existingColumns.Contains("LocalAreaNetworkMode"))
{
string sql = "ALTER TABLE Users ADD COLUMN LocalAreaNetworkMode BOOL DEFAULT (0);";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 LocalAreaNetworkMode
if (!existingColumns.Contains("DarkMode"))
{
string sql = "ALTER TABLE Users ADD COLUMN DarkMode BOOL DEFAULT (0);";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 LocalAreaNetworkMode
if (!existingColumns.Contains("MessageNotificationMode"))
{
string sql = "ALTER TABLE Users ADD COLUMN MessageNotificationMode BOOL DEFAULT (0);";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 GUID
if (!existingColumns.Contains("GUID"))
{
string sql = "ALTER TABLE Users ADD COLUMN GUID TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
string sqlupdata = "UPDATE Users SET GUID = '"+ Models.AppModels.GetAppUniqueId() + "';";
SQLitedata.ExecuteNonQuery(sqlupdata, null);
}
}
}
private static void CheckAndRepairTableSchemaNotification()
{
// 获取现有字段列表
// 使用 PRAGMA table_info 获取结构
string pragmaSql = "PRAGMA table_info('Notification');";
// 复用现有的 ExecuteReader 方法
// ExecuteReader 返回 null 代表出错,否则返回 DataReader
using (var reader = SQLitedata.ExecuteReader(pragmaSql, null))
{
if (reader == null) return;
var existingColumns = new List<string>();
while (reader.Read())
{
// PRAGMA table_info 返回结果的第2列(Index=1)是列名
string colName = reader.GetString(1);
existingColumns.Add(colName);
}
reader.Close(); // 记得关闭 Reader
// 检查并添加缺失字段 (直接拼接 SQL)
// 检查 Title
if (!existingColumns.Contains("Title"))
{
string sql = "ALTER TABLE Notification ADD COLUMN Title TEXT NULL;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 Content
if (!existingColumns.Contains("Content"))
{
string sql = "ALTER TABLE Notification ADD COLUMN Content TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 Time
if (!existingColumns.Contains("Time"))
{
string sql = "ALTER TABLE Notification ADD COLUMN Time TEXT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
// 检查 Type
if (!existingColumns.Contains("Type"))
{
string sql = "ALTER TABLE Notification ADD COLUMN Type INT;";
SQLitedata.ExecuteNonQuery(sql, null);
}
}
}
// --- 逻辑分支 表不存在,直接创建 ---
private static void CreateFreshUserTable()
{
// 建表语句
string createSql = @"
CREATE TABLE Users (
User TEXT NOT NULL,
UserId TEXT,
UserName TEXT,
UserPassword TEXT,
Enterprise TEXT,
Department TEXT,
ServerID TEXT,
Groups TEXT,
ProxyID TEXT,
LogNo BOOL DEFAULT (0),
LocalAreaNetworkMode BOOL DEFAULT (0),
DarkMode BOOL DEFAULT (0),
MessageNotificationMode BOOL DEFAULT (0),
GUID TEXT
);";
// 复用现有的 ExecuteNonQuery 方法
SQLitedata.ExecuteNonQuery(createSql, null);
string Sql = @"
INSERT INTO Users (User,UserId ,UserName ,UserPassword ,Enterprise ,Department ,ServerID ,Groups ,ProxyID ,LogNo ,LocalAreaNetworkMode ,DarkMode ,MessageNotificationMode ,GUID
)VALUES('SUNLIGHT','SUNLIGHT','SUNLIGHT','SUNLIGHT','SUNLIGHT','ENGINEER','TEST','ENGINEER','',0,0,0,0,'"+ Models.AppModels.GetAppUniqueId() + "');";
// 复用现有的 ExecuteNonQuery 方法
SQLitedata.ExecuteNonQuery(Sql, null);
}
private static void CreateFreshUserTableNotification()
{
// 建表语句
string createSql = @"
CREATE TABLE Notification (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Title TEXT,
Content TEXT,
Time TEXT,
Type INT
);";
// 复用现有的 ExecuteNonQuery 方法
SQLitedata.ExecuteNonQuery(createSql, null);
}
}
}

504
Class/SqliteHelper.cs

@ -0,0 +1,504 @@
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SunlightAggregationTerminal.Class
{
public class ClsLock : IDisposable
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
public IDisposable Read()
{
_lock.EnterReadLock();
return new DisposableAction(() => _lock.ExitReadLock());
}
public void Dispose() => _lock?.Dispose();
private class DisposableAction : IDisposable
{
private readonly Action _action;
public DisposableAction(Action action) => _action = action;
public void Dispose() => _action?.Invoke();
}
}
public class SqliteHelper
{
#region 字段
private SqliteTransaction? dbTrans = null!;
private static readonly Dictionary<string, ClsLock> RWL = new Dictionary<string, ClsLock>();
private readonly string mDataFile;
private readonly string mPassWord = "";
private readonly string lockName = "";
private SqliteConnection mConn = null!;
#endregion
#region 构造函数
public SqliteHelper(string dataFile)
{
this.mDataFile = dataFile ?? throw new ArgumentNullException(nameof(dataFile));
this.mDataFile = dataFile; // 注意:原代码有两次赋值,保留逻辑
if (!RWL.ContainsKey(dataFile))
{
lockName = dataFile;
RWL.Add(dataFile, new ClsLock());
}
}
public SqliteHelper(string dataFile, string PassWord)
{
this.mDataFile = dataFile ?? throw new ArgumentNullException(nameof(dataFile));
this.mPassWord = PassWord ?? throw new ArgumentNullException(nameof(PassWord));
this.mDataFile = dataFile;
if (!RWL.ContainsKey(dataFile))
{
lockName = dataFile;
RWL.Add(dataFile, new ClsLock());
}
}
#endregion
#region 打开/关闭 数据库
// 注意:Microsoft.Data.Sqlite.Core 不支持连接字符串构建器,需手动拼接
// 加密支持通常需要 Microsoft.Data.Sqlite.Core + SQLitePCLRaw.bundle_green (且加密功能可能受限)
public void Open()
{
// 构建连接字符串
var connectionString = $"Data Source={mDataFile}";
if (!string.IsNullOrWhiteSpace(mPassWord))
{
connectionString += $";Password={mPassWord}"; // 注意:原生 Microsoft.Data.Sqlite 不支持加密
// 如果需要加密,通常需要使用 SQLitePCLRaw 的特定提供程序或第三方库
}
mConn = new SqliteConnection(connectionString);
// 如果文件不存在,Open() 会自动创建数据库文件
// 但表结构需要你自己执行 CREATE TABLE
mConn.Open();
Console.WriteLine("The database was opened successfully");
}
public void Close()
{
if (this.mConn != null)
{
try
{
this.mConn.Close();
// 注意:不要在这里移除 RWL,因为这是静态的,可能影响其他实例
// if (RWL.ContainsKey(LockName)) { RWL.Remove(LockName); }
}
catch
{
Console.WriteLine("Shutdown failed");
}
}
Console.WriteLine("The database was shut down successfully");
}
#endregion
#region 事务
public void BeginTrain()
{
EnsureConnection();
dbTrans = mConn.BeginTransaction();
}
public void DBCommit()
{
try
{
dbTrans?.Commit();
dbTrans?.Dispose();
dbTrans = null; // 提交后置空
}
catch (Exception)
{
dbTrans?.Rollback();
dbTrans?.Dispose();
dbTrans = null;
}
}
#endregion
#region 工具
// OpenConnection 逻辑已合并到 Open() 方法中,因为 SqliteConnection 构造函数只接受字符串
public SqliteConnection Connection
{
get { return mConn; }
private set { mConn = value ?? throw new ArgumentNullException(); }
}
protected void EnsureConnection()
{
if (this.mConn == null)
{
throw new Exception("SQLiteManager.Connection=null");
}
if (mConn.State != ConnectionState.Open)
{
mConn.Open();
}
}
public string GetDataFile() => this.mDataFile;
public bool TableExists(string table)
{
if (string.IsNullOrEmpty(table)) return false;
EnsureConnection();
// 注意:参数化查询在表名/列名上无效,这里使用字符串拼接(需确保table变量安全)
// 或者使用 PRAGMA table_info(table_name)
var sql = $"SELECT count(*) FROM sqlite_master WHERE type='table' AND name='{table}'";
using (var cmd = new SqliteCommand(sql, Connection))
{
var result = cmd.ExecuteScalar();
return Convert.ToInt32(result) > 0;
}
}
public bool Vacuum()
{
try
{
using (var Command = new SqliteCommand("VACUUM", Connection))
{
Command.ExecuteNonQuery();
}
return true;
}
catch (Exception) // SqliteException
{
return false;
}
}
/// <summary>
/// 将 IDataReader 转换为 DataSet
/// </summary>
/// <param name="reader">已经执行了查询的 DataReader</param>
/// <returns>包含数据的 DataSet</returns>
public DataSet ReaderToDataSet(IDataReader reader)
{
DataSet ds = new DataSet();
do
{
DataTable schemaTable = reader.GetSchemaTable()!;
DataTable dataTable = new DataTable();
if (schemaTable != null)
{
// 创建列
foreach (DataRow schemaRow in schemaTable.Rows)
{
string colName = schemaRow["ColumnName"].ToString()!;
Type dataType = (Type)schemaRow["DataType"];
// 防止列名重复
string uniqueColName = colName;
int i = 1;
while (dataTable.Columns.Contains(uniqueColName))
{
uniqueColName = colName + i++;
}
DataColumn column = new DataColumn(uniqueColName, dataType);
dataTable.Columns.Add(column);
}
// 读取行
while (reader.Read())
{
object[] values = new object[dataTable.Columns.Count];
for (int i = 0; i < dataTable.Columns.Count; i++)
{
values[i] = reader.IsDBNull(i) ? DBNull.Value : reader.GetValue(i);
}
dataTable.Rows.Add(values);
}
}
else
{
// 如果没有 Schema (例如 "SELECT 1" 这种标量查询)
// 需要手动创建列
for (int i = 0; i < reader.FieldCount; i++)
{
dataTable.Columns.Add("Column" + i);
}
while (reader.Read())
{
object[] values = new object[reader.FieldCount];
reader.GetValues(values);
dataTable.Rows.Add(values);
}
}
ds.Tables.Add(dataTable);
} while (reader.NextResult()); // 处理多个结果集
return ds;
}
#endregion
#region 执行SQL (Reader & Scalar)
public SqliteDataReader? ExecuteReader(string sql, SqliteParameter[]? paramArr)
{
if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
EnsureConnection();
// 注意:using 语句会立即释放 Reader,导致无法读取
// 这里的 RWL 锁在 using 外部处理,或者调用方自己处理锁
// 这里简化逻辑,直接返回 Reader,调用方需自行管理连接状态
var cmd = new SqliteCommand(sql, Connection);
if (paramArr != null)
{
cmd.Parameters.AddRange(paramArr);
}
// CommandBehavior.CloseConnection 确保 Reader 关闭时连接也关闭
// 但这里我们管理连接,所以不加
try
{
// 注意:不要在这里 Dispose Command,否则 Reader 会失效
// 返回 Reader,由调用方在读取完毕后关闭
return cmd.ExecuteReader();
}
catch(Exception ex)
{
Console.WriteLine("[SQLITE_ExecuteReader:err]" + ex.ToString());
cmd?.Dispose();
return null;
}
}
public DataSet? ExecuteDataSet(string sql, SqliteParameter[]? paramArr)
{
var dat = ExecuteReader(sql, paramArr);
if (dat == null)
{
return null;
}
return ReaderToDataSet(dat);
}
// ExecuteScalar
public object? ExecuteScalar(string sql, SqliteParameter[]? paramArr)
{
if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
EnsureConnection();
using (RWL[lockName].Read())
{
using (var cmd = new SqliteCommand(sql, Connection))
{
if (paramArr != null)
{
cmd.Parameters.AddRange(paramArr);
}
try
{
return cmd.ExecuteScalar();
}
catch (Exception ex)
{
Console.WriteLine("[SQLITE_ExecuteScalar:err]" + ex.ToString());
return null;
}
}
}
}
// QueryOne (返回字典或对象)
public Dictionary<string, object?>? QueryOne(string table, string conditionCol, object conditionVal)
{
if (string.IsNullOrEmpty(table)) return null;
EnsureConnection();
string sql = $"SELECT * FROM [{table}]"; // 使用 [] 防止关键字冲突
var parameters = new List<SqliteParameter>();
if (!string.IsNullOrEmpty(conditionCol))
{
sql += $" WHERE [{conditionCol}] = @{conditionCol}";
parameters.Add(new SqliteParameter($"@{conditionCol}", conditionVal));
}
using (var cmd = new SqliteCommand(sql, Connection))
{
cmd.Parameters.AddRange(parameters.ToArray());
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
var dict = new Dictionary<string, object?>();
for (int i = 0; i < reader.FieldCount; i++)
{
var value = reader.GetValue(i);
if (value == DBNull.Value) value = null;
dict[reader.GetName(i)] = value;
}
return dict;
}
}
}
return null;
}
#endregion
#region 增 删 改
// 注意:参数前缀统一为 @
public int InsertData(string table, Dictionary<string, object> entity)
{
if (string.IsNullOrEmpty(table)) throw new ArgumentNullException(nameof(table));
EnsureConnection();
string sql = BuildInsert(table, entity);
var parameters = BuildParamArray(entity);
return ExecuteNonQuery(sql, parameters);
}
public int Update(string table, Dictionary<string, object> entity, string where, SqliteParameter[]? whereParams)
{
if (string.IsNullOrEmpty(table)) throw new ArgumentNullException(nameof(table));
EnsureConnection();
string sql = BuildUpdate(table, entity);
var parameters = BuildParamArray(entity);
if (!string.IsNullOrEmpty(where))
{
sql += " WHERE " + where;
if (whereParams != null)
{
var combined = new SqliteParameter[parameters.Length + whereParams.Length];
parameters.CopyTo(combined, 0);
whereParams.CopyTo(combined, parameters.Length);
parameters = combined;
}
}
return ExecuteNonQuery(sql, parameters);
}
public int Delete(string table, string where, SqliteParameter[]? whereParams)
{
if (string.IsNullOrEmpty(table)) return 0;
EnsureConnection();
string sql = $"DELETE FROM [{table}]";
if (!string.IsNullOrEmpty(where))
{
sql += " WHERE " + where;
}
return ExecuteNonQuery(sql, whereParams);
}
public int ExecuteNonQuery(string sql, SqliteParameter[]? paramArr)
{
if (string.IsNullOrEmpty(sql)) throw new ArgumentNullException(nameof(sql));
EnsureConnection();
using (RWL[lockName].Read()) // 注意:写操作通常建议用 Write 锁
{
using (var cmd = new SqliteCommand(sql, Connection))
{
if (paramArr != null)
{
cmd.Parameters.AddRange(paramArr);
}
try
{
return cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine("[SQLITE_ExecuteScalar:err]" + ex.ToString());
return 0;
}
}
}
}
// 构建 SQL 和 参数的方法保持不变,仅泛型类型变更
private SqliteParameter[] BuildParamArray(Dictionary<string, object> entity)
{
var list = new List<SqliteParameter>();
foreach (string key in entity.Keys)
{
// 注意:参数名必须包含 @ 前缀
list.Add(new SqliteParameter($"@{key}", entity[key]));
}
return list.ToArray();
}
private string BuildInsert(string table, Dictionary<string, object> entity)
{
var buf = new StringBuilder();
buf.Append("INSERT INTO ").Append(table).Append(" (");
var cols = new List<string>();
var vals = new List<string>();
foreach (string key in entity.Keys)
{
cols.Add(key);
vals.Add($"@{key}");
}
buf.Append(string.Join(",", cols)).Append(") VALUES (");
buf.Append(string.Join(",", vals)).Append(")");
return buf.ToString();
}
private string BuildUpdate(string table, Dictionary<string, object> entity)
{
var buf = new StringBuilder();
buf.Append("UPDATE ").Append(table).Append(" SET ");
var sets = new List<string>();
foreach (string key in entity.Keys)
{
sets.Add($"{key} = @{key}");
}
buf.Append(string.Join(",", sets));
return buf.ToString();
}
// 辅助方法:DataRow 转 Dictionary (由于没有 DataTable,通常直接从 Reader 转)
// 这里保留原方法签名,但实际使用中可能需要调整
public Dictionary<string, object?> DataTableToDictionary(DataTable dataTable)
{
var result = new Dictionary<string, object?>();
if (dataTable.Rows.Count > 0)
{
var row = dataTable.Rows[0];
foreach (DataColumn column in dataTable.Columns)
{
var value = row[column];
if (value == DBNull.Value) value = null;
result[column.ColumnName] = value;
}
}
return result;
}
#endregion
}
}

54
Class/TcpServer.cs

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Text;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace SunlightAggregationTerminal.Class
{
public class TcpServer
{
public static string ServerIP = "";
public static string ServerName = "";
public static string ServerPassword = "";
public static string ServerTerminal = "";
public static TcpClient tcpClient = new TcpClient();
public static void Configuration(string ip, string Name, string Password, string Terminal)
{
ServerIP = ip;
ServerName = Name;
ServerPassword = Password;
ServerTerminal = Terminal;
}
public static async void Query(string Dat)
{
//发送字符串数据
await tcpClient.SendAsync("hello");
}
private static async Task CreateCustomService()
{
tcpClient.Config.SetRemoteIPHost(ServerIP);
tcpClient.Connecting = (client, e) => { return EasyTask.CompletedTask; };//即将连接到服务器,此时已经创建socket,但是还未建立tcp
tcpClient.Connected = (client, e) => { return EasyTask.CompletedTask; };//成功连接到服务器
tcpClient.Closing = (client, e) => { return EasyTask.CompletedTask; };//即将从服务器断开连接。此处仅主动断开才有效。
tcpClient.Closed = (client, e) => { return EasyTask.CompletedTask; };//从服务器断开连接,当连接不成功时不会触发。
#region Tcp客户端使用Received异步委托接收数据
tcpClient.Received = (client, e) =>
{
//从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。
var mes = e.Memory.Span.ToString(Encoding.UTF8);
tcpClient.Logger.Info($"客户端接收到信息:{mes}");
return EasyTask.CompletedTask;
};
#endregion
await tcpClient.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
}
}
}

1
Images/adduserfilled.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><path fill-rule="evenodd" d="M362.666667,277.333333 L362.666667,341.333333 L426.666667,341.333333 L426.666667,384 L362.666667,384 L362.666667,448 L320,448 L320,384 L256,384 L256,341.333333 L320,341.333333 L320,277.333333 L362.666667,277.333333 Z M186.666667,192 C246.976162,192 296.152141,241.030193 298.573248,302.451465 L298.666667,307.2 L298.666667,320 L234.666667,320 L234.666667,384 L0,384 L0,307.2 C0,245.167377 47.6682428,194.586368 107.383368,192.09609 L112,192 L186.666667,192 Z M149.333333,3.55271368e-14 C190.570594,3.55271368e-14 224,33.4294054 224,74.6666667 C224,114.529353 192.762078,147.096031 153.430084,149.222851 L149.333333,149.333333 C108.096072,149.333333 74.6666667,115.903928 74.6666667,74.6666667 C74.6666667,34.8039808 105.904589,2.2373024 145.236582,0.110482347 L149.333333,3.55271368e-14 Z" transform="translate(64 42.667)"/></svg>

After

Width:  |  Height:  |  Size: 946 B

8
Images/infofeed.svg

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="512px" height="512px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="drop" fill="#000000" transform="translate(64.000000, 85.333333)">
<path d="M128.021333,42.688 L384.042667,42.688 L384.042667,0.0213333333 L128.021333,0.0213333333 L128.021333,42.688 Z M0.0426666667,85.3546667 L85.376,85.3546667 L85.376,1.42108547e-14 L0.0426666667,1.42108547e-14 L0.0426666667,85.3546667 Z M128.021333,128.021333 L384.042667,128.021333 L384.042667,85.3546667 L128.021333,85.3546667 L128.021333,128.021333 Z M298.688,298.688 L384.042667,298.688 L384.042667,213.333333 L298.688,213.333333 L298.688,298.688 Z M-1.42108547e-14,256.021333 L256,256.021333 L256,213.333333 L-1.42108547e-14,213.333333 L-1.42108547e-14,256.021333 Z M0.0426666667,341.354667 L256.042667,341.354667 L256.042667,298.688 L0.0426666667,298.688 L0.0426666667,341.354667 Z" id="Combined-Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

1
Images/ixnotifications.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 512 512"><path fill="currentColor" fill-rule="evenodd" d="M149.333 149.333V384h101.183l154.817 85.333l-.04-85.333h64.04V149.333zM426.667 192v149.333h-64.04v55.698l-101.143-55.698H192V192zm-64-128v42.667h-256v170.666H64V64z"/></svg>

After

Width:  |  Height:  |  Size: 309 B

BIN
Images/lucidescanline.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

8
Images/search.svg

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="512px" height="512px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon" fill="#000000" transform="translate(42.666667, 85.333333)">
<path d="M224,1.42108547e-14 C300.583485,1.42108547e-14 362.666667,62.0831814 362.666667,138.666667 C362.666667,169.260685 352.758877,197.540615 335.976875,220.472881 L429.476859,313.973637 L399.306965,344.143531 L305.80721,250.642814 C282.874767,267.425258 254.594461,277.333333 224,277.333333 C182.528564,277.333333 145.309244,259.127865 119.898048,230.272939 L150.156212,200.014605 C167.76564,221.187509 194.308707,234.666667 224,234.666667 C277.019336,234.666667 320,191.686003 320,138.666667 C320,85.6473307 277.019336,42.6666667 224,42.6666667 C199.739363,42.6666667 177.580626,51.6659715 160.67991,66.5084604 L130.465152,36.2951919 C155.124422,13.7519355 187.956242,1.42108547e-14 224,1.42108547e-14 Z M102.248384,38.2483876 L192,128 L102.248384,217.751611 L72.0784993,187.581721 L110.315,149.333 L7.10542736e-15,149.333333 L7.10542736e-15,106.666667 L110.316,106.666 L72.0784993,68.418278 L102.248384,38.2483876 Z" id="Combined-Shape"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

9
InfoPage.xaml

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.InfoPage"
Title="">
<VerticalStackLayout>
</VerticalStackLayout>
</ContentPage>

9
InfoPage.xaml.cs

@ -0,0 +1,9 @@
namespace SunlightAggregationTerminal;
public partial class InfoPage : ContentPage
{
public InfoPage()
{
InitializeComponent();
}
}

48
MauiProgram.cs

@ -0,0 +1,48 @@
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Handlers;
using SunlightAggregationTerminal.Models;
using ZXing.Net.Maui.Controls;
namespace SunlightAggregationTerminal
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseBarcodeReader()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureMauiHandlers(handlers =>
{
// 这里的 "NoUnderline" 是自定义映射名称,可以随意命名
EntryHandler.Mapper.AppendToMapping("NoUnderline", (handler, view) =>
{
#if ANDROID
// 将背景 tint 设为透明
// 这会移除 Android 默认的下划线颜色
handler.PlatformView.BackgroundTintList = Android.Content.Res.ColorStateList.ValueOf(Android.Graphics.Color.Transparent);
// 如果想彻底移除背景绘制(不仅仅是颜色透明)
// handler.PlatformView.Background = null;
#endif
});
});
// 将 HttpClient 注册为单例服务
builder.Services.AddSingleton<HttpClient>();
builder.Services.AddSingleton<DataModel>();
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
}

89
Models/AppModels.cs

@ -0,0 +1,89 @@
using CommunityToolkit.Mvvm.ComponentModel;
using SunlightAggregationTerminal.Class;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace SunlightAggregationTerminal.Models
{
public class AppModels : ObservableObject
{
public static SqliteHelper sqliteHelper = SQLiteConfig.Config(Path.Combine(FileSystem.AppDataDirectory, "MyAppData.db"))!; //数据库连接路径(获取各平台的应用数据目录)
public AppModels()
{
if(sqliteHelper != null)
{//获取配置信息
sqliteHelper.Open();
var userdata= sqliteHelper.ExecuteDataSet("select * from Users Where LogNo = 1", null);
sqliteHelper.Close();
if (userdata != null)
{
var dat = userdata.Tables[0].AsEnumerable().FirstOrDefault();
if (dat != null)
{
App.GlobalData.UserName = dat.Field<string>("UserName") ?? "";
App.GlobalData.UserId = dat.Field<string>("UserId") ?? "";
App.GlobalData.User = dat.Field<string>("User") ?? "";
App.GlobalData.UserPassword = dat.Field<string>("UserPassword") ?? "";
App.GlobalData.Enterprise = dat.Field<string>("Enterprise") ?? "";
App.GlobalData.Department = dat.Field<string>("Department") ?? "";
App.GlobalData.Groups = dat.Field<string>("Groups") ?? "";
App.GlobalData.ServerID = dat.Field<string>("ServerID") ?? "";
App.GlobalData.ProxyID = dat.Field<string>("ProxyID") ?? "";
App.GlobalData.LogNo = Convert.ToBoolean(dat.Field<object>("LogNo"));
App.GlobalData.LocalAreaNetworkMode = Convert.ToBoolean(dat.Field<object>("LocalAreaNetworkMode"));
App.GlobalData.DarkMode = Convert.ToBoolean(dat.Field<object>("DarkMode"));
App.GlobalData.MessageNotificationMode = Convert.ToBoolean(dat.Field<object>("MessageNotificationMode"));
TcpServer.Configuration(App.GlobalData.ServerID, App.GlobalData.User, App.GlobalData.UserPassword, dat.Field<string>("GUID") ?? "Not");
}
}
}
}
public static void Updata(string sql)
{
sqliteHelper.Open();
sqliteHelper.ExecuteNonQuery(sql, null);
sqliteHelper.Close();
}
public static void INSERT(string sql)
{
sqliteHelper.Open();
sqliteHelper.ExecuteNonQuery(sql, null);
sqliteHelper.Close();
}
public static DataSet? Select(string sql)
{
sqliteHelper.Open();
var userdata = sqliteHelper.ExecuteDataSet(sql, null);
sqliteHelper.Close();
return userdata;
}
public static string GetAppUniqueId()
{
const string key = "sunlight_app_unique_id";
// 尝试从安全存储中读取
string? id = SecureStorage.GetAsync(key).Result;
if (string.IsNullOrEmpty(id))
{
// 如果不存在,生成一个新的 GUID
id = Guid.NewGuid().ToString();
// 保存到安全存储
SecureStorage.SetAsync(key, id).Wait();
}
return id;
}
}
}

40
Models/DataModel.cs

@ -0,0 +1,40 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Text;
namespace SunlightAggregationTerminal.Models
{
#pragma warning disable CA1416
public partial class DataModel : ObservableObject
{
[ObservableProperty]
public partial string User { get; set; }
[ObservableProperty]
public partial string UserId { get; set; }
[ObservableProperty]
public partial string UserName { get; set; }
[ObservableProperty]
public partial string UserPassword { get; set; }
[ObservableProperty]
public partial string Enterprise { get; set; }
[ObservableProperty]
public partial string Department { get; set; }
[ObservableProperty]
public partial string ServerID { get; set; }
[ObservableProperty]
public partial string Groups { get; set; }
[ObservableProperty]
public partial string ProxyID { get; set; }
[ObservableProperty]
public partial bool LogNo { get; set; }
public bool IsNotLogNo => !LogNo;
[ObservableProperty]
public partial bool LocalAreaNetworkMode { get; set; }
[ObservableProperty]
public partial bool DarkMode { get; set; }
[ObservableProperty]
public partial bool MessageNotificationMode { get; set; }
}
}

58
Models/DataSource.cs

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Linq;
namespace SunlightAggregationTerminal.Models
{
// 定义枚举
public enum MsgType
{
Notice, // 通知
Message, // 信息
System // 系统
}
// 定义显示用的模型
public class NotificationItem
{
public Int64 Id { get; set; }
public string? Title { get; set; }
public string? Content { get; set; }
public DateTime Time { get; set; }
public MsgType Type { get; set; }
}
public static class DataService
{
public static DataSet? data = AppModels.Select("select * from Notification Where Time > '" + DateTime.Now.AddDays(-30) + "'");
// 直接返回 List
public static List<NotificationItem> _NotificationData = new List<NotificationItem>();
public static List<NotificationItem> GetAllItems()
{
_NotificationData.Clear();
if (data != null)
{
foreach (DataRow dr in data.Tables[0].Rows)
{
_NotificationData.Add(new NotificationItem
{
Id = dr.Field<Int64>("Id"),
Title = dr.Field<string>("Title") ,
Content = dr.Field<string>("Content"),
Time = DateTime.Parse(dr.Field<string>("Time")!),
Type = (MsgType)dr.Field<Int64>("Type")
});
}
}
// 倒序返回
return _NotificationData.OrderByDescending(x => x.Time).ToList();
}
}
}

60
NotificationPage.xaml

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.NotificationPage"
Loaded="ContentPage_Loaded"
Title="通知中心">
<ScrollView>
<VerticalStackLayout Padding="20" Spacing="20">
<!-- 通知卡片 -->
<Border BackgroundColor="White"
Stroke="#CCCCCC"
Margin="0,0,0,10"
StrokeShape="RoundRectangle 10">
<Grid ColumnDefinitions="Auto,*,Auto" Padding="15" ColumnSpacing="15">
<!-- 文字内容 -->
<VerticalStackLayout Grid.Column="1" VerticalOptions="Center">
<Label x:Name="NoticeItemTitle" FontAttributes="Bold" FontSize="22" TextColor="Black"/>
<Label x:Name="NoticeItemContent" FontSize="16" TextColor="Gray" LineBreakMode="WordWrap"/>
</VerticalStackLayout>
</Grid>
<!-- 点击事件 -->
<Border.GestureRecognizers>
<TapGestureRecognizer Tapped="OnNoticeTapped"/>
</Border.GestureRecognizers>
</Border>
<!-- 信息卡片 -->
<Border BackgroundColor="White"
Stroke="#CCCCCC"
Margin="0,0,0,10"
StrokeShape="RoundRectangle 10">
<Grid ColumnDefinitions="Auto,*,Auto" Padding="15" ColumnSpacing="15">
<VerticalStackLayout Grid.Column="1" VerticalOptions="Center">
<Label x:Name="MessageItemTitle" FontAttributes="Bold" FontSize="22" TextColor="Black"/>
<Label x:Name="MessageItemContent" FontSize="16" TextColor="Gray" LineBreakMode="WordWrap"/>
</VerticalStackLayout>
</Grid>
<Border.GestureRecognizers>
<TapGestureRecognizer Tapped="OnMessageTapped"/>
</Border.GestureRecognizers>
</Border>
<!-- 系统卡片 -->
<Border BackgroundColor="White"
Stroke="#CCCCCC"
Margin="0,0,0,10"
StrokeShape="RoundRectangle 10">
<Grid ColumnDefinitions="Auto,*,Auto" Padding="15" ColumnSpacing="15">
<VerticalStackLayout Grid.Column="1" VerticalOptions="Center">
<Label x:Name="SystemItemTitle" FontAttributes="Bold" FontSize="22" TextColor="Black"/>
<Label x:Name="SystemItemContent" FontSize="16" TextColor="Gray" LineBreakMode="WordWrap"/>
</VerticalStackLayout>
</Grid>
<Border.GestureRecognizers>
<TapGestureRecognizer Tapped="OnSystemTapped"/>
</Border.GestureRecognizers>
</Border>
</VerticalStackLayout>
</ScrollView>
</ContentPage>

93
NotificationPage.xaml.cs

@ -0,0 +1,93 @@
using SunlightAggregationTerminal.Models;
using System.Data;
using System.Linq;
namespace SunlightAggregationTerminal;
public partial class NotificationPage : ContentPage
{
public NotificationItem NoticeItem { get; set; } = new NotificationItem();
public NotificationItem MessageItem { get; set; } = new NotificationItem();
public NotificationItem SystemItem { get; set; } = new NotificationItem();
public NotificationPage()
{
InitializeComponent();
this.BindingContext = this;
}
private void ContentPage_Loaded(object sender, EventArgs e)
{
LoadSummaryData();
}
private void LoadSummaryData()
{
var allItems = DataService.GetAllItems();
// 筛选出 Type 为 Notice 的数据,并取第一条
// 对应 DataTable 的 Select("Type = ...").First()
var noticeItem = allItems.FirstOrDefault(x => x.Type == MsgType.Notice);
// 读取数据 (注意判空,防止找不到数据时报错)
if (noticeItem != null)
{
NoticeItemTitle.Text = noticeItem.Title;
NoticeItemContent.Text = noticeItem.Content;
}
else
{
// 处理没有找到数据的情况
NoticeItemTitle.Text = "暂无通知";
NoticeItemContent.Text = "通知";
}
var messageItem = allItems.FirstOrDefault(x => x.Type == MsgType.Message);
// 读取数据 (注意判空,防止找不到数据时报错)
if (messageItem != null)
{
MessageItemTitle.Text = messageItem.Title;
MessageItemContent.Text = messageItem.Content;
}
else
{
// 处理没有找到数据的情况
MessageItemTitle.Text = "暂无通知";
MessageItemContent.Text = "信息";
}
var systemItem = allItems.FirstOrDefault(x => x.Type == MsgType.System);
// 读取数据 (注意判空,防止找不到数据时报错)
if (systemItem != null)
{
SystemItemTitle.Text = systemItem.Title;
SystemItemContent.Text = systemItem.Content;
}
else
{
// 处理没有找到数据的情况
SystemItemTitle.Text = "暂无通知";
SystemItemContent.Text = "系统信息";
}
}
// 点击跳转到独立页面
private async void OnNoticeTapped(object sender, TappedEventArgs e)
{
await Navigation.PushAsync(new NotificationView.NoticePage());
}
private async void OnMessageTapped(object sender, TappedEventArgs e)
{
await Navigation.PushAsync(new NotificationView.MessagePage());
}
private async void OnSystemTapped(object sender, TappedEventArgs e)
{
await Navigation.PushAsync(new NotificationView.SystemPage());
}
}

39
NotificationView/MessagePage.xaml

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.NotificationView.MessagePage"
Title="信息">
<CollectionView x:Name="CardCollectionView"
ItemsSource="{Binding MessageItems}"
RemainingItemsThreshold="5"
RemainingItemsThresholdReached="CardCollectionView_RemainingItemsThresholdReached">
<!-- 保持滚动条 -->
<CollectionView.ItemTemplate>
<DataTemplate>
<!-- 这里定义单个卡片的 UI 结构 -->
<Border BackgroundColor="White"
Stroke="#CCCCCC"
StrokeThickness="1"
Margin="10,0,10,10"
StrokeShape="RoundRectangle 10">
<Grid Padding="15">
<VerticalStackLayout>
<Label Text="{Binding Title}"
FontAttributes="Bold"
FontSize="22"
TextColor="Black"/>
<Label Text="{Binding Time}"
FontSize="14"
TextColor="Black"
LineBreakMode="WordWrap"/>
<Label Text="{Binding Content}"
FontSize="16"
TextColor="Gray"
LineBreakMode="WordWrap"/>
</VerticalStackLayout>
</Grid>
</Border>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>

42
NotificationView/MessagePage.xaml.cs

@ -0,0 +1,42 @@
using Microsoft.Maui.Controls.Shapes;
using SunlightAggregationTerminal.Models;
using System.Collections.ObjectModel;
namespace SunlightAggregationTerminal.NotificationView;
public partial class MessagePage : ContentPage
{
public ObservableCollection<NotificationItem> MessageItems { get; set; } = new();
public MessagePage()
{
InitializeComponent();
this.BindingContext = this;
LoadData(); // 加载数据
}
private void LoadData()
{
// 假设这是你获取数据的地方
var allData = DataService._NotificationData;
if (allData != null)
{
// 清空旧数据
MessageItems.Clear();
// 按时间倒序,并只取最新的 N 条(例如 50 条)
var itemsToShow = allData.OrderByDescending(x => x.Time).Take(50).Where(x => x.Type == MsgType.Message);
// 将数据添加到集合中,UI 会自动更新
foreach (var item in itemsToShow)
{
MessageItems.Add(item);
}
}
}
private void CardCollectionView_RemainingItemsThresholdReached(object sender, EventArgs e)
{
}
}

39
NotificationView/NoticePage.xaml

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.NotificationView.NoticePage"
Title="通知">
<CollectionView x:Name="CardCollectionView"
ItemsSource="{Binding NoticeItems}"
RemainingItemsThreshold="5"
RemainingItemsThresholdReached="CardCollectionView_RemainingItemsThresholdReached">
<!-- 保持滚动条 -->
<CollectionView.ItemTemplate>
<DataTemplate>
<!-- 这里定义单个卡片的 UI 结构 -->
<Border BackgroundColor="White"
Stroke="#CCCCCC"
StrokeThickness="1"
Margin="10,0,10,10"
StrokeShape="RoundRectangle 10">
<Grid Padding="15">
<VerticalStackLayout>
<Label Text="{Binding Title}"
FontAttributes="Bold"
FontSize="22"
TextColor="Black"/>
<Label Text="{Binding Time}"
FontSize="14"
TextColor="Black"
LineBreakMode="WordWrap"/>
<Label Text="{Binding Content}"
FontSize="16"
TextColor="Gray"
LineBreakMode="WordWrap"/>
</VerticalStackLayout>
</Grid>
</Border>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>

43
NotificationView/NoticePage.xaml.cs

@ -0,0 +1,43 @@
using Microsoft.Maui.Controls.Shapes;
using SunlightAggregationTerminal.Models;
using System.Collections.ObjectModel;
using System.Data;
namespace SunlightAggregationTerminal.NotificationView;
public partial class NoticePage : ContentPage
{
public ObservableCollection<NotificationItem> NoticeItems { get; set; } = new();
public NoticePage()
{
InitializeComponent();
this.BindingContext = this;
LoadData(); // 加载数据
}
private void LoadData()
{
// 假设这是你获取数据的地方
var allData = DataService._NotificationData;
if (allData != null)
{
// 清空旧数据
NoticeItems.Clear();
// 按时间倒序,并只取最新的 N 条(例如 50 条)
var itemsToShow = allData.OrderByDescending(x => x.Time).Take(50).Where(x => x.Type == MsgType.Notice);
// 将数据添加到集合中,UI 会自动更新
foreach (var item in itemsToShow)
{
NoticeItems.Add(item);
}
}
}
private void CardCollectionView_RemainingItemsThresholdReached(object sender, EventArgs e)
{
}
}

39
NotificationView/SystemPage.xaml

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.NotificationView.SystemPage"
Title="系统信息">
<CollectionView x:Name="CardCollectionView"
ItemsSource="{Binding SystemItems}"
RemainingItemsThreshold="5"
RemainingItemsThresholdReached="CardCollectionView_RemainingItemsThresholdReached">
<!-- 保持滚动条 -->
<CollectionView.ItemTemplate>
<DataTemplate>
<!-- 这里定义单个卡片的 UI 结构 -->
<Border BackgroundColor="White"
Stroke="#CCCCCC"
StrokeThickness="1"
Margin="10,0,10,10"
StrokeShape="RoundRectangle 10">
<Grid Padding="15">
<VerticalStackLayout>
<Label Text="{Binding Title}"
FontAttributes="Bold"
FontSize="22"
TextColor="Black"/>
<Label Text="{Binding Time}"
FontSize="14"
TextColor="Black"
LineBreakMode="WordWrap"/>
<Label Text="{Binding Content}"
FontSize="16"
TextColor="Gray"
LineBreakMode="WordWrap"/>
</VerticalStackLayout>
</Grid>
</Border>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>

42
NotificationView/SystemPage.xaml.cs

@ -0,0 +1,42 @@
using Microsoft.Maui.Controls.Shapes;
using SunlightAggregationTerminal.Models;
using System.Collections.ObjectModel;
namespace SunlightAggregationTerminal.NotificationView;
public partial class SystemPage : ContentPage
{
public ObservableCollection<NotificationItem> SystemItems { get; set; } = new();
public SystemPage()
{
InitializeComponent();
this.BindingContext = this;
LoadData(); // 加载数据
}
private void LoadData()
{
// 假设这是你获取数据的地方
var allData = DataService._NotificationData;
if (allData != null)
{
// 清空旧数据
SystemItems.Clear();
// 按时间倒序,并只取最新的 N 条(例如 50 条)
var itemsToShow = allData.OrderByDescending(x => x.Time).Take(50).Where(x => x.Type == MsgType.System);
// 将数据添加到集合中,UI 会自动更新
foreach (var item in itemsToShow)
{
SystemItems.Add(item);
}
}
}
private void CardCollectionView_RemainingItemsThresholdReached(object sender, EventArgs e)
{
}
}

7
Platforms/Android/AndroidManifest.xml

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
</manifest>

11
Platforms/Android/MainActivity.cs

@ -0,0 +1,11 @@
using Android.App;
using Android.Content.PM;
using Android.OS;
namespace SunlightAggregationTerminal
{
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
}
}

16
Platforms/Android/MainApplication.cs

@ -0,0 +1,16 @@
using Android.App;
using Android.Runtime;
namespace SunlightAggregationTerminal
{
[Application(UsesCleartextTraffic = true)]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}

6
Platforms/Android/Resources/values/colors.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#512BD4</color>
<color name="colorPrimaryDark">#2B0B98</color>
<color name="colorAccent">#2B0B98</color>
</resources>

10
Platforms/MacCatalyst/AppDelegate.cs

@ -0,0 +1,10 @@
using Foundation;
namespace SunlightAggregationTerminal
{
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}

14
Platforms/MacCatalyst/Entitlements.plist

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<!-- See https://aka.ms/maui-publish-app-store#add-entitlements for more information about adding entitlements.-->
<dict>
<!-- App Sandbox must be enabled to distribute a MacCatalyst app through the Mac App Store. -->
<key>com.apple.security.app-sandbox</key>
<true/>
<!-- When App Sandbox is enabled, this value is required to open outgoing network connections. -->
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

40
Platforms/MacCatalyst/Info.plist

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- The Mac App Store requires you specify if the app uses encryption. -->
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption -->
<!-- <key>ITSAppUsesNonExemptEncryption</key> -->
<!-- Please indicate <true/> or <false/> here. -->
<!-- Specify the category for your app here. -->
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype -->
<!-- <key>LSApplicationCategoryType</key> -->
<!-- <string>public.app-category.YOUR-CATEGORY-HERE</string> -->
<key>UIDeviceFamily</key>
<array>
<integer>2</integer>
</array>
<key>LSApplicationCategoryType</key>
<string>public.app-category.lifestyle</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>

16
Platforms/MacCatalyst/Program.cs

@ -0,0 +1,16 @@
using ObjCRuntime;
using UIKit;
namespace SunlightAggregationTerminal
{
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}

8
Platforms/Windows/App.xaml

@ -0,0 +1,8 @@
<maui:MauiWinUIApplication
x:Class="SunlightAggregationTerminal.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:SunlightAggregationTerminal.WinUI">
</maui:MauiWinUIApplication>

25
Platforms/Windows/App.xaml.cs

@ -0,0 +1,25 @@
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 SunlightAggregationTerminal.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();
}
}

46
Platforms/Windows/Package.appxmanifest

@ -0,0 +1,46 @@
<?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="40C4D887-7034-46F6-A7C2-185E7FC94FC4" 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>

17
Platforms/Windows/app.manifest

@ -0,0 +1,17 @@
<?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="SunlightAggregationTerminal.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>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
</assembly>

10
Platforms/iOS/AppDelegate.cs

@ -0,0 +1,10 @@
using Foundation;
namespace SunlightAggregationTerminal
{
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}

32
Platforms/iOS/Info.plist

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
</dict>
</plist>

16
Platforms/iOS/Program.cs

@ -0,0 +1,16 @@
using ObjCRuntime;
using UIKit;
namespace SunlightAggregationTerminal
{
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}

51
Platforms/iOS/Resources/PrivacyInfo.xcprivacy

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This is the minimum required version of the Apple Privacy Manifest for .NET MAUI apps.
The contents below are needed because of APIs that are used in the .NET framework and .NET MAUI SDK.
You are responsible for adding extra entries as needed for your application.
More information: https://aka.ms/maui-privacy-manifest
-->
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
</array>
</dict>
<!--
The entry below is only needed when you're using the Preferences API in your app.
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict> -->
</array>
</dict>
</plist>

224
ProfilePage.xaml

@ -0,0 +1,224 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.ProfilePage"
Title="">
<Grid RowDefinitions="Auto, *"
ColumnDefinitions="*, Auto"
BackgroundColor="#F5F5F5">
<!-- 顶部个人信息区 -->
<Border Padding="20"
Grid.Row="0"
Grid.Column="0" >
<Grid RowDefinitions="Auto, *"
ColumnDefinitions="*, Auto">
<HorizontalStackLayout VerticalOptions="Center" Spacing="15">
<!-- 信息 -->
<VerticalStackLayout Grid.Row="0" Grid.Column="0"
VerticalOptions="Center">
<Label Text="{Binding UserName}"
FontSize="26"
FontAttributes="Bold"
TextColor="Black" />
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="ID:" FontSize="12" TextColor="Gray" Margin="0,0,0,0"/>
<Label Text="{Binding UserId}" FontSize="12" TextColor="Gray" Margin="0,0,0,0"/>
</HorizontalStackLayout>
</VerticalStackLayout>
</HorizontalStackLayout>
</Grid>
</Border>
<ScrollView Grid.Row="1" Grid.Column="0">
<VerticalStackLayout Spacing="0" Padding="0">
<Border BackgroundColor="White" Stroke="#CCCCCC" Margin="5,5,5,0" StrokeShape="RoundRectangle 10">
<Grid>
<VerticalStackLayout Spacing="0" Padding="0">
<Label Text="管理" FontSize="12" TextColor="Gray" Margin="15,5,0,5" />
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="0" />
<VerticalStackLayout>
<ContentView HeightRequest="50" Padding="15,0">
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="公司:" VerticalOptions="Center" />
<Label Text="{Binding Enterprise}" TextColor="Gray" VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
</ContentView>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="15,0" />
<ContentView HeightRequest="50" Padding="15,0">
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="部门:" VerticalOptions="Center" />
<Label Text="{Binding Department}" TextColor="Gray" VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
</ContentView>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="15,0" />
<ContentView HeightRequest="50" Padding="15,0">
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="组:" VerticalOptions="Center" />
<Label Text="{Binding Groups}" TextColor="Gray" VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
</ContentView>
</VerticalStackLayout>
</VerticalStackLayout>
</Grid>
</Border>
<!-- 账号与安全 -->
<Border BackgroundColor="White" Stroke="#CCCCCC" Margin="5,5,5,0" StrokeShape="RoundRectangle 10">
<Grid>
<VerticalStackLayout Spacing="0" Padding="0">
<Label Text="账号" FontSize="12" TextColor="Gray" Margin="15,5,0,5" />
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="0" />
<VerticalStackLayout>
<ContentView HeightRequest="50" Padding="15,0">
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="账号:" VerticalOptions="Center" />
<Label Text="{Binding User}" TextColor="Gray"
VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
</ContentView>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="15,0" />
<ContentView HeightRequest="50" Padding="15,0">
<Grid>
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="姓名:" VerticalOptions="Center" />
<Label Text="{Binding UserName}" TextColor="Gray"
VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
<Label Text=">" TextColor="Gray"
VerticalOptions="Center" HorizontalOptions="End"
Margin="0,0,10,0" FontSize="24"/>
</Grid>
<ContentView.GestureRecognizers>
<TapGestureRecognizer Tapped="UserName_Tapped"/>
</ContentView.GestureRecognizers>
</ContentView>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="15,0" />
<ContentView HeightRequest="50" Padding="15,0">
<Grid>
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="密码:" VerticalOptions="Center" />
<Label Text="******" TextColor="Gray"
VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
<Label Text=">" TextColor="Gray"
VerticalOptions="Center" HorizontalOptions="End"
Margin="0,0,10,0" FontSize="24"/>
</Grid>
<ContentView.GestureRecognizers>
<TapGestureRecognizer Tapped="UserPassword_Tapped"/>
</ContentView.GestureRecognizers>
</ContentView>
</VerticalStackLayout>
</VerticalStackLayout>
</Grid>
</Border>
<!-- 通用设置 -->
<Border BackgroundColor="White" Stroke="#CCCCCC"
Margin="5,5,5,0" StrokeShape="RoundRectangle 10">
<Grid>
<VerticalStackLayout Spacing="0" Padding="0">
<Label Text="通用" FontSize="12"
TextColor="Gray" Margin="15,10,0,5" />
<BoxView HeightRequest="1" Color="#E0E0E0" />
<VerticalStackLayout>
<ContentView HeightRequest="50" Padding="15,0">
<Grid>
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="空间清理" VerticalOptions="Center"/>
<Label Text="" TextColor="Gray"
VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
<Label Text=">" TextColor="Gray"
VerticalOptions="Center" HorizontalOptions="End"
Margin="0,0,10,0" FontSize="24"/>
</Grid>
</ContentView>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="15,0" />
<ContentView HeightRequest="40" Padding="15,0">
<Grid VerticalOptions="Center">
<Label Text="消息通知" VerticalOptions="Center" />
<Switch IsToggled="{Binding MessageNotificationMode}" HorizontalOptions="End"
Toggled="MessageNotificationMode_Toggled" VerticalOptions="Center" />
</Grid>
</ContentView>
<ContentView HeightRequest="40" Padding="15,0">
<Grid VerticalOptions="Center">
<Label Text="深色模式" VerticalOptions="Center"/>
<Switch IsToggled="{Binding DarkMode}" HorizontalOptions="End"
Toggled="DarkMode_Toggled" VerticalOptions="Center"/>
</Grid>
</ContentView>
</VerticalStackLayout>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="0,0,0,0" />
</VerticalStackLayout>
</Grid>
</Border>
<!-- 连接设置 -->
<Border BackgroundColor="White" Stroke="#CCCCCC" Margin="5,5,5,0" StrokeShape="RoundRectangle 10">
<Grid >
<VerticalStackLayout Spacing="0" Padding="0">
<Label Text="连接" FontSize="12" TextColor="Gray" Margin="15,10,0,5" />
<BoxView HeightRequest="1" Color="#E0E0E0" />
<VerticalStackLayout>
<ContentView HeightRequest="50" Padding="15,0">
<Grid>
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="服务器:" VerticalOptions="Center" />
<Label Text="{Binding ServerID}" TextColor="Gray" VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
<Label Text=">" TextColor="Gray" VerticalOptions="Center" HorizontalOptions="End"
Margin="0,0,10,0" FontSize="24"/>
</Grid>
<ContentView.GestureRecognizers>
<TapGestureRecognizer Tapped="ServerID_Tapped"/>
</ContentView.GestureRecognizers>
</ContentView>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="15,0" />
<ContentView HeightRequest="50" Padding="15,0">
<Grid>
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="代理:" VerticalOptions="Center" />
<Label Text="{Binding ProxyID}" TextColor="Gray" VerticalOptions="Center" Margin="0,0,10,0"/>
</HorizontalStackLayout>
<Label Text=">" TextColor="Gray" VerticalOptions="Center" HorizontalOptions="End"
Margin="0,0,10,0" FontSize="24"/>
</Grid>
<ContentView.GestureRecognizers>
<TapGestureRecognizer Tapped="ProxyID_Tapped"/>
</ContentView.GestureRecognizers>
</ContentView>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="15,0" />
<ContentView HeightRequest="40" Padding="15,0">
<Grid VerticalOptions="Center">
<Label Text="内网直连" VerticalOptions="Center" />
<Switch IsToggled="{Binding LocalAreaNetworkMode}" HorizontalOptions="End"
Toggled="LocalAreaNetworkMode_Toggled" VerticalOptions="Center"/>
</Grid>
</ContentView>
</VerticalStackLayout>
<BoxView HeightRequest="1" Color="#E0E0E0" Margin="0,0,0,0" />
</VerticalStackLayout>
</Grid>
</Border>
<!-- 退出登录 -->
<Button x:Name="Out_Button"
Text="退出登录"
BackgroundColor="White"
TextColor="Red"
BorderColor="Red"
BorderWidth="1"
Margin="20, 30"
CornerRadius="8"
Clicked="Out_Clicked" />
</VerticalStackLayout>
</ScrollView>
</Grid>
</ContentPage>

97
ProfilePage.xaml.cs

@ -0,0 +1,97 @@
using CommunityToolkit.Mvvm.ComponentModel;
using SunlightAggregationTerminal.Models;
using SunlightAggregationTerminal.View;
using System.Collections.ObjectModel;
namespace SunlightAggregationTerminal;
public partial class ProfilePage : ContentPage
{
public ProfilePage()
{
InitializeComponent();
this.BindingContext = App.GlobalData;
}
private async void Out_Clicked(object sender, EventArgs e)
{
bool shouldExit = await DisplayAlertAsync("退出确认", "确定要退出吗?", "是", "否");
if (shouldExit)
{
App.GlobalData.LogNo = false;
var logpage = new View.LogPage();
await Navigation.PushModalAsync(logpage);
}
}
private async void UserName_Tapped(object sender, TappedEventArgs e)
{
string result = await DisplayPromptAsync("","姓名");
// 处理结果
if (!string.IsNullOrWhiteSpace(result))
{
App.GlobalData.UserName=result;
string sql = @"UPDATE Users SET UserName = '" + App.GlobalData.UserName
+ "' WHERE User='" + App.GlobalData.User + "';";
AppModels.Updata(sql);
}
}
private async void UserPassword_Tapped(object sender, TappedEventArgs e)
{
string result = await DisplayPromptAsync("", "密码");
// 处理结果
if (!string.IsNullOrWhiteSpace(result))
{
App.GlobalData.UserPassword = result;
string sql = @"UPDATE Users SET UserPassword = '" + App.GlobalData.UserPassword
+ "' WHERE User='" + App.GlobalData.User + "';";
AppModels.Updata(sql);
}
}
private async void ServerID_Tapped(object sender, TappedEventArgs e)
{
string result = await DisplayPromptAsync("", "服务器地址");
// 处理结果
if (!string.IsNullOrWhiteSpace(result))
{
App.GlobalData.ServerID = result;
string sql = @"UPDATE Users SET ServerID = '" + App.GlobalData.ServerID
+ "' WHERE User='" + App.GlobalData.User + "';";
AppModels.Updata(sql);
}
}
private async void ProxyID_Tapped(object sender, TappedEventArgs e)
{
string result = await DisplayPromptAsync("", "代理");
// 处理结果
if (!string.IsNullOrWhiteSpace(result))
{
App.GlobalData.ProxyID = result;
string sql = @"UPDATE Users SET ProxyID = '" + App.GlobalData.ProxyID
+ "' WHERE User='" + App.GlobalData.User + "';";
AppModels.Updata(sql);
}
}
private void MessageNotificationMode_Toggled(object sender, ToggledEventArgs e)
{
string sql = @"UPDATE Users SET MessageNotificationMode = "+ App.GlobalData.MessageNotificationMode
+ " WHERE User='" + App.GlobalData.User + "';";
AppModels.Updata(sql);
}
private void DarkMode_Toggled(object sender, ToggledEventArgs e)
{
string sql = @"UPDATE Users SET DarkMode = " + App.GlobalData.DarkMode
+ " WHERE User='" + App.GlobalData.User + "';";
AppModels.Updata(sql);
}
private void LocalAreaNetworkMode_Toggled(object sender, ToggledEventArgs e)
{
string sql = @"UPDATE Users SET LocalAreaNetworkMode = " + App.GlobalData.LocalAreaNetworkMode
+ " WHERE User='" + App.GlobalData.User + "';";
AppModels.Updata(sql);
}
}

8
Properties/launchSettings.json

@ -0,0 +1,8 @@
{
"profiles": {
"Windows Machine": {
"commandName": "Project",
"nativeDebugging": false
}
}
}

43
QueryPage.xaml

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.QueryPage"
Title="">
<Grid RowDefinitions="Auto, *" ColumnDefinitions="*, Auto">
<Border Grid.Row="0"
Grid.Column="0"
Stroke="#CCCCCC"
StrokeThickness="1"
StrokeShape="RoundRectangle 10"
BackgroundColor="White"
Padding="0,0"
VerticalOptions="Center"
Margin="10,10,0,10">
<Entry x:Name="ResultEntry"
Grid.Row="0"
Grid.Column="0"
FontSize="Medium"
Placeholder="等待扫码..."
VerticalOptions="Center"
BackgroundColor="Transparent"
Margin="0,0,0,0" >
</Entry>
</Border>
<!--扫码按钮-->
<Button Grid.Row="0"
Grid.Column="1"
Clicked="OnScanButtonClicked"
BackgroundColor="Transparent"
WidthRequest="60"
HeightRequest="60"
Margin="10">
<Button.ImageSource>
<!-- 设置图标 -->
<FileImageSource File="lucidescanline.png" />
</Button.ImageSource>
</Button>
<VerticalStackLayout>
</VerticalStackLayout>
</Grid>
</ContentPage>

38
QueryPage.xaml.cs

@ -0,0 +1,38 @@
namespace SunlightAggregationTerminal;
public partial class QueryPage : ContentPage
{
// 定义一个静态的回调动作
public static Action<string>? OnScanQuery;
public QueryPage()
{
InitializeComponent();
OnScanQuery = (result) =>
{
MainThread.BeginInvokeOnMainThread(() =>
{
ResultEntry.Text = result;
});
};
}
private async void OnScanButtonClicked(object sender, EventArgs e)
{
// 跳转到扫码页面
await Navigation.PushAsync(new ScanPage(0));
}
// 处理“回车/完成”按钮点击
/* private void OnEntryCompleted()
{
string inputText = ResultEntry.Text;
if (!string.IsNullOrWhiteSpace(inputText))
{
// 在这里处理用户手动输入的内容
// 例如:验证格式、跳转页面、或者搜索商品
//DisplayAlert("提示", $"你输入了: {inputText}", "确定");
}
}*/
}

4
Resources/AppIcon/appicon.svg

@ -0,0 +1,4 @@
<?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>

After

Width:  |  Height:  |  Size: 228 B

8
Resources/AppIcon/appiconfg.svg

@ -0,0 +1,8 @@
<?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>

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
Resources/Fonts/OpenSans-Regular.ttf

Binary file not shown.

BIN
Resources/Fonts/OpenSans-Semibold.ttf

Binary file not shown.

BIN
Resources/Images/dotnet_bot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

15
Resources/Raw/AboutAssets.txt

@ -0,0 +1,15 @@
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();
}

8
Resources/Splash/splash.svg

@ -0,0 +1,8 @@
<?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>

After

Width:  |  Height:  |  Size: 1.8 KiB

44
Resources/Styles/Colors.xaml

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?>
<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>

434
Resources/Styles/Styles.xaml

@ -0,0 +1,434 @@
<?xml version="1.0" encoding="UTF-8" ?>
<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="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="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="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="TitleBar">
<Setter Property="MinimumHeightRequest" Value="32"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="TitleActiveStates">
<VisualState x:Name="TitleBarTitleActive">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="ForegroundColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="TitleBarTitleInactive">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="ForegroundColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</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>

19
ScanPage.xaml

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.ScanPage"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.Maui.Controls"
Title="扫一扫">
<Grid>
<zxing:CameraBarcodeReaderView x:Name="barcodeReader"
BarcodesDetected="OnBarcodesDetected"
IsDetecting="True"
CameraLocation="Rear"/>
<GraphicsView x:Name="overlayGraphicsView"
IsVisible="False"
VerticalOptions="Fill"
HorizontalOptions="Fill"
BackgroundColor="Transparent" />
</Grid>
</ContentPage>

30
ScanPage.xaml.cs

@ -0,0 +1,30 @@
using SunlightAggregationTerminal.View;
using ZXing.Multi;
using ZXing.Net.Maui;
namespace SunlightAggregationTerminal;
public partial class ScanPage : ContentPage
{
private int mode;
public ScanPage(int i)
{
InitializeComponent();
mode = i;
}
private async void OnBarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
var firstResult = e.Results?.FirstOrDefault();
if (firstResult is not null)
{
barcodeReader.IsDetecting = false;
// 触发回调
if (mode == 0) QueryPage.OnScanQuery?.Invoke(firstResult.Value);
if (mode == 3) LogPage.OnScanProfile?.Invoke(firstResult.Value);
await Navigation.PopAsync();
}
}
}

131
SunlightAggregationTerminal.csproj

@ -0,0 +1,131 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net10.0-android</TargetFrameworks>
<TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</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>SunlightAggregationTerminal</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Enable XAML source generation for faster build times and improved performance.
This generates C# code from XAML at compile time instead of runtime inflation.
To disable, remove this line.
For individual files, you can override by setting Inflator metadata:
<MauiXaml Update="MyPage.xaml" Inflator="Default" /> (reverts to defaults: Runtime for Debug, XamlC for Release)
<MauiXaml Update="MyPage.xaml" Inflator="Runtime" /> (force runtime inflation) -->
<MauiXamlInflator>SourceGen</MauiXamlInflator>
<!-- Display name -->
<ApplicationTitle>SunlightAggregationTerminal</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.companyname.sunlightaggregationterminal</ApplicationId>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
<WindowsPackageType>None</WindowsPackageType>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</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>
</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>
<None Remove="Images\adduserfilled.svg" />
<None Remove="Images\infofeed.svg" />
<None Remove="Images\ixnotifications.svg" />
<None Remove="Images\lucidescanline.png" />
<None Remove="Images\search.svg" />
</ItemGroup>
<ItemGroup>
<MauiImage Include="Images\adduserfilled.svg" />
<MauiImage Include="Images\infofeed.svg" />
<MauiImage Include="Images\ixnotifications.svg" />
<MauiImage Include="Images\lucidescanline.png" />
<MauiImage Include="Images\search.svg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.7" />
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.51" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.7" />
<PackageReference Include="TouchSocket" Version="4.2.7" />
<PackageReference Include="ZXing.Net.Maui.Controls" Version="0.7.4" />
</ItemGroup>
<ItemGroup>
<MauiXaml Update="InfoPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NotificationPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NotificationView\MessagePage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NotificationView\NoticePage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="NotificationView\SystemPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="ProfilePage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="QueryPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="ScanPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="View\LogPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="View\LogWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>
<ItemGroup>
<Folder Include="Services\" />
</ItemGroup>
</Project>

3
SunlightAggregationTerminal.slnx

@ -0,0 +1,3 @@
<Solution>
<Project Path="SunlightAggregationTerminal.csproj" />
</Solution>

65
View/LogPage.xaml

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.View.LogPage"
Title="LogPage">
<VerticalStackLayout Padding="30" Spacing="20"
VerticalOptions="Center"
HorizontalOptions="Center"
MaximumWidthRequest="600">
<Grid RowDefinitions="Auto, *" ColumnDefinitions="*, Auto">
<HorizontalStackLayout VerticalOptions="Center" Spacing="15">
<!-- 信息 -->
<VerticalStackLayout Grid.Row="0"
Grid.Column="0"
VerticalOptions="Center">
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="服务器:" FontSize="26" TextColor="Black" />
<Entry x:Name="Enterprise"
Text="IP"
FontSize="26"
TextColor="Gray"
WidthRequest="300"/>
</HorizontalStackLayout>
</VerticalStackLayout>
</HorizontalStackLayout>
<Button Grid.Row="0" Grid.Column="1"
Clicked="OnScanButtonClicked"
BackgroundColor="Transparent"
WidthRequest="60"
HeightRequest="60" >
<Button.ImageSource>
<!-- 设置图标 -->
<FileImageSource File="lucidescanline.png" />
</Button.ImageSource>
</Button>
</Grid>
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="用户:" FontSize="26" TextColor="Black" />
<!-- 用户名输入 -->
<Entry x:Name="UsernameEntry"
Placeholder="请输入用户名"
FontSize="26"
WidthRequest="380"/>
</HorizontalStackLayout>
<HorizontalStackLayout VerticalOptions="Center">
<Label Text="密码:" FontSize="26" TextColor="Black"/>
<!-- 密码输入 -->
<Entry x:Name="PasswordEntry"
Placeholder="请输入密码"
IsPassword="True"
FontSize="26"
WidthRequest="380"/>
</HorizontalStackLayout>
<!-- 登录按钮 -->
<Button Text="登 录"
BackgroundColor="#512BD4"
TextColor="White"
HeightRequest="50"
WidthRequest="100"
CornerRadius="10"
Margin="0,10,0,0"
Clicked="Log_Clicked"/>
</VerticalStackLayout>
</ContentPage>

61
View/LogPage.xaml.cs

@ -0,0 +1,61 @@
using SunlightAggregationTerminal.Class;
using SunlightAggregationTerminal.Models;
using System.Data;
namespace SunlightAggregationTerminal.View;
public partial class LogPage : ContentPage
{
public static Action<string>? OnScanProfile;
public LogPage()
{
InitializeComponent();
OnScanProfile = (result) =>
{
MainThread.BeginInvokeOnMainThread(() =>
{
Enterprise.Text = result;
});
};
}
private void OnScanButtonClicked(object sender, EventArgs e)
{
// 跳转到扫码页面
var scanPage = new ScanPage(3);
Navigation.PushAsync(scanPage);
}
private async void Log_Clicked(object sender, EventArgs e)//登录
{
TcpServer.Configuration(Enterprise.Text, UsernameEntry.Text, PasswordEntry.Text,
Models.AppModels.GetAppUniqueId());
// while(!App.GlobalData.LogNo);
/*
var temp_name = AppModels.Select("SELECT * FROM Users WHERE User='" + UsernameEntry.Text + "' LIMIT 1;");
if (temp_name != null)
{
//存在更新账号状态
//var name = temp_name.Tables[0].AsEnumerable().FirstOrDefault();
string sql = @"UPDATE Users SET ServerID = '" + Enterprise.Text
+ "', UserPassword = '" + PasswordEntry.Text+
"', LogNo = true WHERE User='" + UsernameEntry.Text + "';";
AppModels.Updata(sql);
}
else
{
//插入账号
string sql = @"INSERT INTO Users (User,UserId ,UserName ,UserPassword ,
Enterprise ,Department ,ServerID ,Groups ,
ProxyID ,LogNo ,LocalAreaNetworkMode ,DarkMode ,
MessageNotificationMode ,GUID
)VALUES('"+ UsernameEntry.Text + "','SUNLIGHT','SUNLIGHT','"+ PasswordEntry.Text +
"','SUNLIGHT','ENGINEER','"+ Enterprise.Text + "','ENGINEER',"+
" '',0,0,0,0,'" + Models.AppModels.GetAppUniqueId() + "');";
AppModels.INSERT(sql);
}*/
await Navigation.PopModalAsync();
}
}

6
View/LogWindow.xaml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<Window xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SunlightAggregationTerminal.View.LogWindow"
Title="LogWindow">
</Window>

19
View/LogWindow.xaml.cs

@ -0,0 +1,19 @@
namespace SunlightAggregationTerminal.View;
public partial class LogWindow : Window
{
public LogWindow()
{
InitializeComponent();
Page = new ContentPage()
{
Content = new VerticalStackLayout
{
Children = {
new Label { HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, Text = "Welcome to .NET MAUI!"
}
}
}
};
}
}
Loading…
Cancel
Save