Initial commit: suixinkan_ios UIKit rewrite project.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 14:33:31 +08:00
commit 9edf993432
297 changed files with 47151 additions and 0 deletions

View File

@ -0,0 +1,75 @@
//
// OrderDetailViewModel.swift
// suixinkan
//
// Created by Codex on 2026/6/22.
//
import Foundation
@MainActor
/// ViewModel
final class OrderDetailViewModel {
var onChange: (() -> Void)?
private(set) var detail: StoreOrderDetailResponse? { didSet { onChange?() } }
private(set) var loading = false { didSet { onChange?() } }
private(set) var errorMessage: String? { didSet { onChange?() } }
private(set) var contextMessage: String? { didSet { onChange?() } }
/// 使
var display: StoreOrderDetailDisplay {
if let detail {
return StoreOrderDetailDisplay(detail: detail)
}
return StoreOrderDetailDisplay(item: item)
}
///
var projectInfo: StoreOrderProjectInfo? {
detail?.multiTravel?.projectInfo
}
///
var shootingList: [StoreOrderShootingListItem] {
detail?.multiTravel?.shootingList ?? []
}
private let item: OrderEntity
/// ViewModel
init(item: OrderEntity) {
self.item = item
}
/// ID
func load(api: OrderServing, fallbackStoreId: Int?, forceReload: Bool = false) async {
guard !loading else { return }
if forceReload {
detail = nil
} else {
guard detail == nil else { return }
}
guard let storeId = item.storeId ?? fallbackStoreId, storeId > 0 else {
contextMessage = "缺少门店上下文,当前展示订单摘要。"
return
}
loading = true
errorMessage = nil
contextMessage = nil
defer { loading = false }
do {
detail = try await api.storeOrderDetail(storeId: storeId, orderNumber: item.orderNumber)
} catch {
errorMessage = error.localizedDescription
contextMessage = "详情加载失败,当前展示订单摘要。"
}
}
///
func clearError() {
errorMessage = nil
}
}

View File

@ -0,0 +1,608 @@
//
// OrderLongTailViewModels.swift
// suixinkan
//
// Created by Codex on 2026/6/25.
//
import Foundation
@MainActor
/// ViewModel退
final class DepositOrderListViewModel {
var onChange: (() -> Void)?
private(set) var orders: [DepositOrderListItem] = [] { didSet { onChange?() } }
private(set) var total = 0 { didSet { onChange?() } }
private(set) var page = 1 { didSet { onChange?() } }
private(set) var hasMore = false { didSet { onChange?() } }
private(set) var loading = false { didSet { onChange?() } }
private(set) var loadingMore = false { didSet { onChange?() } }
private(set) var operatingOrderNumber: String? { didSet { onChange?() } }
var errorMessage: String? { didSet { onChange?() } }
private let pageSize = 10
///
func reload(api: OrderServing, scenicId: Int?, reset: Bool) async {
guard let scenicId, scenicId > 0 else {
resetState()
return
}
if reset {
page = 1
total = 0
orders = []
loading = true
} else {
guard !loadingMore, hasMore else { return }
loadingMore = true
}
defer {
loading = false
loadingMore = false
}
do {
let result = try await api.depositOrderList(scenicId: scenicId, page: page, pageSize: pageSize)
total = result.total
if page == 1 {
orders = result.list
} else {
orders.append(contentsOf: result.list)
}
page += 1
hasMore = orders.count < result.total && !result.list.isEmpty
errorMessage = nil
} catch {
if reset {
orders = []
total = 0
page = 1
hasMore = false
}
errorMessage = error.localizedDescription
}
}
///
func writeOff(api: OrderServing, scenicId: Int?, orderNumber: String) async -> Bool {
let normalized = orderNumber.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalized.isEmpty, operatingOrderNumber == nil else { return false }
guard let scenicId, scenicId > 0 else {
errorMessage = "缺少景区上下文"
return false
}
operatingOrderNumber = normalized
defer { operatingOrderNumber = nil }
do {
try await api.depositOrderWriteOff(orderNumber: normalized)
await reload(api: api, scenicId: scenicId, reset: true)
return true
} catch {
errorMessage = error.localizedDescription
return false
}
}
/// 退
func refund(api: OrderServing, scenicId: Int?, orderNumber: String, reason: String) async -> Bool {
let normalizedOrderNumber = orderNumber.trimmingCharacters(in: .whitespacesAndNewlines)
let normalizedReason = reason.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalizedOrderNumber.isEmpty, operatingOrderNumber == nil else { return false }
guard !normalizedReason.isEmpty else {
errorMessage = "请填写退款原因"
return false
}
guard let scenicId, scenicId > 0 else {
errorMessage = "缺少景区上下文"
return false
}
operatingOrderNumber = normalizedOrderNumber
defer { operatingOrderNumber = nil }
do {
try await api.depositOrderRefund(orderNumber: normalizedOrderNumber, refundReason: normalizedReason)
await reload(api: api, scenicId: scenicId, reset: true)
return true
} catch {
errorMessage = error.localizedDescription
return false
}
}
///
func clearError() {
errorMessage = nil
}
private func resetState() {
orders = []
total = 0
page = 1
hasMore = false
loading = false
loadingMore = false
operatingOrderNumber = nil
errorMessage = nil
}
}
@MainActor
/// ViewModel
final class DepositOrderDetailViewModel {
var onChange: (() -> Void)?
private(set) var detail: StoreOrderDetailResponse? { didSet { onChange?() } }
private(set) var loading = false { didSet { onChange?() } }
var errorMessage: String? { didSet { onChange?() } }
///
func load(api: OrderServing, storeId: Int?, orderNumber: String) async {
let normalized = orderNumber.trimmingCharacters(in: .whitespacesAndNewlines)
guard let storeId, storeId > 0 else {
reset(message: "当前账号缺少门店信息")
return
}
guard !normalized.isEmpty else {
reset(message: "订单号不能为空")
return
}
detail = nil
loading = true
defer { loading = false }
do {
detail = try await api.storeOrderDetail(storeId: storeId, orderNumber: normalized)
errorMessage = nil
} catch {
detail = nil
errorMessage = error.localizedDescription
}
}
///
func clearError() {
errorMessage = nil
}
private func reset(message: String) {
detail = nil
loading = false
errorMessage = message
}
}
@MainActor
/// ViewModel
final class DepositOrderShootingInfoViewModel {
var onChange: (() -> Void)?
private(set) var detail: StoreOrderShootingDetailResponse? { didSet { onChange?() } }
private(set) var loading = false { didSet { onChange?() } }
var errorMessage: String? { didSet { onChange?() } }
///
func load(api: OrderServing, storeId: Int?, orderNumber: String, scenicSpotId: Int, photogUid: Int) async {
let normalized = orderNumber.trimmingCharacters(in: .whitespacesAndNewlines)
guard let storeId, storeId > 0 else {
reset(message: "当前账号缺少门店信息")
return
}
guard !normalized.isEmpty else {
reset(message: "订单号不能为空")
return
}
detail = nil
loading = true
defer { loading = false }
do {
detail = try await api.storeOrderShootingDetail(
storeId: storeId,
orderNumber: normalized,
scenicSpotId: scenicSpotId,
photogUid: photogUid
)
errorMessage = nil
} catch {
detail = nil
errorMessage = error.localizedDescription
}
}
///
func clearError() {
errorMessage = nil
}
private func reset(message: String) {
detail = nil
loading = false
errorMessage = message
}
}
@MainActor
/// 退 ViewModel退
final class OrderRefundViewModel {
var onChange: (() -> Void)?
var mode: OrderRefundMode = .full { didSet { onChange?() } }
var amount = "" { didSet { onChange?() } }
var reason = "" { didSet { onChange?() } }
private(set) var submitting = false { didSet { onChange?() } }
var errorMessage: String? { didSet { onChange?() } }
/// 退
func canRefund(_ item: OrderEntity) -> Bool {
(item.orderStatus == 18 || item.orderStatus == 30) &&
item.orderType != 19 &&
availableAmount(for: item) > 0
}
/// 退
func begin(item: OrderEntity) {
mode = .full
amount = availableAmountText(for: item)
reason = ""
errorMessage = nil
}
/// 退
func submit(api: OrderServing, item: OrderEntity) async -> Bool {
guard !submitting else { return false }
guard canRefund(item) else {
errorMessage = "当前订单不可退款"
return false
}
let normalizedReason = reason.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalizedReason.isEmpty else {
errorMessage = "请输入退款原因"
return false
}
let refundAmount: String
switch mode {
case .full:
refundAmount = availableAmountText(for: item)
case .partial:
guard let normalized = Self.normalizedMoney(amount) else {
errorMessage = "请输入有效的退款金额"
return false
}
guard (Decimal(string: normalized) ?? 0) <= availableAmount(for: item) else {
errorMessage = "退款金额不能大于可退金额"
return false
}
refundAmount = normalized
}
submitting = true
errorMessage = nil
defer { submitting = false }
do {
try await api.orderRefund(
orderNumber: item.orderNumber,
refundType: mode,
refundAmount: refundAmount,
refundReason: normalizedReason
)
return true
} catch {
errorMessage = error.localizedDescription
return false
}
}
/// 退
func availableAmount(for item: OrderEntity) -> Decimal {
let paid = Self.decimalValue(item.actualPayAmount)
if paid > 0 { return paid }
return Self.decimalValue(item.actualRefundAmount.isEmpty ? item.refundAmount : item.actualRefundAmount)
}
/// 退
func availableAmountText(for item: OrderEntity) -> String {
Self.moneyText(availableAmount(for: item))
}
/// nil
static func normalizedMoney(_ rawValue: String) -> String? {
let text = rawValue.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: ",", with: "")
guard !text.isEmpty else { return nil }
let pattern = #"^\d+(\.\d{1,2})?$"#
guard text.range(of: pattern, options: .regularExpression) != nil,
let value = Decimal(string: text),
value > 0 else {
return nil
}
return moneyText(value)
}
private static func decimalValue(_ text: String) -> Decimal {
Decimal(string: text.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: ",", with: "")) ?? 0
}
private static func moneyText(_ value: Decimal) -> String {
let number = NSDecimalNumber(decimal: value)
let handler = NSDecimalNumberHandler(
roundingMode: .plain,
scale: 2,
raiseOnExactness: false,
raiseOnOverflow: false,
raiseOnUnderflow: false,
raiseOnDivideByZero: false
)
return String(format: "%.2f", number.rounding(accordingToBehavior: handler).doubleValue)
}
}
@MainActor
/// ViewModel
final class HistoricalShootingInfoViewModel {
var onChange: (() -> Void)?
private(set) var projectName = "" { didSet { onChange?() } }
private(set) var projectTypeName = "" { didSet { onChange?() } }
private(set) var spots: [PhotogSpotItem] = [] { didSet { onChange?() } }
private(set) var loading = false { didSet { onChange?() } }
var errorMessage: String? { didSet { onChange?() } }
///
func load(api: OrderServing, orderNumber: String) async {
let normalized = orderNumber.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalized.isEmpty else {
reset(message: "订单号不能为空")
return
}
loading = true
defer { loading = false }
do {
let result = try await api.multiTravelShootHistory(orderNumber: normalized)
projectName = result.projectName
projectTypeName = result.projectTypeName
spots = result.photogSpotList
errorMessage = nil
} catch {
reset(message: error.localizedDescription)
}
}
///
func clearError() {
errorMessage = nil
}
private func reset(message: String) {
projectName = ""
projectTypeName = ""
spots = []
loading = false
errorMessage = message
}
}
@MainActor
/// ViewModel
final class MultiTravelTaskUploadViewModel {
var onChange: (() -> Void)?
var orderNumber = "" { didSet { onChange?() } }
var selectedSpotId: Int? { didSet { onChange?() } }
private(set) var spots: [MultiTravelVerifiedScenicSpotItem] = [] { didSet { onChange?() } }
var selectedCloudFiles: [TaskCloudSelectionItem] = [] { didSet { onChange?() } }
var selectedLocalFiles: [TaskLocalUploadItem] = [] { didSet { onChange?() } }
private(set) var isLoadingSpots = false { didSet { onChange?() } }
private(set) var isSubmitting = false { didSet { onChange?() } }
private(set) var didSubmitSuccessfully = false { didSet { onChange?() } }
var errorMessage: String? { didSet { onChange?() } }
private var loadedSpotOrderNumber = ""
///
var selectedSpotName: String {
spots.first(where: { $0.id == selectedSpotId })?.name ?? "请选择打卡点"
}
///
var canSubmit: Bool {
!isSubmitting &&
selectedSpotId != nil &&
(!selectedCloudFiles.isEmpty || !selectedLocalFiles.isEmpty) &&
!selectedLocalFiles.contains(where: \.isUploading)
}
///
init(initialOrderNumber: String = "") {
orderNumber = initialOrderNumber
}
///
func loadSpots(api: OrderServing) async {
let normalized = orderNumber.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalized.isEmpty else {
resetSpotSelection()
return
}
isLoadingSpots = true
defer { isLoadingSpots = false }
do {
let result = try await api.multiTravelVerifiedScenicSpotList(orderNumber: normalized)
spots = result
loadedSpotOrderNumber = normalized
if !spots.contains(where: { $0.id == selectedSpotId }) {
selectedSpotId = spots.first?.id
}
errorMessage = nil
} catch {
resetSpotSelection()
errorMessage = error.localizedDescription
}
}
///
func selectSpot(id: Int?) {
selectedSpotId = id
}
/// ID
func mergeCloudFiles(_ files: [TaskCloudSelectionItem]) {
var next = selectedCloudFiles
for file in files where !next.contains(where: { $0.id == file.id }) {
next.append(file)
}
selectedCloudFiles = next
}
///
func removeCloudFile(id: Int) {
selectedCloudFiles.removeAll { $0.id == id }
}
///
func addLocalFile(data: Data, fileName: String) {
selectedLocalFiles.append(
TaskLocalUploadItem(
id: UUID(),
data: data,
fileName: fileName,
fileType: Self.fileType(for: fileName),
remark: "",
uploadedURL: nil,
progress: 0,
errorMessage: nil
)
)
}
///
func removeLocalFile(id: UUID) {
selectedLocalFiles.removeAll { $0.id == id }
}
/// OSS URL
@discardableResult
func submit(api: OrderServing, uploadService: any OSSUploadServing, scenicId: Int?) async -> Bool {
guard !isSubmitting else { return false }
guard let requestContext = validateBeforeSubmit(scenicId: scenicId) else { return false }
isSubmitting = true
didSubmitSuccessfully = false
errorMessage = nil
defer { isSubmitting = false }
do {
let uploadFiles = try await uploadLocalFiles(uploadService: uploadService, scenicId: requestContext.scenicId)
try await api.multiTravelUploadMaterial(
MultiTravelUploadMaterialRequest(
orderNumber: requestContext.orderNumber,
scenicSpotId: requestContext.scenicSpotId,
cloudFile: selectedCloudFiles.map { MultiTravelCloudFileItem(fileId: $0.id) },
uploadFile: uploadFiles
)
)
didSubmitSuccessfully = true
return true
} catch {
errorMessage = error.localizedDescription
return false
}
}
///
func clearError() {
errorMessage = nil
}
private func validateBeforeSubmit(scenicId: Int?) -> SubmitContext? {
let normalizedOrderNumber = orderNumber.trimmingCharacters(in: .whitespacesAndNewlines)
guard let scenicId, scenicId > 0 else {
errorMessage = "请先选择景区"
return nil
}
guard !normalizedOrderNumber.isEmpty else {
errorMessage = "请先填写订单号"
return nil
}
guard loadedSpotOrderNumber == normalizedOrderNumber else {
errorMessage = "请先刷新打卡点"
return nil
}
guard let selectedSpotId else {
errorMessage = "请选择打卡点"
return nil
}
guard !selectedCloudFiles.isEmpty || !selectedLocalFiles.isEmpty else {
errorMessage = "请至少选择一个素材文件"
return nil
}
guard !selectedLocalFiles.contains(where: \.isUploading) else {
errorMessage = "文件上传中,请稍后提交"
return nil
}
return SubmitContext(scenicId: scenicId, orderNumber: normalizedOrderNumber, scenicSpotId: selectedSpotId)
}
private func uploadLocalFiles(uploadService: any OSSUploadServing, scenicId: Int) async throws -> [MultiTravelUploadFileItem] {
var uploadFiles: [MultiTravelUploadFileItem] = []
for index in selectedLocalFiles.indices {
if let uploadedURL = selectedLocalFiles[index].uploadedURL {
uploadFiles.append(MultiTravelUploadFileItem(fileName: selectedLocalFiles[index].fileName, fileUrl: uploadedURL))
continue
}
selectedLocalFiles[index].errorMessage = nil
let fileID = selectedLocalFiles[index].id
do {
let url = try await uploadService.uploadTaskFile(
data: selectedLocalFiles[index].data,
fileName: selectedLocalFiles[index].fileName,
fileType: selectedLocalFiles[index].fileType,
scenicId: scenicId,
onProgress: { [weak self] progress in
Task { @MainActor in
self?.updateLocalFileProgress(id: fileID, progress: progress)
}
}
)
selectedLocalFiles[index].uploadedURL = url
selectedLocalFiles[index].progress = 100
uploadFiles.append(MultiTravelUploadFileItem(fileName: selectedLocalFiles[index].fileName, fileUrl: url))
} catch {
selectedLocalFiles[index].progress = 0
selectedLocalFiles[index].errorMessage = error.localizedDescription
throw error
}
}
return uploadFiles
}
private func updateLocalFileProgress(id: UUID, progress: Int) {
guard let index = selectedLocalFiles.firstIndex(where: { $0.id == id }) else { return }
selectedLocalFiles[index].progress = progress
}
private func resetSpotSelection() {
spots = []
selectedSpotId = nil
loadedSpotOrderNumber = ""
isLoadingSpots = false
}
private static func fileType(for fileName: String) -> Int {
let ext = URL(fileURLWithPath: fileName).pathExtension.lowercased()
return ["mp4", "mov", "m4v", "avi"].contains(ext) ? 1 : 2
}
private struct SubmitContext {
let scenicId: Int
let orderNumber: String
let scenicSpotId: Int
}
}

View File

@ -0,0 +1,213 @@
//
// OrdersViewModel.swift
// suixinkan
//
// Created by Codex on 2026/6/22.
//
import Foundation
@MainActor
/// ViewModel
final class OrdersViewModel {
var onChange: (() -> Void)?
var selectedEntry: OrdersEntry = .storeOrders { didSet { onChange?() } }
var selectedStatus = 0 { didSet { onChange?() } }
var searchPhone = "" { didSet { onChange?() } }
var filterStartDate: Date? { didSet { onChange?() } }
var filterEndDate: Date? { didSet { onChange?() } }
private(set) var loading = false { didSet { onChange?() } }
private(set) var loadingMore = false { didSet { onChange?() } }
private(set) var isVerifying = false { didSet { onChange?() } }
private(set) var currentVerifyingOrderNumber: String? { didSet { onChange?() } }
private(set) var storeOrders: [OrderEntity] = [] { didSet { onChange?() } }
private(set) var storeTotal = 0 { didSet { onChange?() } }
private(set) var storePage = 1 { didSet { onChange?() } }
private(set) var storeHasMore = false { didSet { onChange?() } }
private(set) var writeOffOrders: [WriteOffOrderItem] = [] { didSet { onChange?() } }
private(set) var writeOffTotal = 0 { didSet { onChange?() } }
private(set) var writeOffPage = 1 { didSet { onChange?() } }
private(set) var writeOffHasMore = false { didSet { onChange?() } }
///
var normalizedPhone: String? {
let text = searchPhone.trimmingCharacters(in: .whitespacesAndNewlines)
return text.isEmpty ? nil : text
}
///
func reload(api: OrderServing, scenicId: Int?, storeId: Int? = nil, roleId: Int?, showLoading: Bool = true) async throws {
switch selectedEntry {
case .storeOrders:
try await reloadStoreOrders(api: api, scenicId: scenicId, roleId: roleId, showLoading: showLoading)
case .verificationOrders:
try await reloadWriteOffOrders(api: api, scenicId: scenicId, storeId: storeId, showLoading: showLoading)
}
}
///
func reloadStoreOrders(api: OrderServing, scenicId: Int?, roleId: Int?, showLoading: Bool = true) async throws {
guard let scenicId else {
storeOrders = []
storeTotal = 0
storePage = 1
storeHasMore = false
return
}
if showLoading { loading = true }
defer { if showLoading { loading = false } }
let result = try await api.orderList(
scenicId: scenicId,
page: 1,
pageSize: pageSize,
orderStatus: orderStatusQuery,
userPhone: normalizedPhone,
startTime: startTimeQuery,
endTime: endTimeQuery,
isRefined: refinedQuery,
isScenicAdmin: roleId == 53
)
storeOrders = result.list
storeTotal = result.total
storePage = 1
storeHasMore = storeOrders.count < result.total && !result.list.isEmpty
}
///
func loadMoreStoreOrders(api: OrderServing, scenicId: Int?, roleId: Int?) async throws {
guard !loadingMore, storeHasMore, let scenicId else { return }
loadingMore = true
defer { loadingMore = false }
let nextPage = storePage + 1
let result = try await api.orderList(
scenicId: scenicId,
page: nextPage,
pageSize: pageSize,
orderStatus: orderStatusQuery,
userPhone: normalizedPhone,
startTime: startTimeQuery,
endTime: endTimeQuery,
isRefined: refinedQuery,
isScenicAdmin: roleId == 53
)
storeOrders.append(contentsOf: result.list)
storeTotal = result.total
storePage = nextPage
storeHasMore = storeOrders.count < result.total && !result.list.isEmpty
}
///
func reloadWriteOffOrders(api: OrderServing, scenicId: Int?, storeId: Int? = nil, showLoading: Bool = true) async throws {
guard let scenicId else {
writeOffOrders = []
writeOffTotal = 0
writeOffPage = 1
writeOffHasMore = false
return
}
if showLoading { loading = true }
defer { if showLoading { loading = false } }
let result = try await api.writeOffList(scenicId: scenicId, storeId: storeId, page: 1, pageSize: pageSize)
writeOffOrders = result.list
writeOffTotal = result.total
writeOffPage = 1
writeOffHasMore = writeOffOrders.count < result.total && !result.list.isEmpty
}
///
func loadMoreWriteOffOrders(api: OrderServing, scenicId: Int?, storeId: Int? = nil) async throws {
guard !loadingMore, writeOffHasMore, let scenicId else { return }
loadingMore = true
defer { loadingMore = false }
let nextPage = writeOffPage + 1
let result = try await api.writeOffList(scenicId: scenicId, storeId: storeId, page: nextPage, pageSize: pageSize)
writeOffOrders.append(contentsOf: result.list)
writeOffTotal = result.total
writeOffPage = nextPage
writeOffHasMore = writeOffOrders.count < result.total && !result.list.isEmpty
}
///
func verify(api: OrderServing, scenicId: Int, storeId: Int? = nil, orderNumber: String) async throws {
guard !isVerifying else { return }
isVerifying = true
currentVerifyingOrderNumber = orderNumber
defer {
isVerifying = false
currentVerifyingOrderNumber = nil
}
try await api.writeOff(orderNumber: orderNumber)
let refreshed = try await api.writeOffList(scenicId: scenicId, storeId: storeId, page: 1, pageSize: pageSize)
writeOffOrders = refreshed.list
writeOffTotal = refreshed.total
writeOffPage = 1
writeOffHasMore = writeOffOrders.count < refreshed.total && !refreshed.list.isEmpty
}
///
func matchedWriteOffOrder(for rawCode: String) -> (orderNumber: String, matched: WriteOffOrderItem?)? {
guard let orderNumber = OrderNumberParser.parse(rawCode) else {
return nil
}
let matched = writeOffOrders.first {
$0.orderNumber.caseInsensitiveCompare(orderNumber) == .orderedSame
}
return (matched?.orderNumber ?? orderNumber, matched)
}
///
func writeOffOrder(matching orderNumber: String) -> WriteOffOrderItem? {
writeOffOrders.first {
$0.orderNumber.caseInsensitiveCompare(orderNumber) == .orderedSame
}
}
///
func markVerifyingForTests(orderNumber: String) {
isVerifying = true
currentVerifyingOrderNumber = orderNumber
}
private let pageSize = 20
private var orderStatusQuery: Int? {
selectedStatus > 0 ? selectedStatus : nil
}
private var startTimeQuery: String? {
filterStartDate.map { Self.dayFormatter.string(from: $0) }
}
private var endTimeQuery: String? {
filterEndDate.map { Self.dayFormatter.string(from: $0) }
}
private var refinedQuery: Int? {
switch selectedStatus {
case -1:
return 1
case -2:
return 2
default:
return nil
}
}
private static let dayFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
}