init: init proj
This commit is contained in:
262
entry/src/main/ets/pages/AccountListPage.ets
Normal file
262
entry/src/main/ets/pages/AccountListPage.ets
Normal file
@@ -0,0 +1,262 @@
|
||||
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'))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user