0
0
Fork 0
This repository has been archived on 2026-06-03. You can view files and clone it, but cannot push or open issues or pull requests.
Classroom-Assistant/index.html

1585 lines
42 KiB
HTML
Raw Permalink Blame History

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>课堂小助手</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #ffffff;
min-height: 100vh;
color: #333;
}
.title-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 9999;
display: flex;
justify-content: space-between;
align-items: center;
background: #f8f9fa;
padding: 8px 15px;
border-bottom: 1px solid #e9ecef;
-webkit-app-region: drag;
user-select: none;
}
.title-bar-left {
display: flex;
align-items: center;
gap: 10px;
}
.app-icon {
width: 24px;
height: 24px;
border-radius: 4px;
}
.app-title {
font-size: 14px;
font-weight: 600;
color: #495057;
}
.title-bar-menu {
display: flex;
gap: 5px;
-webkit-app-region: no-drag;
}
.menu-btn {
padding: 4px 12px;
background: transparent;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
color: #495057;
transition: background 0.2s;
}
.menu-btn:hover {
background: #e9ecef;
}
.title-bar-right {
display: flex;
align-items: center;
gap: 2px;
-webkit-app-region: no-drag;
}
.window-btn {
width: 36px;
height: 28px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
color: #495057;
transition: background 0.2s;
}
.window-btn:hover {
background: #e9ecef;
}
.window-btn.close:hover {
background: #dc3545;
color: white;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
padding-top: 50px;
}
.main-content {
display: grid;
grid-template-columns: 220px 1fr;
gap: 20px;
}
.sidebar {
background: #f8f9fa;
border-radius: 10px;
padding: 15px;
height: fit-content;
border: 1px solid #e9ecef;
}
.menu-title {
font-size: 14px;
font-weight: 600;
color: #495057;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #dee2e6;
}
.menu-list {
list-style: none;
}
.menu-item {
padding: 10px 12px;
margin-bottom: 4px;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 10px;
color: #495057;
font-weight: 500;
font-size: 14px;
}
.menu-item:hover {
background: #e9ecef;
}
.menu-item.active {
background: #667eea;
color: white;
}
.menu-icon {
font-size: 16px;
width: 20px;
text-align: center;
}
.content-area {
background: #ffffff;
border-radius: 10px;
padding: 25px;
border: 1px solid #e9ecef;
}
.welcome-section {
text-align: center;
padding: 30px 20px;
}
.welcome-title {
font-size: 28px;
color: #212529;
margin-bottom: 10px;
font-weight: 700;
}
.welcome-subtitle {
font-size: 16px;
color: #6c757d;
margin-bottom: 25px;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 15px;
margin-top: 25px;
}
.feature-card {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
transition: all 0.2s ease;
border: 1px solid #e9ecef;
}
.feature-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.feature-icon {
width: 50px;
height: 50px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 12px;
}
.feature-icon.blue {
background: #667eea;
color: white;
}
.feature-icon.green {
background: #28a745;
color: white;
}
.feature-icon.orange {
background: #fd7e14;
color: white;
}
.feature-icon.purple {
background: #6f42c1;
color: white;
}
.feature-title {
font-size: 16px;
font-weight: 600;
color: #212529;
margin-bottom: 8px;
}
.feature-desc {
font-size: 13px;
color: #6c757d;
line-height: 1.5;
}
.stats-section {
margin-top: 30px;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
}
.stat-card {
background: #f8f9fa;
border-radius: 8px;
padding: 15px;
text-align: center;
border: 1px solid #e9ecef;
}
.stat-number {
font-size: 28px;
font-weight: 700;
color: #667eea;
margin-bottom: 5px;
}
.stat-label {
font-size: 13px;
color: #6c757d;
}
.quick-actions {
margin-top: 25px;
display: flex;
gap: 10px;
justify-content: center;
}
.btn {
padding: 10px 20px;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
border: none;
font-size: 14px;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5a6fd6;
transform: translateY(-1px);
}
.btn-secondary {
background: #f8f9fa;
color: #667eea;
border: 1px solid #667eea;
}
.btn-secondary:hover {
background: #667eea;
color: white;
}
.version-info {
text-align: center;
margin-top: 25px;
padding-top: 15px;
border-top: 1px solid #e9ecef;
color: #adb5bd;
font-size: 12px;
}
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
}
.stats-section {
grid-template-columns: repeat(2, 1fr);
}
.quick-actions {
flex-direction: column;
align-items: center;
}
}
.page {
display: none;
}
.page.active {
display: block;
}
.timer-container {
text-align: center;
padding: 20px;
}
.timer-tabs {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 30px;
}
.timer-tab {
padding: 10px 30px;
border: 2px solid #667eea;
background: #f8f9fa;
color: #667eea;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: all 0.2s;
}
.timer-tab.active {
background: #667eea;
color: white;
}
.timer-display-container {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin: 30px 0;
cursor: pointer;
padding: 20px;
border-radius: 12px;
transition: background 0.2s;
}
.timer-display-container:hover {
background: #f8f9fa;
}
.timer-display-container.clickable {
cursor: pointer;
}
.timer-display-container.not-clickable {
cursor: default;
}
.timer-display-container.not-clickable:hover {
background: transparent;
}
.timer-digit-group {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.timer-digit-wrapper {
display: flex;
align-items: center;
gap: 5px;
}
.timer-digit-btn {
width: 80px;
height: 100px;
background: #f8f9fa;
border: 3px solid #dee2e6;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 60px;
font-weight: 700;
color: #212529;
cursor: pointer;
transition: all 0.2s;
user-select: none;
}
.timer-digit-btn:hover {
border-color: #667eea;
background: #e9ecef;
}
.timer-digit-btn.editing {
border-color: #667eea;
background: #fff;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}
.timer-digit-btn.running {
background: #667eea;
color: white;
border-color: #667eea;
cursor: default;
}
.timer-adj-btns {
display: flex;
gap: 8px;
}
.timer-adj-btn {
width: 50px;
height: 45px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
font-size: 24px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.timer-adj-btn:hover {
background: #5a6fd6;
}
.timer-adj-btn:active {
transform: scale(0.95);
}
.timer-separator {
font-size: 60px;
font-weight: 700;
color: #212529;
margin: 0 5px;
display: flex;
align-items: center;
height: 100px;
}
.timer-label {
font-size: 16px;
color: #6c757d;
font-weight: 500;
}
.timer-fullscreen-btn {
position: absolute;
bottom: 20px;
right: 20px;
width: 50px;
height: 50px;
background: #f8f9fa;
border: 2px solid #dee2e6;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
transition: all 0.2s;
}
.timer-fullscreen-btn:hover {
background: #e9ecef;
border-color: #667eea;
}
.timer-fullscreen-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #1a1a2e;
z-index: 10000;
flex-direction: column;
align-items: center;
justify-content: center;
}
.timer-fullscreen-overlay.active {
display: flex;
}
.timer-fullscreen-display {
display: flex;
align-items: center;
gap: 30px;
}
.timer-fullscreen-digit {
font-size: 200px;
font-weight: 700;
color: #ffffff;
font-family: 'Consolas', 'Monaco', monospace;
min-width: 200px;
text-align: center;
}
.timer-fullscreen-separator {
font-size: 200px;
font-weight: 700;
color: #ffffff;
}
.timer-fullscreen-close {
position: absolute;
bottom: 40px;
right: 40px;
width: 80px;
height: 80px;
background: rgba(255, 255, 255, 0.1);
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
color: white;
transition: all 0.2s;
}
.timer-fullscreen-close:hover {
background: rgba(255, 255, 255, 0.2);
}
.timer-controls {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 40px;
}
.timer-btn {
padding: 18px 60px;
border: none;
border-radius: 10px;
font-size: 22px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.timer-btn.start {
background: #28a745;
color: white;
}
.timer-btn.start:hover {
background: #218838;
}
.timer-btn.pause {
background: #ffc107;
color: #212529;
}
.timer-btn.pause:hover {
background: #e0a800;
}
.timer-btn.reset {
background: #6c757d;
color: white;
}
.timer-btn.reset:hover {
background: #5a6268;
}
.random-container {
text-align: center;
padding: 20px;
}
.random-number {
font-size: 200px;
font-weight: 700;
color: #667eea;
margin: 40px 0;
font-family: 'Consolas', 'Monaco', monospace;
min-height: 250px;
display: flex;
align-items: center;
justify-content: center;
}
.random-controls {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 30px;
}
.random-btn {
padding: 15px 50px;
border: none;
border-radius: 6px;
font-size: 18px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.random-btn.generate {
background: #667eea;
color: white;
}
.random-btn.generate:hover {
background: #5a6fd6;
}
.random-btn.stop {
background: #dc3545;
color: white;
}
.random-btn.stop:hover {
background: #c82333;
}
.random-settings {
margin-top: 30px;
padding: 25px;
background: #f8f9fa;
border-radius: 12px;
display: inline-flex;
align-items: center;
gap: 15px;
}
.random-settings label {
font-size: 18px;
color: #495057;
font-weight: 500;
}
.number-input-group {
display: flex;
align-items: center;
gap: 5px;
}
.number-adj-btn {
width: 50px;
height: 50px;
background: #667eea;
color: white;
border: none;
border-radius: 10px;
font-size: 24px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.number-adj-btn:hover {
background: #5a6fd6;
}
.number-adj-btn:active {
transform: scale(0.95);
}
.number-display {
width: 100px;
height: 50px;
background: white;
border: 2px solid #dee2e6;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: 700;
color: #212529;
}
.settings-container {
max-width: 600px;
margin: 0 auto;
}
.settings-title {
font-size: 22px;
font-weight: 700;
color: #212529;
margin-bottom: 25px;
text-align: center;
}
.settings-section {
background: #f8f9fa;
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid #e9ecef;
}
.settings-section-title {
font-size: 16px;
font-weight: 600;
color: #495057;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #dee2e6;
}
.settings-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
}
.settings-item + .settings-item {
border-top: 1px solid #e9ecef;
}
.settings-label {
font-size: 14px;
color: #495057;
}
.settings-hint {
display: block;
font-size: 12px;
color: #adb5bd;
margin-top: 2px;
font-weight: normal;
}
.settings-radio-group {
display: flex;
gap: 15px;
}
.settings-radio-item {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
font-size: 14px;
color: #495057;
}
.settings-radio-item input[type="radio"] {
accent-color: #667eea;
width: 16px;
height: 16px;
cursor: pointer;
}
.settings-custom-input {
display: none;
align-items: center;
gap: 8px;
margin-top: 10px;
padding: 10px;
background: white;
border-radius: 8px;
}
.settings-custom-input.visible {
display: flex;
}
.settings-custom-input label {
font-size: 14px;
color: #495057;
white-space: nowrap;
}
.settings-input {
width: 100px;
padding: 8px 12px;
border: 2px solid #dee2e6;
border-radius: 6px;
font-size: 14px;
text-align: center;
transition: border-color 0.2s;
}
.settings-input:focus {
outline: none;
border-color: #667eea;
}
.settings-unit {
font-size: 14px;
color: #6c757d;
}
.toggle-switch {
position: relative;
width: 48px;
height: 26px;
cursor: pointer;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #dee2e6;
border-radius: 26px;
transition: all 0.3s;
}
.toggle-slider::before {
content: '';
position: absolute;
width: 20px;
height: 20px;
left: 3px;
bottom: 3px;
background: white;
border-radius: 50%;
transition: all 0.3s;
}
.toggle-switch input:checked + .toggle-slider {
background: #667eea;
}
.toggle-switch input:checked + .toggle-slider::before {
transform: translateX(22px);
}
.about-section {
text-align: center;
}
.about-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 15px;
gap: 15px;
}
.about-logo {
width: 128px;
height: 128px;
border-radius: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.about-info {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.about-name {
font-size: 18px;
font-weight: 600;
color: #343a40;
margin: 0;
}
.about-version {
font-size: 14px;
color: #868e96;
margin: 0;
}
.about-link {
font-size: 14px;
color: #667eea;
text-decoration: none;
padding: 8px 20px;
border: 1px solid #667eea;
border-radius: 20px;
transition: all 0.3s;
}
.about-link:hover {
background: #667eea;
color: white;
}
</style>
</head>
<body>
<div class="title-bar">
<div class="title-bar-left">
<img class="app-icon" src="assets/icon.ico" alt="课堂小助手">
<span class="app-title">课堂小助手</span>
</div>
<!--
<div class="title-bar-menu">
<button class="menu-btn" onclick="showFileMenu()">文件</button>
<button class="menu-btn" onclick="showViewMenu()">视图</button>
<button class="menu-btn" onclick="showHelpMenu()">帮助</button>
</div>
-->
<div class="title-bar-right">
<button class="window-btn minimize" onclick="minimizeWindow()" title="最小化"></button>
<button class="window-btn close" onclick="closeWindow()" title="关闭"></button>
</div>
</div>
<div class="container">
<div class="main-content">
<nav class="sidebar">
<div class="menu-title">功能菜单</div>
<ul class="menu-list">
<li class="menu-item active" onclick="showPage('home')">
<span class="menu-icon"><EFBFBD></span>
<span>工作台</span>
</li>
<li class="menu-item" onclick="showPage('timer')">
<span class="menu-icon">⏱️</span>
<span>高效计时</span>
</li>
<li class="menu-item" onclick="showPage('random')">
<span class="menu-icon">🎲</span>
<span>随机摇号</span>
</li>
<li class="menu-item" onclick="showPage('settings')">
<span class="menu-icon">⚙️</span>
<span>设置</span>
</li>
</ul>
</nav>
<main class="content-area">
<div id="page-home" class="page active">
<div class="welcome-section">
<h2 class="welcome-title">欢迎使用课堂小助手</h2>
<p class="welcome-subtitle">让教学更高效,让课堂更有趣</p>
</div>
</div>
<div id="page-timer" class="page">
<div class="timer-container">
<div class="timer-tabs">
<button class="timer-tab active" onclick="switchTimerMode('countdown')">倒计时</button>
<button class="timer-tab" onclick="switchTimerMode('stopwatch')">正计时</button>
</div>
<div class="timer-display-container" onclick="handleTimerDisplayClick()" id="timer-display-area">
<div class="timer-digit-group">
<div class="timer-adj-btns">
<button class="timer-adj-btn" onclick="adjustDigit('h-tens', 1)">+</button>
<button class="timer-adj-btn" onclick="adjustDigit('h-ones', 1)">+</button>
</div>
<div class="timer-digit-wrapper">
<div class="timer-digit-btn" id="h-tens" onclick="cycleDigit(this)">0</div>
<div class="timer-digit-btn" id="h-ones" onclick="cycleDigit(this)">0</div>
</div>
<div class="timer-adj-btns">
<button class="timer-adj-btn" onclick="adjustDigit('h-tens', -1)">-</button>
<button class="timer-adj-btn" onclick="adjustDigit('h-ones', -1)">-</button>
</div>
<div class="timer-label"></div>
</div>
<div class="timer-separator">:</div>
<div class="timer-digit-group">
<div class="timer-adj-btns">
<button class="timer-adj-btn" onclick="adjustDigit('m-tens', 1)">+</button>
<button class="timer-adj-btn" onclick="adjustDigit('m-ones', 1)">+</button>
</div>
<div class="timer-digit-wrapper">
<div class="timer-digit-btn" id="m-tens" onclick="cycleDigit(this)">0</div>
<div class="timer-digit-btn" id="m-ones" onclick="cycleDigit(this)">5</div>
</div>
<div class="timer-adj-btns">
<button class="timer-adj-btn" onclick="adjustDigit('m-tens', -1)">-</button>
<button class="timer-adj-btn" onclick="adjustDigit('m-ones', -1)">-</button>
</div>
<div class="timer-label"></div>
</div>
<div class="timer-separator">:</div>
<div class="timer-digit-group">
<div class="timer-adj-btns">
<button class="timer-adj-btn" onclick="adjustDigit('s-tens', 1)">+</button>
<button class="timer-adj-btn" onclick="adjustDigit('s-ones', 1)">+</button>
</div>
<div class="timer-digit-wrapper">
<div class="timer-digit-btn" id="s-tens" onclick="cycleDigit(this)">0</div>
<div class="timer-digit-btn" id="s-ones" onclick="cycleDigit(this)">0</div>
</div>
<div class="timer-adj-btns">
<button class="timer-adj-btn" onclick="adjustDigit('s-tens', -1)">-</button>
<button class="timer-adj-btn" onclick="adjustDigit('s-ones', -1)">-</button>
</div>
<div class="timer-label"></div>
</div>
</div>
<div class="timer-controls">
<button class="timer-btn start" id="timer-start-btn" onclick="startTimer()">开始</button>
<button class="timer-btn pause" id="timer-pause-btn" onclick="pauseTimer()" style="display:none;">暂停</button>
<button class="timer-btn reset" onclick="resetTimer()">重置</button>
</div>
<button class="timer-fullscreen-btn" onclick="openFullscreen()" title="全屏"></button>
</div>
</div>
<div id="timer-fullscreen" class="timer-fullscreen-overlay">
<div class="timer-fullscreen-display">
<div class="timer-fullscreen-digit" id="fs-h">00</div>
<div class="timer-fullscreen-separator">:</div>
<div class="timer-fullscreen-digit" id="fs-m">00</div>
<div class="timer-fullscreen-separator">:</div>
<div class="timer-fullscreen-digit" id="fs-s">00</div>
</div>
<button class="timer-fullscreen-close" onclick="closeFullscreen()"></button>
</div>
<div id="page-random" class="page">
<div class="random-container">
<div class="random-number" id="random-display">?</div>
<div class="random-controls">
<button class="random-btn generate" id="random-start-btn" onclick="startRandom()">开始摇号</button>
</div>
<div class="random-settings">
<label>最大号码:</label>
<div class="number-input-group">
<button class="number-adj-btn" onclick="adjustRandomMax(-10)">-10</button>
<button class="number-adj-btn" onclick="adjustRandomMax(-1)">-</button>
<div class="number-display" id="random-max-display">75</div>
<button class="number-adj-btn" onclick="adjustRandomMax(1)">+</button>
<button class="number-adj-btn" onclick="adjustRandomMax(10)">+10</button>
</div>
</div>
</div>
</div>
<div id="page-settings" class="page">
<div class="settings-container">
<h2 class="settings-title">设置</h2>
<div class="settings-section">
<div class="settings-section-title">⏱️ 高效计时</div>
<div class="settings-item">
<span class="settings-label">默认填充的时间</span>
<div class="settings-radio-group">
<label class="settings-radio-item">
<input type="radio" name="timer-fill-mode" value="last" checked onchange="handleFillModeChange(this.value)">
来自上次设定
</label>
<label class="settings-radio-item">
<input type="radio" name="timer-fill-mode" value="custom" onchange="handleFillModeChange(this.value)">
自定义
</label>
</div>
</div>
<div class="settings-custom-input" id="custom-seconds-container">
<label>自定义倒计时默认填充时间</label>
<input type="number" class="settings-input" id="custom-seconds-input" min="1" max="86400" value="300" oninput="handleCustomSecondsChange(this.value)">
<span class="settings-unit"></span>
</div>
</div>
<div class="settings-section">
<div class="settings-section-title">🎲 随机摇号</div>
<div class="settings-item">
<span class="settings-label">跳过动画</span>
<label class="toggle-switch">
<input type="checkbox" id="skip-animation-toggle" onchange="handleSkipAnimationChange(this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="settings-item">
<span class="settings-label">智能匹配
<span class="settings-hint">避免重复抽中同一号码</span>
</span>
<label class="toggle-switch">
<input type="checkbox" id="smart-match-toggle" onchange="handleSmartMatchChange(this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="settings-section about-section">
<div class="settings-section-title">关于</div>
<div class="about-content">
<img src="assets/icon.ico" alt="Logo" class="about-logo">
<div class="about-info">
<h3 class="about-name">课堂小助手 Classroom Assistant</h3>
<p class="about-version" id="about-version"></p>
<a href="#" class="about-link" onclick="openSourceCode(event)">源代码</a>
</div>
</div>
</div>
</div>
</div>
<div class="version-info">
<p id="version-info">课堂小助手 Copyrights 2026 Yunyun By CaelLab</p>
</div>
</main>
</div>
</div>
<script>
function minimizeWindow() {
if (window.electronAPI) {
window.electronAPI.windowMinimize()
}
}
function closeWindow() {
if (window.electronAPI) {
window.electronAPI.windowClose()
}
}
function showPage(pageName) {
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'))
document.querySelectorAll('.menu-item').forEach(m => m.classList.remove('active'))
document.getElementById('page-' + pageName).classList.add('active')
event.currentTarget.classList.add('active')
}
let timerMode = 'countdown'
let timerInterval = null
let timerSeconds = 0
let isTimerRunning = false
let isTimerPaused = false
function switchTimerMode(mode) {
if (isTimerRunning || isTimerPaused) return
timerMode = mode
document.querySelectorAll('.timer-tab').forEach(t => t.classList.remove('active'))
event.currentTarget.classList.add('active')
resetTimer()
updateTimerButtonsVisibility()
}
function updateTimerButtonsVisibility() {
const btns = document.querySelectorAll('.timer-adj-btn')
const digitBtns = document.querySelectorAll('.timer-digit-btn')
const displayArea = document.getElementById('timer-display-area')
const isActive = isTimerRunning || isTimerPaused
const hideAdj = isActive || timerMode === 'stopwatch'
btns.forEach(b => b.style.visibility = hideAdj ? 'hidden' : 'visible')
digitBtns.forEach(b => {
if (isActive) {
b.classList.add('running')
} else {
b.classList.remove('running')
}
if (timerMode === 'stopwatch' && !isActive) {
b.style.cursor = 'default'
} else {
b.style.cursor = isActive ? 'default' : 'pointer'
}
})
document.querySelectorAll('.timer-tab').forEach(t => {
t.style.opacity = isActive ? '0.5' : '1'
t.style.pointerEvents = isActive ? 'none' : 'auto'
})
if (displayArea) {
if (isActive) {
displayArea.classList.remove('not-clickable')
displayArea.classList.add('clickable')
} else {
displayArea.classList.remove('clickable')
displayArea.classList.add('not-clickable')
}
}
}
function adjustDigit(id, delta) {
if (isTimerRunning) return
const el = document.getElementById(id)
let val = parseInt(el.textContent) + delta
const isTens = id.includes('tens')
const isHour = id.startsWith('h-')
if (isTens) {
const maxTens = isHour ? 9 : 5
val = val < 0 ? maxTens : val > maxTens ? 0 : val
} else {
val = val < 0 ? 9 : val > 9 ? 0 : val
}
el.textContent = val
}
function cycleDigit(el) {
if (isTimerRunning) return
let val = parseInt(el.textContent) + 1
const isTens = el.id.includes('tens')
const isHour = el.id.startsWith('h-')
if (isTens) {
const maxTens = isHour ? 9 : 5
val = val > maxTens ? 0 : val
} else {
val = val > 9 ? 0 : val
}
el.textContent = val
}
function getTimerSeconds() {
const h = parseInt(document.getElementById('h-tens').textContent) * 10 + parseInt(document.getElementById('h-ones').textContent)
const m = parseInt(document.getElementById('m-tens').textContent) * 10 + parseInt(document.getElementById('m-ones').textContent)
const s = parseInt(document.getElementById('s-tens').textContent) * 10 + parseInt(document.getElementById('s-ones').textContent)
return h * 3600 + m * 60 + s
}
function setTimerDigits(totalSeconds) {
const h = Math.floor(totalSeconds / 3600)
const m = Math.floor((totalSeconds % 3600) / 60)
const s = totalSeconds % 60
document.getElementById('h-tens').textContent = Math.floor(h / 10)
document.getElementById('h-ones').textContent = h % 10
document.getElementById('m-tens').textContent = Math.floor(m / 10)
document.getElementById('m-ones').textContent = m % 10
document.getElementById('s-tens').textContent = Math.floor(s / 10)
document.getElementById('s-ones').textContent = s % 10
}
async function loadTimeLastSeconds() {
if (window.electronAPI) {
try {
const fillMode = await window.electronAPI.getTimeFillMode()
let seconds
if (fillMode === 'custom') {
seconds = await window.electronAPI.getTimeCustomSeconds()
} else {
seconds = await window.electronAPI.getTimeLastSeconds()
}
setTimerDigits(seconds)
} catch (error) {
console.log('加载计时器记忆失败:', error)
}
}
}
function startTimer() {
if (isTimerRunning) return
if (!isTimerPaused) {
if (timerMode === 'countdown') {
timerSeconds = getTimerSeconds()
if (timerSeconds <= 0) return
if (window.electronAPI) {
window.electronAPI.setTimeLastSeconds(timerSeconds)
}
} else {
timerSeconds = 0
setTimerDigits(0)
}
}
isTimerRunning = true
isTimerPaused = false
document.getElementById('timer-start-btn').style.display = 'none'
document.getElementById('timer-pause-btn').style.display = 'inline-block'
updateTimerButtonsVisibility()
timerInterval = setInterval(() => {
if (timerMode === 'countdown') {
timerSeconds--
if (timerSeconds <= 0) {
timerSeconds = 0
clearInterval(timerInterval)
isTimerRunning = false
isTimerPaused = false
document.getElementById('timer-start-btn').style.display = 'inline-block'
document.getElementById('timer-pause-btn').style.display = 'none'
updateTimerButtonsVisibility()
setTimerDigits(0)
alert('倒计时结束!')
return
}
} else {
timerSeconds++
}
setTimerDigits(timerSeconds)
}, 1000)
}
function pauseTimer() {
if (!isTimerRunning) return
isTimerRunning = false
isTimerPaused = true
clearInterval(timerInterval)
document.getElementById('timer-start-btn').style.display = 'inline-block'
document.getElementById('timer-pause-btn').style.display = 'none'
updateTimerButtonsVisibility()
}
function resetTimer() {
isTimerRunning = false
isTimerPaused = false
clearInterval(timerInterval)
document.getElementById('timer-start-btn').style.display = 'inline-block'
document.getElementById('timer-pause-btn').style.display = 'none'
if (timerMode === 'countdown') {
const savedSeconds = getTimerSeconds()
setTimerDigits(savedSeconds > 0 ? savedSeconds : 300)
} else {
timerSeconds = 0
setTimerDigits(0)
}
updateTimerButtonsVisibility()
}
function handleTimerDisplayClick() {
const isActive = isTimerRunning || isTimerPaused
if (isActive) {
openFullscreen()
}
}
let fullscreenInterval = null
let wasMaximizedBeforeFullscreen = false
async function openFullscreen() {
if (window.electronAPI) {
wasMaximizedBeforeFullscreen = await window.electronAPI.windowIsMaximized()
if (!wasMaximizedBeforeFullscreen) {
window.electronAPI.windowMaximize()
}
}
setTimeout(() => {
const overlay = document.getElementById('timer-fullscreen')
overlay.classList.add('active')
updateFullscreenDisplay()
fullscreenInterval = setInterval(updateFullscreenDisplay, 100)
}, 100)
}
function closeFullscreen() {
const overlay = document.getElementById('timer-fullscreen')
overlay.classList.remove('active')
if (fullscreenInterval) {
clearInterval(fullscreenInterval)
fullscreenInterval = null
}
if (window.electronAPI && !wasMaximizedBeforeFullscreen) {
window.electronAPI.windowRestore()
}
}
function updateFullscreenDisplay() {
const h = document.getElementById('h-tens').textContent + document.getElementById('h-ones').textContent
const m = document.getElementById('m-tens').textContent + document.getElementById('m-ones').textContent
const s = document.getElementById('s-tens').textContent + document.getElementById('s-ones').textContent
document.getElementById('fs-h').textContent = h
document.getElementById('fs-m').textContent = m
document.getElementById('fs-s').textContent = s
}
let isRandomRunning = false
let randomMax = 75
let randomTimeout = null
let skipAnimation = false
let smartMatch = true
async function loadRandomMax() {
if (window.electronAPI) {
const max = await window.electronAPI.getRandomMaxNumber()
randomMax = max
document.getElementById('random-max-display').textContent = max
}
}
async function adjustRandomMax(delta) {
if (isRandomRunning) return
randomMax = Math.max(1, randomMax + delta)
document.getElementById('random-max-display').textContent = randomMax
if (window.electronAPI) {
await window.electronAPI.setRandomMaxNumber(randomMax)
await window.electronAPI.resetSmartMatchWeights()
}
}
async function startRandom() {
if (isRandomRunning) return
isRandomRunning = true
document.getElementById('random-start-btn').disabled = true
document.getElementById('random-start-btn').style.opacity = '0.5'
document.getElementById('random-display').style.color = '#667eea'
let finalNum
if (smartMatch && window.electronAPI) {
finalNum = await window.electronAPI.pickWeightedRandom()
} else {
finalNum = Math.floor(Math.random() * randomMax) + 1
}
if (skipAnimation) {
document.getElementById('random-display').textContent = finalNum
isRandomRunning = false
document.getElementById('random-start-btn').disabled = false
document.getElementById('random-start-btn').style.opacity = '1'
} else {
rollRandom(25, 0, finalNum)
}
}
function rollRandom(delay, count, finalNum) {
const num = Math.floor(Math.random() * randomMax) + 1
document.getElementById('random-display').textContent = num
const maxRolls = 15 + Math.floor(Math.random() * 8)
const newDelay = count > maxRolls * 0.5 ? delay * 1.12 : delay
if (newDelay < 350) {
randomTimeout = setTimeout(() => rollRandom(newDelay, count + 1, finalNum), newDelay)
} else {
document.getElementById('random-display').textContent = finalNum
isRandomRunning = false
document.getElementById('random-start-btn').disabled = false
document.getElementById('random-start-btn').style.opacity = '1'
document.getElementById('random-display').style.color = '#667eea'
}
}
async function loadAppInfo() {
try {
if (window.electronAPI) {
const version = await window.electronAPI.getAppVersion()
const appName = await window.electronAPI.getAppName()
document.getElementById('version-info').textContent =
appName + ' v' + version + ' | Copyrights 2026 Yunyun By CaelLab'
document.getElementById('about-version').textContent = 'v' + version
}
} catch (error) {
console.log('无法获取应用信息:', error)
}
}
function handleFillModeChange(mode) {
const container = document.getElementById('custom-seconds-container')
if (mode === 'custom') {
container.classList.add('visible')
} else {
container.classList.remove('visible')
}
if (window.electronAPI) {
window.electronAPI.setTimeFillMode(mode)
}
}
let customSecondsDebounce = null
function handleCustomSecondsChange(value) {
clearTimeout(customSecondsDebounce)
customSecondsDebounce = setTimeout(() => {
const seconds = Math.max(1, Math.min(86400, parseInt(value) || 300))
if (window.electronAPI) {
window.electronAPI.setTimeCustomSeconds(seconds)
}
}, 500)
}
function handleSkipAnimationChange(checked) {
skipAnimation = checked
if (window.electronAPI) {
window.electronAPI.setRandomSkipAnimation(checked)
}
}
function handleSmartMatchChange(checked) {
smartMatch = checked
if (window.electronAPI) {
window.electronAPI.setSmartMatch(checked)
if (checked) {
window.electronAPI.resetSmartMatchWeights()
}
}
}
async function loadSettings() {
if (window.electronAPI) {
const fillMode = await window.electronAPI.getTimeFillMode()
const radios = document.querySelectorAll('input[name="timer-fill-mode"]')
radios.forEach(radio => {
radio.checked = radio.value === fillMode
})
if (fillMode === 'custom') {
document.getElementById('custom-seconds-container').classList.add('visible')
}
const customSeconds = await window.electronAPI.getTimeCustomSeconds()
document.getElementById('custom-seconds-input').value = customSeconds
skipAnimation = await window.electronAPI.getRandomSkipAnimation()
document.getElementById('skip-animation-toggle').checked = skipAnimation
smartMatch = await window.electronAPI.getSmartMatch()
document.getElementById('smart-match-toggle').checked = smartMatch
}
}
function openSourceCode(e) {
e.preventDefault()
if (window.electronAPI) {
window.electronAPI.openExternalUrl('https://git.caellab.com/yunyun/Classroom-Assistant')
}
}
document.addEventListener('DOMContentLoaded', async () => {
loadAppInfo()
loadRandomMax()
await loadTimeLastSeconds()
loadSettings()
})
</script>
</body>
</html>