Files
LanMountainDesktop/mocks/class-schedule-mock.html
2026-05-12 16:46:49 +08:00

460 lines
11 KiB
HTML
Raw Permalink 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>课程表组件 Mock - 阑山桌面</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg-primary: #F7F8FC;
--bg-secondary: #ECEFF6;
--bg-card: #FFFFFF;
--text-primary: #151821;
--text-secondary: #667084;
--text-muted: #9AA3B2;
--border-color: rgba(0,0,0,0.06);
--surface-raised: #FFFFFF;
--accent: #FF4D5A;
--time-color: #848B99;
--divider-color: rgba(0,0,0,0.04);
}
.dark {
--bg-primary: #171A21;
--bg-secondary: #0C0E14;
--bg-card: rgba(255,255,255,0.04);
--text-primary: #F9FBFF;
--text-secondary: #848B99;
--text-muted: #5A6170;
--border-color: rgba(255,255,255,0.08);
--surface-raised: #1E2230;
--accent: #4FC3F7;
--time-color: #6B7280;
--divider-color: rgba(255,255,255,0.04);
}
body {
font-family: -apple-system, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
background: #1a1a2e;
display: flex;
flex-direction: column;
align-items: center;
gap: 32px;
padding: 40px 20px;
min-height: 100vh;
}
.controls {
display: flex;
gap: 16px;
align-items: center;
}
.controls button {
padding: 8px 20px;
border: 1px solid rgba(255,255,255,0.2);
border-radius: 8px;
background: rgba(255,255,255,0.1);
color: #fff;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.controls button:hover { background: rgba(255,255,255,0.2); }
.controls button.active { background: rgba(79,195,247,0.3); border-color: #4FC3F7; }
.mock-container {
display: flex;
gap: 32px;
flex-wrap: wrap;
justify-content: center;
}
.widget {
width: 320px;
border-radius: 24px;
overflow: hidden;
background: linear-gradient(135deg, var(--bg-primary), var(--bg-secondary));
border: 1px solid var(--border-color);
box-shadow: 0 8px 32px rgba(0,0,0,0.12);
transition: all 0.3s;
}
.widget.large {
width: 380px;
}
.widget-header {
padding: 16px 16px 10px 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.date-group {
display: flex;
align-items: baseline;
gap: 1px;
}
.date-group .month, .date-group .day {
font-size: 32px;
font-weight: 700;
color: var(--text-primary);
line-height: 1;
}
.date-group .slash {
font-size: 32px;
font-weight: 700;
color: var(--accent);
line-height: 1;
margin: 0 1px;
}
.header-center {
flex: 1;
text-align: center;
}
.weekday {
font-size: 15px;
font-weight: 600;
color: var(--text-secondary);
}
.class-count-badge {
padding: 4px 10px;
border-radius: 10px;
background: rgba(79,195,247,0.12);
font-size: 13px;
font-weight: 600;
color: var(--accent);
white-space: nowrap;
}
.dark .class-count-badge {
background: rgba(79,195,247,0.15);
}
.course-list {
padding: 4px 12px 12px 12px;
display: flex;
flex-direction: column;
gap: 6px;
}
.course-item {
display: grid;
grid-template-columns: 44px 1fr;
gap: 8px;
align-items: stretch;
min-height: 56px;
}
.time-column {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end;
padding-right: 4px;
gap: 2px;
}
.time-start {
font-size: 12px;
font-weight: 600;
color: var(--time-color);
line-height: 1.2;
font-variant-numeric: tabular-nums;
}
.time-end {
font-size: 10px;
font-weight: 500;
color: var(--text-muted);
line-height: 1.2;
font-variant-numeric: tabular-nums;
}
.course-card {
border-radius: 12px;
padding: 10px 12px;
position: relative;
overflow: hidden;
transition: all 0.2s;
min-height: 52px;
display: flex;
flex-direction: column;
gap: 2px;
}
.course-card.current {
min-height: 60px;
}
.course-card .accent-bar {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
border-radius: 0 2px 2px 0;
}
.course-name {
font-size: 14px;
font-weight: 700;
line-height: 1.3;
padding-left: 4px;
}
.course-detail {
font-size: 11px;
font-weight: 500;
color: var(--text-secondary);
line-height: 1.3;
padding-left: 4px;
}
.progress-container {
margin-top: 4px;
padding-left: 4px;
display: flex;
align-items: center;
gap: 6px;
}
.progress-bar {
flex: 1;
height: 3px;
border-radius: 2px;
background: rgba(255,255,255,0.15);
overflow: hidden;
}
.dark .progress-bar {
background: rgba(255,255,255,0.1);
}
.progress-fill {
height: 100%;
border-radius: 2px;
transition: width 0.5s ease;
}
.progress-text {
font-size: 10px;
font-weight: 600;
min-width: 28px;
text-align: right;
}
.break-indicator {
display: grid;
grid-template-columns: 44px 1fr;
gap: 8px;
padding: 2px 0;
}
.break-line {
grid-column: 2;
border-top: 1px dashed var(--divider-color);
margin: 2px 12px;
}
.status-empty {
padding: 40px 16px;
text-align: center;
color: var(--text-muted);
font-size: 14px;
}
.label {
color: #aaa;
font-size: 13px;
text-align: center;
margin-top: -16px;
}
</style>
</head>
<body>
<div class="controls">
<button id="btnLight" class="active" onclick="setTheme('light')">☀️ 亮色</button>
<button id="btnDark" onclick="setTheme('dark')">🌙 暗色</button>
</div>
<div class="mock-container">
<div>
<div class="widget" id="widgetLight">
<div class="widget-header">
<div class="date-group">
<span class="month">7</span>
<span class="slash">/</span>
<span class="day">24</span>
</div>
<div class="header-center">
<span class="weekday">周一</span>
</div>
<div class="class-count-badge">6节课</div>
</div>
<div class="course-list" id="courseListLight"></div>
</div>
<p class="label">2×4 标准尺寸</p>
</div>
<div>
<div class="widget large" id="widgetDark">
<div class="widget-header">
<div class="date-group">
<span class="month">7</span>
<span class="slash">/</span>
<span class="day">24</span>
</div>
<div class="header-center">
<span class="weekday">周一</span>
</div>
<div class="class-count-badge">6节课</div>
</div>
<div class="course-list" id="courseListDark"></div>
</div>
<p class="label">4×4 大尺寸</p>
</div>
</div>
<script>
const SUBJECT_COLORS = {
'语文': '#5B8FF9',
'数学': '#F6903D',
'英语': '#5AD8A6',
'物理': '#E8684A',
'化学': '#9270CA',
'生物': '#FF9845',
'历史': '#1E9493',
'地理': '#FF99C3',
'政治': '#7262FD',
'体育': '#78D3F8',
'音乐': '#F25E7E',
'美术': '#C2A1FD',
};
const DEFAULT_COLOR = '#8B95A5';
function getColor(name) {
for (const [key, val] of Object.entries(SUBJECT_COLORS)) {
if (name.includes(key)) return val;
}
let hash = 5381;
for (let i = 0; i < name.length; i++) hash = ((hash << 5) + hash) ^ name.charCodeAt(i);
const keys = Object.keys(SUBJECT_COLORS);
return SUBJECT_COLORS[keys[Math.abs(hash) % keys.length]];
}
function hexToRgba(hex, alpha) {
const r = parseInt(hex.slice(1,3), 16);
const g = parseInt(hex.slice(3,5), 16);
const b = parseInt(hex.slice(5,7), 16);
return `rgba(${r},${g},${b},${alpha})`;
}
const courses = [
{ name: '语文', start: '08:00', end: '08:45', detail: '王老师 · 教室301', isCurrent: false, progress: 0 },
{ name: '数学', start: '08:55', end: '09:40', detail: '李老师 · 教室205', isCurrent: true, progress: 0.62 },
{ name: '英语', start: '09:50', end: '10:35', detail: '张老师 · 教室108', isCurrent: false, progress: 0 },
{ name: '物理', start: '10:45', end: '11:30', detail: '赵老师 · 实验室2', isCurrent: false, progress: 0 },
{ name: '化学', start: '14:00', end: '14:45', detail: '陈老师 · 实验室1', isCurrent: false, progress: 0 },
{ name: '生物', start: '14:55', end: '15:40', detail: '刘老师 · 教室303', isCurrent: false, progress: 0 },
];
function renderCourseList(containerId, isDark) {
const container = document.getElementById(containerId);
container.innerHTML = '';
courses.forEach((course, idx) => {
const color = getColor(course.name);
const bgAlpha = course.isCurrent ? 0.15 : 0.07;
const bgColor = hexToRgba(color, bgAlpha);
const fgColor = isDark
? hexToRgba(color, 1).replace('rgb', 'rgb').replace(')', ',1)') || color
: color;
if (idx > 0) {
const breakEl = document.createElement('div');
breakEl.className = 'break-indicator';
breakEl.innerHTML = `<div></div><div class="break-line"></div>`;
container.appendChild(breakEl);
}
const item = document.createElement('div');
item.className = 'course-item';
const timeCol = document.createElement('div');
timeCol.className = 'time-column';
timeCol.innerHTML = `
<span class="time-start">${course.start}</span>
<span class="time-end">${course.end}</span>
`;
const card = document.createElement('div');
card.className = 'course-card' + (course.isCurrent ? ' current' : '');
card.style.background = bgColor;
let cardHTML = '';
if (course.isCurrent) {
cardHTML += `<div class="accent-bar" style="background:${color}"></div>`;
}
cardHTML += `<div class="course-name" style="color:${fgColor}">${course.name}</div>`;
cardHTML += `<div class="course-detail">${course.detail}</div>`;
if (course.isCurrent) {
const pct = Math.round(course.progress * 100);
cardHTML += `
<div class="progress-container">
<div class="progress-bar">
<div class="progress-fill" style="width:${pct}%;background:${color}"></div>
</div>
<span class="progress-text" style="color:${fgColor}">${pct}%</span>
</div>
`;
}
card.innerHTML = cardHTML;
item.appendChild(timeCol);
item.appendChild(card);
container.appendChild(item);
});
}
function setTheme(theme) {
const btnLight = document.getElementById('btnLight');
const btnDark = document.getElementById('btnDark');
const widgetLight = document.getElementById('widgetLight');
const widgetDark = document.getElementById('widgetDark');
if (theme === 'dark') {
btnDark.classList.add('active');
btnLight.classList.remove('active');
widgetLight.classList.add('dark');
widgetDark.classList.add('dark');
} else {
btnLight.classList.add('active');
btnDark.classList.remove('active');
widgetLight.classList.remove('dark');
widgetDark.classList.remove('dark');
}
renderCourseList('courseListLight', theme === 'dark');
renderCourseList('courseListDark', theme === 'dark');
}
renderCourseList('courseListLight', false);
renderCourseList('courseListDark', false);
</script>
</body>
</html>