60sapi接口搭建完毕,数据库连接测试成功,登录注册部分简单完成

This commit is contained in:
2025-09-02 19:45:50 +08:00
parent b139fb14d9
commit e1f8885c6c
150 changed files with 53045 additions and 8 deletions

View File

@@ -0,0 +1,190 @@
/* 背景样式文件 */
/* 主体背景 */
body {
background: linear-gradient(135deg, #e8f5e8 0%, #c8e6c9 50%, #a5d6a7 100%);
background-attachment: fixed;
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
}
/* 背景动画 */
@keyframes gradientShift {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
/* 装饰性背景元素 */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(circle at 20% 80%, rgba(76, 175, 80, 0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(129, 199, 132, 0.1) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(165, 214, 167, 0.08) 0%, transparent 50%);
pointer-events: none;
z-index: -1;
}
/* 浮动装饰圆点 */
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(2px 2px at 20px 30px, rgba(76, 175, 80, 0.3), transparent),
radial-gradient(2px 2px at 40px 70px, rgba(129, 199, 132, 0.2), transparent),
radial-gradient(1px 1px at 90px 40px, rgba(165, 214, 167, 0.3), transparent),
radial-gradient(1px 1px at 130px 80px, rgba(76, 175, 80, 0.2), transparent),
radial-gradient(2px 2px at 160px 30px, rgba(129, 199, 132, 0.3), transparent);
background-repeat: repeat;
background-size: 200px 100px;
animation: float 20s linear infinite;
pointer-events: none;
z-index: -1;
opacity: 0.6;
}
@keyframes float {
0% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
100% {
transform: translateY(0px);
}
}
/* 题目容器背景增强 */
.question-container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow:
0 8px 32px rgba(26, 77, 26, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
/* 错误容器背景 */
.error-container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
/* 结果容器背景 */
.result-container {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
/* 代码块背景 */
.code-block {
background: rgba(248, 249, 250, 0.9);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
/* 选项背景 */
.option {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
.option:hover {
background: rgba(76, 175, 80, 0.05);
}
.option.selected {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.15), rgba(76, 175, 80, 0.08));
}
/* 按钮背景增强 */
.submit-btn {
background: linear-gradient(135deg, #4caf50, #45a049);
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.2);
}
.show-answer-btn {
background: linear-gradient(135deg, #2196f3, #1976d2);
box-shadow: 0 4px 15px rgba(33, 150, 243, 0.2);
}
.retry-btn {
background: linear-gradient(135deg, #ff9800, #f57c00);
box-shadow: 0 4px 15px rgba(255, 152, 0, 0.2);
}
.refresh-btn {
background: linear-gradient(135deg, #4caf50, #45a049);
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.2);
}
/* 移动端背景优化 */
@media (max-width: 768px) {
body {
background-attachment: scroll;
}
body::after {
opacity: 0.4;
background-size: 150px 75px;
}
.question-container {
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
}
/* 高对比度模式支持 */
@media (prefers-contrast: high) {
body {
background: #f0f8f0;
}
body::before,
body::after {
display: none;
}
.question-container {
background: #ffffff;
border: 2px solid #4caf50;
}
}
/* 减少动画模式支持 */
@media (prefers-reduced-motion: reduce) {
body {
animation: none;
background: #e8f5e8;
}
body::after {
animation: none;
}
.refresh-btn:hover {
transform: scale(1.1);
}
}

View File

@@ -0,0 +1,597 @@
/* 基础样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
line-height: 1.6;
color: #2d5a27;
min-height: 100vh;
overflow-x: hidden;
}
/* 容器布局 */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* 头部样式 */
.header {
text-align: center;
margin-bottom: 40px;
padding: 30px 0;
}
.header h1 {
font-size: 2.5rem;
color: #1a4d1a;
margin-bottom: 10px;
font-weight: 700;
text-shadow: 0 2px 4px rgba(26, 77, 26, 0.1);
}
.subtitle {
font-size: 1.1rem;
color: #4a7c59;
opacity: 0.8;
}
/* 主内容区域 */
.main-content {
flex: 1;
display: flex;
justify-content: center;
align-items: flex-start;
}
/* 加载动画 */
.loading {
text-align: center;
padding: 60px 20px;
}
.spinner {
width: 50px;
height: 50px;
border: 4px solid #e8f5e8;
border-top: 4px solid #4caf50;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading p {
color: #4a7c59;
font-size: 1.1rem;
}
/* 题目容器 */
.question-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 40px;
box-shadow: 0 10px 30px rgba(26, 77, 26, 0.1);
border: 2px solid rgba(76, 175, 80, 0.2);
width: 100%;
max-width: 800px;
backdrop-filter: blur(10px);
}
/* 题目头部 */
.question-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 2px solid #e8f5e8;
}
.question-id {
font-size: 1.1rem;
color: #4caf50;
font-weight: 600;
background: linear-gradient(135deg, #e8f5e8, #c8e6c9);
padding: 8px 16px;
border-radius: 20px;
}
.refresh-btn {
background: linear-gradient(135deg, #4caf50, #45a049);
border: none;
border-radius: 50%;
width: 45px;
height: 45px;
color: white;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.refresh-btn:hover {
transform: rotate(180deg) scale(1.1);
box-shadow: 0 5px 15px rgba(76, 175, 80, 0.3);
}
/* 题目文本 */
.question-text h2 {
font-size: 1.5rem;
color: #1a4d1a;
margin-bottom: 25px;
text-align: center;
}
/* 代码块 */
.code-block {
background: #f8f9fa;
border: 2px solid #e8f5e8;
border-radius: 12px;
margin: 25px 0;
overflow-x: auto;
position: relative;
}
.code-block pre {
margin: 0;
padding: 25px;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.95rem;
line-height: 1.5;
color: #2d5a27;
white-space: pre-wrap;
word-wrap: break-word;
background: transparent !important;
}
.code-block code {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
background: transparent !important;
}
/* 代码高亮自定义样式 - 丰富的语法高亮 */
.code-block .hljs {
background: transparent !important;
color: #333333 !important;
}
/* JavaScript 关键字 - 蓝色 */
.code-block .hljs-keyword {
color: #0066cc !important;
font-weight: 600;
}
/* 字符串 - 绿色 */
.code-block .hljs-string {
color: #22aa22 !important;
}
/* 数字 - 橙色 */
.code-block .hljs-number {
color: #ff6600 !important;
}
/* 函数名 - 紫色 */
.code-block .hljs-function,
.code-block .hljs-title.function_ {
color: #9933cc !important;
font-weight: 600;
}
/* 变量名 - 深蓝色 */
.code-block .hljs-variable,
.code-block .hljs-name {
color: #0066aa !important;
}
/* 注释 - 灰色 */
.code-block .hljs-comment {
color: #888888 !important;
font-style: italic;
}
/* 内置对象和方法 - 深紫色 */
.code-block .hljs-built_in {
color: #663399 !important;
font-weight: 500;
}
/* 字面量 (true, false, null) - 红色 */
.code-block .hljs-literal {
color: #cc0000 !important;
font-weight: 600;
}
/* 操作符 - 深灰色 */
.code-block .hljs-operator {
color: #666666 !important;
}
/* 标点符号 - 深灰色 */
.code-block .hljs-punctuation {
color: #666666 !important;
}
/* 属性名 - 深蓝色 */
.code-block .hljs-property,
.code-block .hljs-attr {
color: #0066aa !important;
}
/* 类名和构造函数 - 深绿色 */
.code-block .hljs-title.class_,
.code-block .hljs-title {
color: #228833 !important;
font-weight: 600;
}
/* 参数 - 深蓝色 */
.code-block .hljs-params {
color: #0066aa !important;
}
/* 正则表达式 - 深红色 */
.code-block .hljs-regexp {
color: #aa0066 !important;
}
/* 模板字符串 - 深绿色 */
.code-block .hljs-template-variable,
.code-block .hljs-template-tag {
color: #228833 !important;
}
/* 选项容器 */
.options-container {
margin: 30px 0;
}
.option {
background: rgba(255, 255, 255, 0.8);
border: 2px solid #e8f5e8;
border-radius: 12px;
padding: 15px 20px;
margin: 12px 0;
cursor: pointer;
transition: all 0.3s ease;
font-size: 1rem;
position: relative;
overflow: hidden;
}
.option:hover {
border-color: #4caf50;
background: rgba(76, 175, 80, 0.05);
transform: translateX(5px);
}
.option.selected {
border-color: #4caf50;
background: linear-gradient(135deg, rgba(76, 175, 80, 0.1), rgba(76, 175, 80, 0.05));
color: #1a4d1a;
font-weight: 600;
}
.option.correct {
border-color: #4caf50;
background: linear-gradient(135deg, rgba(76, 175, 80, 0.2), rgba(76, 175, 80, 0.1));
color: #1a4d1a;
}
.option.incorrect {
border-color: #f44336;
background: linear-gradient(135deg, rgba(244, 67, 54, 0.1), rgba(244, 67, 54, 0.05));
color: #c62828;
}
/* 按钮样式 */
.action-buttons {
display: flex;
gap: 15px;
margin: 30px 0;
justify-content: center;
flex-wrap: wrap;
}
.submit-btn, .show-answer-btn, .retry-btn, .export-btn {
padding: 12px 30px;
border: none;
border-radius: 25px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
min-width: 120px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.submit-btn {
background: linear-gradient(135deg, #4caf50, #45a049);
color: white;
}
.submit-btn:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(76, 175, 80, 0.3);
}
.submit-btn:disabled {
background: #cccccc;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.show-answer-btn {
background: linear-gradient(135deg, #2196f3, #1976d2);
color: white;
}
.show-answer-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(33, 150, 243, 0.3);
}
.retry-btn {
background: linear-gradient(135deg, #ff9800, #f57c00);
color: white;
}
.retry-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(255, 152, 0, 0.3);
}
.export-btn {
background: linear-gradient(135deg, #9c27b0, #7b1fa2);
color: white;
}
.export-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(156, 39, 176, 0.3);
}
.export-btn svg {
width: 16px;
height: 16px;
}
/* 结果容器 */
.result-container {
margin-top: 30px;
padding: 25px;
background: rgba(255, 255, 255, 0.9);
border-radius: 15px;
border: 2px solid #e8f5e8;
}
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e8f5e8;
}
.result-status {
font-size: 1.2rem;
font-weight: 600;
}
.result-status.correct {
color: #4caf50;
}
.result-status.incorrect {
color: #f44336;
}
.correct-answer {
font-weight: 600;
color: #4caf50;
background: rgba(76, 175, 80, 0.1);
padding: 5px 12px;
border-radius: 15px;
}
.explanation {
color: #2d5a27;
line-height: 1.7;
font-size: 1rem;
}
.explanation pre {
background: #f8f9fa;
border: 1px solid #e8f5e8;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
overflow-x: auto;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
}
/* 错误容器 */
.error-container {
text-align: center;
padding: 60px 20px;
background: rgba(255, 255, 255, 0.9);
border-radius: 20px;
border: 2px solid rgba(244, 67, 54, 0.2);
max-width: 500px;
margin: 0 auto;
}
.error-icon {
font-size: 3rem;
margin-bottom: 20px;
}
.error-container h3 {
color: #f44336;
margin-bottom: 15px;
font-size: 1.5rem;
}
.error-container p {
color: #666;
margin-bottom: 25px;
font-size: 1.1rem;
}
/* 底部 */
.footer {
text-align: center;
padding: 30px 0;
margin-top: 40px;
color: #4a7c59;
opacity: 0.7;
border-top: 1px solid rgba(76, 175, 80, 0.2);
}
/* 平板端适配 (768px - 1024px) */
@media (max-width: 1024px) and (min-width: 768px) {
.container {
padding: 15px;
}
.header h1 {
font-size: 2.2rem;
}
.question-container {
padding: 30px;
}
.action-buttons {
flex-wrap: wrap;
}
}
/* 手机端适配 (最大768px) */
@media (max-width: 768px) {
.container {
padding: 10px;
}
.header {
margin-bottom: 25px;
padding: 20px 0;
}
.header h1 {
font-size: 1.8rem;
}
.subtitle {
font-size: 1rem;
}
.question-container {
padding: 20px;
border-radius: 15px;
}
.question-header {
flex-direction: column;
gap: 15px;
text-align: center;
}
.question-text h2 {
font-size: 1.3rem;
}
.code-block pre {
padding: 15px;
font-size: 0.85rem;
}
.option {
padding: 12px 15px;
font-size: 0.95rem;
}
.action-buttons {
flex-direction: column;
align-items: center;
}
.submit-btn, .show-answer-btn, .retry-btn {
width: 100%;
max-width: 200px;
}
.result-header {
flex-direction: column;
gap: 10px;
text-align: center;
}
.explanation {
font-size: 0.95rem;
}
.explanation pre {
padding: 10px;
font-size: 0.8rem;
}
}
/* 小屏手机适配 (最大480px) */
@media (max-width: 480px) {
.container {
padding: 8px;
}
.header h1 {
font-size: 1.6rem;
}
.question-container {
padding: 15px;
}
.question-id {
font-size: 1rem;
padding: 6px 12px;
}
.refresh-btn {
width: 40px;
height: 40px;
}
.code-block pre {
font-size: 0.8rem;
padding: 12px;
}
.option {
padding: 10px 12px;
font-size: 0.9rem;
}
.submit-btn, .show-answer-btn, .retry-btn {
padding: 10px 20px;
font-size: 0.95rem;
}
}

View File

@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>随机JavaScript趣味题</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/background.css">
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
<script src="https://cdn.bootcdn.net/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"></script>
</head>
<body>
<div class="container">
<header class="header">
<h1>JavaScript趣味题</h1>
<p class="subtitle">测试你的JavaScript知识</p>
</header>
<main class="main-content">
<div class="loading" id="loading">
<div class="spinner"></div>
<p>正在加载题目...</p>
</div>
<div class="question-container" id="questionContainer" style="display: none;">
<div class="question-header">
<span class="question-id" id="questionId">题目 #1</span>
<button class="refresh-btn" id="refreshBtn" title="获取新题目">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="23 4 23 10 17 10"></polyline>
<polyline points="1 20 1 14 7 14"></polyline>
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
</svg>
</button>
</div>
<div class="question-text" id="questionText">
<h2>输出是什么?</h2>
</div>
<div class="code-block" id="codeBlock">
<pre><code id="codeContent" class="language-javascript"></code></pre>
</div>
<div class="options-container" id="optionsContainer">
<!-- 选项将通过JavaScript动态生成 -->
</div>
<div class="action-buttons">
<button class="submit-btn" id="submitBtn" disabled>提交答案</button>
<button class="show-answer-btn" id="showAnswerBtn">查看答案</button>
<button class="export-btn" id="exportBtn" title="导出为Markdown文件">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
导出MD
</button>
</div>
<div class="result-container" id="resultContainer" style="display: none;">
<div class="result-header">
<span class="result-status" id="resultStatus"></span>
<span class="correct-answer" id="correctAnswer"></span>
</div>
<div class="explanation" id="explanation">
<!-- 解析内容 -->
</div>
</div>
</div>
<div class="error-container" id="errorContainer" style="display: none;">
<div class="error-icon">⚠️</div>
<h3>加载失败</h3>
<p id="errorMessage">网络连接异常,请稍后重试</p>
<button class="retry-btn" id="retryBtn">重新加载</button>
</div>
</main>
<footer class="footer">
<p>JavaScript趣味题集合</p>
</footer>
</div>
<script src="js/script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,565 @@
// JavaScript趣味题应用
class JSQuizApp {
constructor() {
this.apiEndpoints = [
'https://60s-cf.viki.moe',
'https://60s.viki.moe',
'https://60s.b23.run',
'https://60s.114128.xyz',
'https://60s-cf.114128.xyz'
];
this.currentApiIndex = 0;
this.currentQuestion = null;
this.selectedOption = null;
this.isAnswered = false;
this.initElements();
this.bindEvents();
this.loadQuestion();
}
// 初始化DOM元素
initElements() {
this.elements = {
loading: document.getElementById('loading'),
questionContainer: document.getElementById('questionContainer'),
errorContainer: document.getElementById('errorContainer'),
questionId: document.getElementById('questionId'),
questionText: document.getElementById('questionText'),
codeContent: document.getElementById('codeContent'),
optionsContainer: document.getElementById('optionsContainer'),
submitBtn: document.getElementById('submitBtn'),
showAnswerBtn: document.getElementById('showAnswerBtn'),
refreshBtn: document.getElementById('refreshBtn'),
retryBtn: document.getElementById('retryBtn'),
exportBtn: document.getElementById('exportBtn'),
resultContainer: document.getElementById('resultContainer'),
resultStatus: document.getElementById('resultStatus'),
correctAnswer: document.getElementById('correctAnswer'),
explanation: document.getElementById('explanation'),
errorMessage: document.getElementById('errorMessage')
};
}
// 绑定事件
bindEvents() {
this.elements.submitBtn.addEventListener('click', () => this.submitAnswer());
this.elements.showAnswerBtn.addEventListener('click', () => this.showAnswer());
this.elements.refreshBtn.addEventListener('click', () => this.loadQuestion());
this.elements.retryBtn.addEventListener('click', () => this.loadQuestion());
this.elements.exportBtn.addEventListener('click', () => this.exportToMarkdown());
}
// 显示加载状态
showLoading() {
this.elements.loading.style.display = 'block';
this.elements.questionContainer.style.display = 'none';
this.elements.errorContainer.style.display = 'none';
}
// 显示题目
showQuestion() {
this.elements.loading.style.display = 'none';
this.elements.questionContainer.style.display = 'block';
this.elements.errorContainer.style.display = 'none';
}
// 显示错误
showError(message) {
this.elements.loading.style.display = 'none';
this.elements.questionContainer.style.display = 'none';
this.elements.errorContainer.style.display = 'block';
this.elements.errorMessage.textContent = message;
}
// 获取当前API地址
getCurrentApiUrl() {
return `${this.apiEndpoints[this.currentApiIndex]}/v2/awesome-js`;
}
// 切换到下一个API
switchToNextApi() {
this.currentApiIndex = (this.currentApiIndex + 1) % this.apiEndpoints.length;
}
// 加载题目
async loadQuestion() {
this.showLoading();
this.resetQuestion();
let attempts = 0;
const maxAttempts = this.apiEndpoints.length;
while (attempts < maxAttempts) {
try {
const response = await fetch(this.getCurrentApiUrl(), {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
timeout: 10000
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.code === 200 && data.data) {
this.currentQuestion = data.data;
this.displayQuestion();
return;
} else {
throw new Error(data.message || '数据格式错误');
}
} catch (error) {
console.warn(`API ${this.getCurrentApiUrl()} 请求失败:`, error.message);
attempts++;
if (attempts < maxAttempts) {
this.switchToNextApi();
console.log(`切换到备用API: ${this.getCurrentApiUrl()}`);
} else {
this.showError(`所有API接口都无法访问请检查网络连接后重试。\n最后尝试的错误: ${error.message}`);
return;
}
}
}
}
// 重置题目状态
resetQuestion() {
this.selectedOption = null;
this.isAnswered = false;
this.elements.resultContainer.style.display = 'none';
this.elements.submitBtn.disabled = true;
this.elements.submitBtn.textContent = '提交答案';
this.elements.showAnswerBtn.style.display = 'inline-block';
// 清空选项容器,防止重复显示
this.elements.optionsContainer.innerHTML = '';
// 移除所有选项的事件监听器
const existingOptions = document.querySelectorAll('.option');
existingOptions.forEach(option => {
option.removeEventListener('click', this.selectOption);
});
}
// 显示题目内容
displayQuestion() {
const question = this.currentQuestion;
console.log('显示题目:', question);
// 设置题目ID
this.elements.questionId.textContent = `题目 #${question.id}`;
// 设置题目文本
this.elements.questionText.innerHTML = `<h2>${this.escapeHtml(question.question)}</h2>`;
// 设置代码内容并应用语法高亮
this.elements.codeContent.textContent = question.code;
// 应用语法高亮
if (typeof hljs !== 'undefined') {
hljs.highlightElement(this.elements.codeContent);
}
// 确保选项容器已清空
this.elements.optionsContainer.innerHTML = '';
// 生成选项
this.generateOptions(question.options);
this.showQuestion();
}
// 生成选项
generateOptions(options) {
// 确保清空容器
this.elements.optionsContainer.innerHTML = '';
// 验证选项数据
if (!Array.isArray(options) || options.length === 0) {
console.error('选项数据无效:', options);
return;
}
// 移除可能存在的重复选项
const uniqueOptions = [...new Set(options)];
uniqueOptions.forEach((option, index) => {
const optionElement = document.createElement('div');
optionElement.className = 'option';
optionElement.textContent = option;
optionElement.dataset.index = index;
optionElement.dataset.value = option.charAt(0); // A, B, C, D
optionElement.addEventListener('click', () => this.selectOption(optionElement));
this.elements.optionsContainer.appendChild(optionElement);
});
console.log('生成选项:', uniqueOptions);
}
// 选择选项
selectOption(optionElement) {
if (this.isAnswered) return;
// 移除之前的选中状态
document.querySelectorAll('.option.selected').forEach(el => {
el.classList.remove('selected');
});
// 设置当前选中
optionElement.classList.add('selected');
this.selectedOption = optionElement.dataset.value;
// 启用提交按钮
this.elements.submitBtn.disabled = false;
}
// 提交答案
submitAnswer() {
if (!this.selectedOption || this.isAnswered) return;
this.isAnswered = true;
this.elements.submitBtn.disabled = true;
this.elements.submitBtn.textContent = '已提交';
this.elements.showAnswerBtn.style.display = 'none';
const isCorrect = this.selectedOption === this.currentQuestion.answer;
// 显示结果
this.showResult(isCorrect);
// 标记选项
this.markOptions();
}
// 显示答案
showAnswer() {
this.isAnswered = true;
this.elements.submitBtn.disabled = true;
this.elements.submitBtn.textContent = '已显示答案';
this.elements.showAnswerBtn.style.display = 'none';
// 显示结果(不判断对错)
this.showResult(null);
// 标记正确答案
this.markCorrectAnswer();
}
// 显示结果
showResult(isCorrect) {
const resultContainer = this.elements.resultContainer;
const resultStatus = this.elements.resultStatus;
const correctAnswer = this.elements.correctAnswer;
const explanation = this.elements.explanation;
// 设置结果状态
if (isCorrect === true) {
resultStatus.textContent = '✅ 回答正确!';
resultStatus.className = 'result-status correct';
} else if (isCorrect === false) {
resultStatus.textContent = '❌ 回答错误';
resultStatus.className = 'result-status incorrect';
} else {
resultStatus.textContent = '💡 答案解析';
resultStatus.className = 'result-status';
}
// 设置正确答案
correctAnswer.textContent = `正确答案: ${this.currentQuestion.answer}`;
// 设置解析内容
explanation.innerHTML = this.formatExplanation(this.currentQuestion.explanation);
// 显示结果容器
resultContainer.style.display = 'block';
// 滚动到结果区域
setTimeout(() => {
resultContainer.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 100);
}
// 标记选项
markOptions() {
const options = document.querySelectorAll('.option');
const correctAnswer = this.currentQuestion.answer;
options.forEach(option => {
const optionValue = option.dataset.value;
if (optionValue === correctAnswer) {
option.classList.add('correct');
} else if (option.classList.contains('selected')) {
option.classList.add('incorrect');
}
// 禁用点击
option.style.pointerEvents = 'none';
});
}
// 标记正确答案
markCorrectAnswer() {
const options = document.querySelectorAll('.option');
const correctAnswer = this.currentQuestion.answer;
options.forEach(option => {
const optionValue = option.dataset.value;
if (optionValue === correctAnswer) {
option.classList.add('correct');
}
// 禁用点击
option.style.pointerEvents = 'none';
});
}
// 格式化解析内容
formatExplanation(explanation) {
// 转义HTML
let formatted = this.escapeHtml(explanation);
// 处理代码块
formatted = formatted.replace(/```js\n([\s\S]*?)\n```/g, '<pre><code>$1</code></pre>');
formatted = formatted.replace(/```javascript\n([\s\S]*?)\n```/g, '<pre><code>$1</code></pre>');
formatted = formatted.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
// 处理行内代码
formatted = formatted.replace(/`([^`]+)`/g, '<code style="background: #f0f0f0; padding: 2px 4px; border-radius: 3px; font-family: monospace;">$1</code>');
// 处理换行
formatted = formatted.replace(/\n\n/g, '</p><p>');
formatted = formatted.replace(/\n/g, '<br>');
// 包装段落
if (!formatted.includes('<p>')) {
formatted = '<p>' + formatted + '</p>';
}
return formatted;
}
// HTML转义
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 导出为Markdown
exportToMarkdown() {
if (!this.currentQuestion) {
alert('请先加载题目后再导出!');
return;
}
const question = this.currentQuestion;
const timestamp = new Date().toLocaleString('zh-CN');
// 构建Markdown内容
let markdown = `# JavaScript趣味题 #${question.id}\n\n`;
markdown += `> 导出时间: ${timestamp}\n\n`;
// 题目部分
markdown += `## 题目\n\n`;
markdown += `${question.question}\n\n`;
// 代码部分
markdown += `## 代码\n\n`;
markdown += `\`\`\`javascript\n${question.code}\n\`\`\`\n\n`;
// 选项部分
markdown += `## 选项\n\n`;
question.options.forEach((option, index) => {
const letter = String.fromCharCode(65 + index); // A, B, C, D
const isCorrect = letter === question.answer;
markdown += `${letter}. ${option}${isCorrect ? ' ✅' : ''}\n`;
});
markdown += `\n`;
// 答案部分
markdown += `## 正确答案\n\n`;
markdown += `**${question.answer}**\n\n`;
// 解析部分
markdown += `## 答案解析\n\n`;
// 清理解析内容中的HTML标签转换为Markdown格式
let explanation = question.explanation;
explanation = explanation.replace(/<br\s*\/?>/gi, '\n');
explanation = explanation.replace(/<p>/gi, '\n');
explanation = explanation.replace(/<\/p>/gi, '\n');
explanation = explanation.replace(/<code[^>]*>/gi, '`');
explanation = explanation.replace(/<\/code>/gi, '`');
explanation = explanation.replace(/<pre><code>/gi, '\n```javascript\n');
explanation = explanation.replace(/<\/code><\/pre>/gi, '\n```\n');
explanation = explanation.replace(/<[^>]*>/g, ''); // 移除其他HTML标签
explanation = explanation.replace(/\n\s*\n/g, '\n\n'); // 清理多余空行
markdown += explanation.trim() + '\n\n';
// 添加页脚
markdown += `---\n\n`;
markdown += `*本题目来源于JavaScript趣味题集合*\n`;
markdown += `*导出工具: JavaScript趣味题网页版*\n`;
// 创建下载
this.downloadMarkdown(markdown, `JavaScript趣味题_${question.id}_${new Date().getTime()}.md`);
}
// 下载Markdown文件
downloadMarkdown(content, filename) {
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 清理URL对象
setTimeout(() => {
URL.revokeObjectURL(url);
}, 100);
// 显示成功提示
this.showExportSuccess(filename);
}
// 显示导出成功提示
showExportSuccess(filename) {
// 创建临时提示元素
const toast = document.createElement('div');
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: linear-gradient(135deg, #4caf50, #45a049);
color: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
z-index: 10000;
font-size: 14px;
max-width: 300px;
word-wrap: break-word;
animation: slideInRight 0.3s ease-out;
`;
toast.innerHTML = `
<div style="display: flex; align-items: center; gap: 8px;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
<div>
<div style="font-weight: 600;">导出成功!</div>
<div style="font-size: 12px; opacity: 0.9; margin-top: 2px;">${filename}</div>
</div>
</div>
`;
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOutRight {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);
document.body.appendChild(toast);
// 3秒后自动消失
setTimeout(() => {
toast.style.animation = 'slideOutRight 0.3s ease-in';
setTimeout(() => {
if (toast.parentNode) {
document.body.removeChild(toast);
}
if (style.parentNode) {
document.head.removeChild(style);
}
}, 300);
}, 3000);
}
}
// 页面加载完成后初始化应用
document.addEventListener('DOMContentLoaded', () => {
new JSQuizApp();
});
// 添加键盘快捷键支持
document.addEventListener('keydown', (e) => {
// 按R键刷新题目
if (e.key.toLowerCase() === 'r' && !e.ctrlKey && !e.metaKey) {
const refreshBtn = document.getElementById('refreshBtn');
if (refreshBtn && !document.querySelector('.loading').style.display !== 'none') {
refreshBtn.click();
}
}
// 按数字键1-4选择选项
if (['1', '2', '3', '4'].includes(e.key)) {
const options = document.querySelectorAll('.option');
const index = parseInt(e.key) - 1;
if (options[index] && !options[index].style.pointerEvents) {
options[index].click();
}
}
// 按Enter键提交答案
if (e.key === 'Enter') {
const submitBtn = document.getElementById('submitBtn');
if (submitBtn && !submitBtn.disabled) {
submitBtn.click();
}
}
});
// 添加触摸设备支持
if ('ontouchstart' in window) {
document.addEventListener('touchstart', () => {}, { passive: true });
}
// 添加网络状态监听
if ('navigator' in window && 'onLine' in navigator) {
window.addEventListener('online', () => {
console.log('网络连接已恢复');
});
window.addEventListener('offline', () => {
console.log('网络连接已断开');
});
}

View File

@@ -0,0 +1,7 @@
[
"https://60s-cf.viki.moe",
"https://60s.viki.moe",
"https://60s.b23.run",
"https://60s.114128.xyz",
"https://60s-cf.114128.xyz"
]

View File

@@ -0,0 +1,17 @@
{
"code": 200,
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s反馈群 595941841",
"data": {
"id": 11,
"question": "输出是什么?",
"code": "function Person(firstName, lastName) {\n this.firstName = firstName;\n this.lastName = lastName;\n}\n\nconst member = new Person(\"Lydia\", \"Hallie\");\nPerson.getFullName = function () {\n return `${this.firstName} ${this.lastName}`;\n}\n\nconsole.log(member.getFullName());",
"options": [
"A: `TypeError`",
"B: `SyntaxError`",
"C: `Lydia Hallie`",
"D: `undefined` `undefined`"
],
"answer": "A",
"explanation": "你不能像常规对象那样,给构造函数添加属性。如果你想一次性给所有实例添加特性,你应该使用原型。因此本例中,使用如下方式:\n\n```js\nPerson.prototype.getFullName = function () {\n return `${this.firstName} ${this.lastName}`;\n}\n```\n\n这才会使 `member.getFullName()` 起作用。为什么这么做有益的?假设我们将这个方法添加到构造函数本身里。也许不是每个 `Person` 实例都需要这个方法。这将浪费大量内存空间,因为它们仍然具有该属性,这将占用每个实例的内存空间。相反,如果我们只将它添加到原型中,那么它只存在于内存中的一个位置,但是所有实例都可以访问它!"
}
}