| <!DOCTYPE html> |
| <html lang="zh-CN"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> |
| <title>🤗─=≡Σ((( つ•̀ω•́)つ</title> |
| <link rel="shortcut icon" href="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" /> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> |
| |
| <link rel="stylesheet" href="style.css"> |
| |
| <style> |
| |
| :root { |
| --countdown-color: #ff4b1f; |
| --timer-color: #32cd32; |
| --pause-color: #ffcc00; |
| --stop-color: #9ca3af; |
| } |
| .timer-card { |
| transition: background 0.5s, box-shadow 0.5s; |
| display: flex; |
| flex-direction: column; |
| gap: 10px; |
| } |
| .timer-card.countdown-active { border-top: 4px solid var(--countdown-color); } |
| .timer-card.timer-active { border-top: 4px solid var(--timer-color); } |
| .timer-card.pause-active { border-top: 4px solid var(--pause-color); } |
| .timer-input-row { display: flex; gap: 10px; } |
| .timer-input-row input { flex: 1; text-align: center; padding: 8px; } |
| .timer-btn-row { |
| display: grid; |
| grid-template-columns: repeat(4, 1fr); |
| gap: 5px; |
| } |
| #timeDisplay { |
| font-size: 1.8rem; |
| color: #32cd32; |
| width: 100%; |
| text-align: center; |
| font-family: 'Courier New', Courier, monospace; |
| font-weight: bold; |
| letter-spacing: 2px; |
| background: rgba(0,0,0,0.03); |
| border: 1px solid var(--border-light, #e5e7eb); |
| border-radius: 8px; |
| padding: 4px 0; |
| transition: color 0.5s; |
| } |
| .timer-btn-row button:disabled { |
| opacity: 0.5; |
| cursor: not-allowed; |
| background: var(--stop-color) !important; |
| color: white !important; |
| } |
| |
| .earnings-input-wrapper { |
| position: relative; |
| flex: 1; |
| min-width: 100px; |
| } |
| .earnings-input-wrapper input { |
| padding-right: 65px; |
| box-sizing: border-box; |
| } |
| .earnings-input-wrapper[data-suffix]::after { |
| content: attr(data-suffix); |
| position: absolute; |
| top: 50%; |
| right: 12px; |
| transform: translateY(-50%); |
| color: var(--text-tertiary, #888); |
| pointer-events: none; |
| white-space: nowrap; |
| font-size: 0.75rem; |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <audio id="alertSound"><source src="ear0.wav" type="audio/mpeg"></audio> |
| <audio id="coinSound" src="/page/wav/rmb.wav"></audio> |
| <audio id="bigWinSound" src="/page/wav/rmb2.wav"></audio> |
|
|
| <div class="sticky-header-wrapper"> |
| <div class="filter-bar"> |
| <div id="stats"> |
| <div id="total">总数:<span>0</span></div> |
| <div id="chinese">汉字:<span>0</span></div> |
| <div id="punctuation">标点:<span>0</span></div> |
| <div id="alphabet">字母:<span>0</span></div> |
| <div id="numbers">数字:<span>0</span></div> |
| <div id="duplicate">重复:<span>0</span></div> |
| <div id="replace-count">替换:<span>0</span></div> |
| <div id="regex-replace-count">正则:<span>0</span></div> |
| </div> |
| <input type="password" id="globalApiKey" class="global-key-input" placeholder="输入全局 API 密钥 (poixe-xxx)"> |
| <a href="https://poixe.com/pricing?i=token" target="_blank" class="tag-btn">获取API</a> |
| <a href="https://labs.google/fx/zh/tools/flow" target="_blank" class="tag-btn">谷歌香蕉</a> |
| <a href="https://cpuck.com/deepseek" target="_blank" class="tag-btn">密钥测试</a> |
| <button class="tag-btn" id="btnClearOutput" style="color: #ef4444; border-color: #fca5a5;">🗑️ 清除缓存</button> |
| </div> |
| </div> |
|
|
| <div class="workspace"> |
|
|
| <div class="editor-panel"> |
| <div class="editor-tabs"> |
| <button class="editor-tab active" id="tab-text" onclick="switchTab('text')">📝 文本编辑区</button> |
| <button class="editor-tab" id="tab-chat" onclick="switchTab('chat')">💬 智能对话区</button> |
| <button class="editor-tab" id="tab-image" onclick="switchTab('image')">🖼️ 图像生成区</button> |
| <button class="editor-tab" id="tab-media" onclick="switchTab('media')">🎬 媒体解析区</button> |
| <button class="editor-tab" id="tab-api" onclick="switchTab('api')">📡 API 日志</button> |
| </div> |
|
|
| <div class="error-msg" id="errorMsg"></div> |
|
|
| <div id="view-text" class="workspace-view" style="display: flex;"> |
| <div class="view-header"> |
| <div class="view-header-title">📝 文本处理模式</div> |
| <div class="view-header-controls"> |
| <select id="textModelSelect" class="glass-input" style="width: auto; margin-bottom: 0; padding: 6px 30px 6px 10px; height: 34px;"> |
| <option value="deepseek-v4-flash" selected>DeepSeek V4 Flash</option> |
| </select> |
| <button id="aiProcessTextBtn" class="btn bg-blue" style="padding: 6px 15px;">🤖 AI 处理文本</button> |
| </div> |
| </div> |
| <textarea id="textarea" placeholder="非数字:[^0-9]* |
| 中文字符的正则表达式:[\u4e00-\u9fa5] |
| 长度为3-20的所有字符:.{3,20} |
| 由数字和26个英文字母组成的字符串:[A-Za-z0-9]+ |
| 由数字、26个英文字母或者下划线组成的字符串:\w+ 或 \w{3,20} |
| curl.exe -o NUL http://speedtest.tele2.net/10GB.zip |
| |
| sudo passwd root |
| ps -ef | grep python |
| grep -rn 'py' /home/ |
| rm -rf /* |
| |
| conda create -n python38 python=3.8 # 创建虚拟环境 |
| conda activate python38 # 切换虚拟环境 |
| conda env remove -n python38 # 删除虚拟环境 |
| conda create -n python38 --clone old_python38 # 克隆虚拟环境 |
| conda list # 列出已安装的包 |
| conda env list # 列出所有虚拟环境 |
| tracert baidu.cn |
| Get-ChildItem -Recurse -File | Select-String -Pattern # 双引号 Windows 搜索"></textarea> |
| </div> |
|
|
| <div id="view-chat" class="workspace-view" style="display: none;"> |
| <div class="view-header"> |
| <div class="view-header-title">💬 智能连续对话</div> |
| <div class="view-header-controls"> |
| <select id="chatModelSelect" class="glass-input" style="width: auto; margin-bottom: 0; padding: 6px 30px 6px 10px; height: 34px;"> |
| <option value="deepseek-v4-flash" selected>DeepSeek V4 Flash</option> |
| <option value="deepseek-v4-pro">DeepSeek V4 Pro</option> |
| <option value="gpt-5.5-2026-04-23">GPT-5.5 (04-23版)</option> |
| <option value="claude-opus-4-6">Claude Opus 4.6</option> |
| <option value="gemini-3.1-pro-preview">Gemini 3.1 Pro Preview</option> |
| </select> |
| </div> |
| </div> |
| <div id="chatHistory" class="chat-history"> |
| <div style="color: var(--text-tertiary); text-align: center; margin-top: 50px; font-size: 0.9rem;">欢迎使用 AI 对话。上下文已被记忆,上方可自由切换模型。</div> |
| </div> |
| <div class="chat-input-area"> |
| <button id="clearChatBtn" class="btn btn-glass" style="width: 44px; height: 44px; padding: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;" title="清空对话记忆">🗑️</button> |
| <textarea id="chatInput" placeholder="输入您的问题,按 Shift+Enter 换行,Enter 发送..."></textarea> |
| <button id="sendChatBtn" class="btn bg-blue" style="width: 80px; height: 44px; flex-shrink: 0; font-size: 0.95rem;">发送</button> |
| </div> |
| </div> |
|
|
| <div id="view-image" class="workspace-view" style="display: none;"> |
| <div class="image-gen-container"> |
| <div class="preview-placeholder" id="imagePlaceholder"> |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg> |
| <p>AI 绘制的图像将在这里呈现</p> |
| </div> |
| <div class="loader-spinner" id="imageLoader"></div> |
| <div id="aiResultWrapper" style="display: none; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%;"> |
| <img id="aiResultImage" class="result-image" alt="生成的图片"> |
| </div> |
| </div> |
| </div> |
|
|
| <div id="view-media" class="workspace-view" style="display: none; padding: 20px; align-items: center; justify-content: center; overflow-y: auto;"> |
| <div class="preview-placeholder" id="mediaPlaceholder"> |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg> |
| <p>视频预览及抽帧图片将在这里显示</p> |
| </div> |
| <div id="mediaPlayerContainer" style="display: none; flex-direction: column; align-items: center; width: 100%;"> |
| <div id="mediaPlayerWrapper" style="display:flex; justify-content:center; align-items:center; width:100%; margin-bottom: 20px;"></div> |
| <button class="btn btn-glass" id="btnDownloadMedia" style="color: var(--theme-blue);">⬇️ 下载媒体文件</button> |
| </div> |
| <div id="frameDisplayContainer" style="display: none; flex-direction: column; align-items: center; width: 100%; height: 100%; overflow: hidden;"> |
| <img id="frameDisplayedImage" class="media-image-display" src="" alt="Extracted Frame" /> |
| <div id="framePager" style="color: var(--text-tertiary); font-size: 0.8rem; margin-top: 10px; flex-shrink: 0;">0 / 0</div> |
| <div id="mediaToolbar" class="media-toolbar"> |
| <button class="btn btn-glass frame-btn" id="prevButton">⬅️ 上一帧</button> |
| <button class="btn btn-glass frame-btn" id="nextButton">下一帧 ➡️</button> |
| <button class="btn btn-glass frame-btn" id="btnDownloadFrame" style="color: var(--theme-blue);">⬇️ 保存此图</button> |
| <div class="frame-btn" style="width: 1px; background: var(--border-light); margin: 0 4px; flex-shrink: 0;"></div> |
| <button class="btn btn-glass" id="btnInvert">反色处理</button> |
| <button class="btn btn-glass" id="btnGray">去色黑白</button> |
| <button class="btn btn-glass" id="btnRestore">原图还原</button> |
| <button class="btn btn-glass" id="btnDownloadEditedImage">保存图像</button> |
| </div> |
| </div> |
| </div> |
|
|
| <div id="view-api" class="workspace-view" style="display: none; flex-direction: column; padding: 20px;"> |
| <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;"> |
| <h3 style="color: var(--text-primary); font-size: 0.95rem; margin: 0;">📡 接口调试记录</h3> |
| <button class="btn btn-glass" onclick="clearApiLogs()" style="padding: 4px 10px; font-size: 0.8rem;">🗑️ 清空记录</button> |
| </div> |
| <div id="apiLogContainer" style="flex: 1; overflow-y: auto; background: #f9fafb; border: 1px solid var(--border-light); border-radius: 8px; padding: 15px; color: var(--text-primary);"> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="control-panel"> |
|
|
|
|
| <div class="glass-card"> |
| <div class="section-title">🔍 查找与替换 (针对文本区)</div> |
| <input type="text" id="search-input" class="glass-input" placeholder="输入查找文本或正则" value='https?:\/\/[^\s\/$.?#]+[^\s]*?(?=")'> |
| <input type="text" id="replace-input" class="glass-input" placeholder="输入替换内容 (为空则删除)"> |
| <div class="btn-grid" style="grid-template-columns: repeat(4, 1fr);"> |
| <button class="btn btn-glass" id="copy-btn">复制</button> |
| <button class="btn btn-glass" id="paste-btn">粘贴</button> |
| <button class="btn btn-glass" id="remove-duplicates-btn">去重</button> |
| <button class="btn btn-glass" id="replace-btn">替换</button> |
| <button class="btn btn-glass" id="regex-replace-btn">正则</button> |
| <button class="btn btn-glass" id="preserve-matches-btn">保留</button> |
| <button class="btn btn-glass" id="clear-text-btn" style="color: #ef4444;">清空</button> |
| <button class="btn btn-glass" id="preview-web-btn" style="color: var(--theme-blue);">网页</button> |
| </div> |
| </div> |
| |
| <div class="glass-card timer-card" id="timerCardContainer"> |
| <div class="section-title">⏱️ 计时收益计算器</div> |
|
|
| <div class="timer-input-row"> |
| <input class="glass-input" type="number" id="timerHours" placeholder="时(HH)" min="0" max="23"> |
| <input class="glass-input" type="number" id="timerMinutes" placeholder="分(MM)" min="0" max="59" value="15"> |
| <input class="glass-input" type="number" id="timerSeconds" placeholder="秒(SS)" min="0" max="59"> |
| </div> |
| |
| <div id="timeDisplay">00:00:00:00</div> |
|
|
| <div class="timer-btn-row"> |
| <button class="btn" id="startCountdownButton" onclick="startCountdown()" style="background-color: var(--countdown-color); color: white;">倒计时</button> |
| <button class="btn" id="startTimerButton" onclick="startTimer()" style="background-color: var(--timer-color); color: white;">计时器</button> |
| <button class="btn" id="pauseButton" onclick="pauseTimer()" disabled style="background-color: var(--pause-color); color: black;">暂停</button> |
| <button class="btn" id="stopButton" onclick="stopTimer()" disabled style="background-color: transparent; border: 1px solid var(--border-light); color: var(--text-primary);">停止</button> |
| </div> |
|
|
|
|
| <div class="earnings-input-wrapper" data-suffix="" style="margin-bottom: 10px;"> |
| <input id="totalAmount" class="glass-input" value="收益: 0" readonly style="margin-bottom: 0; color: #ef4444; font-family: monospace; font-size: 1.1rem; font-weight: 600;"> |
| </div> |
| <div style="display: flex; gap: 10px; margin-bottom: 10px;"> |
| <div class="earnings-input-wrapper" data-suffix="元"> |
| <input type="number" id="principal" class="glass-input" value="50000" step="10000" placeholder="请输入金额" style="margin-bottom: 0;"> |
| </div> |
| <div class="earnings-input-wrapper" data-suffix="% (年利率)"> |
| <input type="number" id="rate" class="glass-input" placeholder="请输入年利率" value="1" required style="margin-bottom: 0;"> |
| </div> |
| </div> |
| |
| <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;"> |
| <input id="perHourEarnings" class="glass-input" placeholder="每小时" readonly style="margin-bottom: 0; font-size: 0.85rem; padding: 8px 10px;"> |
| <input id="perDayEarnings" class="glass-input" placeholder="每天" readonly style="margin-bottom: 0; font-size: 0.85rem; padding: 8px 10px;"> |
| <input id="perMonthEarnings" class="glass-input" placeholder="每月" readonly style="margin-bottom: 0; font-size: 0.85rem; padding: 8px 10px;"> |
| <input id="perYearEarnings" class="glass-input" placeholder="每年" readonly style="margin-bottom: 0; font-size: 0.85rem; padding: 8px 10px;"> |
| </div> |
| |
|
|
|
|
| </div> |
|
|
|
|
| <div class="glass-card"> |
| <div class="section-title">📦 文件转码 & 媒体元数据 & 抽帧</div> |
| <input type="file" id="fileInput" class="glass-input" accept="image/*,audio/*,video/*"> |
| <input type="number" id="intervalInput" class="glass-input" min="1" placeholder="视频抽帧:输入提取帧率 (FPS)"> |
| |
| <div class="btn-grid" style="grid-template-columns: repeat(3, 1fr); margin-bottom: 10px;"> |
| <button class="btn btn-glass" id="btnToB64">文件编码</button> |
| <button class="btn btn-glass" id="btnFromB64">编码文件</button> |
| <button class="btn btn-glass" id="btnFrames">视频抽帧</button> |
| </div> |
|
|
| <div class="btn-grid" style="grid-template-columns: repeat(2, 1fr);"> |
| <button class="btn btn-glass" id="btnReadAllMeta" style="color: var(--theme-blue);">解读文件元数据</button> |
| <button class="btn btn-glass" id="btnReadBasicMeta" style="color: var(--theme-blue);">元数据信息概览</button> |
| </div> |
|
|
| <div id="metaDisplayContainer" style="display: none; margin-top: 15px; padding: 10px; background: rgba(0, 0, 0, 0.03); border: 1px solid var(--border-light, #e5e7eb); border-radius: 8px;"> |
| <div style="font-size: 0.85rem; font-weight: 600; margin-bottom: 8px; color: var(--text-primary); display: flex; justify-content: space-between;"> |
| <span>📊 媒体元数据 (Metadata)</span> |
| <span id="closeMetaBtn" style="cursor: pointer; color: var(--text-tertiary);" onclick="document.getElementById('metaDisplayContainer').style.display='none'">✖</span> |
| </div> |
| <pre id="metaDataOutput" style="margin: 0; font-size: 0.8rem; font-family: monospace; white-space: pre-wrap; word-wrap: break-word; color: var(--text-secondary); max-height: 150px; overflow-y: auto;"></pre> |
| </div> |
| </div> |
| |
| <div class="glass-card" style="margin-bottom: 20px;"> |
| <div class="section-title collapsible-header collapsed" onclick="toggleCollapse('content-ai-image', this)"> |
| <span>✨ AI 图像生成</span> |
| </div> |
| <div id="content-ai-image" class="collapsible-content collapsed"> |
| <select id="imageModelSelect" class="glass-input" style="margin-top: 5px;"> |
| <option value="gemini-2.5-flash-image">Nano Banana (闪电版)</option> |
| <option value="nano-banana-pro" selected>Nano Banana Pro (高画质)</option> |
| </select> |
| <div class="grid-2" style="grid-template-columns: 1fr 1fr; gap: 10px;"> |
| <select id="aspectRatio" class="glass-input"> |
| <option value="16:9">16:9 (横屏)</option> |
| <option value="1:1">1:1 (头像)</option> |
| <option value="9:16">9:16 (竖屏)</option> |
| <option value="21:9">21:9 (宽屏)</option> |
| </select> |
| <select id="imageSize" class="glass-input"> |
| <option value="2K">2K (2048x2048)</option> |
| <option value="1K" selected>1K (1024x1024)</option> |
| <option value="4K">4K (4096x4096)</option> |
| </select> |
| </div> |
| <textarea id="imagePrompt" class="glass-input" style="min-height: 60px; margin-bottom: 10px;" placeholder="输入图像提示词,例如:赛博朋克风格的橘猫..."></textarea> |
| <button id="generateBtn" class="btn bg-blue" style="width: 100%; padding: 10px; font-size: 0.95rem;">🚀 开始绘制图像</button> |
| </div> |
| </div> |
|
|
| <div class="glass-card"> |
| <div class="section-title collapsible-header collapsed" onclick="toggleCollapse('content-tts', this)"> |
| <span>📢 文本语音朗读 (TTS)</span> |
| </div> |
| <div id="content-tts" class="collapsible-content collapsed"> |
| <label class="tts-checkbox" style="margin-top: 8px;"> |
| <input type="checkbox" id="tts-filterChinese" checked> 只显示中文语音 |
| </label> |
| <select id="tts-voiceSelect" class="glass-input"></select> |
| <div class="tts-row"> |
| <span>语速 (<span id="tts-rate-val">1.0x</span>)</span> |
| <input id="tts-rate" type="range" min="0.1" max="10" step="0.1" value="1" /> |
| </div> |
| <div class="tts-row"> |
| <span>音调 (<span id="tts-pitch-val">1.0</span>)</span> |
| <input id="tts-pitch" type="range" min="0" max="10" step="0.1" value="1" /> |
| </div> |
| <div class="tts-row"> |
| <span>音量 (<span id="tts-volume-val">1.0</span>)</span> |
| <input id="tts-volume" type="range" min="0" max="10" step="0.1" value="1" /> |
| </div> |
| <div style="display: flex; gap: 10px; margin-top: 15px;"> |
| <button id="tts-start" class="btn btn-glass" style="flex: 1; color: var(--theme-blue); border-color: #bfdbfe;">▶ 开始朗读文本区</button> |
| <button id="tts-stop" class="btn btn-glass" style="flex: 1; color: var(--theme-red); border-color: #fca5a5;">■ 停止</button> |
| </div> |
| </div> |
| </div> |
| |
| </div> |
| </div> |
|
|
| <script src="app.js"></script> |
|
|
| <script> |
| |
| let timerInstance; |
| let timerEndTime; |
| let timerStartTime; |
| let timerRemainingTime; |
| let isTimerCountdown = false; |
| let isTimerPaused = false; |
| const displayElement = document.getElementById('timeDisplay'); |
| const soundAlert = document.getElementById('alertSound'); |
| const timerCard = document.getElementById('timerCardContainer'); |
| document.addEventListener('DOMContentLoaded', () => { |
| startTimer(); |
| }); |
| function startCountdown() { |
| if (timerInstance) clearInterval(timerInstance); |
| |
| const hrs = parseInt(document.getElementById('timerHours').value) || 0; |
| const mins = parseInt(document.getElementById('timerMinutes').value) || 0; |
| const secs = parseInt(document.getElementById('timerSeconds').value) || 0; |
| if (hrs === 0 && mins === 0 && secs === 0) { |
| alert('请设置一个有效的倒计时时间。'); |
| return; |
| } |
| timerCard.className = 'glass-card timer-card countdown-active'; |
| displayElement.style.color = 'var(--countdown-color)'; |
| const totalMs = (hrs * 3600 + mins * 60 + secs) * 1000; |
| if (totalMs > 0) { |
| isTimerCountdown = true; |
| timerEndTime = Date.now() + totalMs; |
| timerInstance = setInterval(updateTimerDisplay, 10); |
| document.getElementById('startCountdownButton').disabled = true; |
| document.getElementById('startTimerButton').disabled = false; |
| document.getElementById('pauseButton').disabled = false; |
| document.getElementById('stopButton').disabled = false; |
| } |
| } |
| function startTimer() { |
| if (timerInstance) clearInterval(timerInstance); |
| |
| timerCard.className = 'glass-card timer-card timer-active'; |
| displayElement.style.color = 'var(--timer-color)'; |
| timerStartTime = Date.now(); |
| isTimerPaused = false; |
| isTimerCountdown = false; |
| timerInstance = setInterval(updateTimerDisplay, 10); |
| |
| document.getElementById('startCountdownButton').disabled = false; |
| document.getElementById('startTimerButton').disabled = true; |
| document.getElementById('pauseButton').disabled = false; |
| document.getElementById('stopButton').disabled = false; |
| } |
| function pauseTimer() { |
| timerCard.className = 'glass-card timer-card pause-active'; |
| displayElement.style.color = 'var(--pause-color)'; |
| clearInterval(timerInstance); |
| |
| if (isTimerCountdown) { |
| timerRemainingTime = timerEndTime - Date.now(); |
| } else { |
| timerRemainingTime = Date.now() - timerStartTime; |
| } |
| |
| isTimerPaused = true; |
| const pBtn = document.getElementById('pauseButton'); |
| pBtn.innerText = "继续"; |
| pBtn.onclick = resumeTimer; |
| } |
| function resumeTimer() { |
| timerCard.className = isTimerCountdown ? 'glass-card timer-card countdown-active' : 'glass-card timer-card timer-active'; |
| displayElement.style.color = isTimerCountdown ? 'var(--countdown-color)' : 'var(--timer-color)'; |
| |
| if (isTimerCountdown) { |
| timerEndTime = Date.now() + timerRemainingTime; |
| } else { |
| timerStartTime = Date.now() - timerRemainingTime; |
| } |
| |
| isTimerPaused = false; |
| timerInstance = setInterval(updateTimerDisplay, 10); |
| |
| const pBtn = document.getElementById('pauseButton'); |
| pBtn.innerText = "暂停"; |
| pBtn.onclick = pauseTimer; |
| } |
| function stopTimer() { |
| timerCard.className = 'glass-card timer-card'; |
| displayElement.style.color = 'var(--stop-color)'; |
| clearInterval(timerInstance); |
| displayElement.innerHTML = '00:00:00:00'; |
| resetTimerButtons(); |
| } |
| function updateTimerDisplay() { |
| const currentT = Date.now(); |
| let remaining; |
| |
| if (isTimerCountdown) { |
| remaining = timerEndTime - currentT; |
| if (remaining <= 0) { |
| clearInterval(timerInstance); |
| soundAlert.play(); |
| resetTimerButtons(); |
| displayElement.innerHTML = '00:00:00:00'; |
| return; |
| } |
| } else { |
| remaining = currentT - timerStartTime; |
| } |
| const ms = remaining % 1000; |
| const totalSecs = Math.floor(remaining / 1000); |
| const secs = totalSecs % 60; |
| const totalMins = Math.floor(totalSecs / 60); |
| const mins = totalMins % 60; |
| const hrs = Math.floor(totalMins / 60); |
| displayElement.innerHTML = `${padNumber(hrs)}:${padNumber(mins)}:${padNumber(secs)}:${padNumber(Math.floor(ms / 100), 2)}`; |
| } |
| function padNumber(number, length = 2) { |
| return number.toString().padStart(length, '0'); |
| } |
| function resetTimerButtons() { |
| document.getElementById('startCountdownButton').disabled = false; |
| document.getElementById('startTimerButton').disabled = false; |
| document.getElementById('pauseButton').disabled = true; |
| document.getElementById('stopButton').disabled = true; |
| const pBtn = document.getElementById('pauseButton'); |
| pBtn.innerText = "暂停"; |
| pBtn.onclick = pauseTimer; |
| timerCard.className = 'glass-card timer-card'; |
| isTimerPaused = false; |
| timerRemainingTime = 0; |
| timerInstance = null; |
| } |
| |
| let earningsInterval = null; |
| let earningsLastSoundAmount = 0; |
| let earningsTotalAmount = 0; |
| function startEarningsCalculation() { |
| if(earningsInterval) clearInterval(earningsInterval); |
| const principal = parseFloat(document.getElementById("principal").value); |
| const rate = parseFloat(document.getElementById("rate").value); |
| |
| if (isNaN(principal) || isNaN(rate)) return; |
| earningsTotalAmount = principal; |
| earningsLastSoundAmount = earningsTotalAmount; |
| const perSecondInterest = (principal * (rate / 100) / 365 / 24 / 60 / 60); |
| earningsInterval = setInterval(() => { |
| earningsTotalAmount += perSecondInterest; |
| document.getElementById("totalAmount").value = `收益: ${earningsTotalAmount.toFixed(6)} 元`; |
| updateEarningsDisplay(perSecondInterest); |
| let increaseAmount = earningsTotalAmount - earningsLastSoundAmount; |
| if (increaseAmount >= 10) { |
| playEarningsSound("bigWinSound"); |
| earningsLastSoundAmount = earningsTotalAmount; |
| } else if (increaseAmount >= 1) { |
| playEarningsSound("coinSound"); |
| earningsLastSoundAmount = earningsTotalAmount; |
| } |
| }, 1000); |
| } |
| function updateEarningsDisplay(perSecondInterest) { |
| const perMinute = perSecondInterest * 60; |
| const perHour = perMinute * 60; |
| const perDay = perHour * 24; |
| const perMonth = perDay * 30; |
| const perYear = perDay * 365; |
| document.getElementById("perHourEarnings").value = `每小时: ${perHour.toFixed(2)} 元`; |
| document.getElementById("perDayEarnings").value = `每天: ${perDay.toFixed(2)} 元`; |
| document.getElementById("perMonthEarnings").value = `每月: ${perMonth.toFixed(2)} 元`; |
| document.getElementById("perYearEarnings").value = `每年: ${perYear.toFixed(2)} 元`; |
| } |
| function playEarningsSound(soundId) { |
| const sound = document.getElementById(soundId); |
| if (sound) { |
| sound.pause(); |
| sound.currentTime = 0; |
| sound.play().catch(error => console.error("Audio play failed", error)); |
| } |
| } |
| function stopEarningsCalculation() { |
| clearInterval(earningsInterval); |
| earningsInterval = null; |
| } |
| document.getElementById("principal").addEventListener("input", () => { |
| stopEarningsCalculation(); |
| startEarningsCalculation(); |
| }); |
| document.getElementById("rate").addEventListener("input", () => { |
| stopEarningsCalculation(); |
| startEarningsCalculation(); |
| }); |
| |
| document.addEventListener('DOMContentLoaded', function () { |
| const monthsInput = document.getElementById('months'); |
| const rateInput = document.getElementById('rate'); |
| |
| if(monthsInput && rateInput) { |
| monthsInput.addEventListener('change', function () { |
| const selectedMonths = monthsInput.value; |
| if (typeof monthsToRate !== 'undefined') { |
| const selectedRate = monthsToRate[selectedMonths]; |
| if (selectedRate) { |
| rateInput.value = selectedRate; |
| stopEarningsCalculation(); |
| startEarningsCalculation(); |
| } |
| } |
| }); |
| } |
| |
| |
| startEarningsCalculation(); |
| }); |
| </script> |
| </body> |
| </html> |