将扫码页提升到列表层弹出并补齐 AppDelegate.window,避免扫码时 UIKit 取主窗口崩溃;Toast 改用独立 UIWindow 显示在 sheet 之上;合作获客员支持短信验证修改分成比例,同时优化冷启动根视图创建时机并补充相关测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
151 lines
6.0 KiB
Swift
151 lines
6.0 KiB
Swift
//
|
||
// BindAcquirerView.swift
|
||
// suixinkan
|
||
//
|
||
// Created by Codex on 2026/6/29.
|
||
//
|
||
|
||
import SwiftUI
|
||
|
||
/// 绑定获客员确认页,展示邀请信息并提交接受绑定。
|
||
struct BindAcquirerView: View {
|
||
@EnvironmentObject private var toastCenter: ToastCenter
|
||
@Environment(\.cooperationOrderAPI) private var cooperationOrderAPI
|
||
@Environment(\.dismiss) private var dismiss
|
||
@StateObject private var viewModel = BindAcquirerViewModel()
|
||
let saleUserId: Int
|
||
let onSuccess: () -> Void
|
||
@State private var binding = false
|
||
|
||
var body: some View {
|
||
VStack(spacing: 12) {
|
||
if viewModel.isLoading && viewModel.inviteInfo == nil && !viewModel.isAlreadyBound {
|
||
Spacer()
|
||
ProgressView()
|
||
Spacer()
|
||
} else if let info = viewModel.inviteInfo {
|
||
VStack(alignment: .leading, spacing: 12) {
|
||
VStack(alignment: .leading, spacing: 8) {
|
||
Text(info.title.cooperationNonEmptyOrDefault("绑定获客员"))
|
||
.font(.system(size: 16, weight: .semibold))
|
||
.foregroundStyle(.white)
|
||
if !info.subtitle.isEmpty {
|
||
Text(info.subtitle)
|
||
.font(.system(size: 13))
|
||
.foregroundStyle(.white.opacity(0.9))
|
||
}
|
||
}
|
||
.frame(maxWidth: .infinity, alignment: .leading)
|
||
.padding(16)
|
||
.background(AppDesign.primary, in: RoundedRectangle(cornerRadius: 10))
|
||
|
||
infoCard(title: "获客员", value: info.salerName)
|
||
infoCard(title: "手机号", value: info.salerPhone)
|
||
BindCommissionRateInputRow(
|
||
value: Binding(
|
||
get: { viewModel.commissionRateDraft },
|
||
set: { viewModel.updateCommissionRateDraft($0) }
|
||
),
|
||
enabled: !viewModel.isAlreadyBound
|
||
)
|
||
.padding(14)
|
||
.background(.white, in: RoundedRectangle(cornerRadius: 10))
|
||
}
|
||
.padding(16)
|
||
|
||
Spacer()
|
||
|
||
Button {
|
||
Task { await acceptBind() }
|
||
} label: {
|
||
Text(binding ? "提交中..." : (viewModel.isAlreadyBound ? "已绑定获客员" : "接受邀请"))
|
||
.font(.system(size: 16, weight: .semibold))
|
||
.foregroundStyle(.white)
|
||
.frame(maxWidth: .infinity)
|
||
.frame(height: 48)
|
||
.background(
|
||
viewModel.isAlreadyBound ? AppDesign.textSecondary : AppDesign.primary,
|
||
in: RoundedRectangle(cornerRadius: 8)
|
||
)
|
||
}
|
||
.buttonStyle(.plain)
|
||
.disabled(binding || viewModel.isAlreadyBound)
|
||
.padding(.horizontal, 16)
|
||
.padding(.bottom, 12)
|
||
} else if viewModel.isAlreadyBound {
|
||
Spacer()
|
||
Text("已绑定获客员")
|
||
.font(.system(size: 14))
|
||
.foregroundStyle(AppDesign.textSecondary)
|
||
Spacer()
|
||
|
||
Button {} label: {
|
||
Text("已绑定获客员")
|
||
.font(.system(size: 16, weight: .semibold))
|
||
.foregroundStyle(.white)
|
||
.frame(maxWidth: .infinity)
|
||
.frame(height: 48)
|
||
.background(AppDesign.textSecondary, in: RoundedRectangle(cornerRadius: 8))
|
||
}
|
||
.buttonStyle(.plain)
|
||
.disabled(true)
|
||
.padding(.horizontal, 16)
|
||
.padding(.bottom, 12)
|
||
} else if let error = viewModel.errorMessage {
|
||
Spacer()
|
||
Text(error)
|
||
.font(.system(size: 14))
|
||
.foregroundStyle(AppDesign.textSecondary)
|
||
.multilineTextAlignment(.center)
|
||
.padding()
|
||
Spacer()
|
||
}
|
||
}
|
||
.navigationTitle(viewModel.inviteInfo?.title.cooperationNonEmptyOrDefault("接受邀请") ?? "接受邀请")
|
||
.navigationBarTitleDisplayMode(.inline)
|
||
.background(Color(hex: 0xF5F7FA).ignoresSafeArea())
|
||
.task {
|
||
await viewModel.load(api: cooperationOrderAPI, saleUserId: saleUserId)
|
||
if viewModel.errorMessage != nil && viewModel.inviteInfo == nil && !viewModel.isAlreadyBound {
|
||
if let message = viewModel.errorMessage {
|
||
toastCenter.show(message)
|
||
}
|
||
dismiss()
|
||
}
|
||
}
|
||
}
|
||
|
||
private func infoCard(title: String, value: String) -> some View {
|
||
VStack(alignment: .leading, spacing: 6) {
|
||
Text(title)
|
||
.font(.system(size: 13))
|
||
.foregroundStyle(AppDesign.textSecondary)
|
||
Text(value.isEmpty ? "--" : value)
|
||
.font(.system(size: 15, weight: .medium))
|
||
.foregroundStyle(AppDesign.textPrimary)
|
||
}
|
||
.frame(maxWidth: .infinity, alignment: .leading)
|
||
.padding(14)
|
||
.background(.white, in: RoundedRectangle(cornerRadius: 10))
|
||
}
|
||
|
||
private func acceptBind() async {
|
||
binding = true
|
||
defer { binding = false }
|
||
do {
|
||
try await viewModel.acceptBind(api: cooperationOrderAPI)
|
||
toastCenter.show("绑定成功")
|
||
onSuccess()
|
||
dismiss()
|
||
} catch {
|
||
viewModel.handleBindError(error.localizedDescription.isEmpty ? "绑定失败" : error.localizedDescription)
|
||
if let message = viewModel.errorMessage {
|
||
toastCenter.show(message)
|
||
}
|
||
if !viewModel.isAlreadyBound {
|
||
dismiss()
|
||
}
|
||
}
|
||
}
|
||
}
|