feat(entry): 新增四入口 ViewModel

This commit is contained in:
AndyChen1024
2026-09-11 20:48:04 +08:00
parent 28fc0a1026
commit d9f1893f67
4 changed files with 193 additions and 13 deletions

View File

@@ -0,0 +1,62 @@
import { CheckinRecord } from '../model/CheckinModels';
import { CheckinRepository } from '../data/repository/CheckinRepository';
import { SessionRepository } from '../data/repository/SessionRepository';
import { ServiceLocator } from '../di/ServiceLocator';
/**
* 打卡视图模型:加载打卡数据 + 提交打卡状态。
*/
@ObservedV2
export class CheckinViewModel {
@Trace record: CheckinRecord = new CheckinRecord();
@Trace loggedIn: boolean = false;
@Trace loading: boolean = false;
@Trace submitting: boolean = false;
@Trace error: string = '';
private readonly repository: CheckinRepository;
private readonly session: SessionRepository;
constructor(repository: CheckinRepository, session: SessionRepository) {
this.repository = repository;
this.session = session;
}
static create(): CheckinViewModel {
return new CheckinViewModel(
ServiceLocator.get('checkinRepository') as CheckinRepository,
ServiceLocator.get('sessionRepository') as SessionRepository
);
}
async load(): Promise<void> {
this.loading = true;
this.error = '';
this.loggedIn = this.session.isLoggedIn();
if (!this.loggedIn) {
this.loading = false;
return;
}
try {
this.record = await this.repository.observeCheckin();
} catch (err) {
this.error = JSON.stringify(err);
} finally {
this.loading = false;
}
}
async submit(status: string): Promise<void> {
if (this.submitting) {
return;
}
this.submitting = true;
try {
this.record = await this.repository.submitCheckin(status);
} catch (err) {
this.error = JSON.stringify(err);
} finally {
this.submitting = false;
}
}
}

View File

@@ -1,37 +1,41 @@
import { User } from '../model/User'; import { HomeData } from '../model/HomeModels';
import { UserRepository } from '../data/repository/UserRepository'; import { HomeRepository } from '../data/repository/HomeRepository';
import { SessionRepository } from '../data/repository/SessionRepository';
import { ServiceLocator } from '../di/ServiceLocator'; import { ServiceLocator } from '../di/ServiceLocator';
/** /**
* 首页视图模型:@ObservedV2 三态(loading / error / data)。 * 首页视图模型:@ObservedV2 三态(loading / error / data)。
* 只依赖 UserRepository 契约,不直接接触网络层。 * 根据登录态分流到游客首页 / 原生首页。
*/ */
@ObservedV2 @ObservedV2
export class HomeViewModel { export class HomeViewModel {
@Trace users: User[] = []; @Trace data: HomeData = new HomeData();
@Trace isGuest: boolean = true;
@Trace loading: boolean = false; @Trace loading: boolean = false;
@Trace error: string = ''; @Trace error: string = '';
private readonly repository: UserRepository; private readonly repository: HomeRepository;
private readonly session: SessionRepository;
constructor(repository: UserRepository) { constructor(repository: HomeRepository, session: SessionRepository) {
this.repository = repository; this.repository = repository;
this.session = session;
} }
/**
* 工厂:从 DI 容器解析依赖并装配,避免页面直接触碰 data 层类型。
*/
static create(): HomeViewModel { static create(): HomeViewModel {
return new HomeViewModel(ServiceLocator.get('userRepository') as UserRepository); return new HomeViewModel(
ServiceLocator.get('homeRepository') as HomeRepository,
ServiceLocator.get('sessionRepository') as SessionRepository
);
} }
async load(): Promise<void> { async load(): Promise<void> {
this.loading = true; this.loading = true;
this.error = ''; this.error = '';
try { try {
// 演示:拉取一个示例用户;真实业务可替换为列表接口。 const loggedIn = this.session.isLoggedIn();
const user = await this.repository.getUser('demo-user'); this.isGuest = !loggedIn;
this.users = [user]; this.data = loggedIn ? await this.repository.observeHome() : await this.repository.observeGuestHome();
} catch (err) { } catch (err) {
this.error = JSON.stringify(err); this.error = JSON.stringify(err);
} finally { } finally {

View File

@@ -0,0 +1,56 @@
import { PlanRecord, PlanType } from '../model/PlanModels';
import { PlanRepository } from '../data/repository/PlanRepository';
import { SessionRepository } from '../data/repository/SessionRepository';
import { ServiceLocator } from '../di/ServiceLocator';
/**
* 计划视图模型:周/月/年类型切换 + 计划数据加载。
*/
@ObservedV2
export class PlanViewModel {
@Trace record: PlanRecord = new PlanRecord();
@Trace selectedType: string = PlanType.WEEKLY;
@Trace loggedIn: boolean = false;
@Trace loading: boolean = false;
@Trace error: string = '';
private readonly repository: PlanRepository;
private readonly session: SessionRepository;
constructor(repository: PlanRepository, session: SessionRepository) {
this.repository = repository;
this.session = session;
}
static create(): PlanViewModel {
return new PlanViewModel(
ServiceLocator.get('planRepository') as PlanRepository,
ServiceLocator.get('sessionRepository') as SessionRepository
);
}
async load(): Promise<void> {
this.loading = true;
this.error = '';
this.loggedIn = this.session.isLoggedIn();
if (!this.loggedIn) {
this.loading = false;
return;
}
try {
this.record = await this.repository.observePlan(this.selectedType);
} catch (err) {
this.error = JSON.stringify(err);
} finally {
this.loading = false;
}
}
async selectType(type: string): Promise<void> {
if (this.selectedType === type) {
return;
}
this.selectedType = type;
await this.load();
}
}

View File

@@ -0,0 +1,58 @@
import { UserProfile } from '../model/ProfileModels';
import { ProfileRepository } from '../data/repository/ProfileRepository';
import { SessionRepository } from '../data/repository/SessionRepository';
import { ServiceLocator } from '../di/ServiceLocator';
/**
* 我的视图模型:资料加载 + 模拟登录 / 退出登录。
*/
@ObservedV2
export class ProfileViewModel {
@Trace profile: UserProfile = new UserProfile();
@Trace loggedIn: boolean = false;
@Trace loading: boolean = false;
@Trace error: string = '';
private readonly repository: ProfileRepository;
private readonly session: SessionRepository;
constructor(repository: ProfileRepository, session: SessionRepository) {
this.repository = repository;
this.session = session;
}
static create(): ProfileViewModel {
return new ProfileViewModel(
ServiceLocator.get('profileRepository') as ProfileRepository,
ServiceLocator.get('sessionRepository') as SessionRepository
);
}
async load(): Promise<void> {
this.loading = true;
this.error = '';
this.loggedIn = this.session.isLoggedIn();
if (!this.loggedIn) {
this.loading = false;
return;
}
try {
this.profile = await this.repository.observeProfile();
} catch (err) {
this.error = JSON.stringify(err);
} finally {
this.loading = false;
}
}
async login(): Promise<void> {
await this.session.login();
await this.load();
}
logout(): void {
this.session.logout();
this.loggedIn = false;
this.profile = new UserProfile();
}
}