feat(app): login page with dev-token flow and profile with quota cards(F1)

登录页(LoginPage):
- 品牌区 + 设备标识输入(随机生成一次并持久化到 preferences,可改可重生成)
- 高级设置折叠区:展示/编辑 baseUrl(AuthStore.setBaseUrl),带格式校验与恢复默认
- 登录闭环:loading 态 → AuthStore.loginByDevToken → 成功 router.replaceUrl 首页;
  失败 catch ApiError 用 ErrorBanner 展示 message+hint;已登录态进入直接跳首页

个人页(ProfilePage):
- 用户 ID 脱敏(前 6 后 4)+ 登出(AlertDialog 确认 → AuthStore.logout → 回登录页)
- agent_chat/tryon/report_basic 三配额卡并行拉取,进度条 used/(limit+bonus),
  剩余数大字;周期文案:日配额=明日恢复、report_basic=一次性额度、月度=日期
- 最近流水(limit=10):tx_type 中文映射(消耗/退还/奖励),status 徽标配色区分
- Refresh 下拉刷新(加分项)

共享组件 components/f1/:ErrorBanner、QuotaCard、LedgerItem

偏差说明:
- 设备标识在 @State 之外额外持久化(meida_login/device_key),保证跨启动稳定不重生
- Scroll 隐藏滚动条用 .scrollBar(BarState.Off)(本 SDK 无 scrollBarBarState 属性)
- router.replaceUrl / AlertDialog.show 有 deprecation WARN,属规程指定路由方式,构建门禁通过
This commit is contained in:
xsl
2026-08-15 00:43:46 +08:00
parent 9503c6e259
commit 336b638a1f
5 changed files with 998 additions and 8 deletions
@@ -0,0 +1,71 @@
/**
* QuotaCardView(F1)—— 配额卡片:剩余数大字 + 进度条 + 周期文案。
*
* 数据契约(对齐后端 GET /api/v1/quota):
* used / (limit_value + bonus_granted) 为进度;remaining 为剩余额度。
* 周期文案由父层根据 quota_key + reset_at 算好传入(日=明日恢复/终身=一次性)。
*/
@Component
export struct QuotaCardView {
@Prop title: string = ''
@Prop remaining: number = 0
@Prop used: number = 0
@Prop total: number = 0
@Prop resetText: string = ''
@Prop loaded: boolean = false
@Prop errorText: string = ''
@Prop accent: string = '#C2185B'
build() {
Column({ space: 6 }) {
Text(this.title)
.fontSize(13)
.fontColor('#8A8A8A')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
if (!this.loaded) {
LoadingProgress()
.width(28)
.height(28)
.color('#CCCCCC')
.margin({ top: 10, bottom: 14 })
} else if (this.errorText !== '') {
Text(this.errorText)
.fontSize(13)
.fontColor('#C0392B')
.maxLines(2)
.margin({ top: 6, bottom: 8 })
.textAlign(TextAlign.Center)
} else {
Text(`${this.remaining}`)
.fontSize(34)
.fontWeight(FontWeight.Bold)
.fontColor(this.accent)
Text('剩余次数')
.fontSize(11)
.fontColor('#AAAAAA')
Progress({ value: this.used, total: Math.max(this.total, 1), type: ProgressType.Linear })
.width('100%')
.height(6)
.borderRadius(3)
.color(this.accent)
.backgroundColor('#F1E7EB')
.margin({ top: 4 })
Text(`已用 ${this.used}/${this.total}`)
.fontSize(11)
.fontColor('#999999')
Text(this.resetText)
.fontSize(10)
.fontColor('#B3B3B3')
.maxLines(1)
}
}
.width('100%')
.padding(12)
.borderRadius(16)
.backgroundColor('#FFFFFF')
.alignItems(HorizontalAlign.Center)
}
}