// // 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? @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