import SwiftUI import Kingfisher enum OrderSourceColors { static let primary = Color(hex: 0x0073FF) static let text333 = Color(hex: 0x333333) static let text563 = Color(hex: 0x4B5563) static let text666 = Color(hex: 0x666666) static let text999 = Color(hex: 0x999999) static let border = Color(hex: 0xE6EAF0) static let divider = Color(hex: 0xE9EDF3) static let pageBg = Color(hex: 0xF5F7FA) static let primarySoft = Color(hex: 0xEFF6FF) static let imageEmptyBg = Color(hex: 0xF3F5F8) static let entryBg = Color(hex: 0xF9FAFC) } struct AcquirerCallButton: View { let phone: String let onCall: (String) -> Void var body: some View { Button { onCall(phone) } label: { Image(systemName: "phone.fill") .font(.system(size: 12, weight: .semibold)) .foregroundStyle(OrderSourceColors.primary) .frame(width: 32, height: 32) .background(OrderSourceColors.primarySoft, in: Circle()) } .buttonStyle(.plain) .accessibilityLabel("拨打\(phone)") } } struct SourcePersonRow: View { let person: OrderSourcePerson let selected: Bool let onCall: (String) -> Void let onTap: () -> Void var body: some View { HStack(alignment: .center, spacing: 8) { VStack(alignment: .leading, spacing: 0) { Text(person.name) .font(.system(size: 16, weight: .medium)) .foregroundStyle(OrderSourceColors.text333) if !person.phone.isEmpty { Text(person.phone) .font(.system(size: 14)) .foregroundStyle(OrderSourceColors.text666) .padding(.top, 6) } } .frame(maxWidth: .infinity, alignment: .leading) if !person.phone.isEmpty { AcquirerCallButton(phone: person.phone, onCall: onCall) } Image(systemName: "chevron.right") .font(.system(size: 12, weight: .semibold)) .foregroundStyle(OrderSourceColors.text666) .frame(width: 20, height: 20) } .padding(.horizontal, 14) .padding(.vertical, 14) .background( selected ? OrderSourceColors.primary.opacity(0.05) : Color.white, in: RoundedRectangle(cornerRadius: 8) ) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(selected ? OrderSourceColors.primary : OrderSourceColors.border, lineWidth: 1) ) .contentShape(RoundedRectangle(cornerRadius: 8)) .onTapGesture(perform: onTap) } } struct SourceLeadContent: View { let lead: OrderSourceLead var compact = false var enablePhoneCall = true let onCall: (String) -> Void let onImageTap: ([String], Int) -> Void var body: some View { VStack(alignment: .leading, spacing: compact ? 6 : 10) { if !lead.userPhone.isEmpty { HStack(alignment: .center, spacing: 0) { Text("客户手机号") .font(.system(size: 12)) .foregroundStyle(OrderSourceColors.text666) Text(lead.userPhone) .font(.system(size: 13, weight: .medium)) .foregroundStyle(enablePhoneCall ? OrderSourceColors.primary : OrderSourceColors.text333) .padding(.leading, 10) .onTapGesture { if enablePhoneCall { onCall(lead.userPhone) } } } } if !lead.remark.isEmpty { Text(lead.remark) .font(.system(size: compact ? 12 : 13)) .foregroundStyle(OrderSourceColors.text333) .lineSpacing(compact ? 4 : 6) .lineLimit(compact ? 2 : 4) } if lead.images.isEmpty { if !compact { Text("暂无图片") .font(.system(size: 12)) .foregroundStyle(OrderSourceColors.text666) .frame(maxWidth: .infinity) .padding(.vertical, 16) .background(OrderSourceColors.imageEmptyBg, in: RoundedRectangle(cornerRadius: 6)) } } else { HStack(spacing: 8) { ForEach(Array(lead.images.prefix(3).enumerated()), id: \.offset) { index, imageUrl in leadThumbnail(imageUrl: imageUrl, index: index) } ForEach(0.. some View { Group { if let url = URL(string: imageUrl), !imageUrl.isEmpty { KFImage(url) .resizable() .scaledToFill() } else { OrderSourceColors.imageEmptyBg } } .frame(maxWidth: .infinity) .aspectRatio(1, contentMode: .fit) .clipShape(RoundedRectangle(cornerRadius: 6)) .contentShape(Rectangle()) .onTapGesture { onImageTap(lead.images, index) } } } struct SourceLeadCard: View { let lead: OrderSourceLead let selected: Bool let onCall: (String) -> Void let onImageTap: ([String], Int) -> Void let onSelect: () -> Void var body: some View { VStack(alignment: .leading, spacing: 10) { HStack(alignment: .center) { Text("带客单") .font(.system(size: 13, weight: .medium)) .foregroundStyle(OrderSourceColors.primary) .padding(.horizontal, 8) .padding(.vertical, 4) .background(OrderSourceColors.primary.opacity(0.08), in: RoundedRectangle(cornerRadius: 4)) Spacer() if selected { Image(systemName: "checkmark") .font(.system(size: 14, weight: .bold)) .foregroundStyle(OrderSourceColors.primary) .frame(width: 20, height: 20) } } SourceLeadContent( lead: lead, compact: false, enablePhoneCall: true, onCall: onCall, onImageTap: onImageTap ) Text(selected ? "当前已选择" : "选择此带客单") .font(.system(size: 14)) .foregroundStyle(selected ? OrderSourceColors.primary : .white) .frame(maxWidth: .infinity) .padding(.vertical, 10) .background( selected ? OrderSourceColors.primary.opacity(0.1) : OrderSourceColors.primary, in: RoundedRectangle(cornerRadius: 6) ) .contentShape(Rectangle()) .onTapGesture(perform: onSelect) } .padding(12) .background( selected ? OrderSourceColors.primary.opacity(0.04) : Color.white, in: RoundedRectangle(cornerRadius: 8) ) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(selected ? OrderSourceColors.primary : OrderSourceColors.border, lineWidth: 1) ) .contentShape(Rectangle()) .onTapGesture(perform: onSelect) } } struct AcquirerSelectCard: View { let name: String let phone: String let onTap: () -> Void var body: some View { Button(action: onTap) { VStack(alignment: .leading, spacing: 0) { Text(name) .font(.system(size: 16, weight: .bold)) .foregroundStyle(OrderSourceColors.text333) if !phone.isEmpty { Text(phone) .font(.system(size: 13)) .foregroundStyle(OrderSourceColors.text666) .padding(.top, 6) } } .frame(maxWidth: .infinity, alignment: .leading) .padding(14) .background(.white, in: RoundedRectangle(cornerRadius: 10)) } .buttonStyle(.plain) } } struct ReferralOrderSelectCard: View { let order: ReferralOrder let selected: Bool let onTap: () -> Void var body: some View { Button(action: onTap) { VStack(alignment: .leading, spacing: 8) { Text("带客单号 \(order.displayReferralOrderNo)") .font(.system(size: 15, weight: .bold)) .foregroundStyle(OrderSourceColors.text333) if !order.statusLabel.isEmpty { Text(order.statusLabel) .font(.system(size: 13)) .foregroundStyle(OrderSourceColors.text666) } Text("用户 \(order.userMobile.isEmpty ? "—" : order.userMobile)") .font(.system(size: 12)) .foregroundStyle(OrderSourceColors.text999) if !order.remark.isEmpty { Text(order.remark) .font(.system(size: 13)) .foregroundStyle(OrderSourceColors.text666) } if !order.createdAt.isEmpty { Text(order.createdAt) .font(.system(size: 11)) .foregroundStyle(OrderSourceColors.text999) } } .frame(maxWidth: .infinity, alignment: .leading) .padding(14) .background(.white, in: RoundedRectangle(cornerRadius: 10)) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(selected ? OrderSourceColors.primary : Color.clear, lineWidth: 1.5) ) } .buttonStyle(.plain) } } struct SimpleImagePreviewContent: View { let imageUrls: [String] @Binding var currentIndex: Int var body: some View { TabView(selection: $currentIndex) { ForEach(Array(imageUrls.enumerated()), id: \.offset) { index, urlString in Group { if let url = URL(string: urlString), !urlString.isEmpty { KFImage(url) .resizable() .scaledToFit() } else { Text("无法加载图片") .foregroundStyle(OrderSourceColors.text666) } } .tag(index) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.black) } } .tabViewStyle(.page(indexDisplayMode: imageUrls.count > 1 ? .automatic : .never)) .background(Color.black.ignoresSafeArea()) } } struct SimpleImagePreviewSheet: View { let imageUrls: [String] let startIndex: Int let onDismiss: () -> Void @State private var currentIndex: Int init(imageUrls: [String], startIndex: Int, onDismiss: @escaping () -> Void) { self.imageUrls = imageUrls self.startIndex = startIndex self.onDismiss = onDismiss _currentIndex = State(initialValue: startIndex) } var body: some View { NavigationStack { SimpleImagePreviewContent(imageUrls: imageUrls, currentIndex: $currentIndex) .navigationTitle("图片预览") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("关闭", action: onDismiss) } } } } } struct SimpleImagePreviewScreen: View { let imageUrls: [String] let startIndex: Int @State private var currentIndex: Int init(imageUrls: [String], startIndex: Int) { self.imageUrls = imageUrls self.startIndex = startIndex _currentIndex = State(initialValue: startIndex) } var body: some View { SimpleImagePreviewContent(imageUrls: imageUrls, currentIndex: $currentIndex) .navigationTitle("图片预览") .navigationBarTitleDisplayMode(.inline) } }