chore: sync local changes (2026-03-12)

This commit is contained in:
2026-03-12 18:58:26 +08:00
parent 04a4cb962a
commit 939442e061
348 changed files with 91638 additions and 92091 deletions

View File

@@ -1,128 +1,128 @@
/* 网易云音乐特色背景样式 */
body {
background: linear-gradient(135deg, #2b2b2b 0%, #1e1e1e 50%, #2b2b2b 100%);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
position: relative;
color: #fff;
}
/* 背景渐变动画 */
@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(236, 65, 65, 0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(236, 65, 65, 0.1) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(236, 65, 65, 0.08) 0%, transparent 50%);
pointer-events: none;
z-index: -2;
}
/* 音符装饰效果 */
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(2px 2px at 20px 30px, rgba(236, 65, 65, 0.4), transparent),
radial-gradient(2px 2px at 40px 70px, rgba(236, 65, 65, 0.3), transparent),
radial-gradient(1px 1px at 90px 40px, rgba(236, 65, 65, 0.4), transparent),
radial-gradient(1px 1px at 130px 80px, rgba(236, 65, 65, 0.3), transparent),
radial-gradient(2px 2px at 160px 30px, rgba(236, 65, 65, 0.4), transparent);
background-repeat: repeat;
background-size: 200px 100px;
animation: particleFloat 20s linear infinite;
pointer-events: none;
z-index: -1;
opacity: 0.6;
}
/* 音符浮动动画 */
@keyframes particleFloat {
0% {
transform: translateY(0px);
}
100% {
transform: translateY(-100px);
}
}
/* 音符装饰 */
.music-note {
position: absolute;
font-size: 24px;
color: rgba(236, 65, 65, 0.3);
animation: floatNote 15s linear infinite;
z-index: -1;
}
@keyframes floatNote {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0;
}
10% {
opacity: 0.8;
}
90% {
opacity: 0.8;
}
100% {
transform: translateY(-100vh) rotate(360deg);
opacity: 0;
}
}
/* 响应式背景调整 */
@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;
opacity: 0.4;
}
}
/* 高性能模式 - 减少动画 */
@media (prefers-reduced-motion: reduce) {
body {
animation: none;
background: linear-gradient(135deg, #2b2b2b 0%, #1e1e1e 50%, #2b2b2b 100%);
}
body::after {
animation: none;
}
.music-note {
animation: none;
display: none;
}
/* 网易云音乐特色背景样式 */
body {
background: linear-gradient(135deg, #2b2b2b 0%, #1e1e1e 50%, #2b2b2b 100%);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
position: relative;
color: #fff;
}
/* 背景渐变动画 */
@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(236, 65, 65, 0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(236, 65, 65, 0.1) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(236, 65, 65, 0.08) 0%, transparent 50%);
pointer-events: none;
z-index: -2;
}
/* 音符装饰效果 */
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(2px 2px at 20px 30px, rgba(236, 65, 65, 0.4), transparent),
radial-gradient(2px 2px at 40px 70px, rgba(236, 65, 65, 0.3), transparent),
radial-gradient(1px 1px at 90px 40px, rgba(236, 65, 65, 0.4), transparent),
radial-gradient(1px 1px at 130px 80px, rgba(236, 65, 65, 0.3), transparent),
radial-gradient(2px 2px at 160px 30px, rgba(236, 65, 65, 0.4), transparent);
background-repeat: repeat;
background-size: 200px 100px;
animation: particleFloat 20s linear infinite;
pointer-events: none;
z-index: -1;
opacity: 0.6;
}
/* 音符浮动动画 */
@keyframes particleFloat {
0% {
transform: translateY(0px);
}
100% {
transform: translateY(-100px);
}
}
/* 音符装饰 */
.music-note {
position: absolute;
font-size: 24px;
color: rgba(236, 65, 65, 0.3);
animation: floatNote 15s linear infinite;
z-index: -1;
}
@keyframes floatNote {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0;
}
10% {
opacity: 0.8;
}
90% {
opacity: 0.8;
}
100% {
transform: translateY(-100vh) rotate(360deg);
opacity: 0;
}
}
/* 响应式背景调整 */
@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;
opacity: 0.4;
}
}
/* 高性能模式 - 减少动画 */
@media (prefers-reduced-motion: reduce) {
body {
animation: none;
background: linear-gradient(135deg, #2b2b2b 0%, #1e1e1e 50%, #2b2b2b 100%);
}
body::after {
animation: none;
}
.music-note {
animation: none;
display: none;
}
}

View File

@@ -1,194 +1,194 @@
/* 响应式样式 - 适配不同设备 */
/* 基础样式 - 移动设备优先 */
.container {
width: 95%;
padding: 15px;
margin: 10px auto;
}
header h1 {
font-size: 1.5rem;
}
.update-time {
font-size: 0.8rem;
padding: 6px 12px;
}
.rank-list {
grid-template-columns: 1fr;
gap: 16px;
margin-top: 15px;
}
.rank-item {
border-radius: 10px;
}
.rank-cover {
height: 160px;
}
.rank-info {
padding: 12px;
}
.rank-name {
font-size: 1rem;
margin-bottom: 6px;
}
.rank-desc {
font-size: 0.85rem;
margin-bottom: 10px;
-webkit-line-clamp: 2;
}
.rank-meta {
font-size: 0.75rem;
margin-bottom: 10px;
}
.rank-link {
padding: 6px 14px;
font-size: 0.85rem;
}
/* 平板设备 */
@media screen and (min-width: 768px) {
.container {
width: 90%;
padding: 20px;
margin: 15px auto;
}
header h1 {
font-size: 1.8rem;
}
.update-time {
font-size: 0.85rem;
padding: 7px 14px;
}
.rank-list {
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-top: 20px;
}
.rank-cover {
height: 170px;
}
.rank-info {
padding: 15px;
}
.rank-name {
font-size: 1.1rem;
margin-bottom: 8px;
}
.rank-desc {
font-size: 0.9rem;
margin-bottom: 12px;
-webkit-line-clamp: 3;
}
.rank-meta {
font-size: 0.8rem;
}
.rank-link {
padding: 7px 16px;
font-size: 0.9rem;
}
.loading-spinner {
width: 60px;
height: 60px;
}
}
/* 桌面设备 */
@media screen and (min-width: 1024px) {
.container {
width: 85%;
max-width: 1200px;
margin: 20px auto;
}
header h1 {
font-size: 2.2rem;
}
.update-time {
font-size: 0.9rem;
padding: 8px 16px;
}
.rank-list {
grid-template-columns: repeat(3, 1fr);
gap: 24px;
margin-top: 25px;
}
.rank-cover {
height: 180px;
}
.rank-info {
padding: 18px;
}
.rank-name {
font-size: 1.2rem;
margin-bottom: 10px;
}
.rank-desc {
font-size: 0.95rem;
margin-bottom: 15px;
}
.rank-meta {
font-size: 0.85rem;
margin-bottom: 15px;
}
.rank-link {
padding: 8px 18px;
font-size: 0.95rem;
}
}
/* 大屏幕设备 */
@media screen and (min-width: 1440px) {
.container {
max-width: 1400px;
}
.rank-list {
grid-template-columns: repeat(4, 1fr);
gap: 30px;
}
.rank-cover {
height: 200px;
}
.rank-name {
font-size: 1.25rem;
}
.rank-desc {
font-size: 1rem;
}
.rank-link {
padding: 10px 20px;
font-size: 1rem;
}
/* 响应式样式 - 适配不同设备 */
/* 基础样式 - 移动设备优先 */
.container {
width: 95%;
padding: 15px;
margin: 10px auto;
}
header h1 {
font-size: 1.5rem;
}
.update-time {
font-size: 0.8rem;
padding: 6px 12px;
}
.rank-list {
grid-template-columns: 1fr;
gap: 16px;
margin-top: 15px;
}
.rank-item {
border-radius: 10px;
}
.rank-cover {
height: 160px;
}
.rank-info {
padding: 12px;
}
.rank-name {
font-size: 1rem;
margin-bottom: 6px;
}
.rank-desc {
font-size: 0.85rem;
margin-bottom: 10px;
-webkit-line-clamp: 2;
}
.rank-meta {
font-size: 0.75rem;
margin-bottom: 10px;
}
.rank-link {
padding: 6px 14px;
font-size: 0.85rem;
}
/* 平板设备 */
@media screen and (min-width: 768px) {
.container {
width: 90%;
padding: 20px;
margin: 15px auto;
}
header h1 {
font-size: 1.8rem;
}
.update-time {
font-size: 0.85rem;
padding: 7px 14px;
}
.rank-list {
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-top: 20px;
}
.rank-cover {
height: 170px;
}
.rank-info {
padding: 15px;
}
.rank-name {
font-size: 1.1rem;
margin-bottom: 8px;
}
.rank-desc {
font-size: 0.9rem;
margin-bottom: 12px;
-webkit-line-clamp: 3;
}
.rank-meta {
font-size: 0.8rem;
}
.rank-link {
padding: 7px 16px;
font-size: 0.9rem;
}
.loading-spinner {
width: 60px;
height: 60px;
}
}
/* 桌面设备 */
@media screen and (min-width: 1024px) {
.container {
width: 85%;
max-width: 1200px;
margin: 20px auto;
}
header h1 {
font-size: 2.2rem;
}
.update-time {
font-size: 0.9rem;
padding: 8px 16px;
}
.rank-list {
grid-template-columns: repeat(3, 1fr);
gap: 24px;
margin-top: 25px;
}
.rank-cover {
height: 180px;
}
.rank-info {
padding: 18px;
}
.rank-name {
font-size: 1.2rem;
margin-bottom: 10px;
}
.rank-desc {
font-size: 0.95rem;
margin-bottom: 15px;
}
.rank-meta {
font-size: 0.85rem;
margin-bottom: 15px;
}
.rank-link {
padding: 8px 18px;
font-size: 0.95rem;
}
}
/* 大屏幕设备 */
@media screen and (min-width: 1440px) {
.container {
max-width: 1400px;
}
.rank-list {
grid-template-columns: repeat(4, 1fr);
gap: 30px;
}
.rank-cover {
height: 200px;
}
.rank-name {
font-size: 1.25rem;
}
.rank-desc {
font-size: 1rem;
}
.rank-link {
padding: 10px 20px;
font-size: 1rem;
}
}

View File

@@ -1,474 +1,474 @@
/* 背景样式 */
.background-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
background-color: #f8f9fa;
}
.modern-gradient {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
135deg,
rgba(64, 169, 255, 0.4) 0%,
rgba(120, 192, 255, 0.3) 25%,
rgba(255, 175, 64, 0.2) 50%,
rgba(255, 140, 50, 0.3) 75%,
rgba(255, 122, 69, 0.4) 100%
);
animation: gradient-flow 20s ease-in-out infinite;
border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
}
.modern-gradient::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(
circle at 30% 70%,
rgba(64, 169, 255, 0.5) 0%,
transparent 50%
), radial-gradient(
circle at 70% 30%,
rgba(255, 140, 50, 0.4) 0%,
transparent 50%
);
animation: pulse-effect 15s ease-in-out infinite alternate;
border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
}
@keyframes gradient-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 pulse-effect {
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;
}
}
/* 主样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
color: #333;
background-color: #f8f9fa;
position: relative;
min-height: 100vh;
overflow-x: hidden;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 24px;
position: relative;
z-index: 1;
background-color: rgba(255, 255, 255, 0.85);
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
backdrop-filter: blur(10px);
position: relative;
}
header {
text-align: center;
margin-bottom: 28px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
header h1 {
background: linear-gradient(135deg, #4096ff, #ff7a45);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 14px;
font-size: 2.4rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.update-time {
color: #666;
font-size: 0.9rem;
background-color: rgba(0, 0, 0, 0.03);
padding: 8px 16px;
border-radius: 24px;
display: inline-block;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.rank-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
margin-top: 20px;
}
.rank-item {
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
overflow: hidden;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
border: 1px solid rgba(0, 0, 0, 0.05);
}
.rank-item:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
border-color: rgba(236, 65, 65, 0.3);
}
.rank-cover {
position: relative;
height: 180px;
overflow: hidden;
}
.rank-cover img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.rank-item:hover .rank-cover img {
transform: scale(1.05);
}
.rank-update {
position: absolute;
bottom: 0;
right: 0;
background: rgba(236, 65, 65, 0.8);
color: white;
padding: 4px 10px;
font-size: 0.8rem;
border-top-left-radius: 8px;
}
.rank-info {
padding: 16px;
flex: 1;
display: flex;
flex-direction: column;
}
.rank-name {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 8px;
color: #333;
}
.rank-desc {
font-size: 0.9rem;
color: #666;
margin-bottom: 12px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
flex: 1;
}
.rank-meta {
font-size: 0.8rem;
color: #999;
margin-bottom: 12px;
}
.rank-link {
display: inline-block;
background: linear-gradient(135deg, #ec4141, #ff7a45);
color: white;
text-decoration: none;
padding: 8px 16px;
border-radius: 20px;
font-size: 0.9rem;
text-align: center;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(236, 65, 65, 0.3);
}
.rank-link:hover {
background: linear-gradient(135deg, #d73435, #f06937);
box-shadow: 0 4px 12px rgba(236, 65, 65, 0.4);
transform: translateY(-2px);
}
.hot-title {
font-size: 1.15rem;
margin-bottom: 8px;
color: #333;
text-decoration: none;
display: block;
line-height: 1.5;
font-weight: 500;
transition: color 0.2s ease;
}
.hot-title:hover {
color: #4096ff;
text-decoration: none;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
font-size: 1.1rem;
}
.loading-spinner {
display: inline-block;
width: 50px;
height: 50px;
border: 3px solid rgba(236, 65, 65, 0.3);
border-radius: 50%;
border-top-color: #ec4141;
animation: spin 1s ease-in-out infinite;
margin-bottom: 16px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error-message {
text-align: center;
padding: 40px;
color: #ff4d4f;
font-size: 1.1rem;
}
.retry-button {
background: #ec4141;
color: white;
border: none;
padding: 8px 20px;
border-radius: 20px;
font-size: 0.9rem;
margin-top: 16px;
cursor: pointer;
transition: all 0.3s ease;
}
.retry-button:hover {
background: #d73435;
box-shadow: 0 2px 8px rgba(236, 65, 65, 0.4);
}
footer {
text-align: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid rgba(0, 0, 0, 0.06);
color: #666;
font-size: 0.9rem;
}
/* 音符装饰样式 */
.music-note {
position: fixed;
font-size: 24px;
color: rgba(236, 65, 65, 0.6);
z-index: 0;
pointer-events: none;
animation: floatNote 20s linear infinite;
text-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
}
@keyframes floatNote {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0.7;
}
50% {
transform: translateY(-100px) rotate(180deg);
opacity: 0.9;
}
100% {
transform: translateY(-200px) rotate(360deg);
opacity: 0;
}
}
/* 响应式设计 */
@media (max-width: 1024px) and (min-width: 768px) {
.container {
max-width: 90%;
padding: 20px;
}
header h1 {
font-size: 2.2rem;
}
.hot-item {
padding: 18px;
}
.hot-title {
font-size: 1.1rem;
}
.music-note {
font-size: 22px;
}
}
@media (max-width: 768px) {
body {
background-color: #f8f9fa;
}
.container {
max-width: 95%;
margin: 12px auto;
padding: 16px;
border-radius: 12px;
}
header {
margin-bottom: 20px;
padding-bottom: 16px;
}
header h1 {
font-size: 1.8rem;
margin-bottom: 10px;
}
.update-time {
font-size: 0.85rem;
padding: 6px 12px;
}
.hot-item {
padding: 16px;
margin-bottom: 12px;
border-radius: 10px;
flex-direction: row;
align-items: flex-start;
}
.hot-rank {
font-size: 1.1rem;
margin-right: 14px;
min-width: 32px;
width: 32px;
height: 32px;
margin-top: 2px;
}
.hot-title {
font-size: 1rem;
line-height: 1.5;
margin-bottom: 6px;
}
footer {
margin-top: 24px;
padding-top: 16px;
font-size: 0.85rem;
}
.music-note {
font-size: 20px;
}
}
@media (max-width: 480px) {
.container {
margin: 8px auto;
padding: 14px;
}
header h1 {
font-size: 1.6rem;
}
.hot-item {
padding: 14px;
margin-bottom: 10px;
}
.hot-rank {
font-size: 1rem;
margin-right: 12px;
min-width: 30px;
width: 30px;
height: 30px;
}
.hot-title {
font-size: 0.95rem;
}
.music-note {
font-size: 16px;
}
}
/* 减少动画以节省电池 */
@media (prefers-reduced-motion: reduce) {
.modern-gradient,
.modern-gradient::before {
animation: none;
}
.modern-gradient {
background: linear-gradient(
135deg,
rgba(64, 169, 255, 0.3) 0%,
rgba(255, 175, 64, 0.2) 50%,
rgba(255, 122, 69, 0.25) 100%
);
}
/* 背景样式 */
.background-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
background-color: #f8f9fa;
}
.modern-gradient {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
135deg,
rgba(64, 169, 255, 0.4) 0%,
rgba(120, 192, 255, 0.3) 25%,
rgba(255, 175, 64, 0.2) 50%,
rgba(255, 140, 50, 0.3) 75%,
rgba(255, 122, 69, 0.4) 100%
);
animation: gradient-flow 20s ease-in-out infinite;
border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
}
.modern-gradient::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(
circle at 30% 70%,
rgba(64, 169, 255, 0.5) 0%,
transparent 50%
), radial-gradient(
circle at 70% 30%,
rgba(255, 140, 50, 0.4) 0%,
transparent 50%
);
animation: pulse-effect 15s ease-in-out infinite alternate;
border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
}
@keyframes gradient-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 pulse-effect {
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;
}
}
/* 主样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
color: #333;
background-color: #f8f9fa;
position: relative;
min-height: 100vh;
overflow-x: hidden;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 24px;
position: relative;
z-index: 1;
background-color: rgba(255, 255, 255, 0.85);
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
backdrop-filter: blur(10px);
position: relative;
}
header {
text-align: center;
margin-bottom: 28px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
header h1 {
background: linear-gradient(135deg, #4096ff, #ff7a45);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 14px;
font-size: 2.4rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.update-time {
color: #666;
font-size: 0.9rem;
background-color: rgba(0, 0, 0, 0.03);
padding: 8px 16px;
border-radius: 24px;
display: inline-block;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.rank-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
margin-top: 20px;
}
.rank-item {
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
overflow: hidden;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
border: 1px solid rgba(0, 0, 0, 0.05);
}
.rank-item:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
border-color: rgba(236, 65, 65, 0.3);
}
.rank-cover {
position: relative;
height: 180px;
overflow: hidden;
}
.rank-cover img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.rank-item:hover .rank-cover img {
transform: scale(1.05);
}
.rank-update {
position: absolute;
bottom: 0;
right: 0;
background: rgba(236, 65, 65, 0.8);
color: white;
padding: 4px 10px;
font-size: 0.8rem;
border-top-left-radius: 8px;
}
.rank-info {
padding: 16px;
flex: 1;
display: flex;
flex-direction: column;
}
.rank-name {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 8px;
color: #333;
}
.rank-desc {
font-size: 0.9rem;
color: #666;
margin-bottom: 12px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
flex: 1;
}
.rank-meta {
font-size: 0.8rem;
color: #999;
margin-bottom: 12px;
}
.rank-link {
display: inline-block;
background: linear-gradient(135deg, #ec4141, #ff7a45);
color: white;
text-decoration: none;
padding: 8px 16px;
border-radius: 20px;
font-size: 0.9rem;
text-align: center;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(236, 65, 65, 0.3);
}
.rank-link:hover {
background: linear-gradient(135deg, #d73435, #f06937);
box-shadow: 0 4px 12px rgba(236, 65, 65, 0.4);
transform: translateY(-2px);
}
.hot-title {
font-size: 1.15rem;
margin-bottom: 8px;
color: #333;
text-decoration: none;
display: block;
line-height: 1.5;
font-weight: 500;
transition: color 0.2s ease;
}
.hot-title:hover {
color: #4096ff;
text-decoration: none;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
font-size: 1.1rem;
}
.loading-spinner {
display: inline-block;
width: 50px;
height: 50px;
border: 3px solid rgba(236, 65, 65, 0.3);
border-radius: 50%;
border-top-color: #ec4141;
animation: spin 1s ease-in-out infinite;
margin-bottom: 16px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error-message {
text-align: center;
padding: 40px;
color: #ff4d4f;
font-size: 1.1rem;
}
.retry-button {
background: #ec4141;
color: white;
border: none;
padding: 8px 20px;
border-radius: 20px;
font-size: 0.9rem;
margin-top: 16px;
cursor: pointer;
transition: all 0.3s ease;
}
.retry-button:hover {
background: #d73435;
box-shadow: 0 2px 8px rgba(236, 65, 65, 0.4);
}
footer {
text-align: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid rgba(0, 0, 0, 0.06);
color: #666;
font-size: 0.9rem;
}
/* 音符装饰样式 */
.music-note {
position: fixed;
font-size: 24px;
color: rgba(236, 65, 65, 0.6);
z-index: 0;
pointer-events: none;
animation: floatNote 20s linear infinite;
text-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
}
@keyframes floatNote {
0% {
transform: translateY(0) rotate(0deg);
opacity: 0.7;
}
50% {
transform: translateY(-100px) rotate(180deg);
opacity: 0.9;
}
100% {
transform: translateY(-200px) rotate(360deg);
opacity: 0;
}
}
/* 响应式设计 */
@media (max-width: 1024px) and (min-width: 768px) {
.container {
max-width: 90%;
padding: 20px;
}
header h1 {
font-size: 2.2rem;
}
.hot-item {
padding: 18px;
}
.hot-title {
font-size: 1.1rem;
}
.music-note {
font-size: 22px;
}
}
@media (max-width: 768px) {
body {
background-color: #f8f9fa;
}
.container {
max-width: 95%;
margin: 12px auto;
padding: 16px;
border-radius: 12px;
}
header {
margin-bottom: 20px;
padding-bottom: 16px;
}
header h1 {
font-size: 1.8rem;
margin-bottom: 10px;
}
.update-time {
font-size: 0.85rem;
padding: 6px 12px;
}
.hot-item {
padding: 16px;
margin-bottom: 12px;
border-radius: 10px;
flex-direction: row;
align-items: flex-start;
}
.hot-rank {
font-size: 1.1rem;
margin-right: 14px;
min-width: 32px;
width: 32px;
height: 32px;
margin-top: 2px;
}
.hot-title {
font-size: 1rem;
line-height: 1.5;
margin-bottom: 6px;
}
footer {
margin-top: 24px;
padding-top: 16px;
font-size: 0.85rem;
}
.music-note {
font-size: 20px;
}
}
@media (max-width: 480px) {
.container {
margin: 8px auto;
padding: 14px;
}
header h1 {
font-size: 1.6rem;
}
.hot-item {
padding: 14px;
margin-bottom: 10px;
}
.hot-rank {
font-size: 1rem;
margin-right: 12px;
min-width: 30px;
width: 30px;
height: 30px;
}
.hot-title {
font-size: 0.95rem;
}
.music-note {
font-size: 16px;
}
}
/* 减少动画以节省电池 */
@media (prefers-reduced-motion: reduce) {
.modern-gradient,
.modern-gradient::before {
animation: none;
}
.modern-gradient {
background: linear-gradient(
135deg,
rgba(64, 169, 255, 0.3) 0%,
rgba(255, 175, 64, 0.2) 50%,
rgba(255, 122, 69, 0.25) 100%
);
}
}

View File

@@ -1,49 +1,49 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网易云音乐榜单 - InfoGenie</title>
<meta name="description" content="网易云音乐各类榜单,包括飙升榜、热歌榜、新歌榜等">
<link rel="stylesheet" href="./css/background.css">
<link rel="stylesheet" href="./css/style.css">
<link rel="stylesheet" href="./css/responsive.css">
</head>
<body>
<!-- 背景效果 -->
<div class="background-container">
<div class="modern-gradient"></div>
</div>
<!-- 音符装饰 -->
<div id="music-notes-container"></div>
<div class="container">
<header>
<h1>网易云音乐榜单</h1>
<div class="update-time" id="update-time">加载中...</div>
</header>
<main>
<div id="loading" class="loading">
<div class="loading-spinner"></div>
<p>正在加载榜单数据...</p>
</div>
<div id="error-message" class="error-message" style="display: none;">
<p>加载失败,请稍后再试</p>
<button id="retry-button" class="retry-button">重试</button>
</div>
<div id="rank-list" class="rank-list" style="display: none;"></div>
</main>
<footer>
<p>数据来源网易云音乐官方API</p>
<p>© 2024 InfoGenie - 网易云音乐榜单</p>
</footer>
</div>
<script src="./js/app.js"></script>
</body>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网易云音乐榜单 - InfoGenie</title>
<meta name="description" content="网易云音乐各类榜单,包括飙升榜、热歌榜、新歌榜等">
<link rel="stylesheet" href="./css/background.css">
<link rel="stylesheet" href="./css/style.css">
<link rel="stylesheet" href="./css/responsive.css">
</head>
<body>
<!-- 背景效果 -->
<div class="background-container">
<div class="modern-gradient"></div>
</div>
<!-- 音符装饰 -->
<div id="music-notes-container"></div>
<div class="container">
<header>
<h1>网易云音乐榜单</h1>
<div class="update-time" id="update-time">加载中...</div>
</header>
<main>
<div id="loading" class="loading">
<div class="loading-spinner"></div>
<p>正在加载榜单数据...</p>
</div>
<div id="error-message" class="error-message" style="display: none;">
<p>加载失败,请稍后再试</p>
<button id="retry-button" class="retry-button">重试</button>
</div>
<div id="rank-list" class="rank-list" style="display: none;"></div>
</main>
<footer>
<p>数据来源网易云音乐官方API</p>
<p>© 2024 InfoGenie - 网易云音乐榜单</p>
</footer>
</div>
<script src="./js/app.js"></script>
</body>
</html>

View File

@@ -1,252 +1,252 @@
/**
* 网易云音乐榜单 - 主应用脚本
* 功能获取API数据、渲染榜单、处理错误、自动切换API接口
*/
// 全局变量
const apiUrls = [];
let currentApiIndex = 0;
let rankData = null;
// DOM元素
const loadingElement = document.getElementById('loading');
const errorElement = document.getElementById('error-message');
const rankListElement = document.getElementById('rank-list');
const updateTimeElement = document.getElementById('update-time');
const retryButton = document.getElementById('retry-button');
// 初始化函数
async function init() {
try {
// 获取API接口列表
await loadApiUrls();
// 获取榜单数据
await fetchRankData();
// 添加音符装饰
createMusicNotes();
} catch (error) {
console.error('初始化失败:', error);
showError();
}
}
// 加载API接口列表
async function loadApiUrls() {
try {
const response = await fetch('./接口集合.json');
if (!response.ok) {
throw new Error('无法加载API接口列表');
}
const data = await response.json();
if (Array.isArray(data) && data.length > 0) {
apiUrls.push(...data);
console.log('已加载API接口列表:', apiUrls);
} else {
throw new Error('API接口列表为空');
}
} catch (error) {
console.error('加载API接口列表失败:', error);
// 使用默认API
apiUrls.push('https://60s.api.shumengya.top/v2/ncm-rank');
}
}
// 获取榜单数据
async function fetchRankData() {
showLoading();
// 如果没有API接口显示错误
if (apiUrls.length === 0) {
throw new Error('没有可用的API接口');
}
try {
const apiUrl = apiUrls[currentApiIndex];
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`API请求失败: ${response.status}`);
}
const data = await response.json();
if (data.code === 200 && data.data && Array.isArray(data.data)) {
rankData = data;
renderRankList(data.data);
updateLastUpdateTime(data);
hideLoading();
} else {
throw new Error('API返回数据格式错误');
}
} catch (error) {
console.error('获取榜单数据失败:', error);
// 尝试切换到下一个API
if (tryNextApi()) {
return fetchRankData();
} else {
showError();
}
}
}
// 尝试切换到下一个API
function tryNextApi() {
if (currentApiIndex < apiUrls.length - 1) {
currentApiIndex++;
console.log(`切换到下一个API: ${apiUrls[currentApiIndex]}`);
return true;
}
return false;
}
// 渲染榜单列表
function renderRankList(ranks) {
if (!Array.isArray(ranks) || ranks.length === 0) {
showError('没有榜单数据');
return;
}
rankListElement.innerHTML = '';
ranks.forEach(rank => {
const rankItem = document.createElement('div');
rankItem.className = 'rank-item';
// 构建榜单项HTML
rankItem.innerHTML = `
<div class="rank-cover">
<img src="${rank.cover}" alt="${rank.name}" loading="lazy">
<div class="rank-update">${rank.update_frequency || '定期更新'}</div>
</div>
<div class="rank-info">
<h3 class="rank-name">${rank.name}</h3>
<p class="rank-desc">${rank.description || '暂无描述'}</p>
<div class="rank-meta">
<span class="rank-updated">更新: ${formatDate(rank.updated)}</span>
</div>
<a href="${rank.link}" target="_blank" class="rank-link">查看详情</a>
</div>
`;
rankListElement.appendChild(rankItem);
});
rankListElement.style.display = 'grid';
}
// 更新最后更新时间
function updateLastUpdateTime(data) {
if (data && data.data && data.data.length > 0) {
const latestRank = data.data.reduce((latest, current) => {
const latestDate = latest.updated_at || 0;
const currentDate = current.updated_at || 0;
return currentDate > latestDate ? current : latest;
}, data.data[0]);
if (latestRank && latestRank.updated) {
updateTimeElement.textContent = `最近更新: ${formatDate(latestRank.updated)}`;
} else {
updateTimeElement.textContent = '数据已更新';
}
}
}
// 格式化日期
function formatDate(dateStr) {
if (!dateStr) return '未知';
try {
const date = new Date(dateStr.replace('2025-', '2024-'));
if (isNaN(date.getTime())) return dateStr;
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).replace(/\//g, '-');
} catch (e) {
return dateStr;
}
}
// 创建音符装饰
function createMusicNotes() {
const notesContainer = document.getElementById('music-notes-container');
const notes = ['♪', '♫', '♬', '♩', '♭', '♮'];
const containerWidth = window.innerWidth;
const containerHeight = window.innerHeight;
// 清空容器
notesContainer.innerHTML = '';
// 创建15个音符
for (let i = 0; i < 15; i++) {
const note = document.createElement('div');
note.className = 'music-note';
note.textContent = notes[Math.floor(Math.random() * notes.length)];
// 随机位置
const left = Math.random() * containerWidth;
const top = Math.random() * containerHeight;
// 随机动画延迟
const delay = Math.random() * 20;
const duration = 15 + Math.random() * 15;
// 设置样式
note.style.left = `${left}px`;
note.style.top = `${top}px`;
note.style.animationDelay = `${delay}s`;
note.style.animationDuration = `${duration}s`;
notesContainer.appendChild(note);
}
}
// 显示加载中
function showLoading() {
loadingElement.style.display = 'block';
errorElement.style.display = 'none';
rankListElement.style.display = 'none';
}
// 隐藏加载中
function hideLoading() {
loadingElement.style.display = 'none';
}
// 显示错误信息
function showError(message = '加载失败,请稍后再试') {
loadingElement.style.display = 'none';
errorElement.querySelector('p').textContent = message;
errorElement.style.display = 'block';
}
// 重试按钮点击事件
retryButton.addEventListener('click', () => {
// 重置API索引
currentApiIndex = 0;
// 重新获取数据
fetchRankData();
});
// 窗口大小改变时重新创建音符
window.addEventListener('resize', debounce(createMusicNotes, 300));
// 防抖函数
function debounce(func, wait) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
// 页面加载完成后初始化
/**
* 网易云音乐榜单 - 主应用脚本
* 功能获取API数据、渲染榜单、处理错误、自动切换API接口
*/
// 全局变量
const apiUrls = [];
let currentApiIndex = 0;
let rankData = null;
// DOM元素
const loadingElement = document.getElementById('loading');
const errorElement = document.getElementById('error-message');
const rankListElement = document.getElementById('rank-list');
const updateTimeElement = document.getElementById('update-time');
const retryButton = document.getElementById('retry-button');
// 初始化函数
async function init() {
try {
// 获取API接口列表
await loadApiUrls();
// 获取榜单数据
await fetchRankData();
// 添加音符装饰
createMusicNotes();
} catch (error) {
console.error('初始化失败:', error);
showError();
}
}
// 加载API接口列表
async function loadApiUrls() {
try {
const response = await fetch('./接口集合.json');
if (!response.ok) {
throw new Error('无法加载API接口列表');
}
const data = await response.json();
if (Array.isArray(data) && data.length > 0) {
apiUrls.push(...data);
console.log('已加载API接口列表:', apiUrls);
} else {
throw new Error('API接口列表为空');
}
} catch (error) {
console.error('加载API接口列表失败:', error);
// 使用默认API
apiUrls.push('https://60s.api.shumengya.top/v2/ncm-rank');
}
}
// 获取榜单数据
async function fetchRankData() {
showLoading();
// 如果没有API接口显示错误
if (apiUrls.length === 0) {
throw new Error('没有可用的API接口');
}
try {
const apiUrl = apiUrls[currentApiIndex];
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`API请求失败: ${response.status}`);
}
const data = await response.json();
if (data.code === 200 && data.data && Array.isArray(data.data)) {
rankData = data;
renderRankList(data.data);
updateLastUpdateTime(data);
hideLoading();
} else {
throw new Error('API返回数据格式错误');
}
} catch (error) {
console.error('获取榜单数据失败:', error);
// 尝试切换到下一个API
if (tryNextApi()) {
return fetchRankData();
} else {
showError();
}
}
}
// 尝试切换到下一个API
function tryNextApi() {
if (currentApiIndex < apiUrls.length - 1) {
currentApiIndex++;
console.log(`切换到下一个API: ${apiUrls[currentApiIndex]}`);
return true;
}
return false;
}
// 渲染榜单列表
function renderRankList(ranks) {
if (!Array.isArray(ranks) || ranks.length === 0) {
showError('没有榜单数据');
return;
}
rankListElement.innerHTML = '';
ranks.forEach(rank => {
const rankItem = document.createElement('div');
rankItem.className = 'rank-item';
// 构建榜单项HTML
rankItem.innerHTML = `
<div class="rank-cover">
<img src="${rank.cover}" alt="${rank.name}" loading="lazy">
<div class="rank-update">${rank.update_frequency || '定期更新'}</div>
</div>
<div class="rank-info">
<h3 class="rank-name">${rank.name}</h3>
<p class="rank-desc">${rank.description || '暂无描述'}</p>
<div class="rank-meta">
<span class="rank-updated">更新: ${formatDate(rank.updated)}</span>
</div>
<a href="${rank.link}" target="_blank" class="rank-link">查看详情</a>
</div>
`;
rankListElement.appendChild(rankItem);
});
rankListElement.style.display = 'grid';
}
// 更新最后更新时间
function updateLastUpdateTime(data) {
if (data && data.data && data.data.length > 0) {
const latestRank = data.data.reduce((latest, current) => {
const latestDate = latest.updated_at || 0;
const currentDate = current.updated_at || 0;
return currentDate > latestDate ? current : latest;
}, data.data[0]);
if (latestRank && latestRank.updated) {
updateTimeElement.textContent = `最近更新: ${formatDate(latestRank.updated)}`;
} else {
updateTimeElement.textContent = '数据已更新';
}
}
}
// 格式化日期
function formatDate(dateStr) {
if (!dateStr) return '未知';
try {
const date = new Date(dateStr.replace('2025-', '2024-'));
if (isNaN(date.getTime())) return dateStr;
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).replace(/\//g, '-');
} catch (e) {
return dateStr;
}
}
// 创建音符装饰
function createMusicNotes() {
const notesContainer = document.getElementById('music-notes-container');
const notes = ['♪', '♫', '♬', '♩', '♭', '♮'];
const containerWidth = window.innerWidth;
const containerHeight = window.innerHeight;
// 清空容器
notesContainer.innerHTML = '';
// 创建15个音符
for (let i = 0; i < 15; i++) {
const note = document.createElement('div');
note.className = 'music-note';
note.textContent = notes[Math.floor(Math.random() * notes.length)];
// 随机位置
const left = Math.random() * containerWidth;
const top = Math.random() * containerHeight;
// 随机动画延迟
const delay = Math.random() * 20;
const duration = 15 + Math.random() * 15;
// 设置样式
note.style.left = `${left}px`;
note.style.top = `${top}px`;
note.style.animationDelay = `${delay}s`;
note.style.animationDuration = `${duration}s`;
notesContainer.appendChild(note);
}
}
// 显示加载中
function showLoading() {
loadingElement.style.display = 'block';
errorElement.style.display = 'none';
rankListElement.style.display = 'none';
}
// 隐藏加载中
function hideLoading() {
loadingElement.style.display = 'none';
}
// 显示错误信息
function showError(message = '加载失败,请稍后再试') {
loadingElement.style.display = 'none';
errorElement.querySelector('p').textContent = message;
errorElement.style.display = 'block';
}
// 重试按钮点击事件
retryButton.addEventListener('click', () => {
// 重置API索引
currentApiIndex = 0;
// 重新获取数据
fetchRankData();
});
// 窗口大小改变时重新创建音符
window.addEventListener('resize', debounce(createMusicNotes, 300));
// 防抖函数
function debounce(func, wait) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', init);

View File

@@ -1,3 +1,3 @@
[
"https://60s.api.shumengya.top/v2/ncm-rank"
]
[
"https://60s.api.shumengya.top/v2/ncm-rank"
]