新增门店订单与核销管理列表,对齐 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

@ -0,0 +1,237 @@
//
// OrderListCardView.swift
// suixinkan
//
// Created by Codex on 2026/6/29.
//
import SwiftUI
/// Android OrderView
struct OrderListCardView: View {
let item: OrderEntity
let hideOrderWriteActions: Bool
let showOrderSource: Bool
let sourceSelection: OrderSourceSelection?
let orderSourcePeople: [OrderSourcePerson]
let leadsForPerson: (OrderSourcePerson) -> [OrderSourceLead]
let leadsLoading: Bool
let onRefund: () -> Void
let onVerify: () -> Void
let onUploadTask: () -> Void
let onRefinedChange: (Int) -> Void
let onSendGift: () -> Void
let onCancelOrder: () -> Void
let onHistoricalPhoto: () -> Void
let onCallPhone: () -> Void
let onOrderSourceSheetOpen: () -> Void
let onOrderSourcePersonClick: (OrderSourcePerson) -> Void
let onOrderSourceSelectionChange: (OrderSourceSelection) -> Void
let callSourcePhone: (String) -> Void
private var showRefundButton: Bool {
!hideOrderWriteActions &&
(item.orderStatus == 18 || item.orderStatus == 30) &&
item.orderType != 19
}
private var showVerifyButton: Bool {
!hideOrderWriteActions && (
(item.photoTravel?.needCheckIn == true && item.orderStatus != 30) ||
item.orderType == 19
)
}
var body: some View {
VStack(alignment: .leading, spacing: 0) {
HStack(alignment: .center) {
Text("订单号:\(item.orderNumber)")
.font(.system(size: 14))
.foregroundStyle(.black)
.lineLimit(1)
.minimumScaleFactor(0.72)
Spacer()
OrderStatusChip(text: item.orderStatusName, status: item.orderStatus)
}
Text(item.createdAt.isEmpty ? "--" : item.createdAt)
.font(.system(size: 12))
.foregroundStyle(AppDesign.textSecondary)
.padding(.top, 4)
if !item.orderTypeLabel.isEmpty {
Text(item.orderTypeLabel)
.font(.system(size: 12))
.foregroundStyle(AppDesign.primary)
.padding(.horizontal, 4)
.padding(.vertical, 2)
.background(AppDesign.primarySoft, in: RoundedRectangle(cornerRadius: 4))
.padding(.top, 4)
}
if showOrderSource {
OrderSourcePicker(
selection: sourceSelection,
people: orderSourcePeople,
leadsForPerson: leadsForPerson,
leadsLoading: leadsLoading,
onSheetOpen: onOrderSourceSheetOpen,
onPersonClick: onOrderSourcePersonClick,
onSelectionChange: onOrderSourceSelectionChange,
callPhone: callSourcePhone
)
.padding(.top, 10)
}
if showRefundButton || showVerifyButton {
HStack(spacing: 12) {
if showRefundButton {
OrderActionButton(
title: "退款",
textColor: AppDesign.orderRefundText,
backgroundColor: AppDesign.orderRefundBackground,
maxWidth: showVerifyButton ? nil : .infinity,
action: onRefund
)
.frame(maxWidth: showVerifyButton ? .infinity : nil)
}
if showVerifyButton {
OrderActionButton(title: "核销订单", action: onVerify)
}
}
.padding(.top, 12)
}
if !hideOrderWriteActions && item.orderType == 19 {
Button(action: onUploadTask) {
Text("上传任务")
.font(.system(size: 16))
.foregroundStyle(AppDesign.primary)
.frame(maxWidth: .infinity)
.frame(height: 48)
.background(AppDesign.primarySoft, in: RoundedRectangle(cornerRadius: 12))
}
.buttonStyle(.plain)
.padding(.top, 12)
}
if item.orderStatus == 18 || item.orderStatus == 30 {
OrderRefinedChoiceSection(
currentSelection: item.isRefined,
readOnly: hideOrderWriteActions,
onSelectionChange: onRefinedChange
)
.padding(.top, 12)
}
if item.orderStatus == 50,
!item.actualRefundAmount.isEmpty,
item.actualRefundAmount != "0" {
OrderInfoRow(title: "退款金额", value: "¥\(item.actualRefundAmount)", valueColor: AppDesign.primary)
.padding(.top, 8)
}
VStack(spacing: 8) {
OrderInfoRow(title: "付款金额", value: "¥\(item.actualPayAmount)", valueColor: AppDesign.primary)
OrderInfoRow(
title: "付款时间",
value: OrderListUI.displayPayTime(payTime: item.payTime, depositPayTime: item.depositPayTime)
)
OrderInfoRow(title: "完成时间", value: item.completeTime.isEmpty ? "--" : item.completeTime)
OrderInfoRow(title: "付款方式", value: item.payTypeName.isEmpty ? "--" : item.payTypeName)
OrderInfoRow(title: "用户 UID", value: "\(item.userId)")
OrderInfoRow(title: "手机号", value: OrderListUI.maskPhone(item.phone)) {
HStack(spacing: 7) {
Text(OrderListUI.maskPhone(item.phone))
.font(.system(size: 14, weight: .medium))
.foregroundStyle(.black)
if !hideOrderWriteActions {
OrderPhoneCallButton(action: onCallPhone)
}
}
}
OrderInfoRow(title: "关联项目", value: item.projectName.isEmpty ? "--" : item.projectName)
if let photoTravel = item.photoTravel, !photoTravel.checkInTime.isEmpty {
OrderInfoRow(title: "核销时间", value: photoTravel.checkInTime)
}
if let photoTravel = item.photoTravel,
item.orderType != 19,
item.orderType != 14 {
OrderGiftStepperRow(
title: "修图张数",
valueText: "\(photoTravel.orderPhotoNum + photoTravel.retouchGiftPhotoNum)",
editable: photoTravel.canGiftRetouch && !hideOrderWriteActions,
onTap: onSendGift
)
OrderGiftStepperRow(
title: "视频剪辑",
valueText: "\(photoTravel.orderVideoNum + photoTravel.retouchGiftVideoNum)",
editable: photoTravel.canGiftRetouch && !hideOrderWriteActions,
onTap: onSendGift
)
}
}
.padding(.top, 13)
if !item.remark.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
Divider().overlay(AppDesign.orderInputBackground).padding(.top, 14)
Text("备注信息:")
.font(.system(size: 14))
.foregroundStyle(AppDesign.orderLabelColor)
.padding(.top, 10)
Text(item.remark)
.font(.system(size: 14))
.foregroundStyle(AppDesign.orderLabelColor)
.padding(.top, 6)
}
if let materials = item.multiTravel?.materialList, !materials.isEmpty {
Divider().overlay(AppDesign.orderInputBackground).padding(.top, 12)
Text("历史拍摄信息:")
.font(.system(size: 14))
.foregroundStyle(AppDesign.orderLabelColor)
.padding(.top, 12)
Button(action: onHistoricalPhoto) {
HStack(spacing: 8) {
ForEach(Array(materials.prefix(3).enumerated()), id: \.offset) { index, material in
ZStack {
RemoteImage(
urlString: material.previewURLString,
contentMode: .fill
) {
Color(hex: 0xF4F4F4)
}
.aspectRatio(1.59, contentMode: .fill)
.clipShape(RoundedRectangle(cornerRadius: 8))
if materials.count > 3 && index == 2 {
RoundedRectangle(cornerRadius: 8)
.fill(Color.black.opacity(0.5))
Text("查看更多")
.font(.system(size: 14, weight: .medium))
.foregroundStyle(.white)
}
}
.frame(maxWidth: .infinity)
}
}
}
.buttonStyle(.plain)
.padding(.top, 8)
}
if item.photoTravel?.canCancelOrder == true && !hideOrderWriteActions {
OrderActionButton(title: "取消订单", action: onCancelOrder)
.padding(.top, 12)
}
}
.padding(16)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.white, in: RoundedRectangle(cornerRadius: 12))
.accessibilityIdentifier("orders.store.card.\(item.orderNumber)")
}
}