拆分宣传页视图并扩充 Mock 数据,合作订单支持获客员备注名并修复线索图片网格尺寸。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,100 @@
|
||||
//
|
||||
// ScenicPromotionMediaArtworkView.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/7/2.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
/// 宣传页素材图片渲染组件,支持缩略图与主视觉两种尺寸。
|
||||
struct ScenicPromotionMediaArtwork: View {
|
||||
let media: ScenicPromotionMedia
|
||||
let compact: Bool
|
||||
let useHeroAsset: Bool
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { proxy in
|
||||
if let assetName = useHeroAsset ? (media.heroAssetName ?? media.assetName) : media.assetName {
|
||||
Image(assetName)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: proxy.size.width, height: proxy.size.height)
|
||||
.clipped()
|
||||
} else {
|
||||
ZStack {
|
||||
LinearGradient(
|
||||
colors: media.palette.map { Color(hex: $0) },
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
|
||||
ScenicPromotionMountainLayer()
|
||||
.fill(.white.opacity(0.28))
|
||||
.frame(height: proxy.size.height * 0.45)
|
||||
.offset(y: proxy.size.height * 0.02)
|
||||
|
||||
ScenicPromotionMountainLayer()
|
||||
.fill(Color(hex: 0x0F5B85, alpha: 0.22))
|
||||
.frame(height: proxy.size.height * 0.38)
|
||||
.offset(y: proxy.size.height * 0.13)
|
||||
|
||||
VStack {
|
||||
Spacer()
|
||||
Rectangle()
|
||||
.fill(Color(hex: 0x2D9D4E, alpha: 0.52))
|
||||
.frame(height: proxy.size.height * 0.2)
|
||||
}
|
||||
|
||||
if !compact {
|
||||
Image(systemName: "figure.walk")
|
||||
.font(.system(size: 74, weight: .bold))
|
||||
.foregroundStyle(.white.opacity(0.82))
|
||||
.offset(x: proxy.size.width * 0.23, y: proxy.size.height * 0.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.clipShape(RoundedRectangle(cornerRadius: compact ? 12 : 18))
|
||||
}
|
||||
}
|
||||
|
||||
/// 没有本地图片资源时使用的备用山形占位图。
|
||||
struct ScenicPromotionMountainLayer: Shape {
|
||||
func path(in rect: CGRect) -> Path {
|
||||
var path = Path()
|
||||
path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
|
||||
path.addLine(to: CGPoint(x: rect.width * 0.16, y: rect.height * 0.42))
|
||||
path.addLine(to: CGPoint(x: rect.width * 0.28, y: rect.height * 0.68))
|
||||
path.addLine(to: CGPoint(x: rect.width * 0.42, y: rect.height * 0.25))
|
||||
path.addLine(to: CGPoint(x: rect.width * 0.58, y: rect.height * 0.6))
|
||||
path.addLine(to: CGPoint(x: rect.width * 0.76, y: rect.height * 0.18))
|
||||
path.addLine(to: CGPoint(x: rect.maxX, y: rect.height * 0.56))
|
||||
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
|
||||
path.closeSubpath()
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
#Preview("素材缩略图") {
|
||||
ScenicPromotionMediaArtwork(
|
||||
media: ScenicPromotionPreviewSupport.sampleVideoMedia,
|
||||
compact: true,
|
||||
useHeroAsset: false
|
||||
)
|
||||
.frame(width: 72, height: 66)
|
||||
.padding()
|
||||
}
|
||||
|
||||
#Preview("素材主视觉") {
|
||||
ScenicPromotionMediaArtwork(
|
||||
media: ScenicPromotionPreviewSupport.sampleVideoMedia,
|
||||
compact: false,
|
||||
useHeroAsset: true
|
||||
)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 220)
|
||||
.padding()
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user