Files
suixinkan_ios_new/suixinkan/Features/Orders/Views/OrderListComponents.swift

277 lines
9.1 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// OrderListComponents.swift
// suixinkan
//
// Created by Codex on 2026/6/29.
//
import SwiftUI
/// UI
enum OrderListUI {
///
static func statusColors(for status: Int) -> (text: Color, background: Color) {
switch status {
case 18:
return (AppDesign.orderStatusPaidText, AppDesign.orderStatusPaidBackground)
case 30:
return (AppDesign.orderStatusCompletedText, AppDesign.orderStatusCompletedBackground)
case 50:
return (AppDesign.orderStatusRefundedText, AppDesign.orderStatusRefundedBackground)
default:
return (AppDesign.orderStatusOtherText, AppDesign.orderStatusOtherBackground)
}
}
///
static func maskPhone(_ phone: String) -> String {
let trimmed = phone.trimmingCharacters(in: .whitespacesAndNewlines)
guard trimmed.count >= 7 else { return trimmed.isEmpty ? "--" : trimmed }
return "\(trimmed.prefix(3))****\(trimmed.suffix(4))"
}
/// depositPayTime
static func displayPayTime(payTime: String, depositPayTime: String) -> String {
if payTime.isEmpty {
return depositPayTime.isEmpty ? "--" : depositPayTime
}
if payTime.hasPrefix("1970") || payTime == "0" {
return depositPayTime.isEmpty ? payTime : depositPayTime
}
return payTime
}
}
/// Chip Android OrderView
struct OrderStatusChip: View {
let text: String
let status: Int
var body: some View {
let colors = OrderListUI.statusColors(for: status)
Text(text.isEmpty ? "--" : text)
.font(.system(size: 14))
.foregroundStyle(colors.text)
.padding(.horizontal, 4)
.padding(.vertical, 2)
.background(colors.background, in: RoundedRectangle(cornerRadius: 4))
}
}
///
struct OrderInfoRow: View {
let title: String
let value: String
var valueColor: Color = .black
var trailing: AnyView?
init(title: String, value: String, valueColor: Color = .black, @ViewBuilder trailing: () -> some View = { EmptyView() }) {
self.title = title
self.value = value
self.valueColor = valueColor
let built = trailing()
self.trailing = built is EmptyView ? nil : AnyView(built)
}
var body: some View {
HStack(alignment: .center, spacing: 8) {
Text(title)
.font(.system(size: 14))
.foregroundStyle(AppDesign.orderLabelColor)
Spacer(minLength: 8)
if let trailing {
trailing
} else {
Text(value)
.font(.system(size: 14, weight: .medium))
.foregroundStyle(valueColor)
.multilineTextAlignment(.trailing)
.lineLimit(2)
.minimumScaleFactor(0.75)
}
}
}
}
/// Android AppButton
struct OrderActionButton: View {
let title: String
var textColor: Color = .white
var backgroundColor: Color = AppDesign.primary
var maxWidth: CGFloat? = .infinity
let action: () -> Void
var body: some View {
Button(action: action) {
Text(title)
.font(.system(size: 16))
.foregroundStyle(textColor)
.frame(maxWidth: maxWidth)
.frame(height: 48)
.background(backgroundColor, in: RoundedRectangle(cornerRadius: 8))
}
.buttonStyle(.plain)
}
}
///
struct OrderPhoneCallButton: View {
let action: () -> Void
var body: some View {
Button(action: action) {
Image(systemName: "phone.fill")
.font(.system(size: 12))
.foregroundStyle(AppDesign.primary)
.frame(width: 24, height: 24)
.background(AppDesign.primarySoft, in: Circle())
}
.buttonStyle(.plain)
}
}
/// Android ExclusiveChoiceButtons
struct OrderRefinedChoiceSection: View {
let currentSelection: Int
let readOnly: Bool
let onSelectionChange: (Int) -> Void
var body: some View {
HStack {
Text("需要剪辑修图")
.font(.system(size: 14))
.foregroundStyle(AppDesign.orderLabelColor)
Spacer()
if readOnly {
Text(refinedDisplayText)
.font(.system(size: 14, weight: .medium))
.foregroundStyle(.black)
} else {
HStack(spacing: 16) {
refinedChoiceButton(title: "", value: 1)
refinedChoiceButton(title: "", value: 2)
}
}
}
}
private var refinedDisplayText: String {
switch currentSelection {
case 1: return ""
case 2: return ""
default: return "--"
}
}
private func refinedChoiceButton(title: String, value: Int) -> some View {
let selected = currentSelection == value
return Button {
onSelectionChange(value)
} label: {
HStack(spacing: 4) {
ZStack {
Circle()
.stroke(AppDesign.primary, lineWidth: selected ? 0 : 1.5)
.background(selected ? Circle().fill(AppDesign.primary) : nil)
.frame(width: 20, height: 20)
if selected {
Image(systemName: "checkmark")
.font(.system(size: 10, weight: .bold))
.foregroundStyle(.white)
}
}
Text(title)
.font(.system(size: 14))
.foregroundStyle(.black)
}
}
.buttonStyle(.plain)
}
}
/// /
struct OrderGiftStepperRow: View {
let title: String
let valueText: String
let editable: Bool
let onTap: () -> Void
var body: some View {
HStack {
Text(title)
.font(.system(size: 14))
.foregroundStyle(AppDesign.orderLabelColor)
Spacer()
if editable {
HStack(spacing: 8) {
giftStepButton(symbol: "-", action: onTap)
Text(valueText)
.font(.system(size: 14, weight: .medium))
.foregroundStyle(.black)
giftStepButton(symbol: "+", action: onTap)
}
} else {
Text(valueText)
.font(.system(size: 14, weight: .medium))
.foregroundStyle(.black)
}
}
}
private func giftStepButton(symbol: String, action: @escaping () -> Void) -> some View {
Button(action: action) {
Text(symbol)
.font(.system(size: 16, weight: .bold))
.foregroundStyle(Color(hex: 0xE5E7EB))
.frame(width: 24, height: 24)
.overlay(Circle().stroke(Color(hex: 0xE5E7EB), lineWidth: 1))
}
.buttonStyle(.plain)
}
}
/// pill Tab
struct WriteOffStatusTabBar: View {
let filters: [OrderStatusFilter]
let selectedStatus: Int
let onSelect: (Int) -> Void
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(filters) { filter in
let selected = selectedStatus == filter.id
Button {
onSelect(filter.id)
} label: {
Text(filter.title)
.font(.system(size: 14))
.foregroundStyle(selected ? .white : AppDesign.orderLabelColor)
.padding(.horizontal, 12)
.frame(height: 32)
.background(
selected ? AppDesign.primary : Color(hex: 0xF3F4F6),
in: RoundedRectangle(cornerRadius: 16)
)
}
.buttonStyle(.plain)
}
}
.padding(.horizontal, 16)
}
}
}
/// `.searchable`
enum OrderListSearchBehavior {
///
static func normalized(_ text: String) -> String {
text.trimmingCharacters(in: .whitespacesAndNewlines)
}
///
static func shouldReloadAfterClear(newText: String, appliedQuery: String) -> Bool {
normalized(newText).isEmpty && !appliedQuery.isEmpty
}
}