refactor: 应用参考工程代码模式,优化启动流程和架构

- 新增 SplashPage:启动页,2秒后跳转到 Index
- 新增 MainPage:独立主页组件,含 Tab 导航 + 宽屏/窄屏自适应
- 简化 Index.ets:纯路由入口,初始化数据库,加载 MainPage
- EntryAbility:加载 SplashPage 而非直接加载 Index
- 新增 PageContext:NavPathStack 路由管理(参考工程模式)
- 新增 BreakpointSystem:响应式断点系统 xs/sm/md/lg/xl
- 新增 BaseViewModel:ViewModel 基类,含 attach/detach/dispose 生命周期
- 更新 common/Index.ets 导出新模块
- 更新 main_pages.json 注册 SplashPage 和 MainPage
This commit is contained in:
root
2026-05-01 09:15:36 +08:00
parent 5916d2749c
commit ab3d910387
9 changed files with 325 additions and 112 deletions

View File

@ -3,3 +3,6 @@ export { GraphDatabase, RecallEntity, TimeRangeParams } from './src/main/ets/mod
export { GraphMemoryService } from './src/main/ets/service/GraphMemoryService'; export { GraphMemoryService } from './src/main/ets/service/GraphMemoryService';
export { AIAgentService, ChatMessage, AgentResponse } from './src/main/ets/service/AIAgentService'; export { AIAgentService, ChatMessage, AgentResponse } from './src/main/ets/service/AIAgentService';
export { ImmersiveTabNavigation } from './src/main/ets/component/ImmersiveTabNavigation'; export { ImmersiveTabNavigation } from './src/main/ets/component/ImmersiveTabNavigation';
export { PageContext, RouterParam, IPageContext } from './src/main/ets/routermanager/PageContext';
export { BreakpointType, BreakpointTypes, WidthBreakpoint } from './src/main/ets/util/BreakpointSystem';
export { BaseViewModel, VMEvent } from './src/main/ets/viewmodel/BaseViewModel';

View File

@ -0,0 +1,54 @@
export interface RouterParam {
routerName: string;
param?: object;
}
export interface IPageContext {
openPage(data: RouterParam, animated?: boolean): void;
popPage(animated?: boolean): void;
replacePage(data: RouterParam, animated?: boolean): void;
}
export class PageContext implements IPageContext {
private readonly pathStack: NavPathStack;
constructor() {
this.pathStack = new NavPathStack();
}
public get navPathStack(): NavPathStack {
return this.pathStack;
}
public replacePage(data: RouterParam, animated: boolean = true): void {
try {
this.pathStack.replacePath({ name: data.routerName, param: data.param }, animated);
} catch (err) {
console.error('Open Page ' + data.routerName + ' failed. ' + err.code + ' ' + err.message);
}
}
public openPage(data: RouterParam, animated: boolean = true): void {
try {
this.pathStack.pushPath({ name: data.routerName, param: data.param }, animated);
} catch (err) {
console.error('Open Page ' + data.routerName + ' failed. ' + err.code + ' ' + err.message);
}
}
public popPage(animated: boolean = true): void {
try {
this.pathStack.pop(animated);
} catch (err) {
console.error('Pop Page failed. ' + err.code + ' ' + err.message);
}
}
public popPageByIndex(index: number, animated: boolean = true): void {
this.pathStack.popToIndex(index, animated);
}
public clear(animated: boolean = true): void {
this.pathStack.clear(animated);
}
}

View File

@ -0,0 +1,47 @@
export enum WidthBreakpoint {
WIDTH_XS = 'xs',
WIDTH_SM = 'sm',
WIDTH_MD = 'md',
WIDTH_LG = 'lg',
WIDTH_XL = 'xl'
}
export interface BreakpointTypes<T> {
xs?: T;
sm: T;
md: T;
lg: T;
xl?: T;
}
export class BreakpointType<T> {
private xs: T;
private sm: T;
private md: T;
private lg: T;
private xl: T;
public constructor(param: BreakpointTypes<T>) {
this.xs = param.xs ?? param.sm;
this.sm = param.sm;
this.md = param.md;
this.lg = param.lg;
this.xl = param.xl ?? param.lg;
}
public getValue(currentBreakpoint: WidthBreakpoint): T {
if (currentBreakpoint === WidthBreakpoint.WIDTH_XS) {
return this.xs;
}
if (currentBreakpoint === WidthBreakpoint.WIDTH_SM) {
return this.sm;
}
if (currentBreakpoint === WidthBreakpoint.WIDTH_MD) {
return this.md;
}
if (currentBreakpoint === WidthBreakpoint.WIDTH_XL) {
return this.xl;
}
return this.lg;
}
}

View File

@ -0,0 +1,52 @@
import { BreakpointType, WidthBreakpoint } from '../util/BreakpointSystem';
export interface VMEvent {
}
export class BaseViewModel {
protected isAttached: boolean = false;
protected isDisposed: boolean = false;
protected currentBreakpoint: WidthBreakpoint = WidthBreakpoint.WIDTH_MD;
attach(): void {
if (this.isAttached) {
return;
}
this.isAttached = true;
this.onAttach();
}
detach(): void {
if (!this.isAttached) {
return;
}
this.isAttached = false;
this.onDetach();
}
dispose(): void {
if (this.isDisposed) {
return;
}
this.isDisposed = true;
this.detach();
this.onDispose();
}
protected onAttach(): void {
}
protected onDetach(): void {
}
protected onDispose(): void {
}
public get attached(): boolean {
return this.isAttached;
}
public get disposed(): boolean {
return this.isDisposed;
}
}

View File

@ -1,18 +1,34 @@
import UIAbility from '@ohos.app.ability.UIAbility'; import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import window from '@ohos.window'; import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility { export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) { onCreate(want: Want, param: AbilityConstant.LaunchParam): void {
console.info('EntryAbility onCreate'); console.info('EntryAbility onCreate');
} }
onDestroy() { onDestroy(): void {
console.info('EntryAbility onDestroy'); console.info('EntryAbility onDestroy');
} }
onWindowStageCreate(windowStage: window.WindowStage) { onWindowStageCreate(windowStage: window.WindowStage): void {
const windowClass: window.Window = windowStage.getMainWindowSync();
try {
windowClass.setWindowBackgroundColor('#00000000');
} catch (e) {
console.error('Failed to set background color: ' + (e as BusinessError).message);
}
try {
windowClass.setWindowSystemBarProperties({
statusBarColor: '#00000000',
navigationBarColor: '#00000000'
});
} catch (e) {
console.error('Failed to set system bar properties: ' + (e as BusinessError).message);
}
console.info('EntryAbility onWindowStageCreate'); console.info('EntryAbility onWindowStageCreate');
windowStage.loadContent('pages/Index', (err, data) => { windowStage.loadContent('pages/SplashPage', (err, data) => {
if (err.code) { if (err.code) {
console.error('Failed to load the content. Cause: ' + JSON.stringify(err)); console.error('Failed to load the content. Cause: ' + JSON.stringify(err));
return; return;
@ -21,15 +37,15 @@ export default class EntryAbility extends UIAbility {
}); });
} }
onWindowStageDestroy() { onWindowStageDestroy(): void {
console.info('EntryAbility onWindowStageDestroy'); console.info('EntryAbility onWindowStageDestroy');
} }
onForeground() { onForeground(): void {
console.info('EntryAbility onForeground'); console.info('EntryAbility onForeground');
} }
onBackground() { onBackground(): void {
console.info('EntryAbility onBackground'); console.info('EntryAbility onBackground');
} }
} }

View File

@ -1,19 +1,22 @@
import { GraphDatabase } from '@ohos/common'; import { GraphDatabase } from '@ohos/common';
import { GraphPage } from '@ohos/graph'; import { MainPage } from './MainPage';
import { ChatPage } from '@ohos/chat';
import { SettingsPage } from '@ohos/settings';
import { ImmersiveTabNavigation } from '@ohos/common';
import display from '@ohos.display'; import display from '@ohos.display';
@Entry @Entry
@Component @Component
struct Index { struct Index {
@State currentIndex: number = 0;
private db: GraphDatabase = new GraphDatabase(); private db: GraphDatabase = new GraphDatabase();
private displayCallback?: Callback<number>; private displayCallback?: Callback<number>;
aboutToAppear() { aboutToAppear() {
this.db.init(getContext(this)); this.db.init(getContext(this));
try {
this.displayCallback = (size: number): void => {
};
display.on('change', this.displayCallback);
} catch (e) {
console.error('display.on error: ' + JSON.stringify(e));
}
} }
aboutToDisappear() { aboutToDisappear() {
@ -22,104 +25,7 @@ struct Index {
} }
} }
@Builder
tabContentBuilder() {
Column() {
if (this.currentIndex === 0) {
MainPage({ db: this.db })
} else {
SettingsPage()
}
}
.width('100%')
.height('100%')
}
build() { build() {
Stack() { MainPage({ db: this.db }).width('100%').height('100%');
ImmersiveTabNavigation({
currentIndex: this.currentIndex,
onTabChange: (index: number): void => { this.currentIndex = index; },
contentBuilder: (): void => { this.tabContentBuilder(); }
})
}
.width('100%')
.height('100%')
}
}
@Component
struct MainPage {
@Prop db: GraphDatabase;
@State isWide: boolean = false;
aboutToAppear() {
this.updateBreakpoint();
try {
display.on('change', () => {
this.updateBreakpoint();
});
} catch (e) {
console.error('display.on error: ' + JSON.stringify(e));
}
}
private updateBreakpoint(): void {
try {
const defaultWindow = display.getDefaultDisplaySync();
this.isWide = defaultWindow.width > 520;
} catch (e) {
console.error('updateBreakpoint error: ' + JSON.stringify(e));
}
}
build() {
if (this.isWide) {
Row() {
GraphPage({ db: this.db })
.layoutWeight(1)
.height('100%')
.clip(true)
.borderRadius(12)
.margin({ left: 4, right: 2 })
ChatPage({ db: this.db })
.width(380)
.height('100%')
.clip(true)
.borderRadius(12)
.margin({ left: 2, right: 4 })
}
.width('100%')
.height('100%')
.padding(4)
.backgroundColor('#1A1B2E')
.backgroundBlurStyle(BlurStyle.Regular)
} else {
Column() {
Stack() {
GraphPage({ db: this.db })
}
.height('55%')
.width('100%')
.clip(true)
.borderRadius(12)
.margin({ top: 2, left: 4, right: 4, bottom: 2 })
Stack() {
ChatPage({ db: this.db })
}
.height('45%')
.width('100%')
.clip(true)
.borderRadius(12)
.margin({ top: 2, left: 4, right: 4, bottom: 2 })
}
.width('100%')
.height('100%')
.padding(2)
.backgroundColor('#1A1B2E')
.backgroundBlurStyle(BlurStyle.Regular)
}
} }
} }

View File

@ -0,0 +1,104 @@
import { GraphDatabase } from '@ohos/common';
import { GraphPage } from '@ohos/graph';
import { ChatPage } from '@ohos/chat';
import { SettingsPage } from '@ohos/settings';
import { ImmersiveTabNavigation } from '@ohos/common';
import display from '@ohos.display';
@Component
export struct MainPage {
@Prop db: GraphDatabase;
@State isWide: boolean = false;
@State currentIndex: number = 0;
aboutToAppear() {
this.updateBreakpoint();
try {
display.on('change', () => {
this.updateBreakpoint();
});
} catch (e) {
console.error('display.on error: ' + JSON.stringify(e));
}
}
private updateBreakpoint(): void {
try {
const defaultWindow = display.getDefaultDisplaySync();
this.isWide = defaultWindow.width > 520;
} catch (e) {
console.error('updateBreakpoint error: ' + JSON.stringify(e));
}
}
@Builder
tabContentBuilder() {
Column() {
if (this.currentIndex === 0) {
if (this.isWide) {
Row() {
GraphPage({ db: this.db })
.layoutWeight(1)
.height('100%')
.clip(true)
.borderRadius(12)
.margin({ left: 4, right: 2 });
ChatPage({ db: this.db })
.width(380)
.height('100%')
.clip(true)
.borderRadius(12)
.margin({ left: 2, right: 4 });
}
.width('100%')
.height('100%')
.padding(4)
.backgroundColor('#1A1B2E');
} else {
Column() {
Stack() {
GraphPage({ db: this.db })
}
.height('55%')
.width('100%')
.clip(true)
.borderRadius(12)
.margin({ top: 2, left: 4, right: 4, bottom: 2 });
Stack() {
ChatPage({ db: this.db })
}
.height('45%')
.width('100%')
.clip(true)
.borderRadius(12)
.margin({ top: 2, left: 4, right: 4, bottom: 2 });
}
.width('100%')
.height('100%')
.padding(2)
.backgroundColor('#1A1B2E');
}
} else {
SettingsPage()
}
}
.width('100%')
.height('100%');
}
build() {
Stack() {
ImmersiveTabNavigation({
currentIndex: this.currentIndex,
onTabChange: (index: number): void => {
this.currentIndex = index;
},
contentBuilder: (): void => {
this.tabContentBuilder();
}
})
}
.width('100%')
.height('100%');
}
}

View File

@ -0,0 +1,29 @@
import router from '@ohos.router';
@Entry
@Component
struct SplashPage {
aboutToAppear() {
setTimeout(() => {
router.replaceUrl({ url: 'pages/Index' });
}, 2000);
}
build() {
Column() {
Text('TrulyMEM')
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White);
Text('True Human Memory')
.fontSize(20)
.fontColor(Color.White)
.margin({ top: 16 });
}
.width('100%')
.height('100%')
.backgroundColor('#1A1B2E')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center);
}
}

View File

@ -1,5 +1,7 @@
{ {
"src": [ "src": [
"pages/SplashPage",
"pages/MainPage",
"pages/Index" "pages/Index"
] ]
} }