Files
lingo/design_mockups/list.html
T
ilaandClaude Opus 4.8 d37b83011a design: 新增播放页与课程列表 UI 效果稿(HTML/CSS)
- design_mockups/play.html:播放页,含逐句字幕高亮 + 点句跳转 + 播放器,
  用 ep1 真实台词;CSS 变量化,可迁移进 go-app
- design_mockups/list.html:26 集课程列表,含进度环、Act 音频状态、
  ep18+ 无音频降级态;用真实集标题
- design_mockups/README.md:设计语言与文档对应说明
- 06-tasks:新增 Phase D / T-200 设计任务

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 15:29:36 +08:00

160 lines
7.9 KiB
HTML
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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>课程列表 · 走遍美国精听</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600&family=Hanken+Grotesk:wght@400;500;600;700&family=Noto+Serif+SC:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root{
--ink:#15120d; --ink-2:#1d1812; --card:#221c14; --card-2:#271f16;
--paper:#ece1cd; --muted:#9b8d77; --faint:#5c513f;
--amber:#e6ad5c; --amber-2:#f0c074; --amber-soft:rgba(230,173,92,.13);
--line:rgba(236,225,205,.08);
--serif:"Newsreader",Georgia,serif;
--sans:"Hanken Grotesk",system-ui,sans-serif;
--han:"Noto Serif SC",serif;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
font-family:var(--sans); color:var(--paper);
background:
radial-gradient(120% 70% at 50% -5%, rgba(230,173,92,.10), transparent 50%),
var(--ink);
display:flex; justify-content:center; min-height:100vh;
-webkit-font-smoothing:antialiased;
}
body::after{
content:""; position:fixed; inset:0; pointer-events:none; z-index:50; opacity:.5; mix-blend-mode:overlay;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.4'/%3E%3C/svg%3E");
}
.phone{width:100%;max-width:440px;min-height:100vh;border-left:1px solid var(--line);border-right:1px solid var(--line);padding-bottom:30px}
/* header */
.head{padding:30px 24px 18px}
.brand{display:flex;align-items:baseline;justify-content:space-between}
.brand h1{font-family:var(--han);font-size:25px;font-weight:600;letter-spacing:.04em}
.brand .en{font-family:var(--serif);font-style:italic;font-size:13px;color:var(--amber);letter-spacing:.02em}
.sub{font-size:11.5px;color:var(--muted);letter-spacing:.16em;text-transform:uppercase;margin-top:6px}
/* continue hero */
.hero{
margin:18px 20px 6px;padding:18px 20px;border-radius:18px;position:relative;overflow:hidden;
background:linear-gradient(135deg,var(--card-2),var(--card));border:1px solid var(--line);
}
.hero::before{content:"";position:absolute;right:-40px;top:-40px;width:160px;height:160px;border-radius:50%;
background:radial-gradient(closest-side,rgba(230,173,92,.18),transparent)}
.hero .lab{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--amber);font-weight:600}
.hero .ti{font-family:var(--serif);font-size:22px;font-weight:600;margin-top:7px}
.hero .cn{font-family:var(--han);font-size:12.5px;color:var(--muted);margin-top:2px}
.hero .row{display:flex;align-items:center;gap:14px;margin-top:16px}
.progress{flex:1;height:5px;border-radius:5px;background:rgba(0,0,0,.35);overflow:hidden}
.progress i{display:block;height:100%;width:38%;border-radius:5px;background:linear-gradient(90deg,var(--amber),var(--amber-2))}
.resume{flex:none;display:flex;align-items:center;gap:7px;background:var(--amber);color:#1a1206;
font-weight:700;font-size:13px;border:none;border-radius:30px;padding:9px 16px;cursor:pointer}
.resume:hover{background:var(--amber-2)}
/* section label */
.seclab{display:flex;align-items:center;gap:10px;padding:22px 24px 8px;color:var(--muted)}
.seclab span{font-size:11px;letter-spacing:.18em;text-transform:uppercase}
.seclab .ln{flex:1;height:1px;background:var(--line)}
/* episode list */
.list{padding:4px 14px}
.ep{display:flex;gap:14px;align-items:center;padding:14px 12px;border-radius:14px;cursor:pointer;
transition:background .2s;position:relative}
.ep:hover{background:var(--ink-2)}
.ep + .ep{border-top:1px solid var(--line)}
.num{font-family:var(--serif);font-size:20px;font-style:italic;color:var(--faint);width:30px;flex:none;text-align:center}
.ep.cur .num{color:var(--amber)}
.body{flex:1;min-width:0}
.body .ti{font-family:var(--serif);font-size:18px;font-weight:500;line-height:1.2}
.body .cn{font-family:var(--han);font-size:11.5px;color:var(--muted);margin-top:2px}
.acts{display:flex;gap:6px;margin-top:9px}
.chip{font-size:10px;letter-spacing:.06em;color:var(--muted);border:1px solid var(--line);
border-radius:6px;padding:3px 8px;display:flex;align-items:center;gap:5px}
.chip.done{color:var(--amber);border-color:rgba(230,173,92,.4)}
.chip.done::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--amber)}
.chip.noaudio{color:var(--faint);border-style:dashed}
.ring{flex:none;width:34px;height:34px}
.ring svg{transform:rotate(-90deg)}
.ring .bg{stroke:var(--line)} .ring .fg{stroke:var(--amber);stroke-linecap:round}
.ep.locked{opacity:.5}
.ep.locked .body .ti{font-style:normal}
.badge{font-size:9.5px;letter-spacing:.1em;color:var(--faint);border:1px dashed var(--line);
border-radius:20px;padding:4px 9px;flex:none}
.list .ep{animation:rise .5s both}
@keyframes rise{from{opacity:0;transform:translateY(8px)}}
</style>
</head>
<body>
<div class="phone">
<header class="head">
<div class="brand">
<h1>走遍美国</h1>
<span class="en">Family Album, U.S.A.</span>
</div>
<div class="sub">逐句精听 · Close Listening</div>
</header>
<section class="hero">
<div class="lab">继续学习</div>
<div class="ti">46 Linden Street · Act I</div>
<div class="cn">林登大街 46 号 · 第一幕</div>
<div class="row">
<div class="progress"><i></i></div>
<button class="resume">▶ 续听</button>
</div>
</section>
<div class="seclab"><span>全 26 集</span><div class="ln"></div></div>
<div class="list" id="list"></div>
</div>
<script>
// 真实数据:title / titleCn / 幕数 / 有音频幕数 / 进度(%)
const EPS = [
{n:1, ti:"46 Linden Street", cn:"林登大街46号", acts:3, audio:2, prog:38, cur:true},
{n:2, ti:"The Blind Date", cn:"相亲", acts:3, audio:3, prog:100},
{n:3, ti:"Grandpa's Trunk", cn:"祖父的行李箱", acts:3, audio:3, prog:64},
{n:4, ti:"A Piece of Cake", cn:"小菜一碟", acts:3, audio:3, prog:0},
{n:5, ti:"The Right Magic", cn:"灵丹妙药", acts:3, audio:3, prog:0},
{n:6, ti:"Thanksgiving", cn:"感恩节", acts:3, audio:3, prog:0},
{n:7, ti:"Man's Best Friend", cn:"人类最好的朋友",acts:3, audio:3, prog:0},
{n:8, ti:"You're Going to Be Fine",cn:"你会好起来的",acts:3, audio:3, prog:0},
{n:18, ti:"Making a Difference", cn:"与众不同", acts:3, audio:0, prog:0, locked:true},
{n:19, ti:"I Do", cn:"我愿意", acts:3, audio:0, prog:0, locked:true},
];
const roman = ["Ⅰ","Ⅱ","Ⅲ"];
const list = document.getElementById('list');
EPS.forEach((e,idx)=>{
const el = document.createElement('div');
el.className = 'ep' + (e.cur?' cur':'') + (e.locked?' locked':'');
el.style.animationDelay = (idx*0.04)+'s';
let chips = '';
for(let a=0;a<e.acts;a++){
if(e.locked) chips += `<span class="chip noaudio">Act ${roman[a]}</span>`;
else chips += `<span class="chip ${a<e.audio?'done':''}">Act ${roman[a]}</span>`;
}
const r=15, c=2*Math.PI*r, off=c*(1-e.prog/100);
const right = e.locked
? `<span class="badge">暂无音频</span>`
: `<div class="ring"><svg width="34" height="34"><circle class="bg" cx="17" cy="17" r="${r}" fill="none" stroke-width="3"/><circle class="fg" cx="17" cy="17" r="${r}" fill="none" stroke-width="3" stroke-dasharray="${c}" stroke-dashoffset="${off}"/></svg></div>`;
el.innerHTML = `
<div class="num">${e.n}</div>
<div class="body">
<div class="ti">${e.ti}</div>
<div class="cn">${e.cn}</div>
<div class="acts">${chips}</div>
</div>
${right}`;
list.appendChild(el);
});
</script>
</body>
</html>