Add order tail flows and migrate queue, message, settlement, and audit modules.

Wire deposit orders, historical shooting, and multi-travel uploads into Orders, and replace home placeholders for queue management, message center, scenic settlement, and withdrawal audit.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-25 13:39:02 +08:00
parent fb8430889b
commit 1a0d1c25f4
63 changed files with 9823 additions and 58 deletions

View File

@ -17,6 +17,8 @@ struct StoreOrderDetailView: View {
let item: OrderEntity
@State private var viewModel: OrderDetailViewModel
@State private var refundViewModel = OrderRefundViewModel()
@State private var showRefundSheet = false
/// 使 ViewModel
init(item: OrderEntity) {
@ -96,10 +98,36 @@ struct StoreOrderDetailView: View {
}
Section("后续功能") {
placeholderButton("历史拍摄", title: "历史拍摄")
placeholderButton("任务上传", title: "任务上传")
placeholderButton("视频预告", title: "视频预告")
placeholderButton("退款", title: "订单退款")
Button {
router.navigate(to: .orders(.historicalShooting(orderNumber: viewModel.display.orderNumber)))
} label: {
navigationRow("历史拍摄")
}
if item.orderType == 19 {
Button {
router.navigate(to: .orders(.multiTravelTaskUpload(orderNumber: viewModel.display.orderNumber)))
} label: {
navigationRow("任务上传")
}
}
Button {
router.navigate(to: .orders(.orderTrailer(orderNumber: viewModel.display.orderNumber, title: "视频预告")))
} label: {
navigationRow("视频预告")
}
Button {
router.navigate(to: .orders(.orderTrailer(orderNumber: viewModel.display.orderNumber, title: "尾片上传")))
} label: {
navigationRow("尾片上传")
}
if refundViewModel.canRefund(item) {
Button {
refundViewModel.begin(item: item)
showRefundSheet = true
} label: {
navigationRow("退款")
}
}
}
}
.listStyle(.insetGrouped)
@ -117,6 +145,17 @@ struct StoreOrderDetailView: View {
} message: {
Text(viewModel.errorMessage ?? "")
}
.sheet(isPresented: $showRefundSheet) {
OrderRefundSheet(
item: item,
viewModel: refundViewModel,
onCancel: { showRefundSheet = false },
onSubmit: {
Task { await submitRefund() }
}
)
.presentationDetents([.medium, .large])
}
}
private var displayPayTime: String {
@ -136,20 +175,24 @@ struct StoreOrderDetailView: View {
)
}
///
private func placeholderButton(_ label: String, title: String) -> some View {
Button {
router.navigate(to: .placeholder(title: title))
} label: {
HStack {
Text(label)
Spacer()
Image(systemName: "chevron.right")
.font(.system(size: AppMetrics.FontSize.caption, weight: .semibold))
.foregroundStyle(AppDesign.textSecondary)
}
///
private func navigationRow(_ label: String) -> some View {
HStack {
Text(label)
Spacer()
Image(systemName: "chevron.right")
.font(.system(size: AppMetrics.FontSize.caption, weight: .semibold))
.foregroundStyle(AppDesign.textSecondary)
}
.foregroundStyle(AppDesign.textPrimary)
}
/// 退
private func submitRefund() async {
let success = await refundViewModel.submit(api: ordersAPI, item: item)
guard success else { return }
showRefundSheet = false
toastCenter.show("退款申请已提交")
await viewModel.load(api: ordersAPI, fallbackStoreId: accountContext.currentStore?.id, forceReload: true)
}
/// 0
@ -158,6 +201,79 @@ struct StoreOrderDetailView: View {
}
}
/// 退退
private struct OrderRefundSheet: View {
let item: OrderEntity
@Bindable var viewModel: OrderRefundViewModel
let onCancel: () -> Void
let onSubmit: () -> Void
@FocusState private var focusedField: RefundInputField?
var body: some View {
NavigationStack {
Form {
Section("订单") {
OrderDetailRow(title: "订单号", value: item.orderNumber)
OrderDetailRow(title: "可退金额", value: "¥\(viewModel.availableAmountText(for: item))")
}
Section("退款方式") {
Picker("退款方式", selection: $viewModel.mode) {
ForEach(OrderRefundMode.allCases) { mode in
Text(mode.title).tag(mode)
}
}
.pickerStyle(.segmented)
if viewModel.mode == .partial {
TextField("请输入退款金额", text: $viewModel.amount)
.keyboardType(.decimalPad)
.focused($focusedField, equals: .amount)
}
}
Section("退款原因") {
TextEditor(text: $viewModel.reason)
.frame(minHeight: 96)
.focused($focusedField, equals: .reason)
}
if let errorMessage = viewModel.errorMessage {
Section {
Text(errorMessage)
.font(.system(size: AppMetrics.FontSize.footnote))
.foregroundStyle(Color(hex: 0xEF4444))
}
}
}
.navigationTitle("订单退款")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("取消", action: onCancel)
.disabled(viewModel.submitting)
}
ToolbarItem(placement: .confirmationAction) {
Button(viewModel.submitting ? "提交中..." : "提交", action: onSubmit)
.disabled(viewModel.submitting)
}
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("完成") {
focusedField = nil
}
}
}
}
}
}
/// 退
private enum RefundInputField: Hashable {
case amount
case reason
}
///
struct WriteOffOrderDetailView: View {
@Environment(AccountContext.self) private var accountContext