Files
suixinkan_ios_new/suixinkan/Features/ScenicPromotion/Views/ScenicPromotionView.swift

146 lines
5.0 KiB
Swift
Raw 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.

//
// ScenicPromotionView.swift
// suixinkan
//
// Created by Codex on 2026/7/2.
//
import SwiftUI
///
struct ScenicPromotionView: View {
@StateObject private var viewModel = ScenicPromotionViewModel()
@State private var settingsRoute: ScenicPromotionSettingsRoute?
@State private var qrTapCount = 0
@State private var qrTapResetTask: Task<Void, Never>?
@State private var showPasswordPrompt = false
@State private var settingsPassword = ""
@State private var showPasswordError = false
@State private var showDemoStateMenu = false
@State private var showPlayToast = false
var body: some View {
ZStack {
ScenicPromotionSkyBackground()
.ignoresSafeArea()
Group {
switch viewModel.pageState {
case .idle, .loading:
ScenicPromotionLoadingView()
.transition(.opacity.combined(with: .scale(scale: 0.98)))
case .empty:
ScenicPromotionFullStateView(
icon: "doc.text.image",
title: "暂无宣传页数据",
message: "当前景区还没有配置排队导览宣传内容",
buttonTitle: "重新加载"
) {
Task { await viewModel.load() }
}
.transition(.opacity.combined(with: .move(edge: .bottom)))
case .error(let message):
ScenicPromotionFullStateView(
icon: "wifi.exclamationmark",
title: "加载失败",
message: message,
buttonTitle: "重试"
) {
Task { await viewModel.load() }
}
.transition(.opacity.combined(with: .move(edge: .bottom)))
case .normal:
ScenicPromotionContentView(
viewModel: viewModel,
onQRCodeTap: handleQRCodeTap,
onQRCodeLongPress: { showDemoStateMenu = true },
onVideoTap: { showPlayToast = true }
)
.transition(.opacity.combined(with: .move(edge: .bottom)))
}
}
.animation(.easeInOut(duration: 0.28), value: viewModel.pageState)
}
.ignoresSafeArea(.container, edges: .top)
.toolbar(.hidden, for: .navigationBar)
.appNavigationDestination(item: $settingsRoute) { _ in
ScenicPromotionSettingsView()
}
.alert("输入管理密码", isPresented: $showPasswordPrompt) {
SecureField("请输入密码", text: $settingsPassword)
Button("取消", role: .cancel) {
settingsPassword = ""
}
Button("进入") {
verifySettingsPassword()
}
} message: {
Text("连续点击二维码 3 次后可进入宣传页设置")
}
.alert("密码错误", isPresented: $showPasswordError) {
Button("知道了", role: .cancel) {}
} message: {
Text("请输入正确密码")
}
.alert("视频播放", isPresented: $showPlayToast) {
Button("知道了", role: .cancel) {}
} message: {
Text("Demo 已模拟播放入口,后续接入真实视频地址即可播放。")
}
.confirmationDialog("演示状态", isPresented: $showDemoStateMenu, titleVisibility: .visible) {
ForEach(ScenicPromotionDemoScenario.allCases) { scenario in
Button(scenario.title) {
viewModel.applyScenario(scenario)
}
}
Button("取消", role: .cancel) {}
}
.onDisappear {
qrTapResetTask?.cancel()
}
.task {
await viewModel.loadIfNeeded()
}
}
/// 3
private func handleQRCodeTap() {
qrTapCount += 1
qrTapResetTask?.cancel()
if qrTapCount >= 3 {
qrTapCount = 0
settingsPassword = ""
showPasswordPrompt = true
return
}
qrTapResetTask = Task {
try? await Task.sleep(nanoseconds: 1_100_000_000)
guard !Task.isCancelled else { return }
await MainActor.run {
qrTapCount = 0
}
}
}
/// Mock 1
private func verifySettingsPassword() {
if settingsPassword == "1" {
settingsPassword = ""
settingsRoute = ScenicPromotionSettingsRoute()
} else {
settingsPassword = ""
showPasswordError = true
}
}
}
#if DEBUG
#Preview("宣传页") {
NavigationStack {
ScenicPromotionView()
}
}
#endif