Files
Authenticator/entry/src/main/ets/pages/AccountListPage.ets
2025-10-19 12:32:16 +08:00

263 lines
7.0 KiB
Plaintext

import { router } from '@kit.ArkUI';
import { AccountViewModel } from '../viewmodel/AccountViewModel';
import { Account } from '../model/Account';
import { AccountCard } from '../view/components/AccountCard';
import { TOTPGenerator } from '../common/utils/TOTPGenerator';
import { StorageUtil } from '../common/utils/StorageUtil';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { AppConstants } from '../common/constants/AppConstants';
import { pasteboard } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';
/**
* 账户列表主页面
*/
@Entry
@Component
struct AccountListPage {
@State accounts: Account[] = [];
@State codes: Map<string, string> = new Map();
@State progress: number = 0;
@State remainingSeconds: number = 30;
@State searchText: string = '';
private viewModel: AccountViewModel = new AccountViewModel();
private timer: number = -1;
async aboutToAppear(): Promise<void> {
// 初始化存储
try {
await StorageUtil.init(getContext(this));
hilog.info(AppConstants.LOG_DOMAIN, AppConstants.LOG_TAG, 'Storage initialized');
} catch (error) {
hilog.error(AppConstants.LOG_DOMAIN, AppConstants.LOG_TAG, 'Failed to init storage: %{public}s', JSON.stringify(error));
}
// 设置更新回调
this.viewModel.setUpdateCallback(() => {
this.updateAccounts();
});
// 加载账户
await this.viewModel.loadAccounts();
this.updateAccounts();
// 启动定时器更新验证码
this.startTimer();
}
aboutToDisappear(): void {
this.stopTimer();
}
/**
* 页面显示时重新加载账户
* 处理从添加/编辑页面返回的情况
*/
onPageShow(): void {
// 重新加载账户数据
this.viewModel.loadAccounts();
}
/**
* 更新账户列表
*/
private updateAccounts(): void {
if (this.searchText) {
this.accounts = this.viewModel.searchAccounts(this.searchText);
} else {
this.accounts = this.viewModel.getAccounts();
}
}
/**
* 启动定时器
*/
private startTimer(): void {
// 立即生成一次验证码
this.generateAllCodes();
// 每秒更新一次
this.timer = setInterval(() => {
this.updateProgress();
this.generateAllCodes();
}, 1000);
}
/**
* 停止定时器
*/
private stopTimer(): void {
if (this.timer !== -1) {
clearInterval(this.timer);
this.timer = -1;
}
}
/**
* 更新进度
*/
private updateProgress(): void {
this.progress = TOTPGenerator.getProgress();
this.remainingSeconds = TOTPGenerator.getRemainingSeconds();
}
/**
* 生成所有账户的验证码
*/
private async generateAllCodes(): Promise<void> {
for (const account of this.accounts) {
try {
const code = await this.viewModel.generateCode(account);
this.codes.set(account.id, code);
} catch (error) {
hilog.error(AppConstants.LOG_DOMAIN, AppConstants.LOG_TAG, 'Failed to generate code: %{public}s', JSON.stringify(error));
}
}
}
/**
* 复制验证码到剪贴板
*/
private copyToClipboard(account: Account): void {
const code = this.codes.get(account.id) || '------';
if (code !== '------') {
const pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, code);
const systemPasteboard = pasteboard.getSystemPasteboard();
systemPasteboard.setData(pasteData);
}
}
/**
* 删除账户
*/
private async deleteAccount(account: Account): Promise<void> {
promptAction.showDialog({
title: $r('app.string.delete_confirm'),
buttons: [
{ text: $r('app.string.cancel'), color: $r('app.color.text_secondary') },
{ text: $r('app.string.delete'), color: $r('app.color.danger_color') }
]
}).then(async (result) => {
if (result.index === 1) {
await this.viewModel.deleteAccount(account.id);
this.updateAccounts();
}
});
}
/**
* 编辑账户
*/
private editAccount(account: Account): void {
router.pushUrl({
url: 'pages/AccountDetailPage',
params: { accountId: account.id }
});
}
/**
* 添加账户
*/
private addAccount(): void {
router.pushUrl({
url: 'pages/AddAccountPage'
});
}
build() {
Column() {
// 顶部导航栏
Row() {
Text($r('app.string.app_name'))
.fontSize($r('app.float.title_large'))
.fontColor($r('app.color.text_primary'))
.fontWeight(FontWeight.Bold)
Blank()
// 添加按钮
Button({ type: ButtonType.Circle }) {
Text('+')
.fontSize(28)
.fontColor($r('app.color.card_background'))
}
.width(40)
.height(40)
.backgroundColor($r('app.color.primary_color'))
.onClick(() => {
this.addAccount();
})
}
.width('100%')
.padding({
left: $r('app.float.spacing_large'),
right: $r('app.float.spacing_large'),
top: $r('app.float.spacing_large'),
bottom: $r('app.float.spacing_medium')
})
// 搜索框
if (this.accounts.length > 0) {
Search({ placeholder: $r('app.string.search') })
.margin({
left: $r('app.float.spacing_large'),
right: $r('app.float.spacing_large'),
bottom: $r('app.float.spacing_medium')
})
.onChange((value: string) => {
this.searchText = value;
this.updateAccounts();
})
}
// 账户列表
if (this.accounts.length === 0) {
// 空状态
Column({ space: 12 }) {
Text($r('app.string.no_accounts'))
.fontSize($r('app.float.body_large'))
.fontColor($r('app.color.text_secondary'))
Text($r('app.string.no_accounts_tip'))
.fontSize($r('app.float.body_medium'))
.fontColor($r('app.color.text_tertiary'))
}
.width('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
} else {
// 账户列表
List({ space: 12 }) {
ForEach(this.accounts, (account: Account) => {
ListItem() {
AccountCard({
account: account,
code: this.codes.get(account.id) || '------',
progress: this.progress,
remainingSeconds: this.remainingSeconds,
onCopy: (acc: Account) => {
this.copyToClipboard(acc);
},
onEdit: (acc: Account) => {
this.editAccount(acc);
},
onDelete: (acc: Account) => {
this.deleteAccount(acc);
}
})
}
}, (account: Account) => account.id)
}
.width('100%')
.layoutWeight(1)
.padding({
left: $r('app.float.spacing_large'),
right: $r('app.float.spacing_large')
})
}
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.background_color'))
}
}