迁移合作订单完整能力,并统一 AppRoleCode 角色权限与首页菜单展示。

新增 CooperationOrder 模块(双 Tab 列表、获客员绑定、主 Tab 扫码)、订单来源/带客单绑定及配套 API 测试;同步引入 roleCode 权限匹配与相关单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 13:33:00 +08:00
parent d310d26293
commit 1970572edd
66 changed files with 4247 additions and 201 deletions

View File

@ -0,0 +1,123 @@
//
// CooperationOrderListView.swift
// suixinkan
//
// Created by Codex on 2026/6/29.
//
import SwiftUI
/// 线 Tab
struct CooperationOrderListView: View {
@Environment(\.cooperationOrderAPI) private var cooperationOrderAPI
@Environment(\.globalLoading) private var globalLoading
@StateObject private var viewModel = CooperationOrderListViewModel()
@State private var route: CooperationOrderRoute?
var body: some View {
VStack(spacing: 0) {
CooperationOrderTabs(
selectedTab: viewModel.selectedTab,
onTabSelected: { viewModel.onTabSelected($0) }
)
if viewModel.selectedTab == 1 {
CooperationOrderSearchBar(searchText: $viewModel.searchText) {
Task { await searchOrders(showLoading: true) }
}
}
ScrollView {
LazyVStack(spacing: 12) {
if viewModel.selectedTab == 0 {
if viewModel.hasLoadedLeadsOnce && viewModel.leads.isEmpty {
emptyState("暂无获客员线索")
} else {
ForEach(Array(viewModel.leads.enumerated()), id: \.element.id) { index, lead in
ReferralLeadCard(lead: lead)
.onAppear {
guard index >= viewModel.leads.count - 3 else { return }
Task { await viewModel.loadMore(api: cooperationOrderAPI) }
}
}
}
} else {
if viewModel.hasLoadedOrdersOnce && viewModel.orders.isEmpty {
emptyState("暂无相关合作订单")
} else {
ForEach(Array(viewModel.orders.enumerated()), id: \.element.id) { index, order in
AcquisitionOrderCard(order: order)
.onAppear {
guard index >= viewModel.orders.count - 3 else { return }
Task { await viewModel.loadMore(api: cooperationOrderAPI) }
}
}
}
}
if viewModel.isLoadingMore {
ProgressView()
.tint(CooperationOrderColors.primary)
.frame(width: 24, height: 24)
.padding(16)
}
}
.padding(.horizontal, 14)
.padding(.vertical, 12)
}
.refreshable {
await reloadCurrentTab(showLoading: false)
}
}
.navigationTitle("合作订单")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button("合作获客员") {
route = .acquirer
}
.font(.system(size: 14))
.foregroundStyle(CooperationOrderColors.primary)
}
}
.background(CooperationOrderColors.pageBg.ignoresSafeArea())
.appNavigationDestination(item: $route) { destination in
switch destination {
case .acquirer:
CooperationAcquirerView(onBind: { saleUserId in
route = .bind(saleUserId: saleUserId)
})
case let .bind(saleUserId):
BindAcquirerView(saleUserId: saleUserId, onSuccess: {
route = .acquirer
})
}
}
.task {
await reloadCurrentTab(showLoading: true)
}
.onChange(of: viewModel.selectedTab) { _ in
Task { await reloadCurrentTab(showLoading: true) }
}
}
private func reloadCurrentTab(showLoading: Bool) async {
await globalLoading.withOptionalLoading(showLoading) {
await viewModel.refreshCurrentTab(api: cooperationOrderAPI)
}
}
private func searchOrders(showLoading: Bool) async {
await globalLoading.withOptionalLoading(showLoading) {
await viewModel.searchOrders(api: cooperationOrderAPI)
}
}
private func emptyState(_ text: String) -> some View {
Text(text)
.font(.system(size: 14))
.foregroundStyle(CooperationOrderColors.text999)
.frame(maxWidth: .infinity)
.padding(.vertical, 48)
}
}