新增门店订单与核销管理列表,对齐 Android 订单能力并完善账号级缓存。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 14:46:10 +08:00
parent c4de3d17d0
commit 08492df6ce
51 changed files with 3668 additions and 102 deletions

View File

@ -37,8 +37,8 @@ DEBUG 构建下,“我的”列表会额外展示“首页调试”入口,
## 加载流程
1. `ProfileView.task` 进入页面时调用 `reloadProfile(showToast: false)`
2. 下拉刷新时调用 `reloadProfile(showToast: true)`
1. `ProfileView.task` 进入页面时调用 `reloadProfile()`
2. 下拉刷新时同样调用 `reloadProfile()`,成功后不展示 Toast
3. `ProfileViewModel.reload` 并发请求:
- `ProfileAPI.userInfo`
- `ProfileAPI.realNameInfo`
@ -111,6 +111,7 @@ DEBUG 构建下,“我的”列表会额外展示“首页调试”入口,
## 状态展示规则
- 进入个人信息页时,先用 `AccountContext` 中已恢复的本地缓存(`AccountSnapshot`)预填充昵称、手机号和头像;接口返回后再替换为最新数据。
- 昵称为空时展示“未设置昵称”。
- 真实姓名、手机号和账号状态为空时展示 `--`
- 实名认证状态:

View File

@ -20,9 +20,13 @@ final class ProfileViewModel: ObservableObject {
@Published private(set) var pendingAvatarData: Data?
@Published private(set) var pendingAvatarFileName: String?
@Published private(set) var avatarUploadProgress: Int?
/// 使 AccountContext / AccountSnapshot
private var cachedProfile: AccountProfile?
var displayNickname: String {
nonEmpty(userInfo?.nickname) ?? "未设置昵称"
nonEmpty(userInfo?.nickname)
?? nonEmpty(cachedProfile?.displayName)
?? "未设置昵称"
}
var displayRealName: String {
@ -30,11 +34,11 @@ final class ProfileViewModel: ObservableObject {
}
var displayPhone: String {
nonEmpty(userInfo?.phone) ?? "--"
nonEmpty(userInfo?.phone) ?? nonEmpty(cachedProfile?.phone) ?? "--"
}
var displayAvatarURL: String {
nonEmpty(userInfo?.avatar) ?? ""
nonEmpty(userInfo?.avatar) ?? nonEmpty(cachedProfile?.avatarURL) ?? ""
}
var accountStatusText: String {
@ -53,6 +57,12 @@ final class ProfileViewModel: ObservableObject {
}
}
///
func applyCachedProfile(_ profile: AccountProfile?) {
guard userInfo == nil else { return }
cachedProfile = profile
}
///
func reload(api: ProfileAPI) async throws {
guard !isLoading else { return }

View File

@ -46,6 +46,9 @@ struct DebugHomeMenuPreviewView: View {
appRouter.select(tab)
case .orders(let entry):
appRouter.selectOrders(entry: entry)
case .orderPush(let route):
appRouter.select(.orders)
appRouter.router(for: .orders).navigate(to: .orders(route))
case .destination(let homeRoute):
router.navigate(to: .home(homeRoute))
case .unsupported(_, let title, let reason):
@ -102,6 +105,8 @@ private struct DebugHomeMenuRow: View {
return "Tab"
case .orders:
return "订单"
case .orderPush:
return "订单页"
case .destination(let homeRoute):
if case .modulePlaceholder = homeRoute {
return "占位"
@ -119,7 +124,7 @@ private struct DebugHomeMenuRow: View {
switch route {
case .tab:
return "rectangle.grid.1x2"
case .orders:
case .orders, .orderPush:
return "doc.text"
case .destination(let homeRoute):
if case .modulePlaceholder = homeRoute {
@ -136,7 +141,7 @@ private struct DebugHomeMenuRow: View {
///
private var iconColor: Color {
switch route {
case .tab, .orders, .destination:
case .tab, .orders, .orderPush, .destination:
AppDesign.primary
case .unsupported:
.red

View File

@ -51,10 +51,11 @@ struct ProfileView: View {
.navigationTitle("个人信息")
.navigationBarTitleDisplayMode(.inline)
.task {
await reloadProfile(showToast: false)
viewModel.applyCachedProfile(accountContext.profile)
await reloadProfile()
}
.refreshable {
await reloadProfile(showToast: true)
await reloadProfile()
}
.sheet(isPresented: $showsPasswordSheet) {
PasswordUpdateSheet { password in
@ -517,9 +518,9 @@ struct ProfileView: View {
}
///
private func reloadProfile(showToast: Bool) async {
private func reloadProfile() async {
do {
try await globalLoading.withOptionalLoading(!showToast && viewModel.userInfo == nil, message: "加载中...") {
try await globalLoading.withOptionalLoading(viewModel.userInfo == nil, message: "加载中...") {
try await viewModel.reload(api: profileAPI)
}
if let userInfo = viewModel.userInfo {
@ -527,9 +528,6 @@ struct ProfileView: View {
} else {
accountContext.replaceProfile(viewModel.accountProfileFallback(accountContext.profile))
}
if showToast {
toastCenter.show("个人信息已刷新")
}
} catch is CancellationError {
return
} catch {