263 lines
7.0 KiB
Plaintext
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'))
|
|
}
|
|
}
|