Merge branch 'main' of https://github.com/shumengya/InfoGenie
This commit is contained in:
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
233
frontend/60sapi/实用功能/公网IP地址/css/background.css
Normal file
233
frontend/60sapi/实用功能/公网IP地址/css/background.css
Normal file
@@ -0,0 +1,233 @@
|
||||
/* 动态背景样式 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
|
||||
z-index: -1;
|
||||
animation: backgroundMove 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes backgroundMove {
|
||||
0%, 100% {
|
||||
background:
|
||||
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
|
||||
}
|
||||
25% {
|
||||
background:
|
||||
radial-gradient(circle at 60% 30%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 30% 70%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 80%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
|
||||
}
|
||||
50% {
|
||||
background:
|
||||
radial-gradient(circle at 80% 60%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 20% 30%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 60% 70%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
|
||||
}
|
||||
75% {
|
||||
background:
|
||||
radial-gradient(circle at 40% 90%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 70% 10%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 20% 60%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 浮动粒子效果 */
|
||||
.particles {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 50%;
|
||||
animation: float 15s infinite linear;
|
||||
}
|
||||
|
||||
.particle:nth-child(1) {
|
||||
left: 10%;
|
||||
animation-delay: 0s;
|
||||
animation-duration: 12s;
|
||||
}
|
||||
|
||||
.particle:nth-child(2) {
|
||||
left: 20%;
|
||||
animation-delay: 2s;
|
||||
animation-duration: 18s;
|
||||
}
|
||||
|
||||
.particle:nth-child(3) {
|
||||
left: 30%;
|
||||
animation-delay: 4s;
|
||||
animation-duration: 15s;
|
||||
}
|
||||
|
||||
.particle:nth-child(4) {
|
||||
left: 40%;
|
||||
animation-delay: 6s;
|
||||
animation-duration: 20s;
|
||||
}
|
||||
|
||||
.particle:nth-child(5) {
|
||||
left: 50%;
|
||||
animation-delay: 8s;
|
||||
animation-duration: 14s;
|
||||
}
|
||||
|
||||
.particle:nth-child(6) {
|
||||
left: 60%;
|
||||
animation-delay: 10s;
|
||||
animation-duration: 16s;
|
||||
}
|
||||
|
||||
.particle:nth-child(7) {
|
||||
left: 70%;
|
||||
animation-delay: 12s;
|
||||
animation-duration: 22s;
|
||||
}
|
||||
|
||||
.particle:nth-child(8) {
|
||||
left: 80%;
|
||||
animation-delay: 14s;
|
||||
animation-duration: 13s;
|
||||
}
|
||||
|
||||
.particle:nth-child(9) {
|
||||
left: 90%;
|
||||
animation-delay: 16s;
|
||||
animation-duration: 19s;
|
||||
}
|
||||
|
||||
.particle:nth-child(10) {
|
||||
left: 15%;
|
||||
animation-delay: 18s;
|
||||
animation-duration: 17s;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translateY(100vh) rotate(0deg);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100px) rotate(360deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 网格背景效果 */
|
||||
.grid-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 255, 255, 0.1) 1px, transparent 1px);
|
||||
background-size: 50px 50px;
|
||||
z-index: -1;
|
||||
opacity: 0.3;
|
||||
animation: gridMove 30s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes gridMove {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate(50px, 50px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 光晕效果 */
|
||||
.glow-effect {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: -1;
|
||||
animation: pulse 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 768px) {
|
||||
.grid-background {
|
||||
background-size: 30px 30px;
|
||||
}
|
||||
|
||||
.glow-effect {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.particle {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.grid-background {
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
.glow-effect {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.particle {
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
}
|
||||
}
|
||||
445
frontend/60sapi/实用功能/公网IP地址/css/style.css
Normal file
445
frontend/60sapi/实用功能/公网IP地址/css/style.css
Normal file
@@ -0,0 +1,445 @@
|
||||
/* 全局样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 容器样式 */
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
padding: 3rem 2rem 2rem;
|
||||
background: linear-gradient(135deg, rgba(74, 144, 226, 0.1), rgba(80, 200, 120, 0.1));
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #4a90e2, #50c878);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 0.5rem;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header h1 i {
|
||||
margin-right: 0.5rem;
|
||||
background: linear-gradient(135deg, #4a90e2, #50c878);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: #666;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 2rem;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 查询按钮区域 */
|
||||
.query-section {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.query-btn {
|
||||
background: linear-gradient(135deg, #4a90e2, #50c878);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 1rem 2rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(74, 144, 226, 0.3);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
min-width: 200px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.query-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(74, 144, 226, 0.4);
|
||||
background: linear-gradient(135deg, #3a7bc8, #40a868);
|
||||
}
|
||||
|
||||
.query-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.query-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #4a90e2;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 1rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading p {
|
||||
color: #666;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* IP信息卡片 */
|
||||
.ip-info {
|
||||
animation: fadeInUp 0.6s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.ip-card {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.ip-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.ip-header i {
|
||||
font-size: 1.5rem;
|
||||
color: #4a90e2;
|
||||
}
|
||||
|
||||
.ip-header h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.ip-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
background: linear-gradient(135deg, rgba(74, 144, 226, 0.1), rgba(80, 200, 120, 0.1));
|
||||
border-radius: 15px;
|
||||
border: 2px solid rgba(74, 144, 226, 0.2);
|
||||
}
|
||||
|
||||
.ip-address {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #2c3e50;
|
||||
background: linear-gradient(135deg, #4a90e2, #50c878);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: #4a90e2;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 1rem;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: #3a7bc8;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.ip-details {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background: rgba(248, 249, 250, 0.8);
|
||||
border-radius: 10px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.detail-item:hover {
|
||||
background: rgba(74, 144, 226, 0.1);
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.detail-item i {
|
||||
color: #4a90e2;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.detail-item .label {
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.detail-item .value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* IP地址说明区域 */
|
||||
.ip-explanation {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.ip-explanation h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.3rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.ip-explanation h3 i {
|
||||
color: #4a90e2;
|
||||
}
|
||||
|
||||
.ip-explanation p {
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.features {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
background: rgba(248, 249, 250, 0.8);
|
||||
border-radius: 12px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.feature-item:hover {
|
||||
background: rgba(74, 144, 226, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.feature-item i {
|
||||
color: #4a90e2;
|
||||
font-size: 1.5rem;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.feature-item h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.feature-item p {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 错误信息 */
|
||||
.error-message {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 99, 99, 0.3);
|
||||
animation: fadeInUp 0.6s ease;
|
||||
}
|
||||
|
||||
.error-message i {
|
||||
font-size: 3rem;
|
||||
color: #ff6b6b;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-message p {
|
||||
color: #666;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
background: #ff6b6b;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background: #ff5252;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background: rgba(248, 249, 250, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.header {
|
||||
padding: 2rem 1rem 1.5rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.ip-card, .ip-explanation {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.ip-address {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.ip-display {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.features {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.detail-item .label {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.query-btn {
|
||||
padding: 0.875rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.ip-address {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.ip-card, .ip-explanation {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
139
frontend/60sapi/实用功能/公网IP地址/index.html
Normal file
139
frontend/60sapi/实用功能/公网IP地址/index.html
Normal file
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>公网IP地址查询</title>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/background.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- 头部 -->
|
||||
<header class="header">
|
||||
<h1><i class="fas fa-globe"></i> 公网IP地址查询</h1>
|
||||
<p class="subtitle">快速获取您的公网IP地址信息</p>
|
||||
</header>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<main class="main-content">
|
||||
<!-- 查询按钮区域 -->
|
||||
<div class="query-section">
|
||||
<button id="queryBtn" class="query-btn">
|
||||
<i class="fas fa-search"></i>
|
||||
<span>查询我的IP地址</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 加载动画 -->
|
||||
<div id="loading" class="loading" style="display: none;">
|
||||
<div class="spinner"></div>
|
||||
<p>正在获取IP地址信息...</p>
|
||||
</div>
|
||||
|
||||
<!-- IP信息展示区域 -->
|
||||
<div id="ip-info" class="ip-info" style="display: none;">
|
||||
<div class="ip-card">
|
||||
<div class="ip-header">
|
||||
<i class="fas fa-network-wired"></i>
|
||||
<h2>您的公网IP地址</h2>
|
||||
</div>
|
||||
<div class="ip-display">
|
||||
<span id="ip-address" class="ip-address">---.---.---.---</span>
|
||||
<button id="copyBtn" class="copy-btn" title="复制IP地址">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="ip-details">
|
||||
<div class="detail-item">
|
||||
<i class="fas fa-clock"></i>
|
||||
<span class="label">查询时间:</span>
|
||||
<span id="query-time" class="value">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="fas fa-server"></i>
|
||||
<span class="label">数据来源:</span>
|
||||
<span class="value">60s.viki.moe</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<span class="label">位置信息:</span>
|
||||
<span id="location" class="value">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="fas fa-building"></i>
|
||||
<span class="label">网络服务商:</span>
|
||||
<span id="isp" class="value">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="fas fa-flag"></i>
|
||||
<span class="label">国家:</span>
|
||||
<span id="country" class="value">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="fas fa-map"></i>
|
||||
<span class="label">地区:</span>
|
||||
<span id="region" class="value">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="fas fa-city"></i>
|
||||
<span class="label">城市:</span>
|
||||
<span id="city" class="value">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<i class="fas fa-clock"></i>
|
||||
<span class="label">时区:</span>
|
||||
<span id="timezone" class="value">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IP地址信息说明 -->
|
||||
<div class="ip-explanation">
|
||||
<h3><i class="fas fa-info-circle"></i> 什么是公网IP地址?</h3>
|
||||
<p>公网IP地址是您的设备在互联网上的唯一标识符,由您的网络服务提供商(ISP)分配。通过这个地址,互联网上的其他设备可以找到并与您的设备通信。</p>
|
||||
|
||||
<div class="features">
|
||||
<div class="feature-item">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
<div>
|
||||
<h4>隐私保护</h4>
|
||||
<p>了解您的IP地址有助于保护网络隐私</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<div>
|
||||
<h4>地理位置</h4>
|
||||
<p>IP地址可以大致确定您的地理位置</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<i class="fas fa-cogs"></i>
|
||||
<div>
|
||||
<h4>网络配置</h4>
|
||||
<p>用于网络故障排除和配置</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 错误信息 -->
|
||||
<div id="error-message" class="error-message" style="display: none;">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<p>获取IP地址失败,请检查网络连接或稍后重试</p>
|
||||
<button id="retryBtn" class="retry-btn">重试</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer">
|
||||
<p>© 2024 公网IP地址查询工具 | 数据来源: 60s.viki.moe</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
315
frontend/60sapi/实用功能/公网IP地址/js/script.js
Normal file
315
frontend/60sapi/实用功能/公网IP地址/js/script.js
Normal file
@@ -0,0 +1,315 @@
|
||||
// 公网IP地址查询应用
|
||||
class IPQueryApp {
|
||||
constructor() {
|
||||
this.apiEndpoint = 'https://60s.viki.moe/v2/ip';
|
||||
this.init();
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.createParticles();
|
||||
this.createBackgroundElements();
|
||||
console.log('IP查询应用初始化完成');
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
const queryBtn = document.getElementById('queryBtn');
|
||||
const retryBtn = document.getElementById('retryBtn');
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
|
||||
if (queryBtn) {
|
||||
queryBtn.addEventListener('click', () => this.queryIP());
|
||||
}
|
||||
|
||||
if (retryBtn) {
|
||||
retryBtn.addEventListener('click', () => this.queryIP());
|
||||
}
|
||||
|
||||
if (copyBtn) {
|
||||
copyBtn.addEventListener('click', () => this.copyIP());
|
||||
}
|
||||
|
||||
// 页面加载完成后自动查询一次
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
setTimeout(() => this.queryIP(), 500);
|
||||
});
|
||||
}
|
||||
|
||||
// 创建浮动粒子
|
||||
createParticles() {
|
||||
const particlesContainer = document.createElement('div');
|
||||
particlesContainer.className = 'particles';
|
||||
document.body.appendChild(particlesContainer);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.className = 'particle';
|
||||
particlesContainer.appendChild(particle);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建背景元素
|
||||
createBackgroundElements() {
|
||||
// 创建网格背景
|
||||
const gridBackground = document.createElement('div');
|
||||
gridBackground.className = 'grid-background';
|
||||
document.body.appendChild(gridBackground);
|
||||
|
||||
// 创建光晕效果
|
||||
const glowEffect = document.createElement('div');
|
||||
glowEffect.className = 'glow-effect';
|
||||
document.body.appendChild(glowEffect);
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
showLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
const ipInfo = document.getElementById('ipInfo');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
const queryBtn = document.getElementById('queryBtn');
|
||||
|
||||
if (loading) loading.style.display = 'block';
|
||||
if (ipInfo) ipInfo.style.display = 'none';
|
||||
if (errorMessage) errorMessage.style.display = 'none';
|
||||
if (queryBtn) {
|
||||
queryBtn.disabled = true;
|
||||
queryBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 查询中...';
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏加载状态
|
||||
hideLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
const queryBtn = document.getElementById('queryBtn');
|
||||
|
||||
if (loading) loading.style.display = 'none';
|
||||
if (queryBtn) {
|
||||
queryBtn.disabled = false;
|
||||
queryBtn.innerHTML = '<i class="fas fa-search"></i> 查询我的IP';
|
||||
}
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
showError(message) {
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
const ipInfo = document.getElementById('ip-info');
|
||||
|
||||
if (errorMessage) {
|
||||
errorMessage.style.display = 'block';
|
||||
const errorText = errorMessage.querySelector('p');
|
||||
if (errorText) errorText.textContent = message || '获取IP信息失败,请稍后重试';
|
||||
}
|
||||
if (ipInfo) ipInfo.style.display = 'none';
|
||||
|
||||
this.hideLoading();
|
||||
}
|
||||
|
||||
// 查询IP地址
|
||||
async queryIP() {
|
||||
try {
|
||||
this.showLoading();
|
||||
console.log('开始查询IP地址...');
|
||||
|
||||
const response = await fetch(this.apiEndpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('API响应状态:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP错误: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('API返回数据:', data);
|
||||
|
||||
if (data.code === 200 && data.data) {
|
||||
this.displayIPInfo(data.data);
|
||||
} else {
|
||||
throw new Error(data.message || '获取IP信息失败');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询IP失败:', error);
|
||||
this.showError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示IP信息
|
||||
displayIPInfo(data) {
|
||||
const ipInfo = document.getElementById('ip-info');
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
|
||||
// 更新IP地址显示
|
||||
const ipAddressElement = document.getElementById('ip-address');
|
||||
if (ipAddressElement && data.ip) {
|
||||
ipAddressElement.textContent = data.ip;
|
||||
}
|
||||
|
||||
// 更新查询时间
|
||||
const queryTimeElement = document.getElementById('query-time');
|
||||
if (queryTimeElement) {
|
||||
const now = new Date();
|
||||
queryTimeElement.textContent = now.toLocaleString('zh-CN');
|
||||
}
|
||||
|
||||
// 更新详细信息
|
||||
this.updateDetailItem('location', data.location || '未知');
|
||||
this.updateDetailItem('isp', data.isp || '未知');
|
||||
this.updateDetailItem('country', data.country || '未知');
|
||||
this.updateDetailItem('region', data.region || '未知');
|
||||
this.updateDetailItem('city', data.city || '未知');
|
||||
this.updateDetailItem('timezone', data.timezone || '未知');
|
||||
|
||||
// 显示IP信息,隐藏错误信息
|
||||
if (ipInfo) ipInfo.style.display = 'block';
|
||||
if (errorMessage) errorMessage.style.display = 'none';
|
||||
|
||||
this.hideLoading();
|
||||
console.log('IP信息显示完成');
|
||||
}
|
||||
|
||||
// 更新详细信息项
|
||||
updateDetailItem(id, value) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
}
|
||||
}
|
||||
|
||||
// 复制IP地址
|
||||
async copyIP() {
|
||||
const ipAddressElement = document.getElementById('ip-address');
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
|
||||
if (!ipAddressElement || !ipAddressElement.textContent) {
|
||||
this.showToast('没有可复制的IP地址', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(ipAddressElement.textContent);
|
||||
|
||||
// 更新按钮状态
|
||||
if (copyBtn) {
|
||||
const originalHTML = copyBtn.innerHTML;
|
||||
copyBtn.innerHTML = '<i class="fas fa-check"></i>';
|
||||
copyBtn.style.background = '#50c878';
|
||||
|
||||
setTimeout(() => {
|
||||
copyBtn.innerHTML = originalHTML;
|
||||
copyBtn.style.background = '#4a90e2';
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
this.showToast('IP地址已复制到剪贴板', 'success');
|
||||
console.log('IP地址已复制:', ipAddressElement.textContent);
|
||||
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error);
|
||||
this.showToast('复制失败,请手动选择复制', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示提示消息
|
||||
showToast(message, type = 'info') {
|
||||
// 移除已存在的toast
|
||||
const existingToast = document.querySelector('.toast');
|
||||
if (existingToast) {
|
||||
existingToast.remove();
|
||||
}
|
||||
|
||||
// 创建新的toast
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.innerHTML = `
|
||||
<i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i>
|
||||
<span>${message}</span>
|
||||
`;
|
||||
|
||||
// 添加toast样式
|
||||
toast.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: ${type === 'success' ? '#50c878' : type === 'error' ? '#ff6b6b' : '#4a90e2'};
|
||||
color: white;
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-weight: 500;
|
||||
animation: slideInRight 0.3s ease;
|
||||
max-width: 300px;
|
||||
`;
|
||||
|
||||
// 添加动画样式
|
||||
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';
|
||||
setTimeout(() => {
|
||||
if (toast.parentNode) {
|
||||
toast.remove();
|
||||
}
|
||||
if (style.parentNode) {
|
||||
style.remove();
|
||||
}
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
formatTime(timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
const app = new IPQueryApp();
|
||||
|
||||
// 导出到全局作用域(用于调试)
|
||||
window.IPQueryApp = app;
|
||||
3
frontend/60sapi/实用功能/公网IP地址/接口集合.json
Normal file
3
frontend/60sapi/实用功能/公网IP地址/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.viki.moe/v2/ip"
|
||||
]
|
||||
0
frontend/60sapi/实用功能/公网IP地址/返回接口.json
Normal file
0
frontend/60sapi/实用功能/公网IP地址/返回接口.json
Normal file
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
561
frontend/60sapi/实用功能/哈希解压压缩/css/style.css
Normal file
561
frontend/60sapi/实用功能/哈希解压压缩/css/style.css
Normal file
@@ -0,0 +1,561 @@
|
||||
/* Reset and Base Styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
color: #333;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Header Styles */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 24px;
|
||||
padding: 40px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header-content::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(255, 255, 255, 0.1) 100%);
|
||||
animation: shimmer 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0%, 100% { transform: translateX(-100%); }
|
||||
50% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.logo i {
|
||||
font-size: 48px;
|
||||
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-size: 42px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* Floating Shapes */
|
||||
.header-decoration {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.floating-shapes {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.shape {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.shape-1 {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
top: 10%;
|
||||
left: 10%;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.shape-2 {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
top: 20%;
|
||||
right: 15%;
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.shape-3 {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
bottom: 15%;
|
||||
left: 20%;
|
||||
animation-delay: 4s;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||||
33% { transform: translateY(-20px) rotate(120deg); }
|
||||
66% { transform: translateY(10px) rotate(240deg); }
|
||||
}
|
||||
|
||||
/* Input Section */
|
||||
.input-section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.input-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.input-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header i {
|
||||
font-size: 24px;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.card-header h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#inputText {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
border: 2px solid #e1e5e9;
|
||||
border-radius: 12px;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
#inputText:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
.input-actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
.btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
color: #666;
|
||||
border: 1px solid #e1e5e9;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Results Section */
|
||||
.results-section {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.results-section.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.results-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.result-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4);
|
||||
}
|
||||
|
||||
.result-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.result-card .card-header h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.result-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.result-item label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.result-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(248, 250, 252, 0.8);
|
||||
border: 1px solid #e1e5e9;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.result-value:hover {
|
||||
background: rgba(248, 250, 252, 0.95);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.result-value .placeholder {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #667eea;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
color: #5a67d8;
|
||||
}
|
||||
|
||||
/* Loading Overlay */
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(5px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.loading-overlay.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 4px solid white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Toast Notification */
|
||||
.toast {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
background: linear-gradient(135deg, #4ecdc4, #44a08d);
|
||||
color: white;
|
||||
padding: 16px 24px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
transform: translateX(400px);
|
||||
transition: all 0.3s ease;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.toast i {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.logo i {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.results-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.toast {
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
left: 20px;
|
||||
transform: translateY(100px);
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.input-card,
|
||||
.result-card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card-header h2,
|
||||
.card-header h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.result-value {
|
||||
font-size: 12px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation Classes */
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.slide-in {
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
transform: translateX(-20px);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
221
frontend/60sapi/实用功能/哈希解压压缩/index.html
Normal file
221
frontend/60sapi/实用功能/哈希解压压缩/index.html
Normal file
@@ -0,0 +1,221 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>多功能哈希工具 - Hash Toolkit</title>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Header Section -->
|
||||
<header class="header">
|
||||
<div class="header-content">
|
||||
<div class="logo">
|
||||
<i class="fas fa-fingerprint"></i>
|
||||
<h1>Hash Toolkit</h1>
|
||||
</div>
|
||||
<p class="subtitle">多功能哈希、编码与压缩工具</p>
|
||||
</div>
|
||||
<div class="header-decoration">
|
||||
<div class="floating-shapes">
|
||||
<div class="shape shape-1"></div>
|
||||
<div class="shape shape-2"></div>
|
||||
<div class="shape shape-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<!-- Input Section -->
|
||||
<section class="input-section">
|
||||
<div class="input-card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-edit"></i>
|
||||
<h2>输入内容</h2>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<textarea
|
||||
id="inputText"
|
||||
placeholder="请输入要处理的文本内容...\n支持中文、英文、特殊字符等"
|
||||
rows="6"
|
||||
></textarea>
|
||||
<div class="input-actions">
|
||||
<button id="clearBtn" class="btn btn-secondary">
|
||||
<i class="fas fa-trash"></i>
|
||||
清空
|
||||
</button>
|
||||
<button id="processBtn" class="btn btn-primary">
|
||||
<i class="fas fa-cogs"></i>
|
||||
开始处理
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Results Section -->
|
||||
<section class="results-section" id="resultsSection">
|
||||
<div class="results-grid">
|
||||
<!-- Hash Results -->
|
||||
<div class="result-card hash-card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-hashtag"></i>
|
||||
<h3>哈希算法</h3>
|
||||
</div>
|
||||
<div class="result-items">
|
||||
<div class="result-item">
|
||||
<label>MD5</label>
|
||||
<div class="result-value" id="md5Result">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="md5Result">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>SHA1</label>
|
||||
<div class="result-value" id="sha1Result">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="sha1Result">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>SHA256</label>
|
||||
<div class="result-value" id="sha256Result">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="sha256Result">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>SHA512</label>
|
||||
<div class="result-value" id="sha512Result">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="sha512Result">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Encoding Results -->
|
||||
<div class="result-card encoding-card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-code"></i>
|
||||
<h3>编码转换</h3>
|
||||
</div>
|
||||
<div class="result-items">
|
||||
<div class="result-item">
|
||||
<label>Base64 编码</label>
|
||||
<div class="result-value" id="base64EncodeResult">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="base64EncodeResult">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>Base64 解码</label>
|
||||
<div class="result-value" id="base64DecodeResult">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="base64DecodeResult">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>URL 编码</label>
|
||||
<div class="result-value" id="urlEncodeResult">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="urlEncodeResult">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>URL 解码</label>
|
||||
<div class="result-value" id="urlDecodeResult">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="urlDecodeResult">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Compression Results -->
|
||||
<div class="result-card compression-card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-compress-alt"></i>
|
||||
<h3>压缩算法</h3>
|
||||
</div>
|
||||
<div class="result-items">
|
||||
<div class="result-item">
|
||||
<label>Gzip 压缩</label>
|
||||
<div class="result-value" id="gzipCompressResult">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="gzipCompressResult">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>Gzip 解压</label>
|
||||
<div class="result-value" id="gzipDecompressResult">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="gzipDecompressResult">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>Deflate 压缩</label>
|
||||
<div class="result-value" id="deflateCompressResult">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="deflateCompressResult">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-item">
|
||||
<label>Brotli 压缩</label>
|
||||
<div class="result-value" id="brotliCompressResult">
|
||||
<span class="placeholder">等待处理...</span>
|
||||
<button class="copy-btn" data-target="brotliCompressResult">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div class="loading-overlay" id="loadingOverlay">
|
||||
<div class="loading-spinner">
|
||||
<div class="spinner"></div>
|
||||
<p>正在处理中...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast Notification -->
|
||||
<div class="toast" id="toast">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
<span id="toastMessage">复制成功!</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
380
frontend/60sapi/实用功能/哈希解压压缩/js/script.js
Normal file
380
frontend/60sapi/实用功能/哈希解压压缩/js/script.js
Normal file
@@ -0,0 +1,380 @@
|
||||
// API配置
|
||||
const API_BASE_URL = 'https://60s.viki.moe/v2/hash';
|
||||
|
||||
// DOM元素
|
||||
const elements = {
|
||||
inputText: document.getElementById('inputText'),
|
||||
processBtn: document.getElementById('processBtn'),
|
||||
clearBtn: document.getElementById('clearBtn'),
|
||||
resultsSection: document.getElementById('resultsSection'),
|
||||
loadingOverlay: document.getElementById('loadingOverlay'),
|
||||
toast: document.getElementById('toast'),
|
||||
toastMessage: document.getElementById('toastMessage')
|
||||
};
|
||||
|
||||
// 结果元素映射
|
||||
const resultElements = {
|
||||
md5: document.getElementById('md5Result'),
|
||||
sha1: document.getElementById('sha1Result'),
|
||||
sha256: document.getElementById('sha256Result'),
|
||||
sha512: document.getElementById('sha512Result'),
|
||||
base64Encode: document.getElementById('base64EncodeResult'),
|
||||
base64Decode: document.getElementById('base64DecodeResult'),
|
||||
urlEncode: document.getElementById('urlEncodeResult'),
|
||||
urlDecode: document.getElementById('urlDecodeResult'),
|
||||
gzipCompress: document.getElementById('gzipCompressResult'),
|
||||
gzipDecompress: document.getElementById('gzipDecompressResult'),
|
||||
deflateCompress: document.getElementById('deflateCompressResult'),
|
||||
brotliCompress: document.getElementById('brotliCompressResult')
|
||||
};
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializeEventListeners();
|
||||
addInputAnimation();
|
||||
});
|
||||
|
||||
// 事件监听器初始化
|
||||
function initializeEventListeners() {
|
||||
// 处理按钮点击
|
||||
elements.processBtn.addEventListener('click', handleProcess);
|
||||
|
||||
// 清空按钮点击
|
||||
elements.clearBtn.addEventListener('click', handleClear);
|
||||
|
||||
// 输入框回车键
|
||||
elements.inputText.addEventListener('keydown', function(e) {
|
||||
if (e.ctrlKey && e.key === 'Enter') {
|
||||
handleProcess();
|
||||
}
|
||||
});
|
||||
|
||||
// 复制按钮事件委托
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('.copy-btn')) {
|
||||
const copyBtn = e.target.closest('.copy-btn');
|
||||
const targetId = copyBtn.getAttribute('data-target');
|
||||
const targetElement = document.getElementById(targetId);
|
||||
const textContent = targetElement.textContent.trim();
|
||||
|
||||
if (textContent && textContent !== '等待处理...' && textContent !== '处理失败') {
|
||||
copyToClipboard(textContent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 输入框实时验证
|
||||
elements.inputText.addEventListener('input', function() {
|
||||
const hasContent = this.value.trim().length > 0;
|
||||
elements.processBtn.disabled = !hasContent;
|
||||
|
||||
if (hasContent) {
|
||||
elements.processBtn.classList.remove('disabled');
|
||||
} else {
|
||||
elements.processBtn.classList.add('disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 添加输入动画效果
|
||||
function addInputAnimation() {
|
||||
elements.inputText.addEventListener('focus', function() {
|
||||
this.parentElement.classList.add('focused');
|
||||
});
|
||||
|
||||
elements.inputText.addEventListener('blur', function() {
|
||||
this.parentElement.classList.remove('focused');
|
||||
});
|
||||
}
|
||||
|
||||
// 处理主要功能
|
||||
async function handleProcess() {
|
||||
const inputValue = elements.inputText.value.trim();
|
||||
|
||||
if (!inputValue) {
|
||||
showToast('请输入要处理的内容', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
showLoading(true);
|
||||
resetResults();
|
||||
|
||||
try {
|
||||
// 调用API
|
||||
const response = await fetch(`${API_BASE_URL}?content=${encodeURIComponent(inputValue)}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200 && data.data) {
|
||||
displayResults(data.data);
|
||||
showResultsSection();
|
||||
showToast('处理完成!', 'success');
|
||||
} else {
|
||||
throw new Error(data.message || '处理失败');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('处理错误:', error);
|
||||
showToast(`处理失败: ${error.message}`, 'error');
|
||||
displayError();
|
||||
} finally {
|
||||
showLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示结果
|
||||
function displayResults(data) {
|
||||
try {
|
||||
// 哈希结果
|
||||
updateResultElement('md5', data.md5 || '不可用');
|
||||
|
||||
// SHA系列
|
||||
if (data.sha) {
|
||||
updateResultElement('sha1', data.sha.sha1 || '不可用');
|
||||
updateResultElement('sha256', data.sha.sha256 || '不可用');
|
||||
updateResultElement('sha512', data.sha.sha512 || '不可用');
|
||||
}
|
||||
|
||||
// Base64编码
|
||||
if (data.base64) {
|
||||
updateResultElement('base64Encode', data.base64.encode || '不可用');
|
||||
updateResultElement('base64Decode', data.base64.decode || '不可用');
|
||||
}
|
||||
|
||||
// URL编码
|
||||
if (data.url) {
|
||||
updateResultElement('urlEncode', data.url.encode || '不可用');
|
||||
updateResultElement('urlDecode', data.url.decode || '不可用');
|
||||
}
|
||||
|
||||
// 压缩结果
|
||||
if (data.gzip) {
|
||||
updateResultElement('gzipCompress', data.gzip.compress || '不可用');
|
||||
updateResultElement('gzipDecompress', data.gzip.decompress || '不可用');
|
||||
}
|
||||
|
||||
if (data.deflate) {
|
||||
updateResultElement('deflateCompress', data.deflate.compress || '不可用');
|
||||
}
|
||||
|
||||
if (data.brotli) {
|
||||
updateResultElement('brotliCompress', data.brotli.compress || '不可用');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('显示结果时出错:', error);
|
||||
showToast('显示结果时出错', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 更新单个结果元素
|
||||
function updateResultElement(key, value) {
|
||||
const element = resultElements[key];
|
||||
if (element) {
|
||||
const textSpan = element.querySelector('span') || element;
|
||||
textSpan.textContent = value;
|
||||
textSpan.classList.remove('placeholder');
|
||||
|
||||
// 添加动画效果
|
||||
element.classList.add('slide-in');
|
||||
setTimeout(() => {
|
||||
element.classList.remove('slide-in');
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// 重置结果
|
||||
function resetResults() {
|
||||
Object.values(resultElements).forEach(element => {
|
||||
if (element) {
|
||||
const textSpan = element.querySelector('span') || element;
|
||||
textSpan.textContent = '等待处理...';
|
||||
textSpan.classList.add('placeholder');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 显示错误状态
|
||||
function displayError() {
|
||||
Object.values(resultElements).forEach(element => {
|
||||
if (element) {
|
||||
const textSpan = element.querySelector('span') || element;
|
||||
textSpan.textContent = '处理失败';
|
||||
textSpan.classList.add('placeholder');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 显示结果区域
|
||||
function showResultsSection() {
|
||||
elements.resultsSection.classList.add('show');
|
||||
|
||||
// 平滑滚动到结果区域
|
||||
setTimeout(() => {
|
||||
elements.resultsSection.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 清空功能
|
||||
function handleClear() {
|
||||
elements.inputText.value = '';
|
||||
elements.inputText.focus();
|
||||
elements.resultsSection.classList.remove('show');
|
||||
resetResults();
|
||||
elements.processBtn.disabled = true;
|
||||
elements.processBtn.classList.add('disabled');
|
||||
|
||||
showToast('内容已清空', 'info');
|
||||
}
|
||||
|
||||
// 复制到剪贴板
|
||||
async function copyToClipboard(text) {
|
||||
try {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
} else {
|
||||
// 降级方案
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-999999px';
|
||||
textArea.style.top = '-999999px';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
document.execCommand('copy');
|
||||
textArea.remove();
|
||||
}
|
||||
|
||||
showToast('复制成功!', 'success');
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error);
|
||||
showToast('复制失败,请手动复制', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示/隐藏加载状态
|
||||
function showLoading(show) {
|
||||
if (show) {
|
||||
elements.loadingOverlay.classList.add('show');
|
||||
elements.processBtn.disabled = true;
|
||||
elements.processBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 处理中...';
|
||||
} else {
|
||||
elements.loadingOverlay.classList.remove('show');
|
||||
elements.processBtn.disabled = false;
|
||||
elements.processBtn.innerHTML = '<i class="fas fa-cogs"></i> 开始处理';
|
||||
}
|
||||
}
|
||||
|
||||
// 显示提示消息
|
||||
function showToast(message, type = 'success') {
|
||||
elements.toastMessage.textContent = message;
|
||||
|
||||
// 设置图标和样式
|
||||
const icon = elements.toast.querySelector('i');
|
||||
icon.className = getToastIcon(type);
|
||||
|
||||
elements.toast.className = `toast ${type}`;
|
||||
elements.toast.classList.add('show');
|
||||
|
||||
// 自动隐藏
|
||||
setTimeout(() => {
|
||||
elements.toast.classList.remove('show');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 获取提示图标
|
||||
function getToastIcon(type) {
|
||||
const icons = {
|
||||
success: 'fas fa-check-circle',
|
||||
error: 'fas fa-exclamation-circle',
|
||||
info: 'fas fa-info-circle',
|
||||
warning: 'fas fa-exclamation-triangle'
|
||||
};
|
||||
return icons[type] || icons.success;
|
||||
}
|
||||
|
||||
// 工具函数:防抖
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// 工具函数:节流
|
||||
function throttle(func, limit) {
|
||||
let inThrottle;
|
||||
return function() {
|
||||
const args = arguments;
|
||||
const context = this;
|
||||
if (!inThrottle) {
|
||||
func.apply(context, args);
|
||||
inThrottle = true;
|
||||
setTimeout(() => inThrottle = false, limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加键盘快捷键支持
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// Ctrl+Enter 处理
|
||||
if (e.ctrlKey && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (!elements.processBtn.disabled) {
|
||||
handleProcess();
|
||||
}
|
||||
}
|
||||
|
||||
// Escape 清空
|
||||
if (e.key === 'Escape') {
|
||||
handleClear();
|
||||
}
|
||||
});
|
||||
|
||||
// 页面可见性变化处理
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (document.hidden) {
|
||||
// 页面隐藏时的处理
|
||||
console.log('页面已隐藏');
|
||||
} else {
|
||||
// 页面显示时的处理
|
||||
console.log('页面已显示');
|
||||
}
|
||||
});
|
||||
|
||||
// 错误处理
|
||||
window.addEventListener('error', function(e) {
|
||||
console.error('全局错误:', e.error);
|
||||
showToast('发生未知错误,请刷新页面重试', 'error');
|
||||
});
|
||||
|
||||
// 未处理的Promise拒绝
|
||||
window.addEventListener('unhandledrejection', function(e) {
|
||||
console.error('未处理的Promise拒绝:', e.reason);
|
||||
showToast('网络请求失败,请检查网络连接', 'error');
|
||||
});
|
||||
|
||||
// 导出函数供测试使用
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = {
|
||||
handleProcess,
|
||||
copyToClipboard,
|
||||
showToast,
|
||||
debounce,
|
||||
throttle
|
||||
};
|
||||
}
|
||||
3
frontend/60sapi/实用功能/哈希解压压缩/接口集合.json
Normal file
3
frontend/60sapi/实用功能/哈希解压压缩/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
33
frontend/60sapi/实用功能/哈希解压压缩/返回接口.json
Normal file
33
frontend/60sapi/实用功能/哈希解压压缩/返回接口.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "处理成功",
|
||||
"data": {
|
||||
"source": "你好👋",
|
||||
"md5": "a1b2c3d4e5f6789012345678901234567",
|
||||
"sha": {
|
||||
"sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"sha512": "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
|
||||
},
|
||||
"base64": {
|
||||
"encode": "5L2g5aW9",
|
||||
"decode": "你好"
|
||||
},
|
||||
"url": {
|
||||
"encode": "%E4%BD%A0%E5%A5%BD%F0%9F%91%8B",
|
||||
"decode": "你好👋"
|
||||
},
|
||||
"gzip": {
|
||||
"compress": "H4sIAAAAAAAAA...(压缩后的数据)",
|
||||
"decompress": "你好👋"
|
||||
},
|
||||
"deflate": {
|
||||
"compress": "eJwrz8kvTUlMy...(压缩后的数据)",
|
||||
"decompress": "你好👋"
|
||||
},
|
||||
"brotli": {
|
||||
"compress": "CwWAaGVsbG8g...(压缩后的数据)",
|
||||
"decompress": "你好👋"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
8
frontend/60sapi/实用功能/生成要求模板.txt
Normal file
8
frontend/60sapi/实用功能/生成要求模板.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
1.生成为静态网页,js,css,html分离出来,不要混合在一起放入html里,难以阅读
|
||||
2.网页要适配手机端,电脑端和平板端三个设备分别做不同的css格式,优先优化手机端用户体验
|
||||
3.网页默认风格以淡绿色清新风格为主,除非用户要求
|
||||
4.尽量不要引用外部css,js,实在要引用就使用中国国内的cdn,否则用户可能加载不出来
|
||||
5.返回接口.json储存了网页api返回的数据格式
|
||||
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
|
||||
7.接口集合.json保存了所有已知的后端API接口,一个访问不了尝试自动切换另一个
|
||||
8.在css中有关背景的css单独一个css文件,方便我直接迁移
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
232
frontend/60sapi/实用功能/链接OG信息/css/background.css
Normal file
232
frontend/60sapi/实用功能/链接OG信息/css/background.css
Normal file
@@ -0,0 +1,232 @@
|
||||
/* 高维度背景特效样式 - 神秘高级风格 */
|
||||
|
||||
/* 背景容器 */
|
||||
.background-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(ellipse at center,
|
||||
rgba(15, 0, 30, 0.95) 0%,
|
||||
rgba(5, 0, 15, 0.98) 50%,
|
||||
rgba(0, 0, 0, 1) 100%);
|
||||
}
|
||||
|
||||
/* 几何网格层 */
|
||||
.geometric-grid {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(138, 43, 226, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(138, 43, 226, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(rgba(75, 0, 130, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(75, 0, 130, 0.05) 1px, transparent 1px);
|
||||
background-size: 100px 100px, 100px 100px, 20px 20px, 20px 20px;
|
||||
animation: gridPulse 8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes gridPulse {
|
||||
0%, 100% { opacity: 0.3; transform: scale(1); }
|
||||
50% { opacity: 0.6; transform: scale(1.02); }
|
||||
}
|
||||
|
||||
/* 神经网络效果 */
|
||||
.neural-network {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
radial-gradient(circle at 20% 30%, rgba(138, 43, 226, 0.15) 2px, transparent 2px),
|
||||
radial-gradient(circle at 80% 20%, rgba(75, 0, 130, 0.12) 1px, transparent 1px),
|
||||
radial-gradient(circle at 40% 70%, rgba(147, 0, 211, 0.1) 1.5px, transparent 1.5px),
|
||||
radial-gradient(circle at 90% 80%, rgba(138, 43, 226, 0.08) 1px, transparent 1px),
|
||||
radial-gradient(circle at 10% 90%, rgba(75, 0, 130, 0.1) 2px, transparent 2px);
|
||||
background-size: 200px 200px, 150px 150px, 300px 300px, 180px 180px, 250px 250px;
|
||||
animation: neuralFlow 15s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes neuralFlow {
|
||||
0% { transform: translate(0, 0) rotate(0deg); }
|
||||
25% { transform: translate(-10px, -5px) rotate(90deg); }
|
||||
50% { transform: translate(-5px, -10px) rotate(180deg); }
|
||||
75% { transform: translate(5px, -5px) rotate(270deg); }
|
||||
100% { transform: translate(0, 0) rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 粒子系统 */
|
||||
.particle-system {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
radial-gradient(circle, rgba(138, 43, 226, 0.4) 1px, transparent 1px),
|
||||
radial-gradient(circle, rgba(75, 0, 130, 0.3) 0.5px, transparent 0.5px),
|
||||
radial-gradient(circle, rgba(147, 0, 211, 0.2) 0.8px, transparent 0.8px);
|
||||
background-size: 80px 80px, 120px 120px, 160px 160px;
|
||||
background-position: 0 0, 40px 40px, 80px 80px;
|
||||
animation: particleFloat 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes particleFloat {
|
||||
0%, 100% { transform: translateY(0px) translateX(0px); }
|
||||
25% { transform: translateY(-20px) translateX(10px); }
|
||||
50% { transform: translateY(-10px) translateX(-15px); }
|
||||
75% { transform: translateY(-30px) translateX(5px); }
|
||||
}
|
||||
|
||||
/* 扫描线效果 */
|
||||
.scan-lines {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent 0px,
|
||||
transparent 2px,
|
||||
rgba(138, 43, 226, 0.03) 2px,
|
||||
rgba(138, 43, 226, 0.03) 4px
|
||||
);
|
||||
animation: scanMove 3s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes scanMove {
|
||||
0% { transform: translateY(-100%); }
|
||||
100% { transform: translateY(100%); }
|
||||
}
|
||||
|
||||
/* 全息投影效果 */
|
||||
.holographic-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
linear-gradient(45deg,
|
||||
transparent 30%,
|
||||
rgba(138, 43, 226, 0.05) 50%,
|
||||
transparent 70%),
|
||||
linear-gradient(-45deg,
|
||||
transparent 30%,
|
||||
rgba(75, 0, 130, 0.03) 50%,
|
||||
transparent 70%);
|
||||
background-size: 200px 200px, 150px 150px;
|
||||
animation: holographicShift 12s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes holographicShift {
|
||||
0%, 100% {
|
||||
background-position: 0% 0%, 100% 100%;
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 100%, 0% 0%;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 数据流效果 */
|
||||
.data-stream {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(138, 43, 226, 0.1) 50%,
|
||||
transparent 100%);
|
||||
background-size: 300px 100%;
|
||||
animation: dataFlow 8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dataFlow {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
/* 量子波动效果 */
|
||||
.quantum-waves {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
radial-gradient(ellipse 200px 100px at 50% 0%,
|
||||
rgba(138, 43, 226, 0.1) 0%,
|
||||
transparent 50%),
|
||||
radial-gradient(ellipse 300px 150px at 50% 100%,
|
||||
rgba(75, 0, 130, 0.08) 0%,
|
||||
transparent 50%);
|
||||
animation: quantumPulse 10s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes quantumPulse {
|
||||
0%, 100% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1) rotate(180deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式优化 */
|
||||
@media (max-width: 768px) {
|
||||
.geometric-grid {
|
||||
background-size: 50px 50px, 50px 50px, 10px 10px, 10px 10px;
|
||||
}
|
||||
|
||||
.neural-network {
|
||||
background-size: 100px 100px, 75px 75px, 150px 150px, 90px 90px, 125px 125px;
|
||||
}
|
||||
|
||||
.particle-system {
|
||||
background-size: 40px 40px, 60px 60px, 80px 80px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画偏好 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.geometric-grid,
|
||||
.neural-network,
|
||||
.particle-system,
|
||||
.scan-lines,
|
||||
.holographic-overlay,
|
||||
.data-stream,
|
||||
.quantum-waves {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 高对比度模式 */
|
||||
@media (prefers-contrast: high) {
|
||||
.background-container {
|
||||
background: radial-gradient(ellipse at center,
|
||||
rgba(25, 0, 50, 0.95) 0%,
|
||||
rgba(10, 0, 25, 0.98) 50%,
|
||||
rgba(0, 0, 0, 1) 100%);
|
||||
}
|
||||
|
||||
.geometric-grid {
|
||||
background-image:
|
||||
linear-gradient(rgba(200, 100, 255, 0.2) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(200, 100, 255, 0.2) 1px, transparent 1px);
|
||||
}
|
||||
}
|
||||
1159
frontend/60sapi/实用功能/链接OG信息/css/style.css
Normal file
1159
frontend/60sapi/实用功能/链接OG信息/css/style.css
Normal file
File diff suppressed because it is too large
Load Diff
223
frontend/60sapi/实用功能/链接OG信息/index.html
Normal file
223
frontend/60sapi/实用功能/链接OG信息/index.html
Normal file
@@ -0,0 +1,223 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>链接OG信息查询 - 神秘解析器</title>
|
||||
<meta name="description" content="高级链接OG信息查询工具,解析网页元数据">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/background.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 背景特效容器 -->
|
||||
<div class="background-container">
|
||||
<div class="matrix-rain"></div>
|
||||
<div class="geometric-shapes"></div>
|
||||
<div class="neural-network"></div>
|
||||
</div>
|
||||
|
||||
<!-- 主容器 -->
|
||||
<div class="main-container">
|
||||
<!-- 头部区域 -->
|
||||
<header class="header">
|
||||
<div class="header-content">
|
||||
<div class="logo-section">
|
||||
<i class="fas fa-link logo-icon"></i>
|
||||
<h1 class="title">OG 解析器</h1>
|
||||
<span class="subtitle">链接元数据神秘解析</span>
|
||||
</div>
|
||||
<div class="status-indicator">
|
||||
<div class="pulse-dot"></div>
|
||||
<span class="status-text">系统就绪</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 查询区域 -->
|
||||
<section class="query-section">
|
||||
<div class="input-container">
|
||||
<div class="input-wrapper">
|
||||
<i class="fas fa-globe input-icon"></i>
|
||||
<input type="url" id="url-input" placeholder="输入链接地址进行深度解析..." class="url-input">
|
||||
<div class="input-border"></div>
|
||||
</div>
|
||||
<button id="analyze-btn" class="analyze-btn">
|
||||
<span class="btn-text">开始解析</span>
|
||||
<div class="btn-effects">
|
||||
<div class="ripple"></div>
|
||||
<div class="glow"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div id="loading" class="loading-container" style="display: none;">
|
||||
<div class="loading-content">
|
||||
<div class="scanner">
|
||||
<div class="scanner-line"></div>
|
||||
<div class="scanner-grid">
|
||||
<div class="grid-line"></div>
|
||||
<div class="grid-line"></div>
|
||||
<div class="grid-line"></div>
|
||||
<div class="grid-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="loading-text">
|
||||
<span class="loading-title">正在解析链接</span>
|
||||
<span class="loading-subtitle">深度扫描元数据中...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 结果展示区域 -->
|
||||
<section id="results" class="results-section" style="display: none;">
|
||||
<div class="results-header">
|
||||
<h2 class="results-title">
|
||||
<i class="fas fa-chart-network"></i>
|
||||
解析结果
|
||||
</h2>
|
||||
<div class="results-actions">
|
||||
<button id="copy-btn" class="action-btn">
|
||||
<i class="fas fa-copy"></i>
|
||||
<span>复制数据</span>
|
||||
</button>
|
||||
<button id="clear-btn" class="action-btn">
|
||||
<i class="fas fa-trash"></i>
|
||||
<span>清除结果</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="og-card">
|
||||
<!-- 基础信息 -->
|
||||
<div class="info-section basic-info">
|
||||
<div class="section-header">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<span>基础信息</span>
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<label>标题</label>
|
||||
<div id="og-title" class="info-value">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>描述</label>
|
||||
<div id="og-description" class="info-value">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>网站名称</label>
|
||||
<div id="og-site-name" class="info-value">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>类型</label>
|
||||
<div id="og-type" class="info-value">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 媒体信息 -->
|
||||
<div class="info-section media-info">
|
||||
<div class="section-header">
|
||||
<i class="fas fa-image"></i>
|
||||
<span>媒体信息</span>
|
||||
</div>
|
||||
<div class="media-preview" id="media-preview">
|
||||
<div class="no-media">
|
||||
<i class="fas fa-image-slash"></i>
|
||||
<span>暂无媒体内容</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-details">
|
||||
<div class="info-item">
|
||||
<label>图片URL</label>
|
||||
<div id="og-image" class="info-value url-value">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>图片尺寸</label>
|
||||
<div id="og-image-size" class="info-value">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 技术信息 -->
|
||||
<div class="info-section tech-info">
|
||||
<div class="section-header">
|
||||
<i class="fas fa-code"></i>
|
||||
<span>技术信息</span>
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<label>URL</label>
|
||||
<div id="og-url" class="info-value url-value">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>域名</label>
|
||||
<div id="og-domain" class="info-value">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>语言</label>
|
||||
<div id="og-locale" class="info-value">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>字符编码</label>
|
||||
<div id="og-charset" class="info-value">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 错误信息 -->
|
||||
<div id="error" class="error-container" style="display: none;">
|
||||
<div class="error-content">
|
||||
<div class="error-icon">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</div>
|
||||
<div class="error-text">
|
||||
<h3 class="error-title">解析失败</h3>
|
||||
<p id="error-message" class="error-message">未知错误</p>
|
||||
</div>
|
||||
<button id="retryBtn" class="retry-btn">
|
||||
<i class="fas fa-redo"></i>
|
||||
<span>重新尝试</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示消息 -->
|
||||
<div id="tip-message" class="tip-container">
|
||||
<div class="tip-content">
|
||||
<i class="fas fa-lightbulb tip-icon"></i>
|
||||
<span class="tip-text"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast消息 -->
|
||||
<div id="toast" class="toast-container">
|
||||
<div class="toast-content">
|
||||
<i class="toast-icon"></i>
|
||||
<span class="toast-message"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer">
|
||||
<div class="footer-content">
|
||||
<p class="footer-text">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
高级链接解析系统 | 神秘数据挖掘
|
||||
</p>
|
||||
<div class="footer-links">
|
||||
<span class="footer-link">隐私保护</span>
|
||||
<span class="footer-divider">|</span>
|
||||
<span class="footer-link">安全解析</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
555
frontend/60sapi/实用功能/链接OG信息/js/script.js
Normal file
555
frontend/60sapi/实用功能/链接OG信息/js/script.js
Normal file
@@ -0,0 +1,555 @@
|
||||
// 链接OG信息查询 - JavaScript功能代码
|
||||
// 神秘高级风格的交互体验
|
||||
|
||||
class OGAnalyzer {
|
||||
constructor() {
|
||||
this.apiUrl = 'https://60s.viki.moe/v2/og';
|
||||
this.isAnalyzing = false;
|
||||
this.currentUrl = '';
|
||||
this.animationFrameId = null;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.createBackgroundEffects();
|
||||
this.initializeAnimations();
|
||||
this.showWelcomeMessage();
|
||||
this.initPageAnimations();
|
||||
}
|
||||
|
||||
// 初始化页面动画
|
||||
initPageAnimations() {
|
||||
// 延迟添加动画类,确保CSS已加载
|
||||
setTimeout(() => {
|
||||
const header = document.querySelector('.header');
|
||||
const querySection = document.querySelector('.query-section');
|
||||
|
||||
if (header) header.classList.add('animate-in');
|
||||
if (querySection) querySection.classList.add('animate-in');
|
||||
}, 100);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
const urlInput = document.getElementById('url-input');
|
||||
const analyzeBtn = document.getElementById('analyze-btn');
|
||||
const copyBtn = document.getElementById('copy-btn');
|
||||
const clearBtn = document.getElementById('clear-btn');
|
||||
|
||||
// 输入框事件
|
||||
urlInput.addEventListener('input', (e) => this.handleUrlInput(e));
|
||||
urlInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter' && !this.isAnalyzing) {
|
||||
this.analyzeUrl();
|
||||
}
|
||||
});
|
||||
urlInput.addEventListener('focus', () => this.handleInputFocus());
|
||||
urlInput.addEventListener('blur', () => this.handleInputBlur());
|
||||
|
||||
// 按钮事件
|
||||
analyzeBtn.addEventListener('click', () => this.analyzeUrl());
|
||||
copyBtn.addEventListener('click', () => this.copyResults());
|
||||
clearBtn.addEventListener('click', () => this.clearResults());
|
||||
|
||||
// 键盘快捷键
|
||||
document.addEventListener('keydown', (e) => this.handleKeyboard(e));
|
||||
}
|
||||
|
||||
handleUrlInput(e) {
|
||||
const url = e.target.value.trim();
|
||||
const analyzeBtn = document.getElementById('analyze-btn');
|
||||
|
||||
if (this.isValidUrl(url)) {
|
||||
analyzeBtn.classList.add('ready');
|
||||
e.target.classList.remove('error');
|
||||
} else {
|
||||
analyzeBtn.classList.remove('ready');
|
||||
if (url.length > 0) {
|
||||
e.target.classList.add('error');
|
||||
} else {
|
||||
e.target.classList.remove('error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleInputFocus() {
|
||||
const inputContainer = document.querySelector('.input-container');
|
||||
inputContainer.classList.add('focused');
|
||||
this.createInputGlow();
|
||||
}
|
||||
|
||||
handleInputBlur() {
|
||||
const inputContainer = document.querySelector('.input-container');
|
||||
inputContainer.classList.remove('focused');
|
||||
}
|
||||
|
||||
handleKeyboard(e) {
|
||||
// Ctrl/Cmd + Enter 快速分析
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (!this.isAnalyzing) {
|
||||
this.analyzeUrl();
|
||||
}
|
||||
}
|
||||
|
||||
// Escape 清除结果
|
||||
if (e.key === 'Escape') {
|
||||
this.clearResults();
|
||||
}
|
||||
}
|
||||
|
||||
isValidUrl(string) {
|
||||
try {
|
||||
const url = new URL(string);
|
||||
return url.protocol === 'http:' || url.protocol === 'https:';
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async analyzeUrl() {
|
||||
const urlInput = document.getElementById('url-input');
|
||||
const url = urlInput.value.trim();
|
||||
|
||||
if (!this.isValidUrl(url)) {
|
||||
this.showError('请输入有效的URL地址');
|
||||
this.shakeInput();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isAnalyzing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentUrl = url;
|
||||
this.isAnalyzing = true;
|
||||
this.showLoading();
|
||||
this.hideError();
|
||||
this.hideResults();
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.apiUrl}?url=${encodeURIComponent(url)}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200 && data.data) {
|
||||
await this.displayResults(data.data);
|
||||
this.showSuccessMessage('分析完成!');
|
||||
|
||||
// 添加按钮闪烁效果
|
||||
const analyzeBtn = document.getElementById('analyze-btn');
|
||||
analyzeBtn.classList.add('flash');
|
||||
setTimeout(() => {
|
||||
analyzeBtn.classList.remove('flash');
|
||||
}, 300);
|
||||
} else {
|
||||
throw new Error(data.message || '获取OG信息失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('分析失败:', error);
|
||||
this.showError(`分析失败: ${error.message}`);
|
||||
} finally {
|
||||
this.isAnalyzing = false;
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
const loadingElement = document.getElementById('loading');
|
||||
const analyzeBtn = document.getElementById('analyze-btn');
|
||||
|
||||
loadingElement.classList.add('active');
|
||||
analyzeBtn.disabled = true;
|
||||
analyzeBtn.textContent = '分析中...';
|
||||
|
||||
this.startScannerAnimation();
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
const loadingElement = document.getElementById('loading');
|
||||
const analyzeBtn = document.getElementById('analyze-btn');
|
||||
|
||||
loadingElement.classList.remove('active');
|
||||
analyzeBtn.disabled = false;
|
||||
analyzeBtn.textContent = '开始分析';
|
||||
|
||||
this.stopScannerAnimation();
|
||||
}
|
||||
|
||||
async displayResults(data) {
|
||||
const resultsElement = document.getElementById('results');
|
||||
const ogCard = document.getElementById('og-card');
|
||||
|
||||
// 基础信息
|
||||
this.updateElement('og-title', data.title || '未获取到标题');
|
||||
this.updateElement('og-description', data.description || '未获取到描述');
|
||||
this.updateElement('og-url', data.url || this.currentUrl);
|
||||
this.updateElement('og-site-name', data.site_name || '未知站点');
|
||||
this.updateElement('og-type', data.type || 'website');
|
||||
|
||||
// 媒体信息
|
||||
this.updateImageElement('og-image', data.image);
|
||||
this.updateElement('og-image-alt', data.image_alt || '图片描述不可用');
|
||||
|
||||
// 技术信息
|
||||
this.updateElement('og-locale', data.locale || '未指定');
|
||||
this.updateElement('og-updated-time', this.formatDate(data.updated_time));
|
||||
this.updateElement('response-time', `${Date.now() - this.startTime}ms`);
|
||||
|
||||
// 显示结果
|
||||
resultsElement.classList.add('active');
|
||||
|
||||
// 添加动画效果
|
||||
await this.animateResults();
|
||||
|
||||
// 启用操作按钮
|
||||
document.getElementById('copy-btn').disabled = false;
|
||||
document.getElementById('clear-btn').disabled = false;
|
||||
}
|
||||
|
||||
updateElement(id, content) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.textContent = content;
|
||||
}
|
||||
}
|
||||
|
||||
updateImageElement(id, imageSrc) {
|
||||
const element = document.getElementById(id);
|
||||
if (element && imageSrc) {
|
||||
element.src = imageSrc;
|
||||
element.style.display = 'block';
|
||||
element.onerror = () => {
|
||||
element.style.display = 'none';
|
||||
const placeholder = element.nextElementSibling;
|
||||
if (placeholder && placeholder.classList.contains('image-placeholder')) {
|
||||
placeholder.style.display = 'flex';
|
||||
}
|
||||
};
|
||||
} else if (element) {
|
||||
element.style.display = 'none';
|
||||
const placeholder = element.nextElementSibling;
|
||||
if (placeholder && placeholder.classList.contains('image-placeholder')) {
|
||||
placeholder.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatDate(timestamp) {
|
||||
if (!timestamp) return '未知';
|
||||
try {
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleString('zh-CN');
|
||||
} catch (e) {
|
||||
return '格式错误';
|
||||
}
|
||||
}
|
||||
|
||||
async animateResults() {
|
||||
const cards = document.querySelectorAll('.info-card');
|
||||
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
setTimeout(() => {
|
||||
cards[i].classList.add('animate-in');
|
||||
}, i * 100);
|
||||
}
|
||||
|
||||
// 等待动画完成
|
||||
await new Promise(resolve => setTimeout(resolve, cards.length * 100 + 300));
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
const errorElement = document.getElementById('error-message');
|
||||
const errorText = errorElement.querySelector('.error-text');
|
||||
const inputContainer = document.querySelector('.input-container');
|
||||
|
||||
errorText.textContent = message;
|
||||
errorElement.classList.add('active');
|
||||
|
||||
// 添加震动效果
|
||||
if (inputContainer) {
|
||||
inputContainer.classList.add('shake');
|
||||
setTimeout(() => {
|
||||
inputContainer.classList.remove('shake');
|
||||
}, 600);
|
||||
}
|
||||
|
||||
// 自动隐藏错误信息
|
||||
setTimeout(() => {
|
||||
this.hideError();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
hideError() {
|
||||
const errorElement = document.getElementById('error-message');
|
||||
errorElement.classList.remove('active');
|
||||
}
|
||||
|
||||
hideResults() {
|
||||
const resultsElement = document.getElementById('results');
|
||||
resultsElement.classList.remove('active');
|
||||
|
||||
// 重置动画状态
|
||||
const cards = document.querySelectorAll('.info-card');
|
||||
cards.forEach(card => card.classList.remove('animate-in'));
|
||||
}
|
||||
|
||||
showSuccessMessage(message) {
|
||||
const tipElement = document.getElementById('tip-message');
|
||||
const tipText = tipElement.querySelector('.tip-text');
|
||||
|
||||
tipText.textContent = message;
|
||||
tipElement.classList.add('active');
|
||||
|
||||
setTimeout(() => {
|
||||
tipElement.classList.remove('active');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
shakeInput() {
|
||||
const inputContainer = document.querySelector('.input-container');
|
||||
inputContainer.classList.add('shake');
|
||||
|
||||
setTimeout(() => {
|
||||
inputContainer.classList.remove('shake');
|
||||
}, 600);
|
||||
}
|
||||
|
||||
copyResults() {
|
||||
const ogData = {
|
||||
title: document.getElementById('og-title').textContent,
|
||||
description: document.getElementById('og-description').textContent,
|
||||
url: document.getElementById('og-url').textContent,
|
||||
site_name: document.getElementById('og-site-name').textContent,
|
||||
type: document.getElementById('og-type').textContent,
|
||||
image: document.getElementById('og-image').src,
|
||||
locale: document.getElementById('og-locale').textContent
|
||||
};
|
||||
|
||||
const jsonString = JSON.stringify(ogData, null, 2);
|
||||
|
||||
navigator.clipboard.writeText(jsonString).then(() => {
|
||||
this.showSuccessMessage('结果已复制到剪贴板!');
|
||||
this.flashCopyButton();
|
||||
}).catch(err => {
|
||||
console.error('复制失败:', err);
|
||||
this.showError('复制失败,请手动选择内容');
|
||||
});
|
||||
}
|
||||
|
||||
flashCopyButton() {
|
||||
const copyBtn = document.getElementById('copy-btn');
|
||||
copyBtn.classList.add('flash');
|
||||
|
||||
setTimeout(() => {
|
||||
copyBtn.classList.remove('flash');
|
||||
}, 300);
|
||||
}
|
||||
|
||||
clearResults() {
|
||||
const urlInput = document.getElementById('url-input');
|
||||
const resultsElement = document.getElementById('results');
|
||||
const errorElement = document.getElementById('error-message');
|
||||
|
||||
urlInput.value = '';
|
||||
urlInput.classList.remove('error');
|
||||
resultsElement.classList.remove('active');
|
||||
errorElement.classList.remove('active');
|
||||
|
||||
document.getElementById('analyze-btn').classList.remove('ready');
|
||||
document.getElementById('copy-btn').disabled = true;
|
||||
document.getElementById('clear-btn').disabled = true;
|
||||
|
||||
this.currentUrl = '';
|
||||
|
||||
// 重置动画状态
|
||||
const cards = document.querySelectorAll('.info-card');
|
||||
cards.forEach(card => card.classList.remove('animate-in'));
|
||||
|
||||
this.showSuccessMessage('已清除所有内容');
|
||||
}
|
||||
|
||||
createBackgroundEffects() {
|
||||
const container = document.querySelector('.background-container');
|
||||
|
||||
// 创建各种背景效果层
|
||||
const effects = [
|
||||
'geometric-grid',
|
||||
'neural-network',
|
||||
'particle-system',
|
||||
'scan-lines',
|
||||
'holographic-overlay',
|
||||
'data-stream',
|
||||
'quantum-waves'
|
||||
];
|
||||
|
||||
effects.forEach(effectClass => {
|
||||
const layer = document.createElement('div');
|
||||
layer.className = effectClass;
|
||||
container.appendChild(layer);
|
||||
});
|
||||
}
|
||||
|
||||
createInputGlow() {
|
||||
const inputContainer = document.querySelector('.input-container');
|
||||
|
||||
// 创建光晕效果
|
||||
const glow = document.createElement('div');
|
||||
glow.className = 'input-glow';
|
||||
inputContainer.appendChild(glow);
|
||||
|
||||
setTimeout(() => {
|
||||
if (glow.parentNode) {
|
||||
glow.remove();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
startScannerAnimation() {
|
||||
const scanner = document.querySelector('.scanner');
|
||||
if (scanner) {
|
||||
scanner.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
stopScannerAnimation() {
|
||||
const scanner = document.querySelector('.scanner');
|
||||
if (scanner) {
|
||||
scanner.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
initializeAnimations() {
|
||||
// 初始化页面动画
|
||||
const header = document.querySelector('.header');
|
||||
const querySection = document.querySelector('.query-section');
|
||||
|
||||
setTimeout(() => {
|
||||
header.classList.add('animate-in');
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
querySection.classList.add('animate-in');
|
||||
}, 300);
|
||||
}
|
||||
|
||||
showWelcomeMessage() {
|
||||
const tips = [
|
||||
'支持分析网页的标题、描述、图片等元信息',
|
||||
'可以预览社交媒体分享时的显示效果',
|
||||
'检测网页的SEO优化情况',
|
||||
'分析Open Graph协议标签'
|
||||
];
|
||||
|
||||
setTimeout(() => {
|
||||
this.showSuccessMessage('欢迎使用链接OG信息分析器!');
|
||||
}, 1000);
|
||||
|
||||
// 显示提示信息
|
||||
this.showTips(tips);
|
||||
}
|
||||
|
||||
// 显示提示信息
|
||||
showTips(tips) {
|
||||
const tipElement = document.getElementById('tip-message');
|
||||
const tipText = tipElement.querySelector('.tip-text');
|
||||
|
||||
let currentTip = 0;
|
||||
|
||||
const showNextTip = () => {
|
||||
tipText.textContent = tips[currentTip];
|
||||
tipElement.classList.add('active');
|
||||
tipElement.style.animation = 'fadeInUp 0.5s ease-out';
|
||||
|
||||
setTimeout(() => {
|
||||
tipElement.style.animation = 'fadeOutDown 0.5s ease-in';
|
||||
setTimeout(() => {
|
||||
tipElement.classList.remove('active');
|
||||
currentTip = (currentTip + 1) % tips.length;
|
||||
}, 500);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
// 首次显示
|
||||
setTimeout(showNextTip, 2000);
|
||||
|
||||
// 每8秒显示一次
|
||||
setInterval(showNextTip, 8000);
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
function throttle(func, limit) {
|
||||
let inThrottle;
|
||||
return function() {
|
||||
const args = arguments;
|
||||
const context = this;
|
||||
if (!inThrottle) {
|
||||
func.apply(context, args);
|
||||
inThrottle = true;
|
||||
setTimeout(() => inThrottle = false, limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 检查必要的DOM元素
|
||||
const requiredElements = [
|
||||
'url-input', 'analyze-btn', 'copy-btn', 'clear-btn',
|
||||
'loading', 'results', 'error-message', 'tip-message'
|
||||
];
|
||||
|
||||
const missingElements = requiredElements.filter(id => !document.getElementById(id));
|
||||
|
||||
if (missingElements.length > 0) {
|
||||
console.error('缺少必要的DOM元素:', missingElements);
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
window.ogAnalyzer = new OGAnalyzer();
|
||||
|
||||
// 添加全局错误处理
|
||||
window.addEventListener('error', (e) => {
|
||||
console.error('全局错误:', e.error);
|
||||
if (window.ogAnalyzer) {
|
||||
window.ogAnalyzer.showError('发生未知错误,请刷新页面重试');
|
||||
}
|
||||
});
|
||||
|
||||
// 添加网络状态监听
|
||||
window.addEventListener('online', () => {
|
||||
if (window.ogAnalyzer) {
|
||||
window.ogAnalyzer.showSuccessMessage('网络连接已恢复');
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('offline', () => {
|
||||
if (window.ogAnalyzer) {
|
||||
window.ogAnalyzer.showError('网络连接已断开');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 导出给其他模块使用
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = { OGAnalyzer, debounce, throttle };
|
||||
}
|
||||
3
frontend/60sapi/实用功能/链接OG信息/接口集合.json
Normal file
3
frontend/60sapi/实用功能/链接OG信息/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
66
frontend/60sapi/实用功能/链接OG信息/返回接口.json
Normal file
66
frontend/60sapi/实用功能/链接OG信息/返回接口.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"url": "https://example.com",
|
||||
"title": "示例网站标题",
|
||||
"description": "这是一个示例网站的描述信息,用于展示OG标签解析功能。",
|
||||
"image": "https://example.com/og-image.jpg",
|
||||
"site_name": "示例网站",
|
||||
"type": "website",
|
||||
"locale": "zh_CN",
|
||||
"author": "网站作者",
|
||||
"keywords": "示例,网站,OG标签,元数据",
|
||||
"favicon": "https://example.com/favicon.ico",
|
||||
"canonical_url": "https://example.com",
|
||||
"robots": "index,follow",
|
||||
"viewport": "width=device-width, initial-scale=1.0",
|
||||
"charset": "UTF-8",
|
||||
"language": "zh-CN",
|
||||
"published_time": "2024-01-01T00:00:00Z",
|
||||
"modified_time": "2024-01-15T12:30:00Z",
|
||||
"section": "技术",
|
||||
"tags": ["前端", "元数据", "SEO"],
|
||||
"twitter": {
|
||||
"card": "summary_large_image",
|
||||
"site": "@example",
|
||||
"creator": "@author",
|
||||
"title": "Twitter标题",
|
||||
"description": "Twitter描述",
|
||||
"image": "https://example.com/twitter-image.jpg"
|
||||
},
|
||||
"facebook": {
|
||||
"app_id": "123456789",
|
||||
"admins": "987654321"
|
||||
},
|
||||
"structured_data": {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebPage",
|
||||
"name": "示例网页",
|
||||
"description": "示例网页描述",
|
||||
"url": "https://example.com"
|
||||
},
|
||||
"meta_tags": {
|
||||
"generator": "WordPress 6.0",
|
||||
"theme-color": "#000000",
|
||||
"msapplication-TileColor": "#ffffff",
|
||||
"apple-mobile-web-app-capable": "yes",
|
||||
"apple-mobile-web-app-status-bar-style": "default"
|
||||
},
|
||||
"performance": {
|
||||
"load_time": 1.25,
|
||||
"page_size": "2.3MB",
|
||||
"requests_count": 45
|
||||
},
|
||||
"seo_score": {
|
||||
"overall": 85,
|
||||
"title_score": 90,
|
||||
"description_score": 80,
|
||||
"image_score": 85,
|
||||
"structure_score": 88
|
||||
}
|
||||
},
|
||||
"timestamp": "2024-01-15T12:30:45Z",
|
||||
"request_id": "req_123456789",
|
||||
"processing_time": 0.85
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
107
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/css/background.css
Normal file
107
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/css/background.css
Normal file
@@ -0,0 +1,107 @@
|
||||
.background-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.green-gradient {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 175, 80, 0.3) 0%,
|
||||
rgba(129, 199, 132, 0.2) 25%,
|
||||
rgba(165, 214, 167, 0.1) 50%,
|
||||
rgba(200, 230, 201, 0.2) 75%,
|
||||
rgba(76, 175, 80, 0.3) 100%
|
||||
);
|
||||
animation: green-flow 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.green-gradient::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(
|
||||
circle at 30% 70%,
|
||||
rgba(139, 195, 74, 0.4) 0%,
|
||||
transparent 50%
|
||||
), radial-gradient(
|
||||
circle at 70% 30%,
|
||||
rgba(102, 187, 106, 0.3) 0%,
|
||||
transparent 50%
|
||||
);
|
||||
animation: green-pulse 15s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes green-flow {
|
||||
0%, 100% {
|
||||
transform: rotate(0deg) scale(1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
25% {
|
||||
transform: rotate(90deg) scale(1.1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: rotate(180deg) scale(0.9);
|
||||
opacity: 0.9;
|
||||
}
|
||||
75% {
|
||||
transform: rotate(270deg) scale(1.05);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes green-pulse {
|
||||
0% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2) rotate(180deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) rotate(360deg);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端背景优化 */
|
||||
@media (max-width: 768px) {
|
||||
.green-gradient {
|
||||
animation-duration: 25s;
|
||||
}
|
||||
|
||||
.green-gradient::before {
|
||||
animation-duration: 18s;
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画以节省电池 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.green-gradient,
|
||||
.green-gradient::before {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.green-gradient {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 175, 80, 0.2) 0%,
|
||||
rgba(165, 214, 167, 0.1) 50%,
|
||||
rgba(200, 230, 201, 0.15) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
248
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/css/style.css
Normal file
248
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/css/style.css
Normal file
@@ -0,0 +1,248 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
color: #333;
|
||||
background-color: #f0f8f0;
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 128, 0, 0.1);
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #e8f5e8;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: #2d8f47;
|
||||
margin-bottom: 12px;
|
||||
font-size: 2.2rem;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 2px 4px rgba(45, 143, 71, 0.1);
|
||||
}
|
||||
|
||||
.update-time {
|
||||
color: #6b8e6b;
|
||||
font-size: 0.95rem;
|
||||
background-color: #f0f8f0;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hot-list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.hot-item {
|
||||
padding: 18px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
box-shadow: 0 3px 8px rgba(0, 128, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
|
||||
.hot-item:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 16px rgba(0, 128, 0, 0.15);
|
||||
border-left-color: #4caf50;
|
||||
}
|
||||
|
||||
.hot-rank {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #4caf50;
|
||||
margin-right: 18px;
|
||||
min-width: 35px;
|
||||
text-align: center;
|
||||
background-color: #f0f8f0;
|
||||
border-radius: 50%;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hot-rank.top-1 {
|
||||
background: linear-gradient(135deg, #ff6b6b, #ff8e8e);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hot-rank.top-2 {
|
||||
background: linear-gradient(135deg, #ffa726, #ffb74d);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hot-rank.top-3 {
|
||||
background: linear-gradient(135deg, #ffca28, #ffd54f);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hot-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hot-title {
|
||||
font-size: 1.15rem;
|
||||
margin-bottom: 6px;
|
||||
color: #2c3e2c;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
line-height: 1.4;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hot-title:hover {
|
||||
color: #2d8f47;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #6b8e6b;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e8f5e8;
|
||||
color: #6b8e6b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container {
|
||||
max-width: 90%;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.hot-item {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.hot-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大768px) */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
background-color: #f8fdf8;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 95%;
|
||||
margin: 10px auto;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.update-time {
|
||||
font-size: 0.85rem;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.hot-item {
|
||||
padding: 14px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 8px;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.hot-rank {
|
||||
font-size: 1.1rem;
|
||||
margin-right: 12px;
|
||||
min-width: 30px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.hot-title {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
margin: 5px auto;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.hot-item {
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.hot-rank {
|
||||
font-size: 1rem;
|
||||
margin-right: 10px;
|
||||
min-width: 28px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.hot-title {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.update-time {
|
||||
font-size: 0.8rem;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
}
|
||||
34
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/index.html
Normal file
34
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/index.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>哔哩哔哩热搜榜</title>
|
||||
<link rel="stylesheet" href="./css/style.css">
|
||||
<link rel="stylesheet" href="./css/background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="background-container">
|
||||
<div class="green-gradient"></div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>哔哩哔哩热搜榜</h1>
|
||||
<div class="update-time" id="updateTime"></div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="hot-list" id="hotList">
|
||||
<div class="loading">加载中...</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>数据来源于哔哩哔哩热搜榜</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="./js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
134
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/js/main.js
Normal file
134
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/js/main.js
Normal file
@@ -0,0 +1,134 @@
|
||||
// API接口列表
|
||||
const API_ENDPOINTS = [
|
||||
"https://60s.viki.moe/v2/bili",
|
||||
"https://60s-cf.viki.moe/v2/bili",
|
||||
"https://60s.b23.run/v2/bili",
|
||||
"https://60s.114128.xyz/v2/bili",
|
||||
"https://60s-cf.114128.xyz/v2/bili"
|
||||
];
|
||||
|
||||
// 当前使用的API索引
|
||||
let currentApiIndex = 0;
|
||||
|
||||
// DOM元素
|
||||
const hotListElement = document.getElementById('hotList');
|
||||
const updateTimeElement = document.getElementById('updateTime');
|
||||
|
||||
// 格式化时间
|
||||
function formatDate(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
// 渲染热搜列表
|
||||
function renderHotList(data) {
|
||||
hotListElement.innerHTML = '';
|
||||
|
||||
data.forEach((item, index) => {
|
||||
const hotItem = document.createElement('div');
|
||||
hotItem.className = 'hot-item';
|
||||
|
||||
const rankClass = index < 3 ? `top-${index + 1}` : '';
|
||||
|
||||
hotItem.innerHTML = `
|
||||
<div class="hot-rank ${rankClass}">${index + 1}</div>
|
||||
<div class="hot-content">
|
||||
<a href="${item.link}" class="hot-title" target="_blank">${item.title}</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
hotListElement.appendChild(hotItem);
|
||||
});
|
||||
|
||||
// 更新时间
|
||||
updateTimeElement.textContent = `更新时间:${formatDate(new Date())}`;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
function showLoading() {
|
||||
hotListElement.innerHTML = '<div class="loading">加载中...</div>';
|
||||
}
|
||||
|
||||
// 显示错误状态
|
||||
function showError(message) {
|
||||
hotListElement.innerHTML = `<div class="loading">${message}</div>`;
|
||||
}
|
||||
|
||||
// 获取哔哩哔哩热搜数据
|
||||
async function fetchBiliHotList() {
|
||||
showLoading();
|
||||
|
||||
try {
|
||||
const response = await fetch(API_ENDPOINTS[currentApiIndex]);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 200 && result.data && Array.isArray(result.data)) {
|
||||
if (result.data.length > 0) {
|
||||
renderHotList(result.data);
|
||||
} else {
|
||||
showError('暂无热搜数据');
|
||||
}
|
||||
} else {
|
||||
throw new Error('数据格式错误或无数据');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
|
||||
// 尝试切换到下一个API
|
||||
const nextApiIndex = (currentApiIndex + 1) % API_ENDPOINTS.length;
|
||||
|
||||
if (nextApiIndex !== 0) {
|
||||
// 还有其他API可以尝试
|
||||
currentApiIndex = nextApiIndex;
|
||||
showError('获取数据失败,正在尝试其他接口...');
|
||||
|
||||
// 延迟后重试
|
||||
setTimeout(fetchBiliHotList, 2000);
|
||||
} else {
|
||||
// 所有API都尝试过了
|
||||
currentApiIndex = 0;
|
||||
showError('所有接口都无法访问,请稍后再试');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手动刷新数据
|
||||
function refreshData() {
|
||||
currentApiIndex = 0; // 重置API索引
|
||||
fetchBiliHotList();
|
||||
}
|
||||
|
||||
// 页面加载完成后获取数据
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
fetchBiliHotList();
|
||||
|
||||
// 每隔5分钟刷新一次数据
|
||||
setInterval(fetchBiliHotList, 5 * 60 * 1000);
|
||||
|
||||
// 添加键盘快捷键支持(按R键刷新)
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'r' || event.key === 'R') {
|
||||
event.preventDefault();
|
||||
refreshData();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 页面可见性变化时的处理
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) {
|
||||
// 页面重新可见时刷新数据
|
||||
refreshData();
|
||||
}
|
||||
});
|
||||
3
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/接口集合.json
Normal file
3
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
18
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/返回接口.json
Normal file
18
frontend/60sapi/热搜榜单/哔哩哔哩热搜榜/返回接口.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": [
|
||||
{
|
||||
"title": "18次阅兵76年逆袭路",
|
||||
"link": "https://search.bilibili.com/all?keyword=18%E6%AC%A1%E9%98%85%E5%85%B576%E5%B9%B4%E9%80%86%E8%A2%AD%E8%B7%AF"
|
||||
},
|
||||
{
|
||||
"title": "80年前的今天日本签署投降书",
|
||||
"link": "https://search.bilibili.com/all?keyword=80%E5%B9%B4%E5%89%8D%E7%9A%84%E4%BB%8A%E5%A4%A9%E6%97%A5%E6%9C%AC%E7%AD%BE%E7%BD%B2%E6%8A%95%E9%99%8D%E4%B9%A6"
|
||||
},
|
||||
{
|
||||
"title": "九三阅兵具体安排公布",
|
||||
"link": "https://search.bilibili.com/all?keyword=%E4%B9%9D%E4%B8%89%E9%98%85%E5%85%B5%E5%85%B7%E4%BD%93%E5%AE%89%E6%8E%92%E5%85%AC%E5%B8%83"
|
||||
}
|
||||
]
|
||||
}
|
||||
107
frontend/60sapi/热搜榜单/头条热搜榜/css/background.css
Normal file
107
frontend/60sapi/热搜榜单/头条热搜榜/css/background.css
Normal file
@@ -0,0 +1,107 @@
|
||||
.background-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.green-gradient {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 175, 80, 0.3) 0%,
|
||||
rgba(129, 199, 132, 0.2) 25%,
|
||||
rgba(165, 214, 167, 0.1) 50%,
|
||||
rgba(200, 230, 201, 0.2) 75%,
|
||||
rgba(76, 175, 80, 0.3) 100%
|
||||
);
|
||||
animation: green-flow 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.green-gradient::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(
|
||||
circle at 30% 70%,
|
||||
rgba(139, 195, 74, 0.4) 0%,
|
||||
transparent 50%
|
||||
), radial-gradient(
|
||||
circle at 70% 30%,
|
||||
rgba(102, 187, 106, 0.3) 0%,
|
||||
transparent 50%
|
||||
);
|
||||
animation: green-pulse 15s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes green-flow {
|
||||
0%, 100% {
|
||||
transform: rotate(0deg) scale(1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
25% {
|
||||
transform: rotate(90deg) scale(1.1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: rotate(180deg) scale(0.9);
|
||||
opacity: 0.9;
|
||||
}
|
||||
75% {
|
||||
transform: rotate(270deg) scale(1.05);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes green-pulse {
|
||||
0% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2) rotate(180deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) rotate(360deg);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端背景优化 */
|
||||
@media (max-width: 768px) {
|
||||
.green-gradient {
|
||||
animation-duration: 25s;
|
||||
}
|
||||
|
||||
.green-gradient::before {
|
||||
animation-duration: 18s;
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画以节省电池 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.green-gradient,
|
||||
.green-gradient::before {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.green-gradient {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 175, 80, 0.2) 0%,
|
||||
rgba(165, 214, 167, 0.1) 50%,
|
||||
rgba(200, 230, 201, 0.15) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
291
frontend/60sapi/热搜榜单/头条热搜榜/css/style.css
Normal file
291
frontend/60sapi/热搜榜单/头条热搜榜/css/style.css
Normal file
@@ -0,0 +1,291 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
color: #333;
|
||||
background-color: #f0f8f0;
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 128, 0, 0.1);
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #e8f5e8;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: #2d8f47;
|
||||
margin-bottom: 12px;
|
||||
font-size: 2.2rem;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 2px 4px rgba(45, 143, 71, 0.1);
|
||||
}
|
||||
|
||||
.update-time {
|
||||
color: #6b8e6b;
|
||||
font-size: 0.95rem;
|
||||
background-color: #f0f8f0;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hot-list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.hot-item {
|
||||
padding: 18px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
box-shadow: 0 3px 8px rgba(0, 128, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-left: 4px solid transparent;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hot-item:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 16px rgba(0, 128, 0, 0.15);
|
||||
border-left-color: #4caf50;
|
||||
}
|
||||
|
||||
.hot-rank {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #4caf50;
|
||||
margin-right: 18px;
|
||||
min-width: 35px;
|
||||
text-align: center;
|
||||
background-color: #f0f8f0;
|
||||
border-radius: 50%;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hot-rank.top-1 {
|
||||
background: linear-gradient(135deg, #ff6b6b, #ff8e8e);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hot-rank.top-2 {
|
||||
background: linear-gradient(135deg, #ffa726, #ffb74d);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hot-rank.top-3 {
|
||||
background: linear-gradient(135deg, #ffca28, #ffd54f);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hot-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hot-title {
|
||||
font-size: 1.15rem;
|
||||
margin-bottom: 6px;
|
||||
color: #2c3e2c;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
line-height: 1.4;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hot-title:hover {
|
||||
color: #2d8f47;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.hot-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
font-size: 0.85rem;
|
||||
color: #888;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.hot-value {
|
||||
background: linear-gradient(135deg, #4caf50, #66bb6a);
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.hot-tag {
|
||||
background-color: #e8f5e8;
|
||||
color: #2d8f47;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #6b8e6b;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e8f5e8;
|
||||
color: #6b8e6b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container {
|
||||
max-width: 90%;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.hot-item {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.hot-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大768px) */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
background-color: #f8fdf8;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 95%;
|
||||
margin: 10px auto;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.update-time {
|
||||
font-size: 0.85rem;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.hot-item {
|
||||
padding: 14px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 8px;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.hot-rank {
|
||||
font-size: 1.1rem;
|
||||
margin-right: 12px;
|
||||
min-width: 30px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.hot-title {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.hot-stats {
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
margin: 5px auto;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.hot-item {
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.hot-rank {
|
||||
font-size: 1rem;
|
||||
margin-right: 10px;
|
||||
min-width: 28px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.hot-title {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.update-time {
|
||||
font-size: 0.8rem;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
}
|
||||
34
frontend/60sapi/热搜榜单/头条热搜榜/index.html
Normal file
34
frontend/60sapi/热搜榜单/头条热搜榜/index.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>头条热搜榜</title>
|
||||
<link rel="stylesheet" href="./css/style.css">
|
||||
<link rel="stylesheet" href="./css/background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="background-container">
|
||||
<div class="green-gradient"></div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>头条热搜榜</h1>
|
||||
<div class="update-time" id="updateTime"></div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="hot-list" id="hotList">
|
||||
<div class="loading">加载中...</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>数据来源于头条热搜榜</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="./js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
156
frontend/60sapi/热搜榜单/头条热搜榜/js/main.js
Normal file
156
frontend/60sapi/热搜榜单/头条热搜榜/js/main.js
Normal file
@@ -0,0 +1,156 @@
|
||||
// API接口列表
|
||||
const API_ENDPOINTS = [
|
||||
"https://60s.viki.moe/v2/toutiao",
|
||||
"https://60s-cf.viki.moe/v2/toutiao",
|
||||
"https://60s.b23.run/v2/toutiao",
|
||||
"https://60s.114128.xyz/v2/toutiao",
|
||||
"https://60s-cf.114128.xyz/v2/toutiao"
|
||||
];
|
||||
|
||||
// 当前使用的API索引
|
||||
let currentApiIndex = 0;
|
||||
|
||||
// DOM元素
|
||||
const hotListElement = document.getElementById('hotList');
|
||||
const updateTimeElement = document.getElementById('updateTime');
|
||||
|
||||
// 格式化时间
|
||||
function formatDate(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
// 格式化数字
|
||||
function formatNumber(num) {
|
||||
if (num >= 10000) {
|
||||
return (num / 10000).toFixed(1) + '万';
|
||||
}
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
// 渲染热搜列表
|
||||
function renderHotList(data) {
|
||||
hotListElement.innerHTML = '';
|
||||
|
||||
data.forEach((item, index) => {
|
||||
const hotItem = document.createElement('div');
|
||||
hotItem.className = 'hot-item';
|
||||
|
||||
const rankClass = index < 3 ? `top-${index + 1}` : '';
|
||||
|
||||
// 处理热度值显示
|
||||
const hotValueDisplay = item.hot_value ?
|
||||
`<div class="stat-item"><span class="hot-value">${formatNumber(item.hot_value)} 热度</span></div>` : '';
|
||||
|
||||
// 处理标签显示
|
||||
const tagDisplay = item.tag ?
|
||||
`<div class="stat-item"><span class="hot-tag">${item.tag}</span></div>` : '';
|
||||
|
||||
hotItem.innerHTML = `
|
||||
<div class="hot-rank ${rankClass}">${index + 1}</div>
|
||||
<div class="hot-content">
|
||||
<a href="${item.link}" class="hot-title" target="_blank">${item.title}</a>
|
||||
<div class="hot-stats">
|
||||
${hotValueDisplay}
|
||||
${tagDisplay}
|
||||
${item.source ? `<div class="stat-item">📰 ${item.source}</div>` : ''}
|
||||
${item.time ? `<div class="stat-item">🕒 ${item.time}</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
hotListElement.appendChild(hotItem);
|
||||
});
|
||||
|
||||
// 更新时间
|
||||
updateTimeElement.textContent = `更新时间:${formatDate(new Date())}`;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
function showLoading() {
|
||||
hotListElement.innerHTML = '<div class="loading">加载中...</div>';
|
||||
}
|
||||
|
||||
// 显示错误状态
|
||||
function showError(message) {
|
||||
hotListElement.innerHTML = `<div class="loading">${message}</div>`;
|
||||
}
|
||||
|
||||
// 获取头条热搜数据
|
||||
async function fetchToutiaoHotList() {
|
||||
showLoading();
|
||||
|
||||
try {
|
||||
const response = await fetch(API_ENDPOINTS[currentApiIndex]);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 200 && result.data && Array.isArray(result.data)) {
|
||||
if (result.data.length > 0) {
|
||||
renderHotList(result.data);
|
||||
} else {
|
||||
showError('暂无热搜数据');
|
||||
}
|
||||
} else {
|
||||
throw new Error('数据格式错误或无数据');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
|
||||
// 尝试切换到下一个API
|
||||
const nextApiIndex = (currentApiIndex + 1) % API_ENDPOINTS.length;
|
||||
|
||||
if (nextApiIndex !== 0) {
|
||||
// 还有其他API可以尝试
|
||||
currentApiIndex = nextApiIndex;
|
||||
showError('获取数据失败,正在尝试其他接口...');
|
||||
|
||||
// 延迟后重试
|
||||
setTimeout(fetchToutiaoHotList, 2000);
|
||||
} else {
|
||||
// 所有API都尝试过了
|
||||
currentApiIndex = 0;
|
||||
showError('所有接口都无法访问,请稍后再试');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手动刷新数据
|
||||
function refreshData() {
|
||||
currentApiIndex = 0; // 重置API索引
|
||||
fetchToutiaoHotList();
|
||||
}
|
||||
|
||||
// 页面加载完成后获取数据
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
fetchToutiaoHotList();
|
||||
|
||||
// 每隔5分钟刷新一次数据
|
||||
setInterval(fetchToutiaoHotList, 5 * 60 * 1000);
|
||||
|
||||
// 添加键盘快捷键支持(按R键刷新)
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'r' || event.key === 'R') {
|
||||
event.preventDefault();
|
||||
refreshData();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 页面可见性变化时的处理
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) {
|
||||
// 页面重新可见时刷新数据
|
||||
refreshData();
|
||||
}
|
||||
});
|
||||
3
frontend/60sapi/热搜榜单/头条热搜榜/接口集合.json
Normal file
3
frontend/60sapi/热搜榜单/头条热搜榜/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
30
frontend/60sapi/热搜榜单/头条热搜榜/返回接口.json
Normal file
30
frontend/60sapi/热搜榜单/头条热搜榜/返回接口.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": [
|
||||
{
|
||||
"title": "九三阅兵具体安排公布",
|
||||
"hot_value": 11821633,
|
||||
"tag": "热",
|
||||
"source": "头条新闻",
|
||||
"time": "2小时前",
|
||||
"link": "https://www.toutiao.com/article/7404567890123456789/"
|
||||
},
|
||||
{
|
||||
"title": "九月第一天",
|
||||
"hot_value": 11327170,
|
||||
"tag": "新",
|
||||
"source": "今日头条",
|
||||
"time": "1小时前",
|
||||
"link": "https://www.toutiao.com/article/7404567890123456790/"
|
||||
},
|
||||
{
|
||||
"title": "遇见上合共享津彩",
|
||||
"hot_value": 11222444,
|
||||
"tag": "推荐",
|
||||
"source": "头条资讯",
|
||||
"time": "3小时前",
|
||||
"link": "https://www.toutiao.com/article/7404567890123456791/"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
// API接口列表
|
||||
// 本地后端API接口
|
||||
const LOCAL_API_BASE = 'http://localhost:5000/api/60s';
|
||||
|
||||
// API接口列表(备用)
|
||||
const API_ENDPOINTS = [
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
@@ -9,6 +12,7 @@ const API_ENDPOINTS = [
|
||||
|
||||
// 当前使用的API索引
|
||||
let currentApiIndex = 0;
|
||||
let useLocalApi = true;
|
||||
|
||||
// DOM元素
|
||||
const loadingElement = document.getElementById('loading');
|
||||
@@ -46,6 +50,30 @@ async function loadHotList() {
|
||||
|
||||
// 获取数据
|
||||
async function fetchData() {
|
||||
// 优先尝试本地API
|
||||
if (useLocalApi) {
|
||||
try {
|
||||
const response = await fetch(`${LOCAL_API_BASE}/douyin`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.code === 200 && data.data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('本地API请求失败,切换到外部API:', error);
|
||||
useLocalApi = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用外部API作为备用
|
||||
for (let i = 0; i < API_ENDPOINTS.length; i++) {
|
||||
const apiUrl = API_ENDPOINTS[currentApiIndex];
|
||||
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
107
frontend/60sapi/热搜榜单/知乎热门话题/css/background.css
Normal file
107
frontend/60sapi/热搜榜单/知乎热门话题/css/background.css
Normal file
@@ -0,0 +1,107 @@
|
||||
.background-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.green-gradient {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 175, 80, 0.3) 0%,
|
||||
rgba(129, 199, 132, 0.2) 25%,
|
||||
rgba(165, 214, 167, 0.1) 50%,
|
||||
rgba(200, 230, 201, 0.2) 75%,
|
||||
rgba(76, 175, 80, 0.3) 100%
|
||||
);
|
||||
animation: green-flow 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.green-gradient::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(
|
||||
circle at 30% 70%,
|
||||
rgba(139, 195, 74, 0.4) 0%,
|
||||
transparent 50%
|
||||
), radial-gradient(
|
||||
circle at 70% 30%,
|
||||
rgba(102, 187, 106, 0.3) 0%,
|
||||
transparent 50%
|
||||
);
|
||||
animation: green-pulse 15s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes green-flow {
|
||||
0%, 100% {
|
||||
transform: rotate(0deg) scale(1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
25% {
|
||||
transform: rotate(90deg) scale(1.1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: rotate(180deg) scale(0.9);
|
||||
opacity: 0.9;
|
||||
}
|
||||
75% {
|
||||
transform: rotate(270deg) scale(1.05);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes green-pulse {
|
||||
0% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2) rotate(180deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) rotate(360deg);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端背景优化 */
|
||||
@media (max-width: 768px) {
|
||||
.green-gradient {
|
||||
animation-duration: 25s;
|
||||
}
|
||||
|
||||
.green-gradient::before {
|
||||
animation-duration: 18s;
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画以节省电池 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.green-gradient,
|
||||
.green-gradient::before {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.green-gradient {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(76, 175, 80, 0.2) 0%,
|
||||
rgba(165, 214, 167, 0.1) 50%,
|
||||
rgba(200, 230, 201, 0.15) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
331
frontend/60sapi/热搜榜单/知乎热门话题/css/style.css
Normal file
331
frontend/60sapi/热搜榜单/知乎热门话题/css/style.css
Normal file
@@ -0,0 +1,331 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
color: #333;
|
||||
background-color: #f0f8f0;
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 128, 0, 0.1);
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #e8f5e8;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: #2d8f47;
|
||||
margin-bottom: 12px;
|
||||
font-size: 2.2rem;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 2px 4px rgba(45, 143, 71, 0.1);
|
||||
}
|
||||
|
||||
.update-time {
|
||||
color: #6b8e6b;
|
||||
font-size: 0.95rem;
|
||||
background-color: #f0f8f0;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.topic-list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.topic-item {
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 12px;
|
||||
background-color: white;
|
||||
box-shadow: 0 3px 10px rgba(0, 128, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
border-left: 4px solid transparent;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.topic-item:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(0, 128, 0, 0.15);
|
||||
border-left-color: #4caf50;
|
||||
}
|
||||
|
||||
.topic-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.topic-rank {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: #4caf50;
|
||||
margin-right: 18px;
|
||||
min-width: 35px;
|
||||
text-align: center;
|
||||
background-color: #f0f8f0;
|
||||
border-radius: 50%;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.topic-rank.top-1 {
|
||||
background: linear-gradient(135deg, #ff6b6b, #ff8e8e);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.topic-rank.top-2 {
|
||||
background: linear-gradient(135deg, #ffa726, #ffb74d);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.topic-rank.top-3 {
|
||||
background: linear-gradient(135deg, #ffca28, #ffd54f);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.topic-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.topic-title {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 8px;
|
||||
color: #2c3e2c;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.topic-title:hover {
|
||||
color: #2d8f47;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.topic-detail {
|
||||
font-size: 0.95rem;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 10px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.topic-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
font-size: 0.85rem;
|
||||
color: #888;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.hot-value {
|
||||
background: linear-gradient(135deg, #4caf50, #66bb6a);
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.topic-cover {
|
||||
width: 80px;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
margin-left: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #6b8e6b;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e8f5e8;
|
||||
color: #6b8e6b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container {
|
||||
max-width: 90%;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.topic-item {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.topic-title {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.topic-cover {
|
||||
width: 70px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大768px) */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
background-color: #f8fdf8;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 95%;
|
||||
margin: 10px auto;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.update-time {
|
||||
font-size: 0.85rem;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.topic-item {
|
||||
padding: 16px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.topic-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.topic-rank {
|
||||
font-size: 1.1rem;
|
||||
margin-right: 0;
|
||||
margin-bottom: 10px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.topic-title {
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.topic-detail {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.topic-stats {
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.topic-cover {
|
||||
width: 60px;
|
||||
height: 45px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
padding-top: 15px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
margin: 5px auto;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.topic-item {
|
||||
padding: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.topic-rank {
|
||||
font-size: 1rem;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.topic-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.topic-detail {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.topic-cover {
|
||||
width: 50px;
|
||||
height: 38px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.update-time {
|
||||
font-size: 0.8rem;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
}
|
||||
34
frontend/60sapi/热搜榜单/知乎热门话题/index.html
Normal file
34
frontend/60sapi/热搜榜单/知乎热门话题/index.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>知乎热门话题</title>
|
||||
<link rel="stylesheet" href="./css/style.css">
|
||||
<link rel="stylesheet" href="./css/background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="background-container">
|
||||
<div class="green-gradient"></div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>知乎热门话题</h1>
|
||||
<div class="update-time" id="updateTime"></div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="topic-list" id="topicList">
|
||||
<div class="loading">加载中...</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>数据来源于知乎热门话题</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="./js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
155
frontend/60sapi/热搜榜单/知乎热门话题/js/main.js
Normal file
155
frontend/60sapi/热搜榜单/知乎热门话题/js/main.js
Normal file
@@ -0,0 +1,155 @@
|
||||
// API接口列表
|
||||
const API_ENDPOINTS = [
|
||||
"https://60s.viki.moe/v2/zhihu",
|
||||
"https://60s-cf.viki.moe/v2/zhihu",
|
||||
"https://60s.b23.run/v2/zhihu",
|
||||
"https://60s.114128.xyz/v2/zhihu",
|
||||
"https://60s-cf.114128.xyz/v2/zhihu"
|
||||
];
|
||||
|
||||
// 当前使用的API索引
|
||||
let currentApiIndex = 0;
|
||||
|
||||
// DOM元素
|
||||
const topicListElement = document.getElementById('topicList');
|
||||
const updateTimeElement = document.getElementById('updateTime');
|
||||
|
||||
// 格式化时间
|
||||
function formatDate(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
// 格式化数字
|
||||
function formatNumber(num) {
|
||||
if (num >= 10000) {
|
||||
return (num / 10000).toFixed(1) + '万';
|
||||
}
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
// 渲染话题列表
|
||||
function renderTopicList(data) {
|
||||
topicListElement.innerHTML = '';
|
||||
|
||||
data.forEach((item, index) => {
|
||||
const topicItem = document.createElement('div');
|
||||
topicItem.className = 'topic-item';
|
||||
|
||||
const rankClass = index < 3 ? `top-${index + 1}` : '';
|
||||
|
||||
// 处理封面图片
|
||||
const coverImg = item.cover ?
|
||||
`<img src="${item.cover}" alt="话题封面" class="topic-cover" onerror="this.style.display='none'">` : '';
|
||||
|
||||
topicItem.innerHTML = `
|
||||
<div class="topic-header">
|
||||
<div class="topic-rank ${rankClass}">${index + 1}</div>
|
||||
<div class="topic-content">
|
||||
<a href="${item.link}" class="topic-title" target="_blank">${item.title}</a>
|
||||
${item.detail ? `<div class="topic-detail">${item.detail}</div>` : ''}
|
||||
<div class="topic-stats">
|
||||
${item.hot_value_desc ? `<div class="stat-item"><span class="hot-value">${item.hot_value_desc}</span></div>` : ''}
|
||||
${item.answer_cnt ? `<div class="stat-item">💬 ${formatNumber(item.answer_cnt)} 回答</div>` : ''}
|
||||
${item.follower_cnt ? `<div class="stat-item">👥 ${formatNumber(item.follower_cnt)} 关注</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
${coverImg}
|
||||
</div>
|
||||
`;
|
||||
|
||||
topicListElement.appendChild(topicItem);
|
||||
});
|
||||
|
||||
// 更新时间
|
||||
updateTimeElement.textContent = `更新时间:${formatDate(new Date())}`;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
function showLoading() {
|
||||
topicListElement.innerHTML = '<div class="loading">加载中...</div>';
|
||||
}
|
||||
|
||||
// 显示错误状态
|
||||
function showError(message) {
|
||||
topicListElement.innerHTML = `<div class="loading">${message}</div>`;
|
||||
}
|
||||
|
||||
// 获取知乎热门话题数据
|
||||
async function fetchZhihuTopics() {
|
||||
showLoading();
|
||||
|
||||
try {
|
||||
const response = await fetch(API_ENDPOINTS[currentApiIndex]);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 200 && result.data && Array.isArray(result.data)) {
|
||||
if (result.data.length > 0) {
|
||||
renderTopicList(result.data);
|
||||
} else {
|
||||
showError('暂无热门话题数据');
|
||||
}
|
||||
} else {
|
||||
throw new Error('数据格式错误或无数据');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
|
||||
// 尝试切换到下一个API
|
||||
const nextApiIndex = (currentApiIndex + 1) % API_ENDPOINTS.length;
|
||||
|
||||
if (nextApiIndex !== 0) {
|
||||
// 还有其他API可以尝试
|
||||
currentApiIndex = nextApiIndex;
|
||||
showError('获取数据失败,正在尝试其他接口...');
|
||||
|
||||
// 延迟后重试
|
||||
setTimeout(fetchZhihuTopics, 2000);
|
||||
} else {
|
||||
// 所有API都尝试过了
|
||||
currentApiIndex = 0;
|
||||
showError('所有接口都无法访问,请稍后再试');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手动刷新数据
|
||||
function refreshData() {
|
||||
currentApiIndex = 0; // 重置API索引
|
||||
fetchZhihuTopics();
|
||||
}
|
||||
|
||||
// 页面加载完成后获取数据
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
fetchZhihuTopics();
|
||||
|
||||
// 每隔5分钟刷新一次数据
|
||||
setInterval(fetchZhihuTopics, 5 * 60 * 1000);
|
||||
|
||||
// 添加键盘快捷键支持(按R键刷新)
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'r' || event.key === 'R') {
|
||||
event.preventDefault();
|
||||
refreshData();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 页面可见性变化时的处理
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) {
|
||||
// 页面重新可见时刷新数据
|
||||
refreshData();
|
||||
}
|
||||
});
|
||||
7
frontend/60sapi/热搜榜单/知乎热门话题/接口集合.json
Normal file
7
frontend/60sapi/热搜榜单/知乎热门话题/接口集合.json
Normal 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"
|
||||
]
|
||||
30
frontend/60sapi/热搜榜单/知乎热门话题/返回接口.json
Normal file
30
frontend/60sapi/热搜榜单/知乎热门话题/返回接口.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": [
|
||||
{
|
||||
"title": "你是什么时候意识到抗日战争很艰难的?",
|
||||
"detail": "就不像抗日神剧",
|
||||
"cover": "https://picx.zhimg.com/v2-a07a53b2e23887c7a2440cc3f1984122.png",
|
||||
"hot_value_desc": "1311 万热度",
|
||||
"answer_cnt": 739,
|
||||
"follower_cnt": 1739,
|
||||
"comment_cnt": 0,
|
||||
"created_at": 1622774952000,
|
||||
"created": "2021/06/04 10:49:12",
|
||||
"link": "https://api.zhihu.com/questions/463076881"
|
||||
},
|
||||
{
|
||||
"title": "为什么中国在很多领域都要求自主研发?",
|
||||
"detail": "中国在很多领域都要求自主研发,似乎确认很多东西都有个中国版本,真有这个必要吗?",
|
||||
"cover": "https://pic3.zhimg.com/80/v2-bb4dfa56f138980078da003df436e661_hd.png",
|
||||
"hot_value_desc": "816 万热度",
|
||||
"answer_cnt": 2476,
|
||||
"follower_cnt": 12545,
|
||||
"comment_cnt": 0,
|
||||
"created_at": 1352205751000,
|
||||
"created": "2012/11/06 20:42:31",
|
||||
"link": "https://api.zhihu.com/questions/20579464"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
5.返回接口.json储存了网页api返回的数据格式
|
||||
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
|
||||
7.接口集合.json保存了所有已知的后端API接口,一个访问不了尝试自动切换另一个
|
||||
8.在css中有关背景的css单独一个css文件,方便我直接迁移
|
||||
8.在css中有关背景的css单独一个css文件,方便我直接迁移
|
||||
|
||||
BIN
frontend/assets/App Logo 设计 (2).png
Normal file
BIN
frontend/assets/App Logo 设计 (2).png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
frontend/assets/logo.png
Normal file
BIN
frontend/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 930 KiB |
20652
frontend/react-app/package-lock.json
generated
Normal file
20652
frontend/react-app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
49
frontend/react-app/package.json
Normal file
49
frontend/react-app/package.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "infogenie-frontend",
|
||||
"version": "1.0.0",
|
||||
"description": "✨ 神奇万事通 - 前端React应用",
|
||||
"keywords": ["react", "api", "mobile-first", "responsive"],
|
||||
"author": "神奇万事通",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.15.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"axios": "^1.5.0",
|
||||
"styled-components": "^6.0.7",
|
||||
"react-icons": "^4.11.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"dev": "react-scripts start"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"proxy": "http://localhost:5000"
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
565
frontend/react-app/public/60sapi/娱乐消遣/随机JavaScript趣味题/js/script.js
vendored
Normal file
565
frontend/react-app/public/60sapi/娱乐消遣/随机JavaScript趣味题/js/script.js
vendored
Normal 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('网络连接已断开');
|
||||
});
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
@@ -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` 实例都需要这个方法。这将浪费大量内存空间,因为它们仍然具有该属性,这将占用每个实例的内存空间。相反,如果我们只将它添加到原型中,那么它只存在于内存中的一个位置,但是所有实例都可以访问它!"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/* 背景样式文件 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #a8e6cf 0%, #dcedc1 25%, #ffd3a5 50%, #a8e6cf 75%, #88d8a3 100%);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 背景动画 */
|
||||
@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(39, 174, 96, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(46, 204, 113, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(26, 188, 156, 0.05) 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(39, 174, 96, 0.3), transparent),
|
||||
radial-gradient(2px 2px at 40px 70px, rgba(46, 204, 113, 0.2), transparent),
|
||||
radial-gradient(1px 1px at 90px 40px, rgba(26, 188, 156, 0.3), transparent),
|
||||
radial-gradient(1px 1px at 130px 80px, rgba(39, 174, 96, 0.2), transparent),
|
||||
radial-gradient(2px 2px at 160px 30px, rgba(46, 204, 113, 0.3), transparent);
|
||||
background-repeat: repeat;
|
||||
background-size: 200px 100px;
|
||||
animation: floatDots 20s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes floatDots {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 768px) {
|
||||
body::after {
|
||||
background-size: 150px 75px;
|
||||
animation-duration: 25s;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body::after {
|
||||
background-size: 100px 50px;
|
||||
animation-duration: 30s;
|
||||
}
|
||||
}
|
||||
339
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/css/style.css
Normal file
339
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/css/style.css
Normal file
@@ -0,0 +1,339 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #2c3e50;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 容器布局 */
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: #27ae60;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 0 2px 4px rgba(39, 174, 96, 0.2);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: #7f8c8d;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(39, 174, 96, 0.1);
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.content-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* KFC文案内容 */
|
||||
.kfc-content {
|
||||
min-height: 200px;
|
||||
padding: 30px;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
border-radius: 15px;
|
||||
border-left: 5px solid #27ae60;
|
||||
margin-bottom: 30px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.kfc-content::before {
|
||||
content: '"';
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 15px;
|
||||
font-size: 3rem;
|
||||
color: #27ae60;
|
||||
opacity: 0.3;
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
.kfc-content p {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.8;
|
||||
color: #2c3e50;
|
||||
margin-left: 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
text-align: center;
|
||||
color: #7f8c8d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 按钮组 */
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.generate-btn, .copy-btn {
|
||||
padding: 15px 30px;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.generate-btn {
|
||||
background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(39, 174, 96, 0.3);
|
||||
}
|
||||
|
||||
.generate-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(39, 174, 96, 0.4);
|
||||
}
|
||||
|
||||
.generate-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.generate-btn:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: linear-gradient(135deg, #3498db 0%, #5dade2 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(52, 152, 219, 0.3);
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(52, 152, 219, 0.4);
|
||||
}
|
||||
|
||||
/* 编号信息 */
|
||||
.index-info {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
background: rgba(39, 174, 96, 0.1);
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(39, 174, 96, 0.2);
|
||||
}
|
||||
|
||||
.index-text {
|
||||
color: #27ae60;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#indexNumber {
|
||||
color: #2c3e50;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 底部 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
/* 提示框 */
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #27ae60;
|
||||
color: white;
|
||||
padding: 15px 25px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
transform: translateX(400px);
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 1000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.content-card {
|
||||
padding: 35px;
|
||||
max-width: 550px;
|
||||
}
|
||||
|
||||
.kfc-content {
|
||||
padding: 25px;
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.generate-btn, .copy-btn {
|
||||
padding: 12px 25px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大768px) */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.content-card {
|
||||
padding: 25px;
|
||||
margin: 0 5px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.kfc-content {
|
||||
padding: 20px;
|
||||
min-height: 150px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.kfc-content::before {
|
||||
font-size: 2.5rem;
|
||||
top: 5px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.kfc-content p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.7;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.generate-btn, .copy-btn {
|
||||
padding: 12px 20px;
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-size: 0.8rem;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.toast {
|
||||
right: 10px;
|
||||
left: 10px;
|
||||
transform: translateY(-100px);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.content-card {
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.kfc-content {
|
||||
padding: 15px;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.kfc-content p {
|
||||
font-size: 0.95rem;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.generate-btn, .copy-btn {
|
||||
padding: 10px 15px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
46
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/index.html
Normal file
46
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/index.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>随机KFC文案生成器</title>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1 class="title">🍗 随机KFC文案生成器</h1>
|
||||
<p class="subtitle">疯狂星期四,文案来一套!</p>
|
||||
</header>
|
||||
|
||||
<main class="main">
|
||||
<div class="content-card">
|
||||
<div class="kfc-content" id="kfcContent">
|
||||
<p class="loading-text">点击按钮获取随机KFC文案...</p>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button class="generate-btn" id="generateBtn">
|
||||
<span class="btn-text">生成文案</span>
|
||||
<span class="btn-loading" style="display: none;">生成中...</span>
|
||||
</button>
|
||||
<button class="copy-btn" id="copyBtn" style="display: none;">复制文案</button>
|
||||
</div>
|
||||
|
||||
<div class="index-info" id="indexInfo" style="display: none;">
|
||||
<span class="index-text">文案编号: <span id="indexNumber"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>© 2024 KFC文案生成器 | 让每个星期四都疯狂起来</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
240
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/js/main.js
vendored
Normal file
240
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/js/main.js
vendored
Normal file
@@ -0,0 +1,240 @@
|
||||
// KFC文案生成器主要功能
|
||||
class KFCGenerator {
|
||||
constructor() {
|
||||
this.apiEndpoints = [];
|
||||
this.currentApiIndex = 0;
|
||||
this.isLoading = false;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
async init() {
|
||||
await this.loadApiEndpoints();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
// 加载API接口列表
|
||||
async loadApiEndpoints() {
|
||||
try {
|
||||
const response = await fetch('./接口集合.json');
|
||||
this.apiEndpoints = await response.json();
|
||||
} catch (error) {
|
||||
console.error('加载API接口列表失败:', error);
|
||||
this.showToast('加载接口配置失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
const generateBtn = document.getElementById('generateBtn');
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
|
||||
generateBtn.addEventListener('click', () => this.generateKFC());
|
||||
copyBtn.addEventListener('click', () => this.copyContent());
|
||||
}
|
||||
|
||||
// 生成KFC文案
|
||||
async generateKFC() {
|
||||
if (this.isLoading) return;
|
||||
|
||||
this.setLoadingState(true);
|
||||
|
||||
let success = false;
|
||||
let attempts = 0;
|
||||
const maxAttempts = this.apiEndpoints.length;
|
||||
|
||||
while (!success && attempts < maxAttempts) {
|
||||
try {
|
||||
const apiUrl = this.apiEndpoints[this.currentApiIndex];
|
||||
const data = await this.fetchKFCData(apiUrl);
|
||||
|
||||
if (data && data.code === 200 && data.data && data.data.kfc) {
|
||||
this.displayKFC(data.data);
|
||||
success = true;
|
||||
} else {
|
||||
throw new Error('API返回数据格式错误');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`API ${this.currentApiIndex + 1} 请求失败:`, error);
|
||||
this.currentApiIndex = (this.currentApiIndex + 1) % this.apiEndpoints.length;
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
this.showError('所有API接口都无法访问,请稍后重试');
|
||||
}
|
||||
|
||||
this.setLoadingState(false);
|
||||
}
|
||||
|
||||
// 请求KFC数据
|
||||
async fetchKFCData(apiUrl) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
|
||||
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/v2/kfc`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示KFC文案
|
||||
displayKFC(data) {
|
||||
const contentElement = document.getElementById('kfcContent');
|
||||
const indexElement = document.getElementById('indexNumber');
|
||||
const indexInfo = document.getElementById('indexInfo');
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
|
||||
// 显示文案内容
|
||||
contentElement.innerHTML = `<p>${this.escapeHtml(data.kfc)}</p>`;
|
||||
|
||||
// 显示编号信息
|
||||
if (data.index) {
|
||||
indexElement.textContent = data.index;
|
||||
indexInfo.style.display = 'block';
|
||||
} else {
|
||||
indexInfo.style.display = 'none';
|
||||
}
|
||||
|
||||
// 显示复制按钮
|
||||
copyBtn.style.display = 'inline-block';
|
||||
|
||||
// 添加显示动画
|
||||
contentElement.style.opacity = '0';
|
||||
contentElement.style.transform = 'translateY(20px)';
|
||||
|
||||
setTimeout(() => {
|
||||
contentElement.style.transition = 'all 0.5s ease';
|
||||
contentElement.style.opacity = '1';
|
||||
contentElement.style.transform = 'translateY(0)';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
showError(message) {
|
||||
const contentElement = document.getElementById('kfcContent');
|
||||
contentElement.innerHTML = `<p class="loading-text" style="color: #e74c3c;">${this.escapeHtml(message)}</p>`;
|
||||
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
const indexInfo = document.getElementById('indexInfo');
|
||||
copyBtn.style.display = 'none';
|
||||
indexInfo.style.display = 'none';
|
||||
}
|
||||
|
||||
// 复制文案内容
|
||||
async copyContent() {
|
||||
const contentElement = document.getElementById('kfcContent');
|
||||
const textContent = contentElement.querySelector('p')?.textContent;
|
||||
|
||||
if (!textContent || textContent.includes('点击按钮获取') || textContent.includes('失败')) {
|
||||
this.showToast('没有可复制的内容', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(textContent);
|
||||
} else {
|
||||
// 降级方案
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = textContent;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-999999px';
|
||||
textArea.style.top = '-999999px';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
document.execCommand('copy');
|
||||
textArea.remove();
|
||||
}
|
||||
|
||||
this.showToast('文案已复制到剪贴板', 'success');
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error);
|
||||
this.showToast('复制失败,请手动选择复制', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 设置加载状态
|
||||
setLoadingState(loading) {
|
||||
this.isLoading = loading;
|
||||
const generateBtn = document.getElementById('generateBtn');
|
||||
const btnText = generateBtn.querySelector('.btn-text');
|
||||
const btnLoading = generateBtn.querySelector('.btn-loading');
|
||||
|
||||
if (loading) {
|
||||
generateBtn.disabled = true;
|
||||
btnText.style.display = 'none';
|
||||
btnLoading.style.display = 'inline';
|
||||
} else {
|
||||
generateBtn.disabled = false;
|
||||
btnText.style.display = 'inline';
|
||||
btnLoading.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 显示提示消息
|
||||
showToast(message, type = 'success') {
|
||||
const toast = document.getElementById('toast');
|
||||
toast.textContent = message;
|
||||
toast.className = `toast ${type}`;
|
||||
toast.classList.add('show');
|
||||
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// HTML转义
|
||||
escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const generator = new KFCGenerator();
|
||||
// 页面加载完成后自动生成一条文案
|
||||
setTimeout(() => {
|
||||
generator.generateKFC();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 添加键盘快捷键支持
|
||||
document.addEventListener('keydown', (event) => {
|
||||
// 按空格键生成文案
|
||||
if (event.code === 'Space' && event.target.tagName !== 'INPUT' && event.target.tagName !== 'TEXTAREA') {
|
||||
event.preventDefault();
|
||||
document.getElementById('generateBtn').click();
|
||||
}
|
||||
|
||||
// Ctrl+C 复制文案
|
||||
if (event.ctrlKey && event.key === 'c' && event.target.tagName !== 'INPUT' && event.target.tagName !== 'TEXTAREA') {
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
if (copyBtn.style.display !== 'none') {
|
||||
event.preventDefault();
|
||||
copyBtn.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
7
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/接口集合.json
Normal file
7
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/接口集合.json
Normal 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"
|
||||
]
|
||||
1
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/返回接口.json
Normal file
1
frontend/react-app/public/60sapi/娱乐消遣/随机KFC文案/返回接口.json
Normal file
@@ -0,0 +1 @@
|
||||
{"code":200,"message":"获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841","data":{"index":369,"kfc":"我不想你带着情绪过夜,这样会把你推的越来越远,我不想你离开我,我希望你可以多爱我一点,我不想因为一点点小问题就让我们两之间的感情产生隔阂。对我来说,双向奔赴的感情相处才有意义,看不到希望的事情我没办法坚持太久,我不想浪费时间在一些无意义无结果的事情上,时间应该用来做我该做的事情。很多人走不到最后,是因为理解不同步,但我跟你在一起,我知道你什样的人,我了解你甚至比了解自己还多,我和你在一起,不是因为其他,而是因为今天肯德基疯狂星期四,我希望你能请我吃!"}}
|
||||
167
frontend/react-app/public/60sapi/娱乐消遣/随机一言/css/background.css
Normal file
167
frontend/react-app/public/60sapi/娱乐消遣/随机一言/css/background.css
Normal file
@@ -0,0 +1,167 @@
|
||||
/* 背景样式文件 - 金色光辉主题 */
|
||||
|
||||
/* 主背景 */
|
||||
body {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#fff8dc 0%,
|
||||
#ffeaa7 25%,
|
||||
#fdcb6e 50%,
|
||||
#e17055 75%,
|
||||
#d63031 100%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 背景装饰层 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
radial-gradient(circle at 20% 80%, rgba(255, 215, 0, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(255, 223, 0, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(212, 175, 55, 0.05) 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(255, 215, 0, 0.3), transparent),
|
||||
radial-gradient(2px 2px at 40px 70px, rgba(255, 223, 0, 0.2), transparent),
|
||||
radial-gradient(1px 1px at 90px 40px, rgba(212, 175, 55, 0.4), transparent),
|
||||
radial-gradient(1px 1px at 130px 80px, rgba(255, 215, 0, 0.2), transparent),
|
||||
radial-gradient(2px 2px at 160px 30px, rgba(255, 223, 0, 0.3), transparent);
|
||||
background-repeat: repeat;
|
||||
background-size: 200px 100px;
|
||||
animation: sparkle 20s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 背景动画 */
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sparkle {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100vh);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
|
||||
/* 平板端背景 */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
body::after {
|
||||
background-size: 250px 120px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 电脑端背景 */
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
background-size: 300% 300%;
|
||||
}
|
||||
|
||||
body::after {
|
||||
background-size: 300px 150px;
|
||||
animation-duration: 25s;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端背景优化 */
|
||||
@media (max-width: 767px) {
|
||||
body {
|
||||
background-size: 200% 200%;
|
||||
animation-duration: 10s;
|
||||
}
|
||||
|
||||
body::before {
|
||||
background:
|
||||
radial-gradient(circle at 30% 70%, rgba(255, 215, 0, 0.08) 0%, transparent 40%),
|
||||
radial-gradient(circle at 70% 30%, rgba(255, 223, 0, 0.08) 0%, transparent 40%);
|
||||
}
|
||||
|
||||
body::after {
|
||||
background-size: 150px 80px;
|
||||
animation-duration: 15s;
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕背景 */
|
||||
@media (max-width: 479px) {
|
||||
body {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#fff8dc 0%,
|
||||
#ffeaa7 50%,
|
||||
#fdcb6e 100%
|
||||
);
|
||||
background-size: 150% 150%;
|
||||
}
|
||||
|
||||
body::after {
|
||||
background-size: 120px 60px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 深色模式支持 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#2c1810 0%,
|
||||
#3d2914 25%,
|
||||
#4a3319 50%,
|
||||
#5c3e1f 75%,
|
||||
#6b4423 100%
|
||||
);
|
||||
}
|
||||
|
||||
body::before {
|
||||
background:
|
||||
radial-gradient(circle at 20% 80%, rgba(255, 215, 0, 0.05) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(255, 223, 0, 0.05) 0%, transparent 50%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画效果(用户偏好) */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
body,
|
||||
body::before,
|
||||
body::after {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, #fff8dc 0%, #ffeaa7 50%, #fdcb6e 100%);
|
||||
}
|
||||
}
|
||||
357
frontend/react-app/public/60sapi/娱乐消遣/随机一言/css/style.css
Normal file
357
frontend/react-app/public/60sapi/娱乐消遣/随机一言/css/style.css
Normal file
@@ -0,0 +1,357 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #2c1810;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 容器布局 */
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #d4af37;
|
||||
text-shadow:
|
||||
0 0 10px rgba(212, 175, 55, 0.8),
|
||||
0 0 20px rgba(212, 175, 55, 0.6),
|
||||
0 0 30px rgba(212, 175, 55, 0.4);
|
||||
margin-bottom: 10px;
|
||||
animation: titleGlow 3s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.2rem;
|
||||
color: #b8860b;
|
||||
opacity: 0.9;
|
||||
text-shadow: 0 0 5px rgba(184, 134, 11, 0.5);
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main-content {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 一言容器 */
|
||||
.quote-container {
|
||||
background: linear-gradient(135deg, rgba(255, 215, 0, 0.1), rgba(255, 223, 0, 0.05));
|
||||
border: 2px solid rgba(212, 175, 55, 0.3);
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
margin-bottom: 30px;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(212, 175, 55, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.quote-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
background: linear-gradient(45deg, #ffd700, #ffed4e, #ffd700, #ffed4e);
|
||||
border-radius: 22px;
|
||||
z-index: -1;
|
||||
animation: borderGlow 4s linear infinite;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading {
|
||||
display: none;
|
||||
text-align: center;
|
||||
color: #d4af37;
|
||||
}
|
||||
|
||||
.loading.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid rgba(212, 175, 55, 0.3);
|
||||
border-top: 4px solid #d4af37;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto 15px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
/* 一言显示 */
|
||||
.quote-display {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quote-display.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quote-text {
|
||||
font-size: 1.8rem;
|
||||
line-height: 1.8;
|
||||
color: #2c1810;
|
||||
margin-bottom: 20px;
|
||||
text-shadow: 0 1px 2px rgba(212, 175, 55, 0.1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.quote-index {
|
||||
font-size: 0.9rem;
|
||||
color: #b8860b;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 错误信息 */
|
||||
.error-message {
|
||||
display: none;
|
||||
text-align: center;
|
||||
color: #cd853f;
|
||||
}
|
||||
|
||||
.error-message.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 控制按钮 */
|
||||
.controls {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
background: linear-gradient(135deg, #ffd700, #ffed4e);
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
padding: 15px 30px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #2c1810;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow:
|
||||
0 4px 15px rgba(212, 175, 55, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 6px 20px rgba(212, 175, 55, 0.4),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.refresh-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.refresh-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 1.2rem;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.refresh-btn:hover .btn-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* 底部 */
|
||||
.footer {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
color: #b8860b;
|
||||
opacity: 0.8;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
@keyframes titleGlow {
|
||||
0% {
|
||||
text-shadow:
|
||||
0 0 10px rgba(212, 175, 55, 0.8),
|
||||
0 0 20px rgba(212, 175, 55, 0.6),
|
||||
0 0 30px rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
100% {
|
||||
text-shadow:
|
||||
0 0 15px rgba(212, 175, 55, 1),
|
||||
0 0 25px rgba(212, 175, 55, 0.8),
|
||||
0 0 35px rgba(212, 175, 55, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes borderGlow {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.quote-container {
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.quote-text {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 电脑端适配 (1024px+) */
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.quote-container {
|
||||
padding: 60px;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
.quote-text {
|
||||
font-size: 2.2rem;
|
||||
line-height: 1.9;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
padding: 18px 36px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (小于768px) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.quote-container {
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.quote-text {
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
padding: 12px 24px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕适配 (小于480px) */
|
||||
@media (max-width: 479px) {
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.quote-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.quote-text {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
padding: 10px 20px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
52
frontend/react-app/public/60sapi/娱乐消遣/随机一言/index.html
Normal file
52
frontend/react-app/public/60sapi/娱乐消遣/随机一言/index.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>随机一言 - 金色光辉</title>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1 class="title">随机一言</h1>
|
||||
<p class="subtitle">每一句话都是心灵的光芒</p>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="quote-container">
|
||||
<div class="loading" id="loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>正在获取一言...</p>
|
||||
</div>
|
||||
|
||||
<div class="quote-display" id="quoteDisplay">
|
||||
<div class="quote-text" id="quoteText">
|
||||
点击下方按钮获取一言
|
||||
</div>
|
||||
<div class="quote-index" id="quoteIndex"></div>
|
||||
</div>
|
||||
|
||||
<div class="error-message" id="errorMessage">
|
||||
<div class="error-icon">⚠️</div>
|
||||
<div class="error-text" id="errorText"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="refresh-btn" id="refreshBtn">
|
||||
<span class="btn-icon">🔄</span>
|
||||
<span class="btn-text">获取新一言</span>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>愿每一句话都能温暖你的心</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
225
frontend/react-app/public/60sapi/娱乐消遣/随机一言/js/script.js
vendored
Normal file
225
frontend/react-app/public/60sapi/娱乐消遣/随机一言/js/script.js
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
// 随机一言 JavaScript 功能实现
|
||||
|
||||
class HitokotoApp {
|
||||
constructor() {
|
||||
// API接口列表
|
||||
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.currentEndpointIndex = 0;
|
||||
this.isLoading = false;
|
||||
|
||||
// DOM 元素
|
||||
this.elements = {
|
||||
loading: document.getElementById('loading'),
|
||||
quoteDisplay: document.getElementById('quoteDisplay'),
|
||||
quoteText: document.getElementById('quoteText'),
|
||||
quoteIndex: document.getElementById('quoteIndex'),
|
||||
errorMessage: document.getElementById('errorMessage'),
|
||||
errorText: document.getElementById('errorText'),
|
||||
refreshBtn: document.getElementById('refreshBtn')
|
||||
};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.hideAllStates();
|
||||
this.showQuoteDisplay();
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
this.elements.refreshBtn.addEventListener('click', () => {
|
||||
this.fetchHitokoto();
|
||||
});
|
||||
|
||||
// 键盘快捷键支持
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'Space' && !this.isLoading) {
|
||||
e.preventDefault();
|
||||
this.fetchHitokoto();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 隐藏所有状态
|
||||
hideAllStates() {
|
||||
this.elements.loading.classList.remove('show');
|
||||
this.elements.quoteDisplay.classList.remove('hide');
|
||||
this.elements.errorMessage.classList.remove('show');
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
showLoading() {
|
||||
this.hideAllStates();
|
||||
this.elements.loading.classList.add('show');
|
||||
this.elements.quoteDisplay.classList.add('hide');
|
||||
this.elements.refreshBtn.disabled = true;
|
||||
this.isLoading = true;
|
||||
}
|
||||
|
||||
// 显示一言内容
|
||||
showQuoteDisplay() {
|
||||
this.hideAllStates();
|
||||
this.elements.quoteDisplay.classList.remove('hide');
|
||||
this.elements.refreshBtn.disabled = false;
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
showError(message) {
|
||||
this.hideAllStates();
|
||||
this.elements.errorMessage.classList.add('show');
|
||||
this.elements.errorText.textContent = message;
|
||||
this.elements.refreshBtn.disabled = false;
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
// 获取一言数据
|
||||
async fetchHitokoto() {
|
||||
if (this.isLoading) return;
|
||||
|
||||
this.showLoading();
|
||||
|
||||
// 尝试所有API接口
|
||||
for (let i = 0; i < this.apiEndpoints.length; i++) {
|
||||
const endpointIndex = (this.currentEndpointIndex + i) % this.apiEndpoints.length;
|
||||
const endpoint = this.apiEndpoints[endpointIndex];
|
||||
|
||||
try {
|
||||
const result = await this.tryFetchFromEndpoint(endpoint);
|
||||
if (result.success) {
|
||||
this.currentEndpointIndex = endpointIndex;
|
||||
this.displayHitokoto(result.data);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`接口 ${endpoint} 请求失败:`, error.message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 所有接口都失败
|
||||
this.showError('所有接口都无法访问,请检查网络连接或稍后重试');
|
||||
}
|
||||
|
||||
// 尝试从指定接口获取数据
|
||||
async tryFetchFromEndpoint(endpoint) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
|
||||
|
||||
try {
|
||||
const response = await fetch(`${endpoint}/v2/hitokoto?encoding=text`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 验证返回数据格式
|
||||
if (data.code === 200 && data.data && data.data.hitokoto) {
|
||||
return {
|
||||
success: true,
|
||||
data: data.data
|
||||
};
|
||||
} else {
|
||||
throw new Error('返回数据格式不正确');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (error.name === 'AbortError') {
|
||||
throw new Error('请求超时');
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示一言内容
|
||||
displayHitokoto(data) {
|
||||
// 更新一言文本
|
||||
this.elements.quoteText.textContent = data.hitokoto;
|
||||
|
||||
// 更新序号信息
|
||||
if (data.index) {
|
||||
this.elements.quoteIndex.textContent = `第 ${data.index} 条`;
|
||||
} else {
|
||||
this.elements.quoteIndex.textContent = '';
|
||||
}
|
||||
|
||||
// 添加淡入动画效果
|
||||
this.elements.quoteText.style.opacity = '0';
|
||||
this.elements.quoteIndex.style.opacity = '0';
|
||||
|
||||
setTimeout(() => {
|
||||
this.elements.quoteText.style.transition = 'opacity 0.5s ease';
|
||||
this.elements.quoteIndex.style.transition = 'opacity 0.5s ease';
|
||||
this.elements.quoteText.style.opacity = '1';
|
||||
this.elements.quoteIndex.style.opacity = '1';
|
||||
}, 100);
|
||||
|
||||
this.showQuoteDisplay();
|
||||
|
||||
// 控制台输出调试信息
|
||||
console.log('一言获取成功:', {
|
||||
content: data.hitokoto,
|
||||
index: data.index,
|
||||
endpoint: this.apiEndpoints[this.currentEndpointIndex]
|
||||
});
|
||||
}
|
||||
|
||||
// 获取随机接口(用于负载均衡)
|
||||
getRandomEndpoint() {
|
||||
const randomIndex = Math.floor(Math.random() * this.apiEndpoints.length);
|
||||
return this.apiEndpoints[randomIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化应用
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const app = new HitokotoApp();
|
||||
|
||||
// 添加全局错误处理
|
||||
window.addEventListener('error', (event) => {
|
||||
console.error('页面发生错误:', event.error);
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
console.error('未处理的Promise拒绝:', event.reason);
|
||||
});
|
||||
|
||||
// 页面可见性变化时的处理
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden && !app.isLoading) {
|
||||
// 页面重新可见时,可以选择刷新内容
|
||||
console.log('页面重新可见');
|
||||
}
|
||||
});
|
||||
|
||||
console.log('随机一言应用初始化完成');
|
||||
});
|
||||
|
||||
// 导出应用类(如果需要在其他地方使用)
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = HitokotoApp;
|
||||
}
|
||||
7
frontend/react-app/public/60sapi/娱乐消遣/随机一言/接口集合.json
Normal file
7
frontend/react-app/public/60sapi/娱乐消遣/随机一言/接口集合.json
Normal 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"
|
||||
]
|
||||
8
frontend/react-app/public/60sapi/娱乐消遣/随机一言/返回接口.json
Normal file
8
frontend/react-app/public/60sapi/娱乐消遣/随机一言/返回接口.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"index": 2862,
|
||||
"hitokoto": "你带上罪恶之冠,即使背负上所有罪恶和孤独,绝不让你受伤"
|
||||
}
|
||||
}
|
||||
251
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/css/style.css
Normal file
251
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/css/style.css
Normal file
@@ -0,0 +1,251 @@
|
||||
/* 随机唱歌音频 - 淡绿色清新风格样式 */
|
||||
|
||||
/* 重置样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #a8e6cf 0%, #dcedc1 50%, #ffd3a5 100%);
|
||||
min-height: 100vh;
|
||||
color: #2d5016;
|
||||
line-height: 1.6;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 头部 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 20px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 8px 25px rgba(45, 80, 22, 0.08);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
color: #2d5016;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.header p {
|
||||
color: #5a7c65;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* 用户卡片 */
|
||||
.user-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 16px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 18px rgba(45, 80, 22, 0.08);
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid rgba(129, 199, 132, 0.5);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
color: #2d5016;
|
||||
}
|
||||
|
||||
.meta {
|
||||
color: #5a7c65;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 歌曲信息 */
|
||||
.song-card {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 16px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 18px rgba(45, 80, 22, 0.08);
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.song-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
color: #1b5e20;
|
||||
}
|
||||
|
||||
.song-meta {
|
||||
color: #5a7c65;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 歌词 */
|
||||
.lyrics {
|
||||
background: rgba(129, 199, 132, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
max-height: 220px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.lyrics p {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* 音频播放器卡片 */
|
||||
.audio-card {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 16px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 18px rgba(45, 80, 22, 0.08);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.audio-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: linear-gradient(135deg, #81c784 0%, #66bb6a 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 18px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease;
|
||||
box-shadow: 0 4px 12px rgba(129, 199, 132, 0.35);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 18px rgba(129, 199, 132, 0.45);
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #5a7c65;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 加载与错误 */
|
||||
.loading, .error {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 5px 20px rgba(45, 80, 22, 0.08);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 4px solid #e8f5e8;
|
||||
border-top: 4px solid #81c784;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 18px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* 平板端适配 */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container { padding: 16px; }
|
||||
.header h1 { font-size: 1.8rem; }
|
||||
}
|
||||
|
||||
/* 手机端优先优化 */
|
||||
@media (max-width: 767px) {
|
||||
.container { padding: 12px; }
|
||||
.header { padding: 18px; }
|
||||
.header h1 { font-size: 1.6rem; gap: 8px; }
|
||||
|
||||
.user-card {
|
||||
padding: 16px;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.song-card, .audio-card {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.lyrics {
|
||||
max-height: 180px;
|
||||
text-align: left;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.audio-actions {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info {
|
||||
text-align: center;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
67
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/index.html
Normal file
67
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/index.html
Normal file
@@ -0,0 +1,67 @@
|
||||
<!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>随机唱歌音频 - 60s API 集合</title>
|
||||
<meta name="description" content="随机唱歌音频,数据源自 60s.viki.moe,提供用户信息、歌曲信息、歌词与音频播放。" />
|
||||
|
||||
<link rel="stylesheet" href="./css/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>
|
||||
🎵 随机唱歌音频
|
||||
</h1>
|
||||
<p>数据来自官方/权威源头,以确保稳定与实时 · 支持本地数据回退</p>
|
||||
</header>
|
||||
|
||||
<!-- 加载与错误状态 -->
|
||||
<section id="loading" class="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>正在加载中,请稍候…</p>
|
||||
</section>
|
||||
|
||||
<section id="error" class="error" style="display: none;">
|
||||
<p>获取数据失败,请稍后重试</p>
|
||||
</section>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<main id="content" style="display: none;" class="fade-in">
|
||||
<!-- 用户信息 -->
|
||||
<div class="user-card">
|
||||
<img id="avatar" class="avatar" src="" alt="用户头像" />
|
||||
<div class="user-info">
|
||||
<div class="nickname" id="nickname">-</div>
|
||||
<div class="meta">性别:<span id="gender">-</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 歌曲信息 -->
|
||||
<div class="song-card">
|
||||
<div class="song-title" id="song-title">-</div>
|
||||
<div class="song-meta" id="song-meta">-</div>
|
||||
<div class="lyrics" id="lyrics"></div>
|
||||
</div>
|
||||
|
||||
<!-- 音频播放 -->
|
||||
<div class="audio-card">
|
||||
<audio id="audio" controls style="width: 100%;"></audio>
|
||||
<div class="audio-actions">
|
||||
<button class="btn" id="refresh-btn">换一首</button>
|
||||
<div class="info">
|
||||
❤ 喜欢:<span id="like-count">-</span>
|
||||
· ⏱ 时长:<span id="duration">--:--</span>
|
||||
· 🗓 发布:<span id="publish-time">-</span>
|
||||
· 🔗 <a id="link" href="#" class="btn" style="padding: 6px 10px; border-radius: 8px; background: #81c784;">查看原帖</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="./js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
252
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/js/script.js
vendored
Normal file
252
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/js/script.js
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
// 随机唱歌音频 页面脚本
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const API = {
|
||||
endpoints: [],
|
||||
currentIndex: 0,
|
||||
params: {
|
||||
encoding: 'json'
|
||||
},
|
||||
localFallback: '返回接口.json',
|
||||
// 初始化API接口列表
|
||||
async init() {
|
||||
try {
|
||||
const res = await fetch('./接口集合.json');
|
||||
const endpoints = await res.json();
|
||||
this.endpoints = endpoints.map(endpoint => `${endpoint}/v2/changya`);
|
||||
} catch (e) {
|
||||
// 如果无法加载接口集合,使用默认接口
|
||||
this.endpoints = ['https://60s.viki.moe/v2/changya'];
|
||||
}
|
||||
},
|
||||
// 获取当前接口URL
|
||||
getCurrentUrl() {
|
||||
if (this.endpoints.length === 0) return null;
|
||||
const url = new URL(this.endpoints[this.currentIndex]);
|
||||
Object.entries(this.params).forEach(([k, v]) => url.searchParams.append(k, v));
|
||||
return url.toString();
|
||||
},
|
||||
// 切换到下一个接口
|
||||
switchToNext() {
|
||||
this.currentIndex = (this.currentIndex + 1) % this.endpoints.length;
|
||||
return this.currentIndex < this.endpoints.length;
|
||||
},
|
||||
// 重置到第一个接口
|
||||
reset() {
|
||||
this.currentIndex = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// DOM 元素引用
|
||||
const els = {
|
||||
loading: null,
|
||||
error: null,
|
||||
container: null,
|
||||
avatar: null,
|
||||
nickname: null,
|
||||
gender: null,
|
||||
songTitle: null,
|
||||
songMeta: null,
|
||||
lyrics: null,
|
||||
audio: null,
|
||||
likeCount: null,
|
||||
publishTime: null,
|
||||
link: null,
|
||||
refreshBtn: null,
|
||||
};
|
||||
|
||||
function initDom() {
|
||||
els.loading = document.getElementById('loading');
|
||||
els.error = document.getElementById('error');
|
||||
els.container = document.getElementById('content');
|
||||
|
||||
els.avatar = document.getElementById('avatar');
|
||||
els.nickname = document.getElementById('nickname');
|
||||
els.gender = document.getElementById('gender');
|
||||
els.songTitle = document.getElementById('song-title');
|
||||
els.songMeta = document.getElementById('song-meta');
|
||||
els.lyrics = document.getElementById('lyrics');
|
||||
|
||||
els.audio = document.getElementById('audio');
|
||||
els.likeCount = document.getElementById('like-count');
|
||||
els.publishTime = document.getElementById('publish-time');
|
||||
els.link = document.getElementById('link');
|
||||
els.refreshBtn = document.getElementById('refresh-btn');
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
els.loading.style.display = 'block';
|
||||
els.error.style.display = 'none';
|
||||
els.container.style.display = 'none';
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
els.loading.style.display = 'none';
|
||||
els.error.style.display = 'block';
|
||||
els.container.style.display = 'none';
|
||||
els.error.querySelector('p').textContent = msg || '获取数据失败,请稍后重试';
|
||||
}
|
||||
|
||||
function showContent() {
|
||||
els.loading.style.display = 'none';
|
||||
els.error.style.display = 'none';
|
||||
els.container.style.display = 'block';
|
||||
}
|
||||
|
||||
function formatDuration(ms) {
|
||||
if (!ms && ms !== 0) return '';
|
||||
const totalSeconds = Math.floor(ms / 1000);
|
||||
const m = Math.floor(totalSeconds / 60).toString().padStart(2, '0');
|
||||
const s = (totalSeconds % 60).toString().padStart(2, '0');
|
||||
return `${m}:${s}`;
|
||||
}
|
||||
|
||||
function safeText(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text == null ? '' : String(text);
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
async function fetchFromAPI() {
|
||||
// 初始化API接口列表
|
||||
await API.init();
|
||||
|
||||
// 重置API索引到第一个接口
|
||||
API.reset();
|
||||
|
||||
// 尝试所有API接口
|
||||
for (let i = 0; i < API.endpoints.length; i++) {
|
||||
try {
|
||||
const url = API.getCurrentUrl();
|
||||
console.log(`尝试接口 ${i + 1}/${API.endpoints.length}: ${url}`);
|
||||
|
||||
const resp = await fetch(url, {
|
||||
cache: 'no-store',
|
||||
timeout: 10000 // 10秒超时
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
|
||||
}
|
||||
|
||||
const data = await resp.json();
|
||||
|
||||
if (data && data.code === 200) {
|
||||
console.log(`接口 ${i + 1} 请求成功`);
|
||||
return data;
|
||||
}
|
||||
|
||||
throw new Error(data && data.message ? data.message : '接口返回异常');
|
||||
|
||||
} catch (e) {
|
||||
console.warn(`接口 ${i + 1} 失败:`, e.message);
|
||||
|
||||
// 如果不是最后一个接口,切换到下一个
|
||||
if (i < API.endpoints.length - 1) {
|
||||
API.switchToNext();
|
||||
continue;
|
||||
}
|
||||
|
||||
// 所有接口都失败了
|
||||
console.warn('所有远程接口都失败,尝试本地数据');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchFromLocal() {
|
||||
try {
|
||||
const resp = await fetch(API.localFallback + `?t=${Date.now()}`);
|
||||
if (!resp.ok) throw new Error(`本地文件HTTP ${resp.status}`);
|
||||
const data = await resp.json();
|
||||
return data;
|
||||
} catch (e) {
|
||||
console.error('读取本地返回接口.json失败:', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function render(data) {
|
||||
const d = data?.data || {};
|
||||
const user = d.user || {};
|
||||
const song = d.song || {};
|
||||
const audio = d.audio || {};
|
||||
|
||||
// 用户信息
|
||||
els.avatar.src = user.avatar_url || '';
|
||||
els.avatar.alt = (user.nickname || '用户') + ' 头像';
|
||||
els.nickname.textContent = user.nickname || '未知用户';
|
||||
els.gender.textContent = user.gender === 'female' ? '女' : user.gender === 'male' ? '男' : '未知';
|
||||
|
||||
// 歌曲信息
|
||||
els.songTitle.textContent = song.name || '未知歌曲';
|
||||
els.songMeta.textContent = song.singer ? `演唱:${song.singer}` : '';
|
||||
|
||||
els.lyrics.innerHTML = '';
|
||||
if (Array.isArray(song.lyrics)) {
|
||||
const frag = document.createDocumentFragment();
|
||||
song.lyrics.forEach(line => {
|
||||
const p = document.createElement('p');
|
||||
p.innerHTML = safeText(line);
|
||||
frag.appendChild(p);
|
||||
});
|
||||
els.lyrics.appendChild(frag);
|
||||
}
|
||||
|
||||
// 音频
|
||||
els.audio.src = audio.url || '';
|
||||
els.audio.preload = 'none';
|
||||
|
||||
// 其他信息
|
||||
els.likeCount.textContent = typeof audio.like_count === 'number' ? audio.like_count : '-';
|
||||
const publish = audio.publish || (audio.publish_at ? new Date(audio.publish_at).toLocaleString() : '');
|
||||
els.publishTime.textContent = publish;
|
||||
els.link.href = audio.link || '#';
|
||||
els.link.target = '_blank';
|
||||
|
||||
// 时长信息
|
||||
const durationEl = document.getElementById('duration');
|
||||
durationEl.textContent = formatDuration(audio.duration);
|
||||
|
||||
showContent();
|
||||
}
|
||||
|
||||
async function load() {
|
||||
showLoading();
|
||||
try {
|
||||
// 先尝试远程API
|
||||
const data = await fetchFromAPI();
|
||||
if (data) {
|
||||
render(data);
|
||||
return;
|
||||
}
|
||||
|
||||
// 远程API失败,尝试本地数据
|
||||
const localData = await fetchFromLocal();
|
||||
if (localData) {
|
||||
render(localData);
|
||||
return;
|
||||
}
|
||||
|
||||
// 都失败了
|
||||
showError('获取数据失败,请稍后重试');
|
||||
} catch (e) {
|
||||
console.error('加载数据时发生错误:', e);
|
||||
showError('获取数据失败,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
if (els.refreshBtn) {
|
||||
els.refreshBtn.addEventListener('click', load);
|
||||
}
|
||||
// 快捷键 Ctrl+R 刷新(不拦截浏览器默认刷新)
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initDom();
|
||||
bindEvents();
|
||||
load();
|
||||
});
|
||||
})();
|
||||
7
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/接口集合.json
Normal file
7
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/接口集合.json
Normal 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"
|
||||
]
|
||||
32
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/返回接口.json
Normal file
32
frontend/react-app/public/60sapi/娱乐消遣/随机唱歌音频/返回接口.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"user": {
|
||||
"nickname": "𝑮𝑺_迷鹿_",
|
||||
"gender": "female",
|
||||
"avatar_url": "http://img-cdn.api.singduck.cn/user-img/6afbebcfae6144478c150d0c1d0d5899.jpg"
|
||||
},
|
||||
"song": {
|
||||
"name": "恶作剧",
|
||||
"singer": "王蓝茵",
|
||||
"lyrics": [
|
||||
"我想我会开始想念你",
|
||||
"可是我刚刚才遇见了你",
|
||||
"我怀疑这奇遇只是个恶作剧",
|
||||
"我想我已慢慢喜欢你",
|
||||
"因为我拥有爱情的勇气",
|
||||
"我任性投入你给的恶作剧",
|
||||
"你给的恶作剧"
|
||||
]
|
||||
},
|
||||
"audio": {
|
||||
"url": "http://audio-cdn.api.singduck.cn/ugc/220929_965696173_b822a290c553.wav?auth_key=1755845643-0-0-4029539b73e17337dcac49cc4e0ecfcc",
|
||||
"duration": 35050,
|
||||
"like_count": 955,
|
||||
"link": "https://m.api.singduck.cn/user-piece/toGZlBfZbukck2sHb",
|
||||
"publish": "2022/09/29 18:33:51",
|
||||
"publish_at": 1664447631000
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/* Epic Games 免费游戏 - 淡绿色清新风格样式 */
|
||||
|
||||
/* 重置样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #a8e6cf 0%, #dcedc1 50%, #ffd3a5 100%);
|
||||
min-height: 100vh;
|
||||
color: #2d5016;
|
||||
line-height: 1.6;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 头部 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 20px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 8px 25px rgba(45, 80, 22, 0.08);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.2rem;
|
||||
color: #2d5016;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.header p {
|
||||
color: #5a7c65;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* 统计信息 */
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 16px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 18px rgba(45, 80, 22, 0.08);
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
color: #1b5e20;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #5a7c65;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 游戏网格 */
|
||||
.games-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 游戏卡片 */
|
||||
.game-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 6px 25px rgba(45, 80, 22, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.game-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 35px rgba(45, 80, 22, 0.15);
|
||||
}
|
||||
|
||||
.game-cover {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.game-info {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.game-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: #1b5e20;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.3;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.game-description {
|
||||
color: #5a7c65;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 12px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.game-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.game-price {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
color: #81c784;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.free-price {
|
||||
color: #2e7d32;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.game-seller {
|
||||
color: #5a7c65;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.game-dates {
|
||||
background: rgba(129, 199, 132, 0.1);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 0.85rem;
|
||||
color: #2d5016;
|
||||
}
|
||||
|
||||
.free-period {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.game-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #81c784 0%, #66bb6a 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 16px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease;
|
||||
box-shadow: 0 4px 12px rgba(129, 199, 132, 0.35);
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 18px rgba(129, 199, 132, 0.45);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: linear-gradient(135deg, #a5d6a7 0%, #81c784 100%);
|
||||
}
|
||||
|
||||
/* 状态标签 */
|
||||
.status-badge {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
text-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.status-free {
|
||||
background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%);
|
||||
}
|
||||
|
||||
.status-upcoming {
|
||||
background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);
|
||||
}
|
||||
|
||||
/* 加载与错误 */
|
||||
.loading, .error {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 5px 20px rgba(45, 80, 22, 0.08);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #e8f5e8;
|
||||
border-top: 4px solid #81c784;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
.fade-in {
|
||||
animation: fadeIn 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* 平板端适配 */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container { padding: 16px; }
|
||||
.header h1 { font-size: 2rem; }
|
||||
.games-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
|
||||
}
|
||||
|
||||
/* 手机端优化 */
|
||||
@media (max-width: 767px) {
|
||||
.container { padding: 12px; }
|
||||
.header { padding: 18px; }
|
||||
.header h1 { font-size: 1.8rem; gap: 8px; }
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: 10px 8px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.stat-number { font-size: 1.4rem; }
|
||||
.stat-label { font-size: 0.75rem; }
|
||||
|
||||
.games-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.game-card { margin: 0 4px; }
|
||||
.game-cover { height: 160px; }
|
||||
.game-info { padding: 14px; }
|
||||
|
||||
.game-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机优化 */
|
||||
@media (max-width: 480px) {
|
||||
.stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.game-meta {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 高分辨率显示器优化 */
|
||||
@media (min-width: 1400px) {
|
||||
.games-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<!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>Epic Games 免费游戏 - 60s API 集合</title>
|
||||
<meta name="description" content="Epic Games 免费游戏列表,数据源自 60s.viki.moe,提供当前免费和即将免费的游戏信息。" />
|
||||
|
||||
<link rel="stylesheet" href="./css/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>
|
||||
🎮 Epic Games 免费游戏
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
<!-- 加载与错误状态 -->
|
||||
<section id="loading" class="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>正在加载游戏数据,请稍候…</p>
|
||||
</section>
|
||||
|
||||
<section id="error" class="error" style="display: none;">
|
||||
<p>获取数据失败,请稍后重试</p>
|
||||
<button id="refresh-btn" class="btn" style="margin-top: 15px;">重新加载</button>
|
||||
</section>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<main id="content" style="display: none;" class="fade-in">
|
||||
<!-- 统计信息 -->
|
||||
<div class="stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="total-games">0</div>
|
||||
<div class="stat-label">总游戏数</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="free-now">0</div>
|
||||
<div class="stat-label">当前免费</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number" id="upcoming">0</div>
|
||||
<div class="stat-label">即将免费</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 游戏列表 -->
|
||||
<div class="games-grid" id="games-grid">
|
||||
<!-- 游戏卡片将通过 JavaScript 动态生成 -->
|
||||
</div>
|
||||
|
||||
<!-- 刷新按钮 -->
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<button id="refresh-btn" class="btn btn-secondary">🔄 刷新数据</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="./js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
266
frontend/react-app/public/60sapi/实用功能/EpicGames免费游戏/js/script.js
vendored
Normal file
266
frontend/react-app/public/60sapi/实用功能/EpicGames免费游戏/js/script.js
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
// Epic Games 免费游戏 页面脚本
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const API = {
|
||||
endpoints: [],
|
||||
currentIndex: 0,
|
||||
// 初始化API接口列表
|
||||
async init() {
|
||||
try {
|
||||
const res = await fetch('./接口集合.json');
|
||||
const endpoints = await res.json();
|
||||
this.endpoints = endpoints.map(endpoint => `${endpoint}/v2/epic`);
|
||||
} catch (e) {
|
||||
// 如果无法加载接口集合,使用默认接口
|
||||
this.endpoints = ['https://60s-api.viki.moe/v2/epic'];
|
||||
}
|
||||
},
|
||||
// 获取当前接口URL
|
||||
getCurrentUrl(encoding) {
|
||||
if (this.endpoints.length === 0) return null;
|
||||
const url = new URL(this.endpoints[this.currentIndex]);
|
||||
if (encoding) url.searchParams.set('encoding', encoding);
|
||||
return url.toString();
|
||||
},
|
||||
// 切换到下一个接口
|
||||
switchToNext() {
|
||||
this.currentIndex = (this.currentIndex + 1) % this.endpoints.length;
|
||||
return this.currentIndex < this.endpoints.length;
|
||||
},
|
||||
// 重置到第一个接口
|
||||
reset() {
|
||||
this.currentIndex = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// DOM 元素引用
|
||||
const els = {
|
||||
loading: null,
|
||||
error: null,
|
||||
container: null,
|
||||
gamesGrid: null,
|
||||
totalGames: null,
|
||||
freeNow: null,
|
||||
upcoming: null,
|
||||
refreshBtn: null,
|
||||
};
|
||||
|
||||
function initDom() {
|
||||
els.loading = document.getElementById('loading');
|
||||
els.error = document.getElementById('error');
|
||||
els.container = document.getElementById('content');
|
||||
els.gamesGrid = document.getElementById('games-grid');
|
||||
els.totalGames = document.getElementById('total-games');
|
||||
els.freeNow = document.getElementById('free-now');
|
||||
els.upcoming = document.getElementById('upcoming');
|
||||
els.refreshBtn = document.getElementById('refresh-btn');
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
els.loading.style.display = 'block';
|
||||
els.error.style.display = 'none';
|
||||
els.container.style.display = 'none';
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
els.loading.style.display = 'none';
|
||||
els.error.style.display = 'block';
|
||||
els.container.style.display = 'none';
|
||||
els.error.querySelector('p').textContent = msg || '获取数据失败,请稍后重试';
|
||||
}
|
||||
|
||||
function showContent() {
|
||||
els.loading.style.display = 'none';
|
||||
els.error.style.display = 'none';
|
||||
els.container.style.display = 'block';
|
||||
}
|
||||
|
||||
function safeText(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text == null ? '' : String(text);
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return '';
|
||||
try {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleDateString('zh-CN', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
} catch (e) {
|
||||
return dateStr;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchData(preferLocal = false) {
|
||||
if (preferLocal) {
|
||||
try {
|
||||
const res = await fetch('./返回接口.json', { cache: 'no-store' });
|
||||
const json = await res.json();
|
||||
return json;
|
||||
} catch (e) {
|
||||
throw new Error('本地数据加载失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 重置API索引到第一个接口
|
||||
API.reset();
|
||||
|
||||
// 尝试所有API接口
|
||||
for (let i = 0; i < API.endpoints.length; i++) {
|
||||
try {
|
||||
const url = API.getCurrentUrl();
|
||||
console.log(`尝试接口 ${i + 1}/${API.endpoints.length}: ${url}`);
|
||||
|
||||
const res = await fetch(url, {
|
||||
cache: 'no-store',
|
||||
timeout: 10000 // 10秒超时
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if (json && json.code === 200) {
|
||||
console.log(`接口 ${i + 1} 请求成功`);
|
||||
return json;
|
||||
}
|
||||
|
||||
throw new Error(json && json.message ? json.message : '接口返回异常');
|
||||
|
||||
} catch (e) {
|
||||
console.warn(`接口 ${i + 1} 失败:`, e.message);
|
||||
|
||||
// 如果不是最后一个接口,切换到下一个
|
||||
if (i < API.endpoints.length - 1) {
|
||||
API.switchToNext();
|
||||
continue;
|
||||
}
|
||||
|
||||
// 所有接口都失败了,尝试本地数据
|
||||
console.warn('所有远程接口都失败,尝试本地数据');
|
||||
try {
|
||||
const res = await fetch('./返回接口.json', { cache: 'no-store' });
|
||||
const json = await res.json();
|
||||
return json;
|
||||
} catch (e2) {
|
||||
throw new Error('所有接口和本地数据都无法访问');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createGameCard(game) {
|
||||
const isFree = game.is_free_now;
|
||||
const statusClass = isFree ? 'status-free' : 'status-upcoming';
|
||||
const statusText = isFree ? '限时免费' : '即将免费';
|
||||
|
||||
return `
|
||||
<div class="game-card fade-in">
|
||||
<div class="status-badge ${statusClass}">${statusText}</div>
|
||||
<img class="game-cover" src="${safeText(game.cover)}" alt="${safeText(game.title)} 封面" loading="lazy" />
|
||||
<div class="game-info">
|
||||
<h3 class="game-title">${safeText(game.title)}</h3>
|
||||
<p class="game-description">${safeText(game.description)}</p>
|
||||
|
||||
<div class="game-meta">
|
||||
<div class="game-price">
|
||||
<span class="original-price">${safeText(game.original_price_desc)}</span>
|
||||
<span class="free-price">免费</span>
|
||||
</div>
|
||||
<div class="game-seller">${safeText(game.seller)}</div>
|
||||
</div>
|
||||
|
||||
<div class="game-dates">
|
||||
<div class="free-period">
|
||||
<span>开始:${formatDate(game.free_start)}</span>
|
||||
<span>结束:${formatDate(game.free_end)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="game-actions">
|
||||
<a href="${safeText(game.link)}" target="_blank" class="btn">
|
||||
${isFree ? '立即领取' : '查看详情'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function updateStats(games) {
|
||||
const total = games.length;
|
||||
const freeNow = games.filter(game => game.is_free_now).length;
|
||||
const upcoming = total - freeNow;
|
||||
|
||||
els.totalGames.textContent = total;
|
||||
els.freeNow.textContent = freeNow;
|
||||
els.upcoming.textContent = upcoming;
|
||||
}
|
||||
|
||||
function renderGames(games) {
|
||||
if (!Array.isArray(games) || games.length === 0) {
|
||||
els.gamesGrid.innerHTML = '<div style="grid-column: 1/-1; text-align: center; padding: 40px; color: #5a7c65;">暂无游戏数据</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 按状态排序:免费的在前
|
||||
const sortedGames = [...games].sort((a, b) => {
|
||||
if (a.is_free_now && !b.is_free_now) return -1;
|
||||
if (!a.is_free_now && b.is_free_now) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const html = sortedGames.map(game => createGameCard(game)).join('');
|
||||
els.gamesGrid.innerHTML = html;
|
||||
|
||||
updateStats(games);
|
||||
}
|
||||
|
||||
function render(data) {
|
||||
const games = data?.data || [];
|
||||
renderGames(games);
|
||||
showContent();
|
||||
}
|
||||
|
||||
async function load() {
|
||||
showLoading();
|
||||
|
||||
// 初始化API接口列表
|
||||
await API.init();
|
||||
|
||||
try {
|
||||
const data = await fetchData(false);
|
||||
render(data);
|
||||
} catch (e) {
|
||||
console.error('数据获取失败:', e);
|
||||
showError(e.message || '获取数据失败,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
if (els.refreshBtn) {
|
||||
els.refreshBtn.addEventListener('click', load);
|
||||
}
|
||||
|
||||
// 快捷键 Ctrl+R 刷新(不拦截浏览器默认刷新)
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.ctrlKey && e.key === 'r' && !e.defaultPrevented) {
|
||||
// 不阻止默认行为,让浏览器正常刷新
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initDom();
|
||||
bindEvents();
|
||||
load();
|
||||
});
|
||||
})();
|
||||
@@ -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"
|
||||
]
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": [
|
||||
{
|
||||
"id": "9aa227e2ba294bb1a95c95fde892eb31",
|
||||
"title": "《Totally Reliable Delivery Service》 Standard Edition",
|
||||
"cover": "https://cdn1.epicgames.com/52b90f9a982a404781b189f6a7903226/offer/EGS_TotallyReliableDeliveryService_WereFiveGames_S1-2560x1440-47e6e9562d62705a75ea7b7096d0b8dc.jpg",
|
||||
"original_price": 52,
|
||||
"original_price_desc": "¥52.00",
|
||||
"description": "穿好护腰护具,发动货车,送货的时间到啦!在一个高度互动的沙盒世界中,与最多三位好友一起随意地完成送货。货物已试投,这就是我们靠谱快递(Totally Reliable Delivery Service)的品质保证!",
|
||||
"seller": "Infogrames LLC",
|
||||
"is_free_now": true,
|
||||
"free_start": "2025/08/14 23:00:00",
|
||||
"free_start_at": 1755183600000,
|
||||
"free_end": "2025/08/21 23:00:00",
|
||||
"free_end_at": 1755788400000,
|
||||
"link": "https://store.epicgames.com/store/zh-CN/p/totally-reliable-delivery-service/home"
|
||||
},
|
||||
{
|
||||
"id": "8ea3500dc38e4f429702bf889c172d3d",
|
||||
"title": "Hidden Folks",
|
||||
"cover": "https://cdn1.epicgames.com/spt-assets/7bfd56b0586348dcb139945d9e59f988/hidden-folks-1b7hh.png",
|
||||
"original_price": 47,
|
||||
"original_price_desc": "¥47.00",
|
||||
"description": "Search for hidden folks in hand-drawn, interactive, miniature landscapes. Unfurl tent flaps, cut through bushes, slam doors, and poke some crocodiles! Rooooaaaarrrr!!!!!",
|
||||
"seller": "Adriaan de Jongh",
|
||||
"is_free_now": true,
|
||||
"free_start": "2025/08/14 23:00:00",
|
||||
"free_start_at": 1755183600000,
|
||||
"free_end": "2025/08/21 23:00:00",
|
||||
"free_end_at": 1755788400000,
|
||||
"link": "https://store.epicgames.com/store/zh-CN/p/hidden-folks-239d16"
|
||||
},
|
||||
{
|
||||
"id": "4cbb6c3704d240f19c3dd5f5cb2b0cb4",
|
||||
"title": "Kamaeru",
|
||||
"cover": "https://cdn1.epicgames.com/spt-assets/44313cfbb62b4df5801d0c8d541c2624/kamaeru-40asc.png",
|
||||
"original_price": 62,
|
||||
"original_price_desc": "¥62.00",
|
||||
"description": "Foster a sanctuary for frogs and restore the biodiversity of the wetlands in Kamaeru, a cozy frog collecting game, where you take pictures of frogs, play mini-games and decorate your habitat. Hop right to it!",
|
||||
"seller": "Armor Games Studios",
|
||||
"is_free_now": false,
|
||||
"free_start": "2025/08/21 23:00:00",
|
||||
"free_start_at": 1755788400000,
|
||||
"free_end": "2025/08/28 23:00:00",
|
||||
"free_end_at": 1756393200000,
|
||||
"link": "https://store.epicgames.com/store/zh-CN/p/kamaeru-0c301e"
|
||||
},
|
||||
{
|
||||
"id": "0d9a533f0e684cc18620a8f408e8e72c",
|
||||
"title": "Strange Horticulture",
|
||||
"cover": "https://cdn1.epicgames.com/spt-assets/15e8e3eba65a4763a815d6eae1d763b2/strange-horticulture-offer-2wghv.png",
|
||||
"original_price": 45,
|
||||
"original_price_desc": "¥45.00",
|
||||
"description": "款神秘学解谜游戏,你将扮演当地植物商店的店主,寻找并识别新的植物,悠闲撸猫,与女巫团体交谈,或加入异教。收集各种强大的植物,用它们来影响故事走向,揭开昂德米尔镇的黑暗谜团。",
|
||||
"seller": "Iceberg Interactive",
|
||||
"is_free_now": false,
|
||||
"free_start": "2025/08/21 23:00:00",
|
||||
"free_start_at": 1755788400000,
|
||||
"free_end": "2025/08/28 23:00:00",
|
||||
"free_end_at": 1756393200000,
|
||||
"link": "https://store.epicgames.com/store/zh-CN/p/strange-horticulture-360e80"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/* 玻璃拟态背景相关样式 */
|
||||
body {
|
||||
background: linear-gradient(135deg,
|
||||
#667eea 0%,
|
||||
#764ba2 25%,
|
||||
#f093fb 50%,
|
||||
#f5576c 75%,
|
||||
#4facfe 100%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 20s ease infinite;
|
||||
background-attachment: fixed;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% { background-position: 0% 50%; }
|
||||
25% { background-position: 100% 50%; }
|
||||
50% { background-position: 100% 100%; }
|
||||
75% { background-position: 0% 100%; }
|
||||
100% { background-position: 0% 50%; }
|
||||
}
|
||||
|
||||
/* 玻璃拟态装饰层 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.1) 0%, transparent 40%),
|
||||
radial-gradient(circle at 80% 80%, rgba(255, 255, 255, 0.08) 0%, transparent 40%),
|
||||
radial-gradient(circle at 40% 60%, rgba(255, 255, 255, 0.06) 0%, transparent 30%),
|
||||
radial-gradient(circle at 60% 30%, rgba(255, 255, 255, 0.05) 0%, transparent 35%);
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
animation: glassFloat 25s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
/* 毛玻璃气泡效果 */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
radial-gradient(circle at 10% 20%, rgba(255, 255, 255, 0.3) 2px, transparent 2px),
|
||||
radial-gradient(circle at 30% 40%, rgba(255, 255, 255, 0.25) 3px, transparent 3px),
|
||||
radial-gradient(circle at 50% 60%, rgba(255, 255, 255, 0.2) 1.5px, transparent 1.5px),
|
||||
radial-gradient(circle at 70% 80%, rgba(255, 255, 255, 0.3) 2.5px, transparent 2.5px),
|
||||
radial-gradient(circle at 90% 10%, rgba(255, 255, 255, 0.25) 2px, transparent 2px),
|
||||
radial-gradient(circle at 20% 90%, rgba(255, 255, 255, 0.2) 1px, transparent 1px);
|
||||
background-size: 300px 300px, 250px 250px, 400px 400px, 200px 200px, 350px 350px, 150px 150px;
|
||||
animation: bubbleFloat 30s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
@keyframes glassFloat {
|
||||
0% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-20px) rotate(2deg);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bubbleFloat {
|
||||
0%, 100% {
|
||||
transform: translateX(0) translateY(0);
|
||||
}
|
||||
25% {
|
||||
transform: translateX(-15px) translateY(-10px);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(10px) translateY(-20px);
|
||||
}
|
||||
75% {
|
||||
transform: translateX(-5px) translateY(-15px);
|
||||
}
|
||||
}
|
||||
1105
frontend/react-app/public/60sapi/实用功能/农历信息/css/style.css
Normal file
1105
frontend/react-app/public/60sapi/实用功能/农历信息/css/style.css
Normal file
File diff suppressed because it is too large
Load Diff
71
frontend/react-app/public/60sapi/实用功能/农历信息/index.html
Normal file
71
frontend/react-app/public/60sapi/实用功能/农历信息/index.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>🌙 农历信息查询</title>
|
||||
<link rel="stylesheet" href="css/background.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<div class="header-icon">🏮</div>
|
||||
<h1 class="title">🌙 农历信息查询 📅</h1>
|
||||
<p class="subtitle">传统文化 · 时光转换 · 节气查询</p>
|
||||
|
||||
<div class="date-selector">
|
||||
<div class="input-group">
|
||||
<label for="dateInput" class="input-label">
|
||||
<span class="label-icon">📅</span>
|
||||
选择日期
|
||||
</label>
|
||||
<input type="date" id="dateInput" class="date-input" />
|
||||
</div>
|
||||
<button id="queryBtn" class="query-btn">
|
||||
<span class="btn-icon">🔍</span>
|
||||
查询农历
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="update-time">
|
||||
<span class="time-icon">⏰</span>
|
||||
<span id="updateTime">等待查询...</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="loading" id="loading" style="display: none;">
|
||||
<div class="loading-content">
|
||||
<div class="glass-spinner"></div>
|
||||
<div class="loading-text">
|
||||
<span class="loading-emoji">🔮</span>
|
||||
<p>正在查询农历信息...</p>
|
||||
<div class="loading-dots">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lunar-info" id="lunarInfo" style="display: none;">
|
||||
<!-- 农历信息将动态生成 -->
|
||||
</div>
|
||||
|
||||
<div class="error-message" id="errorMessage" style="display: none;">
|
||||
<div class="error-content">
|
||||
<div class="error-icon">😔</div>
|
||||
<h3>查询失败了</h3>
|
||||
<p>无法获取农历信息,请稍后重试</p>
|
||||
<button onclick="queryLunarInfo()" class="retry-btn">
|
||||
<span>🔄</span>
|
||||
重新查询
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
485
frontend/react-app/public/60sapi/实用功能/农历信息/js/script.js
vendored
Normal file
485
frontend/react-app/public/60sapi/实用功能/农历信息/js/script.js
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
// API接口列表
|
||||
const API_ENDPOINTS = [
|
||||
"https://60s-cf.viki.moe",
|
||||
"https://60s.viki.moe",
|
||||
"https://60s.b23.run",
|
||||
"https://60s.114128.xyz",
|
||||
"https://60s-cf.114128.xyz"
|
||||
];
|
||||
|
||||
// 当前使用的API索引
|
||||
let currentApiIndex = 0;
|
||||
|
||||
// DOM元素
|
||||
const loadingElement = document.getElementById('loading');
|
||||
const lunarInfoElement = document.getElementById('lunarInfo');
|
||||
const errorMessageElement = document.getElementById('errorMessage');
|
||||
const updateTimeElement = document.getElementById('updateTime');
|
||||
const dateInput = document.getElementById('dateInput');
|
||||
const queryBtn = document.getElementById('queryBtn');
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializePage();
|
||||
});
|
||||
|
||||
// 初始化页面
|
||||
function initializePage() {
|
||||
// 设置默认日期为今天
|
||||
const today = new Date();
|
||||
const dateString = today.toISOString().split('T')[0];
|
||||
dateInput.value = dateString;
|
||||
|
||||
// 绑定事件
|
||||
queryBtn.addEventListener('click', queryLunarInfo);
|
||||
dateInput.addEventListener('change', queryLunarInfo);
|
||||
|
||||
// 自动查询当天信息
|
||||
queryLunarInfo();
|
||||
}
|
||||
|
||||
// 查询农历信息
|
||||
async function queryLunarInfo() {
|
||||
const selectedDate = dateInput.value;
|
||||
if (!selectedDate) {
|
||||
showError('请选择查询日期');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading();
|
||||
hideError();
|
||||
hideLunarInfo();
|
||||
|
||||
try {
|
||||
const data = await fetchLunarData(selectedDate);
|
||||
displayLunarInfo(data.data);
|
||||
updateQueryTime();
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error);
|
||||
showError('查询农历信息失败,请稍后重试');
|
||||
}
|
||||
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
// 获取农历数据
|
||||
async function fetchLunarData(date) {
|
||||
for (let i = 0; i < API_ENDPOINTS.length; i++) {
|
||||
const apiUrl = API_ENDPOINTS[currentApiIndex];
|
||||
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/v2/lunar?date=${date}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200 && data.data) {
|
||||
return data;
|
||||
} else {
|
||||
throw new Error('数据格式错误');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`API ${apiUrl} 请求失败:`, error);
|
||||
currentApiIndex = (currentApiIndex + 1) % API_ENDPOINTS.length;
|
||||
|
||||
if (i === API_ENDPOINTS.length - 1) {
|
||||
throw new Error('所有API接口都无法访问');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 显示农历信息
|
||||
function displayLunarInfo(lunarData) {
|
||||
lunarInfoElement.innerHTML = `
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon">📅</div>
|
||||
<div class="card-title">公历信息</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🗓️</div>
|
||||
<div class="item-label">公历日期</div>
|
||||
<div class="item-value">${lunarData.solar.year}年${String(lunarData.solar.month).padStart(2, '0')}月${String(lunarData.solar.day).padStart(2, '0')}日</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🌍</div>
|
||||
<div class="item-label">星期</div>
|
||||
<div class="item-value">${lunarData.solar.week_desc}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon"><3E></div>
|
||||
<div class="item-label">季节</div>
|
||||
<div class="item-value">${lunarData.solar.season_name_desc}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">⭐</div>
|
||||
<div class="item-label">星座</div>
|
||||
<div class="item-value">${lunarData.constellation.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon">🌙</div>
|
||||
<div class="card-title">农历信息</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🏮</div>
|
||||
<div class="item-label">农历日期</div>
|
||||
<div class="item-value">${lunarData.lunar.desc_short}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🐲</div>
|
||||
<div class="item-label">生肖年</div>
|
||||
<div class="item-value">${lunarData.zodiac.year}年</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">⚡</div>
|
||||
<div class="item-label">天干地支</div>
|
||||
<div class="item-value">${lunarData.sixty_cycle.year.name}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🌙</div>
|
||||
<div class="item-label">月相</div>
|
||||
<div class="item-value">${lunarData.phase.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon">🌸</div>
|
||||
<div class="card-title">节气节日</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🍃</div>
|
||||
<div class="item-label">当前节气</div>
|
||||
<div class="item-value">${lunarData.term.stage ? lunarData.term.stage.name : '无节气'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🎊</div>
|
||||
<div class="item-label">法定假日</div>
|
||||
<div class="item-value">${lunarData.legal_holiday ? lunarData.legal_holiday.name : '无假日'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon"><3E></div>
|
||||
<div class="item-label">传统节日</div>
|
||||
<div class="item-value">${lunarData.festival.both_desc || '无特殊节日'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🔢</div>
|
||||
<div class="item-label">一年第几天</div>
|
||||
<div class="item-value">第${lunarData.stats.day_of_year}天</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon">⏰</div>
|
||||
<div class="card-title">时辰干支</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🕐</div>
|
||||
<div class="item-label">当前时辰</div>
|
||||
<div class="item-value">${lunarData.lunar.hour_desc}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">⚡</div>
|
||||
<div class="item-label">时辰干支</div>
|
||||
<div class="item-value">${lunarData.sixty_cycle.hour.name}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🐓</div>
|
||||
<div class="item-label">时辰生肖</div>
|
||||
<div class="item-value">${lunarData.zodiac.hour}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🎵</div>
|
||||
<div class="item-label">纳音</div>
|
||||
<div class="item-value">${lunarData.nayin.hour}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon">🔮</div>
|
||||
<div class="card-title">黄历宜忌</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info-item">
|
||||
<div class="item-icon">✅</div>
|
||||
<div class="item-label">宜</div>
|
||||
<div class="item-value">${formatTabooText(lunarData.taboo.day.recommends)}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">❌</div>
|
||||
<div class="item-label">忌</div>
|
||||
<div class="item-value">${formatTabooText(lunarData.taboo.day.avoids)}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🕐</div>
|
||||
<div class="item-label">时辰宜</div>
|
||||
<div class="item-value">${formatTabooText(lunarData.taboo.hour.recommends)}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🚫</div>
|
||||
<div class="item-label">时辰忌</div>
|
||||
<div class="item-value">${formatTabooText(lunarData.taboo.hour.avoids)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon">🌟</div>
|
||||
<div class="card-title">运势财运</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🎯</div>
|
||||
<div class="item-label">今日运势</div>
|
||||
<div class="item-value">${lunarData.fortune.today_luck}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">💼</div>
|
||||
<div class="item-label">事业运</div>
|
||||
<div class="item-value">${lunarData.fortune.career}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">💰</div>
|
||||
<div class="item-label">财运</div>
|
||||
<div class="item-value">${lunarData.fortune.money}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">💕</div>
|
||||
<div class="item-label">感情运</div>
|
||||
<div class="item-value">${lunarData.fortune.love}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon">📊</div>
|
||||
<div class="card-title">年度统计</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info-item">
|
||||
<div class="item-icon">📈</div>
|
||||
<div class="item-label">年度进度</div>
|
||||
<div class="item-value">${lunarData.stats.percents_formatted.year}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">📅</div>
|
||||
<div class="item-label">本月进度</div>
|
||||
<div class="item-value">${lunarData.stats.percents_formatted.month}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">🗓️</div>
|
||||
<div class="item-label">本周第几天</div>
|
||||
<div class="item-value">第${lunarData.stats.week_of_month}周</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="item-icon">⏰</div>
|
||||
<div class="item-label">今日进度</div>
|
||||
<div class="item-value">${lunarData.stats.percents_formatted.day}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${generateHourlyTaboo(lunarData.taboo.hours)}
|
||||
`;
|
||||
|
||||
showLunarInfo();
|
||||
}
|
||||
|
||||
// 格式化宜忌文本
|
||||
function formatTabooText(text) {
|
||||
if (!text) return '无';
|
||||
return text.replace(/\./g, '、');
|
||||
}
|
||||
|
||||
// 生成十二时辰宜忌
|
||||
function generateHourlyTaboo(hours) {
|
||||
if (!hours || hours.length === 0) return '';
|
||||
|
||||
const hourCards = hours.map(hour => `
|
||||
<div class="hour-item">
|
||||
<div class="hour-name">${hour.hour}</div>
|
||||
<div class="hour-content">
|
||||
<div class="hour-recommends">
|
||||
<span class="hour-label">宜:</span>
|
||||
<span class="hour-text">${formatTabooText(hour.recommends) || '无'}</span>
|
||||
</div>
|
||||
<div class="hour-avoids">
|
||||
<span class="hour-label">忌:</span>
|
||||
<span class="hour-text">${formatTabooText(hour.avoids) || '无'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
return `
|
||||
<div class="info-card hours-card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon">⏰</div>
|
||||
<div class="card-title">十二时辰宜忌</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="hours-grid">
|
||||
${hourCards}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 更新查询时间
|
||||
function updateQueryTime() {
|
||||
const now = new Date();
|
||||
const timeStr = now.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
updateTimeElement.textContent = `查询时间: ${timeStr}`;
|
||||
|
||||
// 添加成功提示
|
||||
showSuccessMessage('🌙 农历信息已更新');
|
||||
}
|
||||
|
||||
// 显示成功消息
|
||||
function showSuccessMessage(message) {
|
||||
// 移除之前的提示
|
||||
const existingToast = document.querySelector('.success-toast');
|
||||
if (existingToast) {
|
||||
existingToast.remove();
|
||||
}
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'success-toast';
|
||||
toast.textContent = message;
|
||||
toast.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(15px);
|
||||
-webkit-backdrop-filter: blur(15px);
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
padding: 12px 20px;
|
||||
border-radius: 25px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.2);
|
||||
z-index: 1000;
|
||||
font-weight: 600;
|
||||
font-size: 0.9em;
|
||||
animation: glassToastSlide 0.5s ease-out;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
`;
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
// 3秒后自动移除
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'glassToastSlideOut 0.5s ease-in forwards';
|
||||
setTimeout(() => toast.remove(), 500);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
function showLoading() {
|
||||
loadingElement.style.display = 'block';
|
||||
}
|
||||
|
||||
// 隐藏加载状态
|
||||
function hideLoading() {
|
||||
loadingElement.style.display = 'none';
|
||||
}
|
||||
|
||||
// 显示农历信息
|
||||
function showLunarInfo() {
|
||||
lunarInfoElement.style.display = 'block';
|
||||
}
|
||||
|
||||
// 隐藏农历信息
|
||||
function hideLunarInfo() {
|
||||
lunarInfoElement.style.display = 'none';
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
function showError(message = '查询失败,请稍后重试') {
|
||||
errorMessageElement.style.display = 'block';
|
||||
const errorContent = errorMessageElement.querySelector('.error-content p');
|
||||
if (errorContent) {
|
||||
errorContent.textContent = message;
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏错误信息
|
||||
function hideError() {
|
||||
errorMessageElement.style.display = 'none';
|
||||
}
|
||||
|
||||
// 添加CSS动画到页面
|
||||
if (!document.querySelector('#toast-styles')) {
|
||||
const style = document.createElement('style');
|
||||
style.id = 'toast-styles';
|
||||
style.textContent = `
|
||||
@keyframes glassToastSlide {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(100px) scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glassToastSlideOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(100px) scale(0.8);
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// 键盘快捷键支持
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
queryLunarInfo();
|
||||
}
|
||||
|
||||
if (e.key === 'r' && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
queryLunarInfo();
|
||||
}
|
||||
});
|
||||
7
frontend/react-app/public/60sapi/实用功能/农历信息/接口集合.json
Normal file
7
frontend/react-app/public/60sapi/实用功能/农历信息/接口集合.json
Normal 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"
|
||||
]
|
||||
647
frontend/react-app/public/60sapi/实用功能/农历信息/返回接口.json
Normal file
647
frontend/react-app/public/60sapi/实用功能/农历信息/返回接口.json
Normal file
@@ -0,0 +1,647 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"solar": {
|
||||
"year": 2025,
|
||||
"month": 9,
|
||||
"day": 1,
|
||||
"hour": 17,
|
||||
"minute": 58,
|
||||
"second": 47,
|
||||
"full": "2025-09-01",
|
||||
"full_with_time": "2025-09-01 17:58:47",
|
||||
"week": 1,
|
||||
"week_desc": "星期一",
|
||||
"week_desc_short": "一",
|
||||
"season": 3,
|
||||
"season_desc": "三季度",
|
||||
"season_desc_short": "三",
|
||||
"season_name": "秋",
|
||||
"season_name_desc": "秋天",
|
||||
"is_leap_year": false
|
||||
},
|
||||
"lunar": {
|
||||
"year": "乙巳",
|
||||
"month": "七",
|
||||
"day": "初十",
|
||||
"hour": "酉",
|
||||
"full_with_hour": "农历乙巳年七月初十酉时",
|
||||
"desc_short": "农历乙巳年七月初十",
|
||||
"year_desc": "农历乙巳年",
|
||||
"month_desc": "七月",
|
||||
"day_desc": "初十",
|
||||
"hour_desc": "酉时",
|
||||
"is_leap_month": false
|
||||
},
|
||||
"stats": {
|
||||
"day_of_year": 244,
|
||||
"week_of_year": 36,
|
||||
"week_of_month": 1,
|
||||
"percents": {
|
||||
"year": 0.665753424657534,
|
||||
"month": 0.0333333333333333,
|
||||
"week": 0.142857142857143,
|
||||
"day": 0.749161909722222
|
||||
},
|
||||
"percents_formatted": {
|
||||
"year": "66.58%",
|
||||
"month": "3.33%",
|
||||
"week": "14.29%",
|
||||
"day": "74.92%"
|
||||
}
|
||||
},
|
||||
"term": {
|
||||
"today": null,
|
||||
"stage": {
|
||||
"name": "处暑",
|
||||
"position": 10,
|
||||
"is_jie": false,
|
||||
"is_qi": true
|
||||
}
|
||||
},
|
||||
"zodiac": {
|
||||
"year": "蛇",
|
||||
"month": "鸡",
|
||||
"day": "鸡",
|
||||
"hour": "鸡"
|
||||
},
|
||||
"sixty_cycle": {
|
||||
"year": {
|
||||
"heaven_stem": "乙",
|
||||
"earth_branch": "巳",
|
||||
"name": "乙巳年",
|
||||
"name_short": "乙巳"
|
||||
},
|
||||
"month": {
|
||||
"heaven_stem": "乙",
|
||||
"earth_branch": "酉",
|
||||
"name": "乙酉月",
|
||||
"name_short": "乙酉"
|
||||
},
|
||||
"day": {
|
||||
"heaven_stem": "癸",
|
||||
"earth_branch": "酉",
|
||||
"name": "癸酉日",
|
||||
"name_short": "癸酉"
|
||||
},
|
||||
"hour": {
|
||||
"heaven_stem": "辛",
|
||||
"earth_branch": "酉",
|
||||
"name": "辛酉时",
|
||||
"name_short": "辛酉"
|
||||
}
|
||||
},
|
||||
"legal_holiday": null,
|
||||
"festival": {
|
||||
"solar": null,
|
||||
"lunar": null,
|
||||
"both_desc": null
|
||||
},
|
||||
"phase": {
|
||||
"name": "宵月",
|
||||
"position": 10
|
||||
},
|
||||
"constellation": {
|
||||
"name": "处女座",
|
||||
"name_short": "处女"
|
||||
},
|
||||
"taboo": {
|
||||
"day": {
|
||||
"recommends": "解除.祭祀.祈福.求嗣.修造.动土.竖柱.上梁.安床.纳畜.盖屋.合脊.起基.入殓.破土.安葬",
|
||||
"avoids": "出火.嫁娶.开光.进人口.出行.词讼.开市.入宅.移徙.赴任"
|
||||
},
|
||||
"hour": {
|
||||
"hour": "酉时",
|
||||
"hour_short": "酉",
|
||||
"avoids": "乘船.造桥",
|
||||
"recommends": "嫁娶.出行.移徙.入宅.开市.赴任.祈福.安床.开仓.盖屋.修造.求财"
|
||||
},
|
||||
"hours": [
|
||||
{
|
||||
"hour": "酉时",
|
||||
"hour_short": "酉",
|
||||
"recommends": "嫁娶.出行.移徙.入宅.开市.赴任.祈福.安床.开仓.盖屋.修造.求财",
|
||||
"avoids": "乘船.造桥"
|
||||
},
|
||||
{
|
||||
"hour": "戌时",
|
||||
"hour_short": "戌",
|
||||
"recommends": "嫁娶.移徙.安葬.进人口.求财",
|
||||
"avoids": "出行.赴任.祈福.祭祀.开光.斋醮"
|
||||
},
|
||||
{
|
||||
"hour": "亥时",
|
||||
"hour_short": "亥",
|
||||
"recommends": "嫁娶.移徙.交易.入宅.开市.安葬.求嗣.求财",
|
||||
"avoids": "出行.赴任.动土.祈福.祭祀.修造.开光.斋醮"
|
||||
},
|
||||
{
|
||||
"hour": "子时",
|
||||
"hour_short": "子",
|
||||
"recommends": "嫁娶.交易.入宅.开市.祈福.安葬.求嗣.求财",
|
||||
"avoids": "出行.移徙.赴任.词讼.修造"
|
||||
},
|
||||
{
|
||||
"hour": "丑时",
|
||||
"hour_short": "丑",
|
||||
"recommends": "嫁娶.祈福.安葬.祭祀.酬神.求财",
|
||||
"avoids": "出行.赴任.动土.修造"
|
||||
},
|
||||
{
|
||||
"hour": "寅时",
|
||||
"hour_short": "寅",
|
||||
"recommends": "嫁娶.出行.交易.开市.赴任.祈福.安床.祭祀.求嗣.求财",
|
||||
"avoids": "盖屋.入殓.上梁"
|
||||
},
|
||||
{
|
||||
"hour": "卯时",
|
||||
"hour_short": "卯",
|
||||
"recommends": "嫁娶.交易.入宅.开市.祈福.安床.安葬.求嗣.求财",
|
||||
"avoids": "出行.赴任.修造"
|
||||
},
|
||||
{
|
||||
"hour": "辰时",
|
||||
"hour_short": "辰",
|
||||
"recommends": "",
|
||||
"avoids": "诸事不宜"
|
||||
},
|
||||
{
|
||||
"hour": "巳时",
|
||||
"hour_short": "巳",
|
||||
"recommends": "嫁娶.出行.移徙.入宅.开市.祈福.安床.盖屋.祭祀.作灶",
|
||||
"avoids": "安葬.修造.开光"
|
||||
},
|
||||
{
|
||||
"hour": "午时",
|
||||
"hour_short": "午",
|
||||
"recommends": "嫁娶.出行.交易.开市.祈福.安床.求嗣.求财",
|
||||
"avoids": "赴任.动土.词讼.修造"
|
||||
},
|
||||
{
|
||||
"hour": "未时",
|
||||
"hour_short": "未",
|
||||
"recommends": "嫁娶.入宅.祈福.安葬.祭祀.修造.酬神.求财",
|
||||
"avoids": "出行.赴任"
|
||||
},
|
||||
{
|
||||
"hour": "申时",
|
||||
"hour_short": "申",
|
||||
"recommends": "嫁娶.出行.开市.赴任.安葬.求财",
|
||||
"avoids": "祈福.祭祀.酬神.斋醮"
|
||||
}
|
||||
]
|
||||
},
|
||||
"julian_day": 2460919.5,
|
||||
"nayin": {
|
||||
"year": "覆灯火",
|
||||
"month": "泉中水",
|
||||
"day": "剑锋金",
|
||||
"hour": "石榴木"
|
||||
},
|
||||
"baizi": {
|
||||
"year_baizi": "性格温和,为人正直诚信。",
|
||||
"day_baizi": "性格温和,为人正直诚信。"
|
||||
},
|
||||
"fortune": {
|
||||
"today_luck": "今日学习运好,适合进修",
|
||||
"career": "领导能力突出,升职有望",
|
||||
"money": "偏财运不错,可小试投资",
|
||||
"love": "感情需要沟通,避免误会"
|
||||
},
|
||||
"constants": {
|
||||
"legal_holiday_list": [
|
||||
{
|
||||
"name": "元旦节",
|
||||
"date": "2025-01-01",
|
||||
"start": "2025-01-01",
|
||||
"end": "2025-01-01"
|
||||
},
|
||||
{
|
||||
"name": "春节",
|
||||
"date": "2025-01-29",
|
||||
"start": "2025-01-26",
|
||||
"end": "2025-02-08"
|
||||
},
|
||||
{
|
||||
"name": "清明节",
|
||||
"date": "2025-04-04",
|
||||
"start": "2025-04-04",
|
||||
"end": "2025-04-06"
|
||||
},
|
||||
{
|
||||
"name": "劳动节",
|
||||
"date": "2025-05-01",
|
||||
"start": "2025-04-27",
|
||||
"end": "2025-05-05"
|
||||
},
|
||||
{
|
||||
"name": "端午节",
|
||||
"date": "2025-05-31",
|
||||
"start": "2025-05-31",
|
||||
"end": "2025-06-02"
|
||||
},
|
||||
{
|
||||
"name": "国庆中秋",
|
||||
"date": "2025-10-01",
|
||||
"start": "2025-09-28",
|
||||
"end": "2025-10-11"
|
||||
}
|
||||
],
|
||||
"phase_list": [
|
||||
{
|
||||
"name": "朔月",
|
||||
"lunar_day": 1
|
||||
},
|
||||
{
|
||||
"name": "既朔月",
|
||||
"lunar_day": 2
|
||||
},
|
||||
{
|
||||
"name": "蛾眉新月",
|
||||
"lunar_day": 3
|
||||
},
|
||||
{
|
||||
"name": "蛾眉新月",
|
||||
"lunar_day": 4
|
||||
},
|
||||
{
|
||||
"name": "蛾眉月",
|
||||
"lunar_day": 5
|
||||
},
|
||||
{
|
||||
"name": "夕月",
|
||||
"lunar_day": 6
|
||||
},
|
||||
{
|
||||
"name": "上弦月",
|
||||
"lunar_day": 7
|
||||
},
|
||||
{
|
||||
"name": "上弦月",
|
||||
"lunar_day": 8
|
||||
},
|
||||
{
|
||||
"name": "九夜月",
|
||||
"lunar_day": 9
|
||||
},
|
||||
{
|
||||
"name": "宵月",
|
||||
"lunar_day": 10
|
||||
},
|
||||
{
|
||||
"name": "宵月",
|
||||
"lunar_day": 11
|
||||
},
|
||||
{
|
||||
"name": "宵月",
|
||||
"lunar_day": 12
|
||||
},
|
||||
{
|
||||
"name": "渐盈凸月",
|
||||
"lunar_day": 13
|
||||
},
|
||||
{
|
||||
"name": "小望月",
|
||||
"lunar_day": 14
|
||||
},
|
||||
{
|
||||
"name": "望月",
|
||||
"lunar_day": 15
|
||||
},
|
||||
{
|
||||
"name": "既望月",
|
||||
"lunar_day": 16
|
||||
},
|
||||
{
|
||||
"name": "立待月",
|
||||
"lunar_day": 17
|
||||
},
|
||||
{
|
||||
"name": "居待月",
|
||||
"lunar_day": 18
|
||||
},
|
||||
{
|
||||
"name": "寝待月",
|
||||
"lunar_day": 19
|
||||
},
|
||||
{
|
||||
"name": "更待月",
|
||||
"lunar_day": 20
|
||||
},
|
||||
{
|
||||
"name": "渐亏凸月",
|
||||
"lunar_day": 21
|
||||
},
|
||||
{
|
||||
"name": "下弦月",
|
||||
"lunar_day": 22
|
||||
},
|
||||
{
|
||||
"name": "下弦月",
|
||||
"lunar_day": 23
|
||||
},
|
||||
{
|
||||
"name": "有明月",
|
||||
"lunar_day": 24
|
||||
},
|
||||
{
|
||||
"name": "有明月",
|
||||
"lunar_day": 25
|
||||
},
|
||||
{
|
||||
"name": "蛾眉残月",
|
||||
"lunar_day": 26
|
||||
},
|
||||
{
|
||||
"name": "蛾眉残月",
|
||||
"lunar_day": 27
|
||||
},
|
||||
{
|
||||
"name": "残月",
|
||||
"lunar_day": 28
|
||||
},
|
||||
{
|
||||
"name": "晓月",
|
||||
"lunar_day": 29
|
||||
},
|
||||
{
|
||||
"name": "晦月",
|
||||
"lunar_day": 30
|
||||
}
|
||||
],
|
||||
"zodiac_list": [
|
||||
"鼠",
|
||||
"牛",
|
||||
"虎",
|
||||
"兔",
|
||||
"龙",
|
||||
"蛇",
|
||||
"马",
|
||||
"羊",
|
||||
"猴",
|
||||
"鸡",
|
||||
"狗",
|
||||
"猪"
|
||||
],
|
||||
"constellation_list": [
|
||||
{
|
||||
"name": "白羊",
|
||||
"desc": "白羊座",
|
||||
"start": "3月21日",
|
||||
"end": "4月19日",
|
||||
"range": "3月21日~4月19日",
|
||||
"start_month": 3,
|
||||
"start_day": 21,
|
||||
"end_month": 4,
|
||||
"end_day": 19
|
||||
},
|
||||
{
|
||||
"name": "金牛",
|
||||
"desc": "金牛座",
|
||||
"start": "4月20日",
|
||||
"end": "5月20日",
|
||||
"range": "4月20日~5月20日",
|
||||
"start_month": 4,
|
||||
"start_day": 20,
|
||||
"end_month": 5,
|
||||
"end_day": 20
|
||||
},
|
||||
{
|
||||
"name": "双子",
|
||||
"desc": "双子座",
|
||||
"start": "5月21日",
|
||||
"end": "6月21日",
|
||||
"range": "5月21日~6月21日",
|
||||
"start_month": 5,
|
||||
"start_day": 21,
|
||||
"end_month": 6,
|
||||
"end_day": 21
|
||||
},
|
||||
{
|
||||
"name": "巨蟹",
|
||||
"desc": "巨蟹座",
|
||||
"start": "6月22日",
|
||||
"end": "7月22日",
|
||||
"range": "6月22日~7月22日",
|
||||
"start_month": 6,
|
||||
"start_day": 22,
|
||||
"end_month": 7,
|
||||
"end_day": 22
|
||||
},
|
||||
{
|
||||
"name": "狮子",
|
||||
"desc": "狮子座",
|
||||
"start": "7月23日",
|
||||
"end": "8月22日",
|
||||
"range": "7月23日~8月22日",
|
||||
"start_month": 7,
|
||||
"start_day": 23,
|
||||
"end_month": 8,
|
||||
"end_day": 22
|
||||
},
|
||||
{
|
||||
"name": "处女",
|
||||
"desc": "处女座",
|
||||
"start": "8月23日",
|
||||
"end": "9月22日",
|
||||
"range": "8月23日~9月22日",
|
||||
"start_month": 8,
|
||||
"start_day": 23,
|
||||
"end_month": 9,
|
||||
"end_day": 22
|
||||
},
|
||||
{
|
||||
"name": "天秤",
|
||||
"desc": "天秤座",
|
||||
"start": "9月23日",
|
||||
"end": "10月23日",
|
||||
"range": "9月23日~10月23日",
|
||||
"start_month": 9,
|
||||
"start_day": 23,
|
||||
"end_month": 10,
|
||||
"end_day": 23
|
||||
},
|
||||
{
|
||||
"name": "天蝎",
|
||||
"desc": "天蝎座",
|
||||
"start": "10月24日",
|
||||
"end": "11月22日",
|
||||
"range": "10月24日~11月22日",
|
||||
"start_month": 10,
|
||||
"start_day": 24,
|
||||
"end_month": 11,
|
||||
"end_day": 22
|
||||
},
|
||||
{
|
||||
"name": "射手",
|
||||
"desc": "射手座",
|
||||
"start": "11月23日",
|
||||
"end": "12月21日",
|
||||
"range": "11月23日~12月21日",
|
||||
"start_month": 11,
|
||||
"start_day": 23,
|
||||
"end_month": 12,
|
||||
"end_day": 21
|
||||
},
|
||||
{
|
||||
"name": "摩羯",
|
||||
"desc": "摩羯座",
|
||||
"start": "12月22日",
|
||||
"end": "1月19日",
|
||||
"range": "12月22日~1月19日",
|
||||
"start_month": 12,
|
||||
"start_day": 22,
|
||||
"end_month": 1,
|
||||
"end_day": 19
|
||||
},
|
||||
{
|
||||
"name": "水瓶",
|
||||
"desc": "水瓶座",
|
||||
"start": "1月20日",
|
||||
"end": "2月18日",
|
||||
"range": "1月20日~2月18日",
|
||||
"start_month": 1,
|
||||
"start_day": 20,
|
||||
"end_month": 2,
|
||||
"end_day": 18
|
||||
},
|
||||
{
|
||||
"name": "双鱼",
|
||||
"desc": "双鱼座",
|
||||
"start": "2月19日",
|
||||
"end": "3月20日",
|
||||
"range": "2月19日~3月20日",
|
||||
"start_month": 2,
|
||||
"start_day": 19,
|
||||
"end_month": 3,
|
||||
"end_day": 20
|
||||
}
|
||||
],
|
||||
"heaven_stems": [
|
||||
"甲",
|
||||
"乙",
|
||||
"丙",
|
||||
"丁",
|
||||
"戊",
|
||||
"己",
|
||||
"庚",
|
||||
"辛",
|
||||
"壬",
|
||||
"癸"
|
||||
],
|
||||
"earth_branches": [
|
||||
"子",
|
||||
"丑",
|
||||
"寅",
|
||||
"卯",
|
||||
"辰",
|
||||
"巳",
|
||||
"午",
|
||||
"未",
|
||||
"申",
|
||||
"酉",
|
||||
"戌",
|
||||
"亥"
|
||||
],
|
||||
"solar_terms": [
|
||||
{
|
||||
"name": "立春",
|
||||
"desc": "春季开始"
|
||||
},
|
||||
{
|
||||
"name": "雨水",
|
||||
"desc": "降雨增多"
|
||||
},
|
||||
{
|
||||
"name": "惊蛰",
|
||||
"desc": "春雷乍响"
|
||||
},
|
||||
{
|
||||
"name": "春分",
|
||||
"desc": "昼夜等长"
|
||||
},
|
||||
{
|
||||
"name": "清明",
|
||||
"desc": "天清地明"
|
||||
},
|
||||
{
|
||||
"name": "谷雨",
|
||||
"desc": "雨生百谷"
|
||||
},
|
||||
{
|
||||
"name": "立夏",
|
||||
"desc": "夏季开始"
|
||||
},
|
||||
{
|
||||
"name": "小满",
|
||||
"desc": "麦粒渐满"
|
||||
},
|
||||
{
|
||||
"name": "芒种",
|
||||
"desc": "麦类收割"
|
||||
},
|
||||
{
|
||||
"name": "夏至",
|
||||
"desc": "白昼最长"
|
||||
},
|
||||
{
|
||||
"name": "小暑",
|
||||
"desc": "天气渐热"
|
||||
},
|
||||
{
|
||||
"name": "大暑",
|
||||
"desc": "一年最热"
|
||||
},
|
||||
{
|
||||
"name": "立秋",
|
||||
"desc": "秋季开始"
|
||||
},
|
||||
{
|
||||
"name": "处暑",
|
||||
"desc": "暑热结束"
|
||||
},
|
||||
{
|
||||
"name": "白露",
|
||||
"desc": "露水增多"
|
||||
},
|
||||
{
|
||||
"name": "秋分",
|
||||
"desc": "昼夜等长"
|
||||
},
|
||||
{
|
||||
"name": "寒露",
|
||||
"desc": "露水渐凉"
|
||||
},
|
||||
{
|
||||
"name": "霜降",
|
||||
"desc": "开始降霜"
|
||||
},
|
||||
{
|
||||
"name": "立冬",
|
||||
"desc": "冬季开始"
|
||||
},
|
||||
{
|
||||
"name": "小雪",
|
||||
"desc": "开始降雪"
|
||||
},
|
||||
{
|
||||
"name": "大雪",
|
||||
"desc": "降雪增多"
|
||||
},
|
||||
{
|
||||
"name": "冬至",
|
||||
"desc": "白昼最短"
|
||||
},
|
||||
{
|
||||
"name": "小寒",
|
||||
"desc": "天气渐冷"
|
||||
},
|
||||
{
|
||||
"name": "大寒",
|
||||
"desc": "一年最冷"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
145
frontend/react-app/public/60sapi/实用功能/实时天气/css/background.css
Normal file
145
frontend/react-app/public/60sapi/实用功能/实时天气/css/background.css
Normal file
@@ -0,0 +1,145 @@
|
||||
/* 背景样式文件 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #d4f1d4 25%, #c8ecc8 50%, #b8e6b8 75%, #a8d5ba 100%);
|
||||
background-attachment: fixed;
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 背景渐变动画 */
|
||||
@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(168, 213, 186, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(107, 183, 123, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(200, 236, 200, 0.1) 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(168, 213, 186, 0.3), transparent),
|
||||
radial-gradient(2px 2px at 40px 70px, rgba(107, 183, 123, 0.2), transparent),
|
||||
radial-gradient(1px 1px at 90px 40px, rgba(200, 236, 200, 0.4), transparent),
|
||||
radial-gradient(1px 1px at 130px 80px, rgba(168, 213, 186, 0.2), transparent),
|
||||
radial-gradient(2px 2px at 160px 30px, rgba(107, 183, 123, 0.3), transparent);
|
||||
background-repeat: repeat;
|
||||
background-size: 200px 100px;
|
||||
animation: float 20s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 云朵装饰效果 */
|
||||
.container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50px;
|
||||
right: -50px;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50px;
|
||||
box-shadow:
|
||||
-30px 20px 0 rgba(255, 255, 255, 0.08),
|
||||
30px 40px 0 rgba(255, 255, 255, 0.06);
|
||||
animation: cloudFloat 25s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.container::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
left: -30px;
|
||||
width: 150px;
|
||||
height: 80px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 40px;
|
||||
box-shadow:
|
||||
20px 15px 0 rgba(255, 255, 255, 0.06),
|
||||
-20px 25px 0 rgba(255, 255, 255, 0.04);
|
||||
animation: cloudFloat 30s ease-in-out infinite reverse;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes cloudFloat {
|
||||
0%, 100% {
|
||||
transform: translateX(0px) translateY(0px);
|
||||
}
|
||||
25% {
|
||||
transform: translateX(20px) translateY(-10px);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-10px) translateY(-20px);
|
||||
}
|
||||
75% {
|
||||
transform: translateX(15px) translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 768px) {
|
||||
body::after {
|
||||
background-size: 150px 75px;
|
||||
}
|
||||
|
||||
.container::before,
|
||||
.container::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #d4f1d4 50%, #a8d5ba 100%);
|
||||
animation: none;
|
||||
}
|
||||
|
||||
body::before,
|
||||
body::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user