/** * [INPUT]: 依赖 react、components/v4/CoverImage、components/v4/Skeleton 的 Skel; * props: course{num,name,intro,aud,art:{pl,dt},cover?}、meta(N系列·M节)、 * cta(脚注动作文案,默认「进入课程 →」)、ctaColor(脚注色,落地页传朱色)、onClick、loading(骨架态) * [OUTPUT]: 对外提供 CourseCard 组件——综合首页/落地页共用的课程卡(16:9 暗底星座缩略/封面 + 文案) * [POS]: components/v4 共享原语,被 screens/v4/hub/HubHome 与 landing/LandingPage 消费;还原 DC 源行 162-181。 * ★ 登录后 Hub 卡即基准形态:落地页不再维护本地变体(原 LpCourseCard 已删),尺寸/图片比例两处恒同源。 * 封面经 CoverImage 三态渲染(加载期暗底、就绪淡入、失败回退星座线稿),不再先画线稿再跳变。 * ★ 封面不叠蒙层:左上编号自带深色 chip 底(与 LessonCard 四角元素同一词汇), * 不靠 textShadow——后台可上传白底海报,浅字带投影在白底上一样糊 * ★ 骨架同源(loading):布局全走模块常量 CC_LAYOUT,真实态与骨架态共用同一段 JSX/style—— * 改 padding/字号骨架自动跟随,独立 CourseCardSkeleton 已删(能消失的分支 > 能写对的分支) * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md */ import { CoverImage } from './CoverImage' import { Skel } from './Skeleton' const ACC = 'var(--zzm-accent,#A6402F)' const paint = (v) => (v === '@' ? ACC : v) // 源用 '@' 占位品牌朱 // ── 布局单一真相源:真实态与骨架态共用 → 改这里两态同时变,永不漂移 ── const CC_LAYOUT = { card: { border: '1px solid rgba(0,0,0,.09)', borderRadius: 16, background: '#FFFFFF', overflow: 'hidden' }, header: { aspectRatio: '16/9', background: '#131316', position: 'relative', overflow: 'hidden' }, body: { padding: '16px 18px 17px' }, } // 星座线稿:仅作「未配置封面 / 封面加载失败」的设计兜底,不再作恒定底层 function ArtSvg({ pl, dt }) { return ( {pl.map((l, i) => ( ))} {dt.map((d, i) => ( ))} ) } export function CourseCard({ course, meta, cta = '进入课程 →', ctaColor = '#161616', onClick, loading = false }) { // ── 骨架态:同一外壳(CC_LAYOUT) + 16:9 暗底头图 + 文案位换 Skel 块;无 onClick/cursor/悬停 ── if (loading) { return (
) } const pl = course.art?.pl ?? [] const dt = course.art?.dt ?? [] return (
} /> {/* 编号自持底色(不靠 textShadow):封面由后台上传,白底海报上的白字带投影一样糊。 与 LessonCard 四角元素同一词汇——深色小底 + 浅字,与封面明暗无关 */}
{course.num}
{course.name}
{course.intro}
适合 · {course.aud}
{meta} {cta}
) }