拆分宣传页视图并扩充 Mock 数据,合作订单支持获客员备注名并修复线索图片网格尺寸。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,145 @@
|
||||
//
|
||||
// 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
|
||||
Reference in New Issue
Block a user