龍魂 · 鸿蒙PC应用开发实战:系统托盘与通知体系深度集成
·
龍魂 · 鸿蒙PC应用开发实战:系统托盘与通知体系深度集成
龍魂系统 · 鸿蒙原生适配层 · PC端系统托盘与通知体系深度集成
DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️ | UID: 9622 | CONFIRM: #CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z
一、核心定位
| 维度 | 说明 |
|---|---|
| 平台 | 鸿蒙 HarmonyOS NEXT · PC版 · 桌面端适配 |
| 语言 | ArkTS · 声明式UI · 窗口管理API |
| 场景 | 系统托盘 · 通知中心 · 快捷菜单 · 全局热键 · 窗口管理 · 多屏适配 |
| 架构 | 龍魂蚁群触角 → 鸿蒙PC窗口框架 → 系统级集成 |
| 主权 | 数据本地 · 国密SM2/SM3签名 · 不上传云端 |
| 设计 | 托盘常驻 · 通知分级 · 快捷操作 · 热键绑定 · 窗口状态持久化 |
二、系统架构
┌─────────────────────────────────────────┐
│ 龍魂系统 · PC适配层 │
│ DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️ │
│ UID: 9622 │
├─────────────────────────────────────────┤
│ 鸿蒙 PC 应用层 │
│ │
│ EntryAbility → PCMainAbility │
│ ├── 系统托盘管理(SystemTray) │
│ ├── 通知中心(NotificationCenter) │
│ ├── 快捷菜单(ContextMenu) │
│ ├── 全局热键(GlobalHotkey) │
│ ├── 窗口管理(WindowManager) │
│ ├── 多屏适配(MultiScreen) │
│ ├── 剪贴板集成(Pasteboard) │
│ ├── 文件拖拽(DragDrop) │
│ └── 国密签名验证(CryptoVerifier) │
├─────────────────────────────────────────┤
│ 鸿蒙系统能力层 │
│ │
│ 窗口(Window) · 通知(Notification) │
│ 系统托盘(SystemTray) · 菜单(Menu) │
│ 热键(Hotkey) · 剪贴板(Pasteboard) │
│ 多屏(Display) · 拖拽(DragDrop) │
│ 后台任务(WorkScheduler) · 文件(File) │
│ 国密(CryptoFramework) · 生物识别(Biometric) │
└─────────────────────────────────────────┘
三、状态建模核心
3.1 数据模型(entry/src/main/ets/models/PCModel.ets)
// entry/src/main/ets/models/PCModel.ets
// 龍魂 · PC应用模型层 · ArkTS
// === DNA常量 ===
const MASTER_DNA = "ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️";
const MASTER_UID = "9622";
const CONFIRM_SEAL = "#CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z";
// === 窗口状态 ===
export enum WindowState {
NORMAL = 'normal', // 正常
MINIMIZED = 'minimized', // 最小化
MAXIMIZED = 'maximized', // 最大化
FULLSCREEN = 'fullscreen', // 全屏
HIDDEN = 'hidden' // 隐藏(托盘常驻)
}
// === 通知级别 ===
export enum NotificationLevel {
SILENT = 'silent', // 静默 - 不打扰
NORMAL = 'normal', // 普通 - 状态栏提示
URGENT = 'urgent', // 紧急 - 弹窗+声音
CRITICAL = 'critical' // 严重 - 强制弹窗+闪烁
}
// === 托盘菜单类型 ===
export enum TrayMenuType {
ITEM = 'item', // 普通项
SEPARATOR = 'separator', // 分隔线
SUBMENU = 'submenu', // 子菜单
CHECKBOX = 'checkbox', // 复选框
RADIO = 'radio' // 单选框
}
// === 热键修饰键 ===
export enum ModifierKey {
CTRL = 'Ctrl',
ALT = 'Alt',
SHIFT = 'Shift',
META = 'Meta',
FN = 'Fn'
}
// === 窗口档案 ===
export class WindowProfile {
id: string;
title: string;
state: WindowState;
// 位置与尺寸
x: number;
y: number;
width: number;
height: number;
// 多屏
displayId: number;
isPrimary: boolean;
// 状态
isAlwaysOnTop: boolean;
isTransparent: boolean;
opacity: number;
// 持久化
dnaSignature: string;
constructor(data: Partial<WindowProfile> = {}) {
this.id = data.id || `WIN-${Date.now()}`;
this.title = data.title || '龍魂PC应用';
this.state = data.state || WindowState.NORMAL;
this.x = data.x || 100;
this.y = data.y || 100;
this.width = data.width || 1280;
this.height = data.height || 720;
this.displayId = data.displayId || 0;
this.isPrimary = data.isPrimary || true;
this.isAlwaysOnTop = data.isAlwaysOnTop || false;
this.isTransparent = data.isTransparent || false;
this.opacity = data.opacity || 1.0;
this.dnaSignature = this.signData();
}
private signData(): string {
const payload = `${this.id}-${this.title}-${this.state}-${this.x}-${this.y}`;
return `SM3-${payload.split('').reduce((a,b)=>a+b.charCodeAt(0),0).toString(16).substring(0,16)}`;
}
// 最小化到托盘
minimizeToTray(): void {
this.state = WindowState.HIDDEN;
}
// 从托盘恢复
restoreFromTray(): void {
this.state = WindowState.NORMAL;
}
// 最大化
maximize(): void {
this.state = WindowState.MAXIMIZED;
}
// 全屏
fullscreen(): void {
this.state = WindowState.FULLSCREEN;
}
// 设置置顶
setAlwaysOnTop(value: boolean): void {
this.isAlwaysOnTop = value;
}
// 设置透明度
setOpacity(value: number): void {
this.opacity = Math.max(0.1, Math.min(1.0, value));
}
// 移动到新显示器
moveToDisplay(displayId: number): void {
this.displayId = displayId;
}
// 序列化位置
toBounds(): { x: number; y: number; width: number; height: number } {
return { x: this.x, y: this.y, width: this.width, height: this.height };
}
}
// === 通知消息 ===
export class NotificationMessage {
id: string;
title: string;
content: string;
level: NotificationLevel;
// 来源
source: string;
sourceIcon: string;
// 时间
timestamp: Date;
expireAt?: Date;
// 操作
actions: NotificationAction[];
// 状态
isRead: boolean;
isPinned: boolean;
// 持久化
dnaSignature: string;
constructor(data: Partial<NotificationMessage> = {}) {
this.id = data.id || `NOTIFY-${Date.now()}-${Math.random().toString(36).substr(2,6)}`;
this.title = data.title || '通知';
this.content = data.content || '';
this.level = data.level || NotificationLevel.NORMAL;
this.source = data.source || '龍魂系统';
this.sourceIcon = data.sourceIcon || '🐉';
this.timestamp = data.timestamp || new Date();
this.expireAt = data.expireAt;
this.actions = data.actions || [];
this.isRead = data.isRead || false;
this.isPinned = data.isPinned || false;
this.dnaSignature = this.signData();
}
private signData(): string {
const payload = `${this.id}-${this.title}-${this.level}-${this.timestamp.getTime()}`;
return `SM3-${payload.split('').reduce((a,b)=>a+b.charCodeAt(0),0).toString(16).substring(0,16)}`;
}
// 标记已读
markAsRead(): void {
this.isRead = true;
}
// 固定/取消固定
togglePin(): void {
this.isPinned = !this.isPinned;
}
// 是否过期
isExpired(): boolean {
if (!this.expireAt) return false;
return new Date() > this.expireAt;
}
// 获取级别颜色
getLevelColor(): string {
switch (this.level) {
case NotificationLevel.SILENT: return '#666';
case NotificationLevel.NORMAL: return '#0066cc';
case NotificationLevel.URGENT: return '#ff6600';
case NotificationLevel.CRITICAL: return '#c41e3a';
default: return '#666';
}
}
// 获取级别图标
getLevelIcon(): string {
switch (this.level) {
case NotificationLevel.SILENT: return '🔕';
case NotificationLevel.NORMAL: return '🔔';
case NotificationLevel.URGENT: return '⚠️';
case NotificationLevel.CRITICAL: return '🚨';
default: return '🔔';
}
}
}
export interface NotificationAction {
id: string;
label: string;
icon?: string;
type: 'primary' | 'secondary' | 'danger';
callback: () => void;
}
// === 托盘菜单项 ===
export class TrayMenuItem {
id: string;
type: TrayMenuType;
label: string;
icon?: string;
shortcut?: string;
isChecked?: boolean;
isEnabled: boolean;
children?: TrayMenuItem[];
action?: () => void;
constructor(data: Partial<TrayMenuItem> = {}) {
this.id = data.id || `MENU-${Date.now()}-${Math.random().toString(36).substr(2,6)}`;
this.type = data.type || TrayMenuType.ITEM;
this.label = data.label || '';
this.icon = data.icon;
this.shortcut = data.shortcut;
this.isChecked = data.isChecked;
this.isEnabled = data.isEnabled !== false;
this.children = data.children;
this.action = data.action;
}
// 创建分隔线
static separator(): TrayMenuItem {
return new TrayMenuItem({ type: TrayMenuType.SEPARATOR });
}
// 创建子菜单
static submenu(label: string, children: TrayMenuItem[]): TrayMenuItem {
return new TrayMenuItem({ type: TrayMenuType.SUBMENU, label, children });
}
}
// === 热键定义 ===
export class HotkeyDefinition {
id: string;
name: string;
modifiers: ModifierKey[];
key: string;
description: string;
scope: 'global' | 'window' | 'app';
action: () => void;
isEnabled: boolean;
constructor(data: Partial<HotkeyDefinition> = {}) {
this.id = data.id || `HOTKEY-${Date.now()}`;
this.name = data.name || '';
this.modifiers = data.modifiers || [];
this.key = data.key || '';
this.description = data.description || '';
this.scope = data.scope || 'global';
this.action = data.action || (() => {});
this.isEnabled = data.isEnabled !== false;
}
// 获取显示文本
getDisplayText(): string {
const mods = this.modifiers.map(m => {
switch (m) {
case ModifierKey.CTRL: return 'Ctrl';
case ModifierKey.ALT: return 'Alt';
case ModifierKey.SHIFT: return 'Shift';
case ModifierKey.META: return 'Win';
case ModifierKey.FN: return 'Fn';
}
}).join('+');
return mods ? `${mods}+${this.key}` : this.key;
}
// 验证热键是否有效
isValid(): boolean {
return this.key.length > 0 && (this.modifiers.length > 0 || this.key.length === 1);
}
}
3.2 PC全局状态
// === PC全局状态 ===
@Observed
export class PCState {
// 窗口管理
windows: Map<string, WindowProfile> = new Map();
activeWindowId: string = '';
// 通知中心
notifications: NotificationMessage[] = [];
unreadCount: number = 0;
// 托盘
trayIcon: string = '🐉';
trayTooltip: string = '龍魂系统';
trayMenu: TrayMenuItem[] = [];
isTrayVisible: boolean = true;
// 热键
hotkeys: Map<string, HotkeyDefinition> = new Map();
// 剪贴板
clipboardHistory: string[] = [];
maxClipboardHistory: number = 50;
// 多屏
displays: DisplayInfo[] = [];
primaryDisplayId: number = 0;
// 统计
get statistics(): PCStats {
return {
totalWindows: this.windows.size,
visibleWindows: Array.from(this.windows.values()).filter(w => w.state !== WindowState.HIDDEN).length,
totalNotifications: this.notifications.length,
unreadNotifications: this.notifications.filter(n => !n.isRead).length,
pinnedNotifications: this.notifications.filter(n => n.isPinned).length,
totalHotkeys: this.hotkeys.size,
activeHotkeys: Array.from(this.hotkeys.values()).filter(h => h.isEnabled).length,
clipboardItems: this.clipboardHistory.length,
displayCount: this.displays.length
};
}
// 创建窗口
createWindow(profile: WindowProfile): void {
this.windows.set(profile.id, profile);
this.activeWindowId = profile.id;
}
// 关闭窗口
closeWindow(id: string): void {
this.windows.delete(id);
if (this.activeWindowId === id) {
this.activeWindowId = Array.from(this.windows.keys())[0] || '';
}
}
// 最小化到托盘
minimizeToTray(id: string): void {
const win = this.windows.get(id);
if (win) {
win.minimizeToTray();
}
}
// 发送通知
sendNotification(notification: NotificationMessage): void {
this.notifications.unshift(notification);
// 限制数量
if (this.notifications.length > 100) {
this.notifications = this.notifications.slice(0, 100);
}
this.updateUnreadCount();
}
// 标记已读
markAsRead(id: string): void {
const notification = this.notifications.find(n => n.id === id);
if (notification) {
notification.markAsRead();
this.updateUnreadCount();
}
}
// 全部已读
markAllAsRead(): void {
this.notifications.forEach(n => n.markAsRead());
this.updateUnreadCount();
}
// 清除通知
clearNotifications(): void {
this.notifications = this.notifications.filter(n => n.isPinned);
this.updateUnreadCount();
}
// 更新未读数
private updateUnreadCount(): void {
this.unreadCount = this.notifications.filter(n => !n.isRead).length;
}
// 注册热键
registerHotkey(hotkey: HotkeyDefinition): boolean {
if (!hotkey.isValid()) return false;
this.hotkeys.set(hotkey.id, hotkey);
return true;
}
// 注销热键
unregisterHotkey(id: string): void {
this.hotkeys.delete(id);
}
// 记录剪贴板
recordClipboard(text: string): void {
if (this.clipboardHistory.includes(text)) {
// 移到顶部
this.clipboardHistory = this.clipboardHistory.filter(t => t !== text);
}
this.clipboardHistory.unshift(text);
if (this.clipboardHistory.length > this.maxClipboardHistory) {
this.clipboardHistory = this.clipboardHistory.slice(0, this.maxClipboardHistory);
}
}
// 获取剪贴板历史
getClipboardHistory(): string[] {
return [...this.clipboardHistory];
}
persist(): void {
AppStorage.setOrCreate('pc_windows', JSON.stringify(Array.from(this.windows.entries())));
AppStorage.setOrCreate('pc_notifications', JSON.stringify(this.notifications.slice(0, 50)));
AppStorage.setOrCreate('pc_hotkeys', JSON.stringify(Array.from(this.hotkeys.entries())));
AppStorage.setOrCreate('pc_clipboard', JSON.stringify(this.clipboardHistory.slice(0, 20)));
}
load(): void {
// 加载持久化数据
}
}
export interface PCStats {
totalWindows: number;
visibleWindows: number;
totalNotifications: number;
unreadNotifications: number;
pinnedNotifications: number;
totalHotkeys: number;
activeHotkeys: number;
clipboardItems: number;
displayCount: number;
}
export interface DisplayInfo {
id: number;
name: string;
width: number;
height: number;
x: number;
y: number;
isPrimary: boolean;
scaleFactor: number;
}
// 全局状态
export const pcState = new PCState();
AppStorage.setOrCreate('pcState', pcState);
四、系统托盘管理
4.1 托盘控制器(entry/src/main/ets/tray/SystemTray.ets)
// entry/src/main/ets/tray/SystemTray.ets
// 龍魂 · 系统托盘管理
import { pcState, PCState, TrayMenuItem, TrayMenuType, WindowState } from '../models/PCModel';
import { navController } from '../navigation/NavigationController';
@Component
export struct SystemTray {
@StorageLink('pcState') pcState: PCState = pcState;
@State private showMenu: boolean = false;
@State private menuPosition: { x: number; y: number } = { x: 0, y: 0 };
// 构建托盘菜单
getTrayMenu(): TrayMenuItem[] {
return [
new TrayMenuItem({
id: 'show',
label: '显示主窗口',
icon: '📱',
shortcut: 'Ctrl+Shift+S',
action: () => { this.showMainWindow(); }
}),
new TrayMenuItem({
id: 'hide',
label: '隐藏到托盘',
icon: '🔽',
shortcut: 'Ctrl+Shift+H',
action: () => { this.hideToTray(); }
}),
TrayMenuItem.separator(),
TrayMenuItem.submenu('快速导航', [
new TrayMenuItem({
id: 'nav_project',
label: '项目管理',
icon: '📊',
action: () => { navController.push('ProjectList'); this.showMainWindow(); }
}),
new TrayMenuItem({
id: 'nav_milestone',
label: '里程碑',
icon: '📈',
action: () => { navController.push('MilestoneTimeline'); this.showMainWindow(); }
}),
new TrayMenuItem({
id: 'nav_weekly',
label: '周报',
icon: '📝',
action: () => { navController.push('WeeklyReview'); this.showMainWindow(); }
}),
new TrayMenuItem({
id: 'nav_stamp',
label: '印章管理',
icon: '🔏',
action: () => { navController.push('StampManagement'); this.showMainWindow(); }
})
]),
TrayMenuItem.separator(),
new TrayMenuItem({
id: 'settings',
label: '设置',
icon: '⚙️',
action: () => { navController.push('Settings'); this.showMainWindow(); }
}),
new TrayMenuItem({
id: 'notifications',
label: `通知中心 (${this.pcState.unreadCount})`,
icon: '🔔',
action: () => { this.showNotificationCenter(); }
}),
TrayMenuItem.separator(),
new TrayMenuItem({
id: 'about',
label: '关于龍魂',
icon: '🐉',
action: () => { this.showAbout(); }
}),
new TrayMenuItem({
id: 'exit',
label: '退出',
icon: '🚪',
shortcut: 'Ctrl+Q',
action: () => { this.exitApp(); }
})
];
}
// 显示主窗口
showMainWindow(): void {
// 恢复所有隐藏窗口
this.pcState.windows.forEach((win, id) => {
if (win.state === WindowState.HIDDEN) {
win.restoreFromTray();
}
});
// 使用鸿蒙窗口API显示窗口
// window.getLastWindow(getContext()).then(win => {
// win.show();
// win.setWindowBrightness(1.0);
// });
console.info('[龍魂] 显示主窗口');
}
// 隐藏到托盘
hideToTray(): void {
this.pcState.windows.forEach((win, id) => {
win.minimizeToTray();
});
// 使用鸿蒙窗口API隐藏窗口
// window.getLastWindow(getContext()).then(win => {
// win.minimize();
// });
console.info('[龍魂] 隐藏到托盘');
}
// 显示通知中心
showNotificationCenter(): void {
// 打开通知中心弹窗
console.info('[龍魂] 显示通知中心');
}
// 显示关于
showAbout(): void {
// 打开关于对话框
console.info('[龍魂] 显示关于');
}
// 退出应用
exitApp(): void {
// 保存状态
this.pcState.persist();
// 退出
// getContext().terminateSelf();
console.info('[龍魂] 退出应用');
}
// 托盘图标点击
onTrayClick(): void {
this.showMainWindow();
}
// 托盘图标右键
onTrayRightClick(event: { x: number; y: number }): void {
this.menuPosition = { x: event.x, y: event.y };
this.showMenu = true;
}
build() {
Column() {
// 托盘图标区域
Row() {
Text(this.pcState.trayIcon)
.fontSize(24)
if (this.pcState.unreadCount > 0) {
Badge({
value: this.pcState.unreadCount.toString(),
position: BadgePosition.RightTop,
style: { badgeSize: 16, badgeColor: '#c41e3a' }
}) {
Text('')
}
}
}
.width(40)
.height(40)
.justifyContent(FlexAlign.Center)
.onClick(() => { this.onTrayClick(); })
.gesture(
LongPressGesture({ duration: 500 })
.onAction((event: GestureEvent) => {
this.onTrayRightClick({ x: event.fingerList[0].localX, y: event.fingerList[0].localY });
})
)
// 托盘提示
Text(this.pcState.trayTooltip)
.fontSize(10)
.fontColor('#666')
}
.width(64)
.height(64)
.backgroundColor('#1a1a1a')
.borderRadius(8)
.justifyContent(FlexAlign.Center)
// 右键菜单(条件渲染)
if (this.showMenu) {
this.TrayMenuBuilder()
}
}
@Builder
TrayMenuBuilder() {
Column() {
ForEach(this.getTrayMenu(), (item: TrayMenuItem) => {
if (item.type === TrayMenuType.SEPARATOR) {
Divider().width('100%').height(1).color('#333').margin({ top: 4, bottom: 4 })
} else if (item.type === TrayMenuType.SUBMENU && item.children) {
Column() {
Text(`${item.icon || ''} ${item.label} ›`)
.fontSize(13)
.fontColor(item.isEnabled ? '#fff' : '#666')
.padding({ left: 12, right: 12, top: 8, bottom: 8 })
// 子菜单
Column() {
ForEach(item.children, (child: TrayMenuItem) => {
Row() {
Text(`${child.icon || ''} ${child.label}`)
.fontSize(12)
.fontColor(child.isEnabled ? '#fff' : '#666')
if (child.shortcut) {
Text(child.shortcut)
.fontSize(10)
.fontColor('#666')
}
}
.width('100%')
.padding({ left: 24, right: 12, top: 6, bottom: 6 })
.onClick(() => { child.action?.(); this.showMenu = false; })
})
}
.width('100%')
.backgroundColor('#0a0a0a')
}
.width('100%')
} else {
Row() {
Text(`${item.icon || ''} ${item.label}`)
.fontSize(13)
.fontColor(item.isEnabled ? '#fff' : '#666')
.layoutWeight(1)
if (item.shortcut) {
Text(item.shortcut)
.fontSize(10)
.fontColor('#666')
}
}
.width('100%')
.padding({ left: 12, right: 12, top: 8, bottom: 8 })
.backgroundColor('#1a1a1a')
.onClick(() => { item.action?.(); this.showMenu = false; })
}
})
}
.width(200)
.backgroundColor('#1a1a1a')
.borderRadius(8)
.shadow({ radius: 8, color: 'rgba(0,0,0,0.5)' })
.position({ x: this.menuPosition.x, y: this.menuPosition.y })
}
}
五、通知中心
5.1 通知中心组件(entry/src/main/ets/notification/NotificationCenter.ets)
// entry/src/main/ets/notification/NotificationCenter.ets
// 龍魂 · 通知中心
import { pcState, NotificationMessage, NotificationLevel } from '../models/PCModel';
@Component
export struct NotificationCenter {
@StorageLink('pcState') pcState: PCState = pcState;
@State private showCenter: boolean = false;
@State private filterLevel: NotificationLevel | 'all' = 'all';
@State private filterRead: 'all' | 'unread' | 'read' = 'all';
// 过滤后的通知
getFilteredNotifications(): NotificationMessage[] {
let result = this.pcState.notifications;
if (this.filterLevel !== 'all') {
result = result.filter(n => n.level === this.filterLevel);
}
if (this.filterRead === 'unread') {
result = result.filter(n => !n.isRead);
} else if (this.filterRead === 'read') {
result = result.filter(n => n.isRead);
}
return result;
}
// 发送测试通知
sendTestNotification(level: NotificationLevel): void {
const titles: Record<NotificationLevel, string> = {
[NotificationLevel.SILENT]: '静默通知',
[NotificationLevel.NORMAL]: '普通通知',
[NotificationLevel.URGENT]: '紧急通知',
[NotificationLevel.CRITICAL]: '严重通知'
};
const contents: Record<NotificationLevel, string> = {
[NotificationLevel.SILENT]: '这是一条静默通知,不会打扰您',
[NotificationLevel.NORMAL]: '项目里程碑已更新,请查看',
[NotificationLevel.URGENT]: '任务延期预警,需要立即处理',
[NotificationLevel.CRITICAL]: '系统检测到严重风险,请立即检查'
};
const notification = new NotificationMessage({
title: titles[level],
content: contents[level],
level,
source: '龍魂系统',
actions: [
{
id: 'view',
label: '查看',
type: 'primary',
callback: () => { console.info('[龍魂] 查看通知'); }
},
{
id: 'dismiss',
label: '忽略',
type: 'secondary',
callback: () => { console.info('[龍魂] 忽略通知'); }
}
]
});
this.pcState.sendNotification(notification);
}
build() {
Column() {
// 头部
this.HeaderBuilder()
// 过滤器
this.FilterBuilder()
// 通知列表
this.NotificationListBuilder()
}
.width('100%')
.height('100%')
.backgroundColor('#0a0a0a')
}
@Builder
HeaderBuilder() {
Row() {
Text('🔔')
.fontSize(24)
.margin({ right: 8 })
Column() {
Text('通知中心')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#fff')
Text(`${this.pcState.unreadCount} 条未读 · ${this.pcState.notifications.length} 条总计`)
.fontSize(12)
.fontColor('#666')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Button() {
Text('✓').fontSize(16).fontColor('#fff')
}
.type(ButtonType.Circle)
.backgroundColor('#333')
.width(36)
.height(36)
.onClick(() => { this.pcState.markAllAsRead(); })
Button() {
Text('🗑').fontSize(16).fontColor('#fff')
}
.type(ButtonType.Circle)
.backgroundColor('#333')
.width(36)
.height(36)
.margin({ left: 8 })
.onClick(() => { this.pcState.clearNotifications(); })
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor('#1a1a1a')
.border({ width: { bottom: 1 }, color: '#333' })
}
@Builder
FilterBuilder() {
Row() {
// 级别过滤
Row() {
ForEach(['all', 'silent', 'normal', 'urgent', 'critical'], (level: string) => {
Button() {
Text(
level === 'all' ? '全部' :
level === 'silent' ? '🔕 静默' :
level === 'normal' ? '🔔 普通' :
level === 'urgent' ? '⚠️ 紧急' : '🚨 严重'
)
.fontSize(11)
.fontColor(this.filterLevel === level ? '#0a0a0a' : '#999')
}
.type(ButtonType.Normal)
.backgroundColor(this.filterLevel === level ? '#c41e3a' : '#1a1a1a')
.borderRadius(6)
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.margin({ right: 4 })
.onClick(() => { this.filterLevel = level as NotificationLevel | 'all'; })
})
}
.layoutWeight(1)
// 已读过滤
Row() {
ForEach(['all', 'unread', 'read'], (status: string) => {
Button() {
Text(
status === 'all' ? '全部' :
status === 'unread' ? '未读' : '已读'
)
.fontSize(11)
.fontColor(this.filterRead === status ? '#0a0a0a' : '#999')
}
.type(ButtonType.Normal)
.backgroundColor(this.filterRead === status ? '#c41e3a' : '#1a1a1a')
.borderRadius(6)
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.margin({ left: 4 })
.onClick(() => { this.filterRead = status as 'all' | 'unread' | 'read'; })
})
}
}
.width('100%')
.height(44)
.padding({ left: 12, right: 12 })
.backgroundColor('#1a1a1a')
.border({ width: { bottom: 1 }, color: '#333' })
}
@Builder
NotificationListBuilder() {
List() {
ForEach(this.getFilteredNotifications(), (notification: NotificationMessage) => {
ListItem() {
this.NotificationCardBuilder(notification)
}
.onClick(() => { this.pcState.markAsRead(notification.id); })
}, (notification: NotificationMessage) => notification.id)
}
.width('100%')
.layoutWeight(1)
.divider({ strokeWidth: 1, color: '#222' })
.scrollBar(BarState.Auto)
.padding({ bottom: 16 })
}
@Builder
NotificationCardBuilder(notification: NotificationMessage) {
Row() {
Column() {
Text(notification.getLevelIcon())
.fontSize(20)
}
.width(36)
.height(36)
.margin({ right: 12 })
.justifyContent(FlexAlign.Center)
Column() {
Row() {
Text(notification.title)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#fff')
.layoutWeight(1)
if (!notification.isRead) {
Circle()
.width(8)
.height(8)
.fill('#c41e3a')
.margin({ right: 8 })
}
if (notification.isPinned) {
Text('📌')
.fontSize(12)
.margin({ right: 8 })
}
Text(notification.timestamp.toLocaleTimeString())
.fontSize(10)
.fontColor('#666')
}
.width('100%')
Text(notification.content)
.fontSize(12)
.fontColor('#888')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 4 })
Row() {
Text(`${notification.sourceIcon} ${notification.source}`)
.fontSize(10)
.fontColor('#666')
.layoutWeight(1)
ForEach(notification.actions, (action: any) => {
Button(action.label)
.fontSize(11)
.fontColor(action.type === 'primary' ? '#fff' : '#888')
.backgroundColor(action.type === 'primary' ? '#c41e3a' : '#333')
.borderRadius(4)
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
.margin({ left: 4 })
.onClick(() => { action.callback(); })
})
}
.width('100%')
.margin({ top: 6 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.padding(12)
.backgroundColor(notification.isRead ? '#0a0a0a' : 'rgba(196,30,58,0.05)')
.borderRadius(8)
.margin({ left: 12, right: 12, top: 8 })
}
}
六、全局热键
6.1 热键管理器(entry/src/main/ets/hotkey/HotkeyManager.ets)
// entry/src/main/ets/hotkey/HotkeyManager.ets
// 龍魂 · 全局热键管理器
import { pcState, HotkeyDefinition, ModifierKey } from '../models/PCModel';
import { navController } from '../navigation/NavigationController';
export class HotkeyManager {
private static instance: HotkeyManager;
private registeredHotkeys: Map<string, HotkeyDefinition> = new Map();
static getInstance(): HotkeyManager {
if (!HotkeyManager.instance) {
HotkeyManager.instance = new HotkeyManager();
}
return HotkeyManager.instance;
}
// 初始化默认热键
initDefaultHotkeys(): void {
// 显示/隐藏主窗口
this.registerHotkey(new HotkeyDefinition({
name: 'toggle_window',
label: '显示/隐藏窗口',
modifiers: [ModifierKey.CTRL, ModifierKey.SHIFT],
key: 'S',
description: '切换主窗口显示状态',
scope: 'global',
action: () => { this.toggleWindow(); }
}));
// 快速搜索
this.registerHotkey(new HotkeyDefinition({
name: 'quick_search',
label: '快速搜索',
modifiers: [ModifierKey.CTRL, ModifierKey.SHIFT],
key: 'F',
description: '打开快速搜索',
scope: 'global',
action: () => { this.openQuickSearch(); }
}));
// 新建项目
this.registerHotkey(new HotkeyDefinition({
name: 'new_project',
label: '新建项目',
modifiers: [ModifierKey.CTRL],
key: 'N',
description: '新建项目',
scope: 'app',
action: () => { navController.push('ProjectEdit'); }
}));
// 打开里程碑
this.registerHotkey(new HotkeyDefinition({
name: 'open_milestone',
label: '打开里程碑',
modifiers: [ModifierKey.CTRL, ModifierKey.SHIFT],
key: 'M',
description: '打开里程碑时间线',
scope: 'app',
action: () => { navController.push('MilestoneTimeline'); }
}));
// 打开周报
this.registerHotkey(new HotkeyDefinition({
name: 'open_weekly',
label: '打开周报',
modifiers: [ModifierKey.CTRL, ModifierKey.SHIFT],
key: 'W',
description: '打开周报管理',
scope: 'app',
action: () => { navController.push('WeeklyReview'); }
}));
// 截图
this.registerHotkey(new HotkeyDefinition({
name: 'screenshot',
label: '截图',
modifiers: [ModifierKey.CTRL, ModifierKey.SHIFT],
key: 'P',
description: '截取屏幕',
scope: 'global',
action: () => { this.takeScreenshot(); }
}));
// 锁定
this.registerHotkey(new HotkeyDefinition({
name: 'lock_app',
label: '锁定应用',
modifiers: [ModifierKey.CTRL, ModifierKey.ALT],
key: 'L',
description: '锁定应用',
scope: 'global',
action: () => { this.lockApp(); }
}));
// 退出
this.registerHotkey(new HotkeyDefinition({
name: 'exit_app',
label: '退出应用',
modifiers: [ModifierKey.CTRL],
key: 'Q',
description: '退出应用',
scope: 'global',
action: () => { this.exitApp(); }
}));
console.info('[龍魂] 默认热键已初始化');
}
// 注册热键
registerHotkey(hotkey: HotkeyDefinition): boolean {
if (!hotkey.isValid()) {
console.error(`[龍魂] 热键无效: ${hotkey.name}`);
return false;
}
// 检查冲突
const conflict = this.findConflict(hotkey);
if (conflict) {
console.warn(`[龍魂] 热键冲突: ${hotkey.name} 与 ${conflict.name}`);
return false;
}
this.registeredHotkeys.set(hotkey.id, hotkey);
pcState.registerHotkey(hotkey);
// 使用鸿蒙热键API注册
// hotkeyManager.registerHotkey({
// modifiers: hotkey.modifiers,
// key: hotkey.key,
// callback: hotkey.action
// });
console.info(`[龍魂] 热键注册: ${hotkey.name} (${hotkey.getDisplayText()})`);
return true;
}
// 注销热键
unregisterHotkey(id: string): void {
const hotkey = this.registeredHotkeys.get(id);
if (hotkey) {
this.registeredHotkeys.delete(id);
pcState.unregisterHotkey(id);
console.info(`[龍魂] 热键注销: ${hotkey.name}`);
}
}
// 查找冲突
findConflict(hotkey: HotkeyDefinition): HotkeyDefinition | undefined {
for (const [id, existing] of this.registeredHotkeys) {
if (existing.key === hotkey.key &&
existing.modifiers.length === hotkey.modifiers.length &&
existing.modifiers.every(m => hotkey.modifiers.includes(m))) {
return existing;
}
}
return undefined;
}
// 获取所有热键
getAllHotkeys(): HotkeyDefinition[] {
return Array.from(this.registeredHotkeys.values());
}
// 切换窗口
private toggleWindow(): void {
// 判断窗口状态
const win = pcState.windows.get(pcState.activeWindowId);
if (win) {
if (win.state === 'hidden') {
win.restoreFromTray();
} else {
win.minimizeToTray();
}
}
}
// 快速搜索
private openQuickSearch(): void {
console.info('[龍魂] 打开快速搜索');
// 打开搜索弹窗
}
// 截图
private takeScreenshot(): void {
console.info('[龍魂] 截图');
// 调用截图API
}
// 锁定应用
private lockApp(): void {
console.info('[龍魂] 锁定应用');
// 显示锁定界面
}
// 退出应用
private exitApp(): void {
console.info('[龍魂] 退出应用');
pcState.persist();
// getContext().terminateSelf();
}
}
// 全局热键管理器
export const hotkeyManager = HotkeyManager.getInstance();
七、主页面实现
7.1 PC主页面(entry/src/main/ets/pages/PCMainPage.ets)
// entry/src/main/ets/pages/PCMainPage.ets
// 龍魂 · PC主页面
import { pcState, PCState, WindowState, NotificationLevel } from '../models/PCModel';
import { SystemTray } from '../tray/SystemTray';
import { NotificationCenter } from '../notification/NotificationCenter';
import { hotkeyManager } from '../hotkey/HotkeyManager';
import { navController } from '../navigation/NavigationController';
@Entry
@Component
struct PCMainPage {
@StorageLink('pcState') pcState: PCState = pcState;
@State private showNotificationCenter: boolean = false;
@State private showHotkeySettings: boolean = false;
@State private showWindowManager: boolean = false;
aboutToAppear() {
// 初始化热键
hotkeyManager.initDefaultHotkeys();
// 加载窗口状态
this.loadWindowState();
// 监听剪贴板
this.listenClipboard();
console.info('[龍魂] PC主页面已初始化');
}
// 加载窗口状态
loadWindowState(): void {
const saved = AppStorage.get<string>('pc_window_state');
if (saved) {
try {
const state = JSON.parse(saved);
// 恢复窗口位置和大小
} catch (e) {
console.error('[龍魂] 加载窗口状态失败', e);
}
}
}
// 监听剪贴板
listenClipboard(): void {
// 使用鸿蒙剪贴板API
// pasteboard.getSystemPasteboard().on('contentChange', () => {
// pasteboard.getSystemPasteboard().getData().then(data => {
// const text = data.getPrimaryText();
// if (text) {
// this.pcState.recordClipboard(text);
// }
// });
// });
}
build() {
Stack() {
// 主内容区
Column() {
this.HeaderBuilder()
this.ContentBuilder()
this.FooterBuilder()
}
.width('100%')
.height('100%')
.backgroundColor('#0a0a0a')
// 通知中心(覆盖层)
if (this.showNotificationCenter) {
Column() {
NotificationCenter()
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.8)')
.onClick(() => { this.showNotificationCenter = false; })
}
// 系统托盘(右下角)
Column() {
SystemTray()
}
.position({ x: '100%', y: '100%' })
.markAnchor({ x: '100%', y: '100%' })
.offset({ x: -80, y: -80 })
}
.width('100%')
.height('100%')
}
@Builder
HeaderBuilder() {
Row() {
Text('🐉').fontSize(28).margin({ right: 8 })
Column() {
Text('龍魂PC应用').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#c41e3a')
Text(`UID:${MASTER_UID} · ${this.pcState.statistics.displayCount}屏`).fontSize(12).fontColor('#666')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
// 通知按钮
Badge({
value: this.pcState.unreadCount.toString(),
position: BadgePosition.RightTop,
style: { badgeSize: 16, badgeColor: '#c41e3a' }
}) {
Button() { Text('🔔').fontSize(20) }
.type(ButtonType.Circle).backgroundColor('#1a1a1a').width(40).height(40)
.onClick(() => { this.showNotificationCenter = true; })
}
// 窗口管理按钮
Button() { Text('🪟').fontSize(20) }
.type(ButtonType.Circle).backgroundColor('#1a1a1a').width(40).height(40)
.margin({ left: 8 })
.onClick(() => { this.showWindowManager = true; })
// 热键设置按钮
Button() { Text('⌨️').fontSize(20) }
.type(ButtonType.Circle).backgroundColor('#1a1a1a').width(40).height(40)
.margin({ left: 8 })
.onClick(() => { this.showHotkeySettings = true; })
}
.width('100%').height(56).padding({ left: 16, right: 16 })
.backgroundColor('#1a1a1a')
.border({ width: { bottom: 1 }, color: '#333' })
}
@Builder
ContentBuilder() {
Row() {
// 侧边栏
this.SidebarBuilder()
// 主内容
Column() {
// 快捷操作
this.QuickActionsBuilder()
// 窗口状态
this.WindowStatusBuilder()
// 热键列表
this.HotkeyListBuilder()
// 剪贴板历史
this.ClipboardHistoryBuilder()
}
.layoutWeight(1)
.padding(16)
}
.width('100%')
.layoutWeight(1)
}
@Builder
SidebarBuilder() {
Column() {
ForEach([
{ icon: '📊', label: '项目', route: 'ProjectList' },
{ icon: '📈', label: '里程碑', route: 'MilestoneTimeline' },
{ icon: '📝', label: '周报', route: 'WeeklyReview' },
{ icon: '🔏', label: '印章', route: 'StampManagement' },
{ icon: '⚙️', label: '设置', route: 'Settings' }
], (item: {icon: string, label: string, route: string}) => {
Column() {
Text(item.icon).fontSize(20)
Text(item.label).fontSize(10).fontColor('#666').margin({ top: 4 })
}
.width(64)
.height(64)
.justifyContent(FlexAlign.Center)
.onClick(() => { navController.push(item.route); })
})
}
.width(80)
.height('100%')
.backgroundColor('#1a1a1a')
.border({ width: { right: 1 }, color: '#333' })
}
@Builder
QuickActionsBuilder() {
Column() {
Text('快捷操作').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#fff').margin({ bottom: 12 })
Row() {
Button('发送测试通知') { this.sendTestNotification(NotificationLevel.NORMAL); }
Button('最小化到托盘') { this.minimizeToTray(); }
Button('全屏模式') { this.fullscreen(); }
Button('置顶窗口') { this.toggleAlwaysOnTop(); }
}
.width('100%').justifyContent(FlexAlign.SpaceAround)
}
.width('100%').margin({ bottom: 16 })
}
@Builder
WindowStatusBuilder() {
Column() {
Text('窗口状态').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#fff').margin({ bottom: 12 })
Row() {
this.StatCard('总窗口', this.pcState.statistics.totalWindows.toString())
this.StatCard('可见', this.pcState.statistics.visibleWindows.toString())
this.StatCard('隐藏', (this.pcState.statistics.totalWindows - this.pcState.statistics.visibleWindows).toString())
this.StatCard('显示器', this.pcState.statistics.displayCount.toString())
}
.width('100%')
}
.width('100%').margin({ bottom: 16 })
}
@Builder
HotkeyListBuilder() {
Column() {
Text('热键列表').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#fff').margin({ bottom: 12 })
List() {
ForEach(hotkeyManager.getAllHotkeys(), (hotkey: HotkeyDefinition) => {
ListItem() {
Row() {
Text(hotkey.name).fontSize(14).fontColor('#fff').layoutWeight(1)
Text(hotkey.getDisplayText()).fontSize(12).fontColor('#c41e3a').backgroundColor('rgba(196,30,58,0.1)').borderRadius(4).padding({ left: 8, right: 8 })
Text(hotkey.scope).fontSize(11).fontColor('#666').margin({ left: 8 })
}
.width('100%').padding(8).backgroundColor('#1a1a1a').borderRadius(8)
}
})
}
.width('100%').height(200)
}
.width('100%').margin({ bottom: 16 })
}
@Builder
ClipboardHistoryBuilder() {
Column() {
Text('剪贴板历史').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#fff').margin({ bottom: 12 })
List() {
ForEach(this.pcState.getClipboardHistory(), (text: string, index: number) => {
ListItem() {
Row() {
Text(`${index + 1}.`).fontSize(12).fontColor('#666').width(24)
Text(text.substring(0, 50)).fontSize(12).fontColor('#fff').layoutWeight(1)
}
.width('100%').padding(8).backgroundColor('#1a1a1a').borderRadius(8)
}
})
}
.width('100%').height(150)
}
.width('100%')
}
@Builder
StatCard(label: string, value: string) {
Column() {
Text(value).fontSize(20).fontWeight(FontWeight.Bold).fontColor('#fff')
Text(label).fontSize(11).fontColor('#666').margin({ top: 4 })
}
.width('25%').alignItems(HorizontalAlign.Center)
}
@Builder
FooterBuilder() {
Row() {
Column() {
Text('🐉').fontSize(20)
Text(`UID:${MASTER_UID}`).fontSize(10).fontColor('#666')
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Row() {
Text(`📋 ${this.pcState.statistics.totalNotifications} 通知`).fontSize(12).fontColor('#888')
Text(`⌨️ ${this.pcState.statistics.activeHotkeys} 热键`).fontSize(12).fontColor('#888').margin({ left: 12 })
Text(`📋 ${this.pcState.statistics.clipboardItems} 剪贴板`).fontSize(12).fontColor('#888').margin({ left: 12 })
}
}
.width('100%').height(40).padding({ left: 16, right: 16 })
.backgroundColor('#1a1a1a')
.border({ width: { top: 1 }, color: '#333' })
}
sendTestNotification(level: NotificationLevel): void {
// 触发通知中心发送测试
}
minimizeToTray(): void {
this.pcState.windows.forEach((win, id) => {
win.minimizeToTray();
});
}
fullscreen(): void {
// 使用鸿蒙窗口API全屏
}
toggleAlwaysOnTop(): void {
this.pcState.windows.forEach((win, id) => {
win.setAlwaysOnTop(!win.isAlwaysOnTop);
});
}
}
const MASTER_DNA = "ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️";
const MASTER_UID = "9622";
八、组件清单
| 文件 | 路径 | 说明 |
|---|---|---|
| PC模型 | entry/src/main/ets/models/PCModel.ets |
WindowProfile/NotificationMessage/TrayMenuItem/HotkeyDefinition/PCState |
| 系统托盘 | entry/src/main/ets/tray/SystemTray.ets |
托盘图标/右键菜单/快捷导航 |
| 通知中心 | entry/src/main/ets/notification/NotificationCenter.ets |
通知列表/过滤/操作 |
| 热键管理 | entry/src/main/ets/hotkey/HotkeyManager.ets |
热键注册/冲突检测/全局监听 |
| PC主页面 | entry/src/main/ets/pages/PCMainPage.ets |
完整PC界面 |
| 数据库 | entry/src/main/ets/database/PCDatabase.ets |
PC状态持久化 |
九、鸿蒙特性使用
| 特性 | 用途 | API |
|---|---|---|
| ArkTS声明式UI | 界面构建 | @Component @Entry |
| 状态管理 | 数据响应 | @State @Observed @StorageLink |
| 窗口管理 | 窗口控制 | window show hide minimize maximize |
| 系统托盘 | 托盘图标 | systemTray setTrayIcon setTrayMenu |
| 通知 | 消息推送 | notificationManager publish cancel |
| 热键 | 全局快捷键 | hotkeyManager registerHotkey |
| 剪贴板 | 复制粘贴 | pasteboard getData setData |
| 多屏 | 显示器管理 | display getAllDisplays |
| 拖拽 | 文件拖拽 | dragDrop onDrag |
| 后台任务 | 托盘常驻 | workScheduler |
| 国密算法 | 数据签名 | cryptoFramework SM2/SM3 |
十、PC端特性对照表
| 特性 | Windows | macOS | 鸿蒙PC | 龍魂实现 |
|---|---|---|---|---|
| 系统托盘 | ✅ | ✅ | ✅ | SystemTray |
| 托盘菜单 | ✅ | ✅ | ✅ | TrayMenuItem |
| 通知中心 | ✅ | ✅ | ✅ | NotificationCenter |
| 通知级别 | ✅ | ✅ | ✅ | 4级分级 |
| 全局热键 | ✅ | ✅ | ✅ | HotkeyManager |
| 窗口置顶 | ✅ | ✅ | ✅ | setAlwaysOnTop |
| 窗口透明 | ✅ | ✅ | ✅ | setOpacity |
| 多屏适配 | ✅ | ✅ | ✅ | DisplayInfo |
| 剪贴板历史 | ✅ | ✅ | ✅ | clipboardHistory |
| 文件拖拽 | ✅ | ✅ | ✅ | DragDrop |
| 深度链接 | ✅ | ✅ | ✅ | URL Scheme |
| 开机启动 | ✅ | ✅ | ✅ | WorkScheduler |
十一、龍魂标识
| 位置 | 内容 |
|---|---|
| 应用名称 | 龍魂PC应用 |
| 标题栏 | 🐉 龍魂PC应用 |
| 托盘提示 | 龍魂系统 |
| 底部标识 | UID:9622 |
| 数据签名 | SM3-哈希 |
| DNA | ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️ |
🐉 龍魂 · 鸿蒙PC应用开发实战:系统托盘与通知体系深度集成 交付完成
DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️
UID: 9622
确认码: #CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z
时间: 2026-07-14
模块: 6核心文件
特性: 系统托盘 · 通知中心 · 快捷菜单 · 全局热键 · 窗口管理 · 多屏适配 · 剪贴板历史 · 国密签名
更多推荐


所有评论(0)