优化结果
This commit is contained in:
@@ -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
InfoGenie-frontend/public/60sapi/实用功能/EpicGames免费游戏/js/script.js
Normal file
266
InfoGenie-frontend/public/60sapi/实用功能/EpicGames免费游戏/js/script.js
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.shumengya.top/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,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
233
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/background.css
Normal file
233
InfoGenie-frontend/public/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
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/style.css
Normal file
445
InfoGenie-frontend/public/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
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/index.html
Normal file
139
InfoGenie-frontend/public/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>
|
||||
343
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/js/script.js
Normal file
343
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/js/script.js
Normal file
@@ -0,0 +1,343 @@
|
||||
// 公网IP地址查询应用
|
||||
class IPQueryApp {
|
||||
constructor() {
|
||||
this.apiEndpoint = 'https://60s.api.shumengya.top/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');
|
||||
}
|
||||
|
||||
// 更新详细信息 - 只显示API提供的数据
|
||||
if (data.location) this.updateDetailItem('location', data.location);
|
||||
else this.hideDetailItem('location');
|
||||
|
||||
if (data.isp) this.updateDetailItem('isp', data.isp);
|
||||
else this.hideDetailItem('isp');
|
||||
|
||||
if (data.country) this.updateDetailItem('country', data.country);
|
||||
else this.hideDetailItem('country');
|
||||
|
||||
if (data.region) this.updateDetailItem('region', data.region);
|
||||
else this.hideDetailItem('region');
|
||||
|
||||
if (data.city) this.updateDetailItem('city', data.city);
|
||||
else this.hideDetailItem('city');
|
||||
|
||||
if (data.timezone) this.updateDetailItem('timezone', data.timezone);
|
||||
else this.hideDetailItem('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;
|
||||
// 显示对应的详细信息行
|
||||
const detailRow = element.closest('.detail-item');
|
||||
if (detailRow) {
|
||||
detailRow.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏详细信息项
|
||||
hideDetailItem(id) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
// 隐藏整个详细信息行
|
||||
const detailRow = element.closest('.detail-item');
|
||||
if (detailRow) {
|
||||
detailRow.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 复制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
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/接口集合.json
Normal file
3
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top/v2/ip"
|
||||
]
|
||||
17
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/返回接口.json
Normal file
17
InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/返回接口.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"ip": "2401:b60:16:83::"
|
||||
}
|
||||
}
|
||||
|
||||
// 注意:此API只返回IP地址,不包含以下信息:
|
||||
// - location (位置信息)
|
||||
// - isp (网络服务商)
|
||||
// - country (国家)
|
||||
// - region (地区)
|
||||
// - city (城市)
|
||||
// - timezone (时区)
|
||||
//
|
||||
// 如需这些信息,需要使用其他API服务
|
||||
575
InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/background.css
Normal file
575
InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/background.css
Normal file
@@ -0,0 +1,575 @@
|
||||
/* 农历主题背景样式 - 动态调节版本 */
|
||||
body {
|
||||
background: linear-gradient(135deg,
|
||||
#fff8dc 0%, /* 玉米丝色 */
|
||||
#ffd700 20%, /* 金黄色 */
|
||||
#ffcc00 40%, /* 亮金色 */
|
||||
#daa520 60%, /* 深金色 */
|
||||
#b8860b 80%, /* 暗金色 */
|
||||
#fff8dc 100% /* 玉米丝色 */
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: goldenShift 25s ease infinite;
|
||||
background-attachment: fixed;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@keyframes goldenShift {
|
||||
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%; }
|
||||
}
|
||||
|
||||
/* 动态颜色调节系统 */
|
||||
.adaptive-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 70%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
|
||||
linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
animation: adaptiveShift 60s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes adaptiveShift {
|
||||
0% {
|
||||
background:
|
||||
radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 70%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
|
||||
linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
|
||||
}
|
||||
25% {
|
||||
background:
|
||||
radial-gradient(circle at 70% 20%, rgba(255, 255, 255, 0.2) 0%, transparent 50%),
|
||||
radial-gradient(circle at 30% 80%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.25) 100%);
|
||||
}
|
||||
50% {
|
||||
background:
|
||||
radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
|
||||
radial-gradient(circle at 10% 90%, rgba(255, 255, 255, 0.12) 0%, transparent 50%),
|
||||
linear-gradient(225deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.22) 100%);
|
||||
}
|
||||
75% {
|
||||
background:
|
||||
radial-gradient(circle at 90% 60%, rgba(255, 255, 255, 0.18) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 10%, rgba(255, 255, 255, 0.08) 0%, transparent 50%),
|
||||
linear-gradient(315deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
|
||||
}
|
||||
100% {
|
||||
background:
|
||||
radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 70%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
|
||||
linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 高清稻穗贴图层 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
/* 主稻穗束 - 高清细节 */
|
||||
radial-gradient(ellipse 1.5px 12px at 50% 45%, #DAA520 0%, #B8860B 30%, transparent 80%),
|
||||
radial-gradient(ellipse 1px 10px at 48% 50%, #FFD700 0%, #DAA520 40%, transparent 75%),
|
||||
radial-gradient(ellipse 1.2px 11px at 52% 48%, #FFCC00 0%, #B8860B 35%, transparent 78%),
|
||||
radial-gradient(ellipse 0.8px 9px at 49% 52%, #F4A460 0%, #DAA520 45%, transparent 70%),
|
||||
radial-gradient(ellipse 1.3px 13px at 51% 46%, #DEB887 0%, #B8860B 38%, transparent 82%),
|
||||
|
||||
/* 次级稻穗 */
|
||||
radial-gradient(ellipse 1px 8px at 30% 35%, #FFD700 0%, #DAA520 50%, transparent 75%),
|
||||
radial-gradient(ellipse 0.9px 7px at 32% 38%, #FFCC00 0%, #B8860B 45%, transparent 70%),
|
||||
radial-gradient(ellipse 1.1px 9px at 28% 36%, #DEB887 0%, #DAA520 40%, transparent 78%),
|
||||
|
||||
radial-gradient(ellipse 1px 8px at 70% 65%, #FFD700 0%, #DAA520 50%, transparent 75%),
|
||||
radial-gradient(ellipse 0.8px 7px at 72% 68%, #F4A460 0%, #B8860B 45%, transparent 70%),
|
||||
radial-gradient(ellipse 1.2px 9px at 68% 66%, #FFCC00 0%, #DAA520 40%, transparent 78%),
|
||||
|
||||
/* 散落稻粒 */
|
||||
radial-gradient(ellipse 0.5px 4px at 20% 80%, #FFD700 0%, transparent 60%),
|
||||
radial-gradient(ellipse 0.6px 5px at 80% 20%, #FFCC00 0%, transparent 65%),
|
||||
radial-gradient(ellipse 0.4px 3px at 15% 25%, #DEB887 0%, transparent 55%),
|
||||
radial-gradient(ellipse 0.7px 6px at 85% 75%, #DAA520 0%, transparent 70%),
|
||||
|
||||
/* 稻穗茎秆 - 更细致 */
|
||||
linear-gradient(88deg, transparent 49%, #9ACD32 49.5%, #8FBC8F 50%, #9ACD32 50.5%, transparent 51%),
|
||||
linear-gradient(92deg, transparent 49%, #8FBC8F 49.5%, #228B22 50%, #8FBC8F 50.5%, transparent 51%),
|
||||
linear-gradient(85deg, transparent 49%, #32CD32 49.5%, #9ACD32 50%, #32CD32 50.5%, transparent 51%);
|
||||
|
||||
background-size:
|
||||
25px 25px, 24px 24px, 26px 26px, 23px 23px, 27px 27px,
|
||||
20px 20px, 19px 19px, 21px 21px,
|
||||
22px 22px, 18px 18px, 23px 23px,
|
||||
15px 15px, 16px 16px, 14px 14px, 17px 17px,
|
||||
80px 80px, 85px 85px, 75px 75px;
|
||||
|
||||
background-position:
|
||||
0 0, 12px 12px, 6px 18px, 18px 6px, 3px 21px,
|
||||
40px 40px, 52px 48px, 35px 55px,
|
||||
120px 120px, 135px 115px, 110px 130px,
|
||||
200px 200px, 220px 180px, 180px 220px, 240px 160px,
|
||||
0 0, 40px 40px, 20px 60px;
|
||||
|
||||
opacity: 0.25;
|
||||
pointer-events: none;
|
||||
z-index: -2;
|
||||
animation: wheatSway 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes wheatSway {
|
||||
0%, 100% {
|
||||
transform: translateX(0) rotate(0deg);
|
||||
}
|
||||
25% {
|
||||
transform: translateX(5px) rotate(0.5deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-3px) rotate(-0.3deg);
|
||||
}
|
||||
75% {
|
||||
transform: translateX(2px) rotate(0.2deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 大型稻穗背景层 */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
/* 主稻穗茎秆 - 右侧大型 */
|
||||
linear-gradient(85deg, transparent 45%, #9ACD32 47%, #8FBC8F 48%, #228B22 49%, #8FBC8F 50%, #9ACD32 51%, transparent 53%),
|
||||
linear-gradient(87deg, transparent 46%, #32CD32 47.5%, #9ACD32 48.5%, #8FBC8F 49.5%, #9ACD32 50.5%, #32CD32 51.5%, transparent 54%),
|
||||
|
||||
/* 主稻穗穗头 - 大型椭圆稻粒群 */
|
||||
radial-gradient(ellipse 8px 25px at 75% 15%, #FFD700 0%, #DAA520 30%, #B8860B 60%, transparent 85%),
|
||||
radial-gradient(ellipse 7px 23px at 77% 18%, #FFCC00 0%, #DAA520 35%, transparent 80%),
|
||||
radial-gradient(ellipse 9px 27px at 73% 12%, #F4A460 0%, #B8860B 40%, transparent 88%),
|
||||
radial-gradient(ellipse 6px 22px at 79% 20%, #DEB887 0%, #DAA520 45%, transparent 75%),
|
||||
radial-gradient(ellipse 8px 24px at 75% 16%, #FFD700 0%, #B8860B 38%, transparent 82%),
|
||||
|
||||
/* 稻穗分支 */
|
||||
radial-gradient(ellipse 5px 18px at 72% 25%, #FFCC00 0%, #DAA520 50%, transparent 75%),
|
||||
radial-gradient(ellipse 4px 16px at 78% 28%, #F4A460 0%, #B8860B 45%, transparent 70%),
|
||||
radial-gradient(ellipse 6px 20px at 70% 22%, #DEB887 0%, #DAA520 40%, transparent 78%),
|
||||
radial-gradient(ellipse 5px 17px at 80% 30%, #FFD700 0%, #B8860B 42%, transparent 76%),
|
||||
|
||||
/* 左侧稻穗茎秆 */
|
||||
linear-gradient(95deg, transparent 15%, #9ACD32 17%, #8FBC8F 18%, #228B22 19%, #8FBC8F 20%, #9ACD32 21%, transparent 23%),
|
||||
|
||||
/* 左侧稻穗穗头 */
|
||||
radial-gradient(ellipse 6px 20px at 25% 25%, #FFD700 0%, #DAA520 30%, transparent 80%),
|
||||
radial-gradient(ellipse 5px 18px at 27% 28%, #FFCC00 0%, #B8860B 35%, transparent 75%),
|
||||
radial-gradient(ellipse 7px 22px at 23% 22%, #F4A460 0%, #DAA520 40%, transparent 85%),
|
||||
|
||||
/* 麦田远景效果 */
|
||||
linear-gradient(180deg, transparent 70%, rgba(255, 215, 0, 0.1) 75%, rgba(218, 165, 32, 0.15) 85%, rgba(255, 215, 0, 0.2) 95%, rgba(255, 215, 0, 0.25) 100%),
|
||||
|
||||
/* 散落稻粒 */
|
||||
radial-gradient(ellipse 2px 8px at 60% 40%, #FFD700 0%, transparent 60%),
|
||||
radial-gradient(ellipse 1.5px 6px at 40% 60%, #FFCC00 0%, transparent 65%),
|
||||
radial-gradient(ellipse 2.5px 10px at 85% 50%, #DEB887 0%, transparent 70%),
|
||||
radial-gradient(ellipse 1.8px 7px at 15% 80%, #DAA520 0%, transparent 68%);
|
||||
|
||||
background-size:
|
||||
/* 主茎秆 */
|
||||
100vw 80vh, 100vw 82vh,
|
||||
/* 主穗头 */
|
||||
50vw 60vh, 48vw 58vh, 52vw 62vh, 46vw 56vh, 50vw 60vh,
|
||||
/* 分支 */
|
||||
40vw 50vh, 38vw 48vh, 42vw 52vh, 36vw 46vh,
|
||||
/* 左侧茎秆 */
|
||||
100vw 70vh,
|
||||
/* 左侧穗头 */
|
||||
35vw 45vh, 33vw 43vh, 37vw 47vh,
|
||||
/* 麦田远景 */
|
||||
100vw 100vh,
|
||||
/* 散落稻粒 */
|
||||
20vw 20vh, 25vw 25vh, 30vw 30vh, 22vw 22vh;
|
||||
|
||||
background-position:
|
||||
/* 主茎秆 */
|
||||
70% 20%, 72% 18%,
|
||||
/* 主穗头 */
|
||||
60% 0%, 62% 2%, 58% -2%, 64% 4%, 60% 1%,
|
||||
/* 分支 */
|
||||
65% 15%, 67% 17%, 63% 13%, 69% 19%,
|
||||
/* 左侧茎秆 */
|
||||
20% 30%,
|
||||
/* 左侧穗头 */
|
||||
15% 20%, 17% 22%, 13% 18%,
|
||||
/* 麦田远景 */
|
||||
0% 0%,
|
||||
/* 散落稻粒 */
|
||||
30% 50%, 50% 70%, 80% 40%, 10% 80%;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
animation: wheatSway 25s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spiralRotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 流星效果容器 */
|
||||
.meteor-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 流星轨迹 */
|
||||
.meteor {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
background: radial-gradient(circle, #FFD700 0%, #FFA500 50%, transparent 100%);
|
||||
border-radius: 50%;
|
||||
box-shadow:
|
||||
0 0 10px #FFD700,
|
||||
0 0 20px #FFA500,
|
||||
0 0 30px #FF8C00;
|
||||
animation: meteorFall linear infinite;
|
||||
}
|
||||
|
||||
/* 流星尾迹 */
|
||||
.meteor::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100px;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
#FFD700 0%,
|
||||
#FFA500 30%,
|
||||
#FF8C00 60%,
|
||||
transparent 100%);
|
||||
transform-origin: 0 50%;
|
||||
transform: rotate(-45deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
@keyframes meteorFall {
|
||||
0% {
|
||||
transform: translateX(-100px) translateY(-100px);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(calc(100vw + 100px)) translateY(calc(100vh + 100px));
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 多个流星的不同轨迹 */
|
||||
.meteor:nth-child(1) {
|
||||
top: 10%;
|
||||
left: -100px;
|
||||
animation-duration: 8s;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.meteor:nth-child(2) {
|
||||
top: 20%;
|
||||
left: -100px;
|
||||
animation-duration: 12s;
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.meteor:nth-child(3) {
|
||||
top: 30%;
|
||||
left: -100px;
|
||||
animation-duration: 10s;
|
||||
animation-delay: 4s;
|
||||
}
|
||||
|
||||
.meteor:nth-child(4) {
|
||||
top: 50%;
|
||||
left: -100px;
|
||||
animation-duration: 15s;
|
||||
animation-delay: 6s;
|
||||
}
|
||||
|
||||
.meteor:nth-child(5) {
|
||||
top: 70%;
|
||||
left: -100px;
|
||||
animation-duration: 9s;
|
||||
animation-delay: 8s;
|
||||
}
|
||||
|
||||
.meteor:nth-child(6) {
|
||||
top: 80%;
|
||||
left: -100px;
|
||||
animation-duration: 11s;
|
||||
animation-delay: 10s;
|
||||
}
|
||||
|
||||
/* 金色粒子效果 */
|
||||
.golden-particles {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
background: radial-gradient(circle, #FFD700 0%, #FFA500 70%, transparent 100%);
|
||||
border-radius: 50%;
|
||||
animation: particleFloat linear infinite;
|
||||
}
|
||||
|
||||
@keyframes particleFloat {
|
||||
0% {
|
||||
transform: translateY(100vh) rotate(0deg);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100px) rotate(360deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 粒子的不同位置和动画时长 */
|
||||
.particle:nth-child(1) { left: 10%; animation-duration: 20s; animation-delay: 0s; }
|
||||
.particle:nth-child(2) { left: 20%; animation-duration: 25s; animation-delay: 2s; }
|
||||
.particle:nth-child(3) { left: 30%; animation-duration: 18s; animation-delay: 4s; }
|
||||
.particle:nth-child(4) { left: 40%; animation-duration: 22s; animation-delay: 6s; }
|
||||
.particle:nth-child(5) { left: 50%; animation-duration: 24s; animation-delay: 8s; }
|
||||
.particle:nth-child(6) { left: 60%; animation-duration: 19s; animation-delay: 10s; }
|
||||
.particle:nth-child(7) { left: 70%; animation-duration: 21s; animation-delay: 12s; }
|
||||
.particle:nth-child(8) { left: 80%; animation-duration: 23s; animation-delay: 14s; }
|
||||
.particle:nth-child(9) { left: 90%; animation-duration: 26s; animation-delay: 16s; }
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.meteor {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.meteor::before {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.particle {
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
body::before {
|
||||
background-size:
|
||||
30px 30px, 25px 25px, 35px 35px, 28px 28px, 32px 32px,
|
||||
40px 40px, 45px 45px;
|
||||
}
|
||||
|
||||
body::after {
|
||||
background-size: 200px 200px, 150px 150px, 100px 100px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 麦穗飘舞特效 */
|
||||
.wheat-floating {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 移动设备性能优化 */
|
||||
@media (max-width: 768px) {
|
||||
.wheat-floating {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.golden-particles {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.meteor-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.adaptive-overlay {
|
||||
animation: none;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.wheat-particle {
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 20px;
|
||||
background: linear-gradient(180deg,
|
||||
#FFD700 0%,
|
||||
#DAA520 50%,
|
||||
#B8860B 100%
|
||||
);
|
||||
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
|
||||
opacity: 0.7;
|
||||
animation: wheatFloat 15s linear infinite;
|
||||
}
|
||||
|
||||
.wheat-particle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 2px;
|
||||
height: 8px;
|
||||
background: #8B7355;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.wheat-particle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 1px;
|
||||
width: 2px;
|
||||
height: 4px;
|
||||
background: #FFEC8C;
|
||||
border-radius: 50%;
|
||||
box-shadow:
|
||||
3px 2px 0 #FFEC8C,
|
||||
1px 6px 0 #FFEC8C,
|
||||
4px 8px 0 #FFEC8C;
|
||||
}
|
||||
|
||||
@keyframes wheatFloat {
|
||||
0% {
|
||||
transform: translateY(-100vh) translateX(0) rotate(0deg);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
90% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh) translateX(50px) rotate(360deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 不同大小和速度的麦穗 */
|
||||
.wheat-particle:nth-child(1) {
|
||||
left: 10%;
|
||||
animation-duration: 12s;
|
||||
animation-delay: 0s;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
.wheat-particle:nth-child(2) {
|
||||
left: 25%;
|
||||
animation-duration: 18s;
|
||||
animation-delay: 2s;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.wheat-particle:nth-child(3) {
|
||||
left: 40%;
|
||||
animation-duration: 15s;
|
||||
animation-delay: 4s;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.wheat-particle:nth-child(4) {
|
||||
left: 60%;
|
||||
animation-duration: 20s;
|
||||
animation-delay: 1s;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.wheat-particle:nth-child(5) {
|
||||
left: 75%;
|
||||
animation-duration: 14s;
|
||||
animation-delay: 3s;
|
||||
transform: scale(0.7);
|
||||
}
|
||||
|
||||
.wheat-particle:nth-child(6) {
|
||||
left: 90%;
|
||||
animation-duration: 16s;
|
||||
animation-delay: 5s;
|
||||
transform: scale(1.0);
|
||||
}
|
||||
|
||||
.wheat-particle:nth-child(7) {
|
||||
left: 5%;
|
||||
animation-duration: 22s;
|
||||
animation-delay: 6s;
|
||||
transform: scale(0.6);
|
||||
}
|
||||
|
||||
.wheat-particle:nth-child(8) {
|
||||
left: 35%;
|
||||
animation-duration: 13s;
|
||||
animation-delay: 2.5s;
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
/* 减少动画偏好设置 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
|
||||
.meteor,
|
||||
.particle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
1398
InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/style.css
Normal file
1398
InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/style.css
Normal file
File diff suppressed because it is too large
Load Diff
109
InfoGenie-frontend/public/60sapi/实用功能/农历信息/index.html
Normal file
109
InfoGenie-frontend/public/60sapi/实用功能/农历信息/index.html
Normal file
@@ -0,0 +1,109 @@
|
||||
<!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="adaptive-overlay"></div>
|
||||
|
||||
<!-- 流星效果容器 -->
|
||||
<div class="meteor-container">
|
||||
<div class="meteor"></div>
|
||||
<div class="meteor"></div>
|
||||
<div class="meteor"></div>
|
||||
<div class="meteor"></div>
|
||||
<div class="meteor"></div>
|
||||
<div class="meteor"></div>
|
||||
</div>
|
||||
|
||||
<!-- 金色粒子效果容器 -->
|
||||
<div class="golden-particles">
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
<div class="particle"></div>
|
||||
</div>
|
||||
|
||||
<!-- 麦穗飘舞特效 -->
|
||||
<div class="wheat-floating">
|
||||
<div class="wheat-particle"></div>
|
||||
<div class="wheat-particle"></div>
|
||||
<div class="wheat-particle"></div>
|
||||
<div class="wheat-particle"></div>
|
||||
<div class="wheat-particle"></div>
|
||||
<div class="wheat-particle"></div>
|
||||
<div class="wheat-particle"></div>
|
||||
<div class="wheat-particle"></div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
481
InfoGenie-frontend/public/60sapi/实用功能/农历信息/js/script.js
Normal file
481
InfoGenie-frontend/public/60sapi/实用功能/农历信息/js/script.js
Normal file
@@ -0,0 +1,481 @@
|
||||
// API接口列表
|
||||
const API_ENDPOINTS = [
|
||||
"https://60s.api.shumengya.top",
|
||||
];
|
||||
|
||||
// 当前使用的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">🍂</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">🎊</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();
|
||||
}
|
||||
});
|
||||
3
InfoGenie-frontend/public/60sapi/实用功能/农历信息/接口集合.json
Normal file
3
InfoGenie-frontend/public/60sapi/实用功能/农历信息/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
647
InfoGenie-frontend/public/60sapi/实用功能/农历信息/返回接口.json
Normal file
647
InfoGenie-frontend/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": "一年最冷"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
561
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/css/style.css
Normal file
561
InfoGenie-frontend/public/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);
|
||||
}
|
||||
212
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/index.html
Normal file
212
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/index.html
Normal file
@@ -0,0 +1,212 @@
|
||||
<!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>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>
|
||||
394
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/js/script.js
Normal file
394
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/js/script.js
Normal file
@@ -0,0 +1,394 @@
|
||||
// API配置
|
||||
const API_BASE_URL = 'https://60s.api.shumengya.top/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'),
|
||||
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.encoded || '不可用');
|
||||
// BASE64解码:只有当输入本身是BASE64格式时才显示解码结果
|
||||
let base64DecodeResult = data.base64.decoded;
|
||||
if (!base64DecodeResult) {
|
||||
// 检查输入是否为有效的BASE64格式
|
||||
const inputValue = elements.inputText.value.trim();
|
||||
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
||||
if (base64Regex.test(inputValue) && inputValue.length % 4 === 0) {
|
||||
try {
|
||||
base64DecodeResult = atob(inputValue);
|
||||
} catch (e) {
|
||||
base64DecodeResult = '解码失败';
|
||||
}
|
||||
} else {
|
||||
base64DecodeResult = '输入非BASE64格式';
|
||||
}
|
||||
}
|
||||
updateResultElement('base64Decode', base64DecodeResult || '不可用');
|
||||
}
|
||||
|
||||
// URL编码
|
||||
if (data.url) {
|
||||
updateResultElement('urlEncode', data.url.encoded || '不可用');
|
||||
updateResultElement('urlDecode', data.url.decoded || '不可用');
|
||||
}
|
||||
|
||||
// 压缩结果(仅显示压缩,不显示解压)
|
||||
if (data.gzip) {
|
||||
updateResultElement('gzipCompress', data.gzip.encoded || '不可用');
|
||||
}
|
||||
|
||||
if (data.deflate) {
|
||||
updateResultElement('deflateCompress', data.deflate.encoded || '不可用');
|
||||
}
|
||||
|
||||
if (data.brotli) {
|
||||
updateResultElement('brotliCompress', data.brotli.encoded || '不可用');
|
||||
}
|
||||
|
||||
} 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
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/接口集合.json
Normal file
3
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
35
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/返回接口.json
Normal file
35
InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/返回接口.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"source": "hello",
|
||||
"md5": "5d41402abc4b2a76b9719d911017c592",
|
||||
"sha": {
|
||||
"sha1": "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d",
|
||||
"sha256": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
|
||||
"sha512": "9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043"
|
||||
},
|
||||
"base64": {
|
||||
"encoded": "aGVsbG8=",
|
||||
"decoded": ""
|
||||
},
|
||||
"url": {
|
||||
"encoded": "hello",
|
||||
"decoded": "hello"
|
||||
},
|
||||
"gzip": {
|
||||
"encoded": "1f8b0800000000000003cb48cdc9c9070086a6103605000000",
|
||||
"decoded": ""
|
||||
},
|
||||
"deflate": {
|
||||
"encoded": "789ccb48cdc9c90700062c0215",
|
||||
"decoded": ""
|
||||
},
|
||||
"brotli": {
|
||||
"encoded": "0b028068656c6c6f03",
|
||||
"decoded": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
注意:实际API返回的字段名是 encoded/decoded,不是 encode/decode
|
||||
137
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/background.css
Normal file
137
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/background.css
Normal file
@@ -0,0 +1,137 @@
|
||||
/* 背景样式文件 */
|
||||
|
||||
/* 页面主背景 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f0fdf4 25%, #dcfce7 50%, #f0fdf4 75%, #e8f5e8 100%);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
/* 背景动画 */
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 容器背景 */
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 翻译框背景 */
|
||||
.translate-box {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(116, 198, 157, 0.3);
|
||||
}
|
||||
|
||||
/* 输入框背景 */
|
||||
#input-text {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
#input-text:focus {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
/* 输出框背景 */
|
||||
.output-text {
|
||||
background: #f8fffe;
|
||||
}
|
||||
|
||||
/* 按钮背景 */
|
||||
.translate-btn {
|
||||
background: linear-gradient(135deg, #74c69d, #52b788);
|
||||
}
|
||||
|
||||
.translate-btn:hover {
|
||||
background: linear-gradient(135deg, #52b788, #40916c);
|
||||
}
|
||||
|
||||
.translate-btn:disabled {
|
||||
background: #b7e4c7;
|
||||
}
|
||||
|
||||
.swap-btn {
|
||||
background: #74c69d;
|
||||
}
|
||||
|
||||
.swap-btn:hover {
|
||||
background: #52b788;
|
||||
}
|
||||
|
||||
/* 语言选择器背景 */
|
||||
.lang-select {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.lang-select:focus {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
/* 发音信息背景 */
|
||||
.pronounce-item {
|
||||
background: rgba(116, 198, 157, 0.1);
|
||||
}
|
||||
|
||||
/* 清除和复制按钮背景 */
|
||||
.clear-btn:hover,
|
||||
.copy-btn:hover {
|
||||
background: rgba(116, 198, 157, 0.1);
|
||||
}
|
||||
|
||||
/* 提示消息背景 */
|
||||
.toast {
|
||||
background: #52b788;
|
||||
}
|
||||
|
||||
.toast.error {
|
||||
background: #e74c3c;
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 767px) {
|
||||
body {
|
||||
background-size: 200% 200%;
|
||||
animation-duration: 10s;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.translate-box {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
backdrop-filter: blur(15px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
background-size: 150% 150%;
|
||||
animation-duration: 8s;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: transparent;
|
||||
backdrop-filter: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.translate-box {
|
||||
background: rgba(255, 255, 255, 0.99);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
}
|
||||
99
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/index.html
Normal file
99
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/index.html
Normal file
@@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>在线翻译 - 支持109种语言</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>在线机器翻译</h1>
|
||||
<p class="subtitle">支持109种语言互译</p>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="translate-box">
|
||||
<div class="language-selector">
|
||||
<div class="lang-group">
|
||||
<label for="from-lang">源语言</label>
|
||||
<select id="from-lang" class="lang-select">
|
||||
<option value="auto">自动检测</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button class="swap-btn" id="swap-btn" title="交换语言">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M7 16l4-4-4-4"></path>
|
||||
<path d="M17 8l-4 4 4 4"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="lang-group">
|
||||
<label for="to-lang">目标语言</label>
|
||||
<select id="to-lang" class="lang-select">
|
||||
<option value="auto">自动选择</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-areas">
|
||||
<div class="input-section">
|
||||
<div class="textarea-header">
|
||||
<span class="detected-lang" id="detected-lang"></span>
|
||||
<button class="clear-btn" id="clear-btn" title="清空">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
id="input-text"
|
||||
placeholder="请输入要翻译的文本..."
|
||||
maxlength="5000"
|
||||
></textarea>
|
||||
<div class="char-count">
|
||||
<span id="char-count">0</span>/5000
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="output-section">
|
||||
<div class="textarea-header">
|
||||
<span class="target-lang" id="target-lang"></span>
|
||||
<button class="copy-btn" id="copy-btn" title="复制">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="output-text" class="output-text">翻译结果将显示在这里...</div>
|
||||
<div class="pronounce-section" id="pronounce-section">
|
||||
<div class="pronounce-item" id="source-pronounce"></div>
|
||||
<div class="pronounce-item" id="target-pronounce"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="translate-btn" id="translate-btn">
|
||||
<span class="btn-text">翻译</span>
|
||||
<div class="loading-spinner" id="loading-spinner"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>数据来源于有道翻译,与其网页端同步</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
452
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/script.js
Normal file
452
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/script.js
Normal file
@@ -0,0 +1,452 @@
|
||||
// 全局变量
|
||||
let supportedLanguages = {};
|
||||
let isTranslating = false;
|
||||
|
||||
// DOM元素
|
||||
const elements = {
|
||||
fromLang: null,
|
||||
toLang: null,
|
||||
inputText: null,
|
||||
outputText: null,
|
||||
translateBtn: null,
|
||||
swapBtn: null,
|
||||
clearBtn: null,
|
||||
copyBtn: null,
|
||||
charCount: null,
|
||||
detectedLang: null,
|
||||
targetLang: null,
|
||||
pronounceSection: null
|
||||
};
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializeElements();
|
||||
loadSupportedLanguages();
|
||||
bindEvents();
|
||||
updateCharCount();
|
||||
});
|
||||
|
||||
// 初始化DOM元素
|
||||
function initializeElements() {
|
||||
elements.fromLang = document.getElementById('from-lang');
|
||||
elements.toLang = document.getElementById('to-lang');
|
||||
elements.inputText = document.getElementById('input-text');
|
||||
elements.outputText = document.getElementById('output-text');
|
||||
elements.translateBtn = document.getElementById('translate-btn');
|
||||
elements.swapBtn = document.getElementById('swap-btn');
|
||||
elements.clearBtn = document.getElementById('clear-btn');
|
||||
elements.copyBtn = document.getElementById('copy-btn');
|
||||
elements.charCount = document.getElementById('char-count');
|
||||
elements.detectedLang = document.getElementById('detected-lang');
|
||||
elements.targetLang = document.getElementById('target-lang');
|
||||
elements.pronounceSection = document.getElementById('pronounce-section');
|
||||
}
|
||||
|
||||
// 加载支持的语言列表
|
||||
async function loadSupportedLanguages() {
|
||||
try {
|
||||
const response = await fetch('https://60s.viki.moe/v2/fanyi/langs');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200 && data.data && Array.isArray(data.data)) {
|
||||
// 转换数组格式为对象格式
|
||||
supportedLanguages = {};
|
||||
supportedLanguages['auto'] = '自动检测';
|
||||
data.data.forEach(lang => {
|
||||
supportedLanguages[lang.code] = lang.label;
|
||||
});
|
||||
populateLanguageSelectors();
|
||||
} else {
|
||||
throw new Error('获取语言列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载语言列表失败:', error);
|
||||
showToast('加载语言列表失败,请刷新页面重试', 'error');
|
||||
// 使用默认语言列表
|
||||
useDefaultLanguages();
|
||||
}
|
||||
}
|
||||
|
||||
// 使用默认语言列表(备用方案)
|
||||
function useDefaultLanguages() {
|
||||
supportedLanguages = {
|
||||
'auto': '自动检测',
|
||||
'zh-CHS': '中文',
|
||||
'en': '英语',
|
||||
'ja': '日语',
|
||||
'ko': '韩语',
|
||||
'fr': '法语',
|
||||
'de': '德语',
|
||||
'es': '西班牙语',
|
||||
'ru': '俄语',
|
||||
'th': '泰语',
|
||||
'ar': '阿拉伯语',
|
||||
'pt': '葡萄牙语',
|
||||
'it': '意大利语'
|
||||
};
|
||||
populateLanguageSelectors();
|
||||
}
|
||||
|
||||
// 填充语言选择器
|
||||
function populateLanguageSelectors() {
|
||||
const fromSelect = elements.fromLang;
|
||||
const toSelect = elements.toLang;
|
||||
|
||||
// 清空现有选项
|
||||
fromSelect.innerHTML = '';
|
||||
toSelect.innerHTML = '';
|
||||
|
||||
// 添加语言选项
|
||||
Object.entries(supportedLanguages).forEach(([code, name]) => {
|
||||
const fromOption = new Option(name, code);
|
||||
const toOption = new Option(name, code);
|
||||
|
||||
fromSelect.appendChild(fromOption);
|
||||
toSelect.appendChild(toOption);
|
||||
});
|
||||
|
||||
// 设置默认值
|
||||
fromSelect.value = 'auto';
|
||||
toSelect.value = 'en';
|
||||
|
||||
// 如果没有auto选项,则设置为中文
|
||||
if (!supportedLanguages['auto']) {
|
||||
fromSelect.value = 'zh-CHS';
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
function bindEvents() {
|
||||
// 输入框事件
|
||||
elements.inputText.addEventListener('input', function() {
|
||||
updateCharCount();
|
||||
clearOutput();
|
||||
});
|
||||
|
||||
elements.inputText.addEventListener('keydown', function(e) {
|
||||
if (e.ctrlKey && e.key === 'Enter') {
|
||||
translateText();
|
||||
}
|
||||
});
|
||||
|
||||
// 按钮事件
|
||||
elements.translateBtn.addEventListener('click', translateText);
|
||||
elements.swapBtn.addEventListener('click', swapLanguages);
|
||||
elements.clearBtn.addEventListener('click', clearInput);
|
||||
elements.copyBtn.addEventListener('click', copyOutput);
|
||||
|
||||
// 语言选择器事件
|
||||
elements.fromLang.addEventListener('change', function() {
|
||||
clearOutput();
|
||||
updateLanguageLabels();
|
||||
});
|
||||
|
||||
elements.toLang.addEventListener('change', function() {
|
||||
clearOutput();
|
||||
updateLanguageLabels();
|
||||
});
|
||||
}
|
||||
|
||||
// 更新字符计数
|
||||
function updateCharCount() {
|
||||
const text = elements.inputText.value;
|
||||
const count = text.length;
|
||||
elements.charCount.textContent = `${count}/5000`;
|
||||
|
||||
if (count > 5000) {
|
||||
elements.charCount.style.color = '#e74c3c';
|
||||
} else {
|
||||
elements.charCount.style.color = '#74c69d';
|
||||
}
|
||||
}
|
||||
|
||||
// 更新语言标签
|
||||
function updateLanguageLabels() {
|
||||
const fromLang = elements.fromLang.value;
|
||||
const toLang = elements.toLang.value;
|
||||
|
||||
elements.detectedLang.textContent = supportedLanguages[fromLang] || '未知语言';
|
||||
elements.targetLang.textContent = supportedLanguages[toLang] || '未知语言';
|
||||
}
|
||||
|
||||
// 翻译文本
|
||||
async function translateText() {
|
||||
const text = elements.inputText.value.trim();
|
||||
|
||||
if (!text) {
|
||||
showToast('请输入要翻译的文本', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.length > 5000) {
|
||||
showToast('文本长度不能超过5000字符', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTranslating) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTranslating(true);
|
||||
|
||||
try {
|
||||
const fromLang = elements.fromLang.value;
|
||||
const toLang = elements.toLang.value;
|
||||
|
||||
// 构建请求URL
|
||||
const params = new URLSearchParams({
|
||||
text: text,
|
||||
from: fromLang,
|
||||
to: toLang
|
||||
});
|
||||
|
||||
const response = await fetch(`https://60s.viki.moe/v2/fanyi?${params}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200 && data.data) {
|
||||
displayTranslationResult(data.data);
|
||||
} else {
|
||||
throw new Error(data.msg || '翻译失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('翻译失败:', error);
|
||||
showToast('翻译失败: ' + error.message, 'error');
|
||||
elements.outputText.textContent = '翻译失败,请重试';
|
||||
} finally {
|
||||
setTranslating(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示翻译结果
|
||||
function displayTranslationResult(data) {
|
||||
// 显示翻译结果
|
||||
const translation = data.target ? data.target.text : '';
|
||||
elements.outputText.textContent = translation;
|
||||
|
||||
// 更新检测到的语言
|
||||
if (data.source && data.source.type_desc) {
|
||||
elements.detectedLang.textContent = `检测: ${data.source.type_desc}`;
|
||||
}
|
||||
|
||||
// 显示发音信息
|
||||
displayPronunciation(data);
|
||||
|
||||
// 如果翻译结果为空
|
||||
if (!translation) {
|
||||
elements.outputText.textContent = '未获取到翻译结果';
|
||||
}
|
||||
}
|
||||
|
||||
// 显示发音信息
|
||||
function displayPronunciation(data) {
|
||||
const pronounceSection = elements.pronounceSection;
|
||||
if (!pronounceSection) {
|
||||
return;
|
||||
}
|
||||
pronounceSection.innerHTML = '';
|
||||
|
||||
// 原文发音
|
||||
if (data.source && data.source.pronounce) {
|
||||
const sourcePhoneticDiv = document.createElement('div');
|
||||
sourcePhoneticDiv.className = 'pronounce-item show';
|
||||
sourcePhoneticDiv.textContent = `原文发音: [${data.source.pronounce}]`;
|
||||
pronounceSection.appendChild(sourcePhoneticDiv);
|
||||
}
|
||||
|
||||
// 译文发音
|
||||
if (data.target && data.target.pronounce) {
|
||||
const targetPhoneticDiv = document.createElement('div');
|
||||
targetPhoneticDiv.className = 'pronounce-item show';
|
||||
targetPhoneticDiv.textContent = `译文发音: [${data.target.pronounce}]`;
|
||||
pronounceSection.appendChild(targetPhoneticDiv);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置翻译状态
|
||||
function setTranslating(translating) {
|
||||
isTranslating = translating;
|
||||
elements.translateBtn.disabled = translating;
|
||||
|
||||
if (translating) {
|
||||
elements.translateBtn.classList.add('loading');
|
||||
} else {
|
||||
elements.translateBtn.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
|
||||
// 交换语言
|
||||
function swapLanguages() {
|
||||
const fromValue = elements.fromLang.value;
|
||||
const toValue = elements.toLang.value;
|
||||
|
||||
// 不能交换自动检测
|
||||
if (fromValue === 'auto') {
|
||||
showToast('自动检测语言无法交换', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
elements.fromLang.value = toValue;
|
||||
elements.toLang.value = fromValue;
|
||||
|
||||
// 交换文本内容
|
||||
const inputText = elements.inputText.value;
|
||||
const outputText = elements.outputText.textContent;
|
||||
|
||||
if (outputText && outputText !== '翻译结果将在这里显示...' && outputText !== '翻译失败,请重试' && outputText !== '未获取到翻译结果') {
|
||||
elements.inputText.value = outputText;
|
||||
elements.outputText.textContent = inputText;
|
||||
}
|
||||
|
||||
updateCharCount();
|
||||
updateLanguageLabels();
|
||||
clearPronunciation();
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
function clearInput() {
|
||||
elements.inputText.value = '';
|
||||
updateCharCount();
|
||||
clearOutput();
|
||||
}
|
||||
|
||||
// 清空输出
|
||||
function clearOutput() {
|
||||
elements.outputText.textContent = '翻译结果将在这里显示...';
|
||||
clearPronunciation();
|
||||
}
|
||||
|
||||
// 清空发音信息
|
||||
function clearPronunciation() {
|
||||
if (elements.pronounceSection) {
|
||||
elements.pronounceSection.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 复制输出
|
||||
function copyOutput() {
|
||||
const text = elements.outputText.textContent;
|
||||
|
||||
if (!text || text === '翻译结果将在这里显示...' || text === '翻译失败,请重试' || text === '未获取到翻译结果') {
|
||||
showToast('没有可复制的内容', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用现代API复制
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showToast('已复制到剪贴板');
|
||||
}).catch(() => {
|
||||
fallbackCopy(text);
|
||||
});
|
||||
} else {
|
||||
fallbackCopy(text);
|
||||
}
|
||||
}
|
||||
|
||||
// 备用复制方法
|
||||
function fallbackCopy(text) {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.opacity = '0';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
showToast('已复制到剪贴板');
|
||||
} catch (err) {
|
||||
showToast('复制失败,请手动复制', 'error');
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
// 显示提示消息
|
||||
function showToast(message, type = 'success') {
|
||||
// 移除现有的toast
|
||||
const existingToast = document.querySelector('.toast');
|
||||
if (existingToast) {
|
||||
existingToast.remove();
|
||||
}
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast ${type}`;
|
||||
toast.textContent = message;
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
// 显示toast
|
||||
setTimeout(() => {
|
||||
toast.classList.add('show');
|
||||
}, 100);
|
||||
|
||||
// 自动隐藏
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
if (toast.parentNode) {
|
||||
toast.parentNode.removeChild(toast);
|
||||
}
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 工具函数:防抖
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// 添加键盘快捷键支持
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// Ctrl+Enter 翻译
|
||||
if (e.ctrlKey && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
translateText();
|
||||
}
|
||||
|
||||
// Ctrl+Shift+C 复制结果
|
||||
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
|
||||
e.preventDefault();
|
||||
copyOutput();
|
||||
}
|
||||
|
||||
// Ctrl+Shift+X 清空输入
|
||||
if (e.ctrlKey && e.shiftKey && e.key === 'X') {
|
||||
e.preventDefault();
|
||||
clearInput();
|
||||
}
|
||||
|
||||
// Ctrl+Shift+S 交换语言
|
||||
if (e.ctrlKey && e.shiftKey && e.key === 'S') {
|
||||
e.preventDefault();
|
||||
swapLanguages();
|
||||
}
|
||||
});
|
||||
|
||||
// 页面可见性变化时的处理
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (document.hidden) {
|
||||
// 页面隐藏时暂停翻译请求
|
||||
if (isTranslating) {
|
||||
setTranslating(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 错误处理
|
||||
window.addEventListener('error', function(e) {
|
||||
console.error('页面错误:', e.error);
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', function(e) {
|
||||
console.error('未处理的Promise拒绝:', e.reason);
|
||||
});
|
||||
441
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/styles.css
Normal file
441
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/styles.css
Normal file
@@ -0,0 +1,441 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
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: #2d5a3d;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
color: #1a4d2e;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: #4a7c59;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.translate-box {
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 30px rgba(26, 77, 46, 0.1);
|
||||
}
|
||||
|
||||
/* 语言选择器 */
|
||||
.language-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 25px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.lang-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.lang-group label {
|
||||
font-size: 0.9rem;
|
||||
color: #2d5a3d;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.lang-select {
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #74c69d;
|
||||
border-radius: 12px;
|
||||
color: #2d5a3d;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.lang-select:focus {
|
||||
outline: none;
|
||||
border-color: #52b788;
|
||||
box-shadow: 0 0 0 3px rgba(116, 198, 157, 0.2);
|
||||
}
|
||||
|
||||
.lang-select:hover {
|
||||
border-color: #52b788;
|
||||
}
|
||||
|
||||
.swap-btn {
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: white;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.swap-btn:hover {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* 文本区域 */
|
||||
.text-areas {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.input-section,
|
||||
.output-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.textarea-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.detected-lang,
|
||||
.target-lang {
|
||||
font-size: 0.9rem;
|
||||
color: #4a7c59;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.clear-btn,
|
||||
.copy-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #74c69d;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.clear-btn:hover,
|
||||
.copy-btn:hover {
|
||||
color: #52b788;
|
||||
}
|
||||
|
||||
#input-text {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
padding: 16px;
|
||||
border: 2px solid #74c69d;
|
||||
border-radius: 12px;
|
||||
font-size: 1rem;
|
||||
color: #2d5a3d;
|
||||
resize: vertical;
|
||||
transition: all 0.3s ease;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
#input-text:focus {
|
||||
outline: none;
|
||||
border-color: #52b788;
|
||||
box-shadow: 0 0 0 3px rgba(116, 198, 157, 0.2);
|
||||
}
|
||||
|
||||
#input-text::placeholder {
|
||||
color: #74c69d;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.output-text {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
padding: 16px;
|
||||
border: 2px solid #b7e4c7;
|
||||
border-radius: 12px;
|
||||
font-size: 1rem;
|
||||
color: #2d5a3d;
|
||||
overflow-y: auto;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
text-align: right;
|
||||
font-size: 0.8rem;
|
||||
color: #74c69d;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.pronounce-section {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.pronounce-item {
|
||||
font-size: 0.9rem;
|
||||
color: #4a7c59;
|
||||
font-style: italic;
|
||||
padding: 5px 10px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pronounce-item.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.translate-btn {
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px 40px;
|
||||
border-radius: 25px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-width: 120px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.translate-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(116, 198, 157, 0.3);
|
||||
}
|
||||
|
||||
.translate-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.translate-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 2px solid white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.translate-btn.loading .btn-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.translate-btn.loading .loading-spinner {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
color: #4a7c59;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 提示消息 */
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
transform: translateX(100%);
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 4px 12px rgba(82, 183, 136, 0.3);
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.toast.error {
|
||||
box-shadow: 0 4px 12px rgba(231, 76, 60, 0.3);
|
||||
}
|
||||
|
||||
/* 平板适配 (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.translate-box {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.text-areas {
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
#input-text,
|
||||
.output-text {
|
||||
height: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大768px) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.translate-box {
|
||||
padding: 20px 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.lang-group {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.swap-btn {
|
||||
align-self: center;
|
||||
margin-top: 0;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.text-areas {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
#input-text,
|
||||
.output-text {
|
||||
height: 150px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.translate-btn {
|
||||
padding: 12px 30px;
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.toast {
|
||||
right: 10px;
|
||||
left: 10px;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.header h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.translate-box {
|
||||
padding: 15px 10px;
|
||||
}
|
||||
|
||||
.lang-select {
|
||||
padding: 10px 12px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#input-text,
|
||||
.output-text {
|
||||
height: 120px;
|
||||
padding: 12px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.translate-btn {
|
||||
padding: 10px 25px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
551
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/在线翻译支持语言列表接口.json
Normal file
551
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/在线翻译支持语言列表接口.json
Normal file
@@ -0,0 +1,551 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": [
|
||||
{
|
||||
"code": "sq",
|
||||
"label": "阿尔巴尼亚语",
|
||||
"alphabet": "A"
|
||||
},
|
||||
{
|
||||
"code": "ga",
|
||||
"label": "爱尔兰语",
|
||||
"alphabet": "A"
|
||||
},
|
||||
{
|
||||
"code": "et",
|
||||
"label": "爱沙尼亚语",
|
||||
"alphabet": "A"
|
||||
},
|
||||
{
|
||||
"code": "ar",
|
||||
"label": "阿拉伯语",
|
||||
"alphabet": "A"
|
||||
},
|
||||
{
|
||||
"code": "am",
|
||||
"label": "阿姆哈拉语",
|
||||
"alphabet": "A"
|
||||
},
|
||||
{
|
||||
"code": "az",
|
||||
"label": "阿塞拜疆语",
|
||||
"alphabet": "A"
|
||||
},
|
||||
{
|
||||
"code": "be",
|
||||
"label": "白俄罗斯语",
|
||||
"alphabet": "B"
|
||||
},
|
||||
{
|
||||
"code": "bg",
|
||||
"label": "保加利亚语",
|
||||
"alphabet": "B"
|
||||
},
|
||||
{
|
||||
"code": "eu",
|
||||
"label": "巴斯克语",
|
||||
"alphabet": "B"
|
||||
},
|
||||
{
|
||||
"code": "is",
|
||||
"label": "冰岛语",
|
||||
"alphabet": "B"
|
||||
},
|
||||
{
|
||||
"code": "pl",
|
||||
"label": "波兰语",
|
||||
"alphabet": "B"
|
||||
},
|
||||
{
|
||||
"code": "bs-Latn",
|
||||
"label": "波斯尼亚语(拉丁语)",
|
||||
"alphabet": "B"
|
||||
},
|
||||
{
|
||||
"code": "fa",
|
||||
"label": "波斯语",
|
||||
"alphabet": "B"
|
||||
},
|
||||
{
|
||||
"code": "da",
|
||||
"label": "丹麦语",
|
||||
"alphabet": "D"
|
||||
},
|
||||
{
|
||||
"code": "de",
|
||||
"label": "德语",
|
||||
"alphabet": "D"
|
||||
},
|
||||
{
|
||||
"code": "ru",
|
||||
"label": "俄语",
|
||||
"alphabet": "E"
|
||||
},
|
||||
{
|
||||
"code": "fr",
|
||||
"label": "法语",
|
||||
"alphabet": "F"
|
||||
},
|
||||
{
|
||||
"code": "tl",
|
||||
"label": "菲律宾语",
|
||||
"alphabet": "F"
|
||||
},
|
||||
{
|
||||
"code": "fi",
|
||||
"label": "芬兰语",
|
||||
"alphabet": "F"
|
||||
},
|
||||
{
|
||||
"code": "fy",
|
||||
"label": "弗里斯兰语",
|
||||
"alphabet": "F"
|
||||
},
|
||||
{
|
||||
"code": "km",
|
||||
"label": "高棉语",
|
||||
"alphabet": "G"
|
||||
},
|
||||
{
|
||||
"code": "ka",
|
||||
"label": "格鲁吉亚语",
|
||||
"alphabet": "G"
|
||||
},
|
||||
{
|
||||
"code": "gu",
|
||||
"label": "古吉拉特语",
|
||||
"alphabet": "G"
|
||||
},
|
||||
{
|
||||
"code": "ko",
|
||||
"label": "韩语",
|
||||
"alphabet": "H"
|
||||
},
|
||||
{
|
||||
"code": "ht",
|
||||
"label": "海地语",
|
||||
"alphabet": "H"
|
||||
},
|
||||
{
|
||||
"code": "ha",
|
||||
"label": "豪萨语",
|
||||
"alphabet": "H"
|
||||
},
|
||||
{
|
||||
"code": "kk",
|
||||
"label": "哈萨克语",
|
||||
"alphabet": "H"
|
||||
},
|
||||
{
|
||||
"code": "nl",
|
||||
"label": "荷兰语",
|
||||
"alphabet": "H"
|
||||
},
|
||||
{
|
||||
"code": "gl",
|
||||
"label": "加利西亚语",
|
||||
"alphabet": "J"
|
||||
},
|
||||
{
|
||||
"code": "ca",
|
||||
"label": "加泰罗尼亚语",
|
||||
"alphabet": "J"
|
||||
},
|
||||
{
|
||||
"code": "cs",
|
||||
"label": "捷克语",
|
||||
"alphabet": "J"
|
||||
},
|
||||
{
|
||||
"code": "ky",
|
||||
"label": "吉尔吉斯斯坦语",
|
||||
"alphabet": "J"
|
||||
},
|
||||
{
|
||||
"code": "kn",
|
||||
"label": "卡纳达语",
|
||||
"alphabet": "K"
|
||||
},
|
||||
{
|
||||
"code": "tlh",
|
||||
"label": "克林贡语",
|
||||
"alphabet": "K"
|
||||
},
|
||||
{
|
||||
"code": "hr",
|
||||
"label": "克罗地亚语",
|
||||
"alphabet": "K"
|
||||
},
|
||||
{
|
||||
"code": "otq",
|
||||
"label": "克洛塔罗乙巳语",
|
||||
"alphabet": "K"
|
||||
},
|
||||
{
|
||||
"code": "co",
|
||||
"label": "科西嘉语",
|
||||
"alphabet": "K"
|
||||
},
|
||||
{
|
||||
"code": "ku",
|
||||
"label": "库尔德语",
|
||||
"alphabet": "K"
|
||||
},
|
||||
{
|
||||
"code": "la",
|
||||
"label": "拉丁语",
|
||||
"alphabet": "L"
|
||||
},
|
||||
{
|
||||
"code": "lo",
|
||||
"label": "老挝语",
|
||||
"alphabet": "L"
|
||||
},
|
||||
{
|
||||
"code": "lv",
|
||||
"label": "拉脱维亚语",
|
||||
"alphabet": "L"
|
||||
},
|
||||
{
|
||||
"code": "lt",
|
||||
"label": "立陶宛语",
|
||||
"alphabet": "L"
|
||||
},
|
||||
{
|
||||
"code": "ro",
|
||||
"label": "罗马尼亚语",
|
||||
"alphabet": "L"
|
||||
},
|
||||
{
|
||||
"code": "lb",
|
||||
"label": "卢森堡语",
|
||||
"alphabet": "L"
|
||||
},
|
||||
{
|
||||
"code": "mg",
|
||||
"label": "马尔加什语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "mt",
|
||||
"label": "马耳他语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "mr",
|
||||
"label": "马拉地语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "ms",
|
||||
"label": "马来语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "ml",
|
||||
"label": "马拉雅拉姆语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "mi",
|
||||
"label": "毛利语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "mk",
|
||||
"label": "马其顿语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "mn",
|
||||
"label": "蒙古语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "bn",
|
||||
"label": "孟加拉语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "my",
|
||||
"label": "缅甸语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "mww",
|
||||
"label": "苗族昂山土语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "hmn",
|
||||
"label": "苗族语",
|
||||
"alphabet": "M"
|
||||
},
|
||||
{
|
||||
"code": "xh",
|
||||
"label": "南非科萨语",
|
||||
"alphabet": "N"
|
||||
},
|
||||
{
|
||||
"code": "zu",
|
||||
"label": "南非祖鲁语",
|
||||
"alphabet": "N"
|
||||
},
|
||||
{
|
||||
"code": "ne",
|
||||
"label": "尼泊尔语",
|
||||
"alphabet": "N"
|
||||
},
|
||||
{
|
||||
"code": "no",
|
||||
"label": "挪威语",
|
||||
"alphabet": "N"
|
||||
},
|
||||
{
|
||||
"code": "pa",
|
||||
"label": "旁遮普语",
|
||||
"alphabet": "P"
|
||||
},
|
||||
{
|
||||
"code": "ps",
|
||||
"label": "普什图语",
|
||||
"alphabet": "P"
|
||||
},
|
||||
{
|
||||
"code": "pt",
|
||||
"label": "葡萄牙语",
|
||||
"alphabet": "P"
|
||||
},
|
||||
{
|
||||
"code": "ny",
|
||||
"label": "齐切瓦语",
|
||||
"alphabet": "Q"
|
||||
},
|
||||
{
|
||||
"code": "ja",
|
||||
"label": "日语",
|
||||
"alphabet": "R"
|
||||
},
|
||||
{
|
||||
"code": "sv",
|
||||
"label": "瑞典语",
|
||||
"alphabet": "R"
|
||||
},
|
||||
{
|
||||
"code": "sr-Latn",
|
||||
"label": "塞尔维亚语(拉丁语)",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "sr-Cyrl",
|
||||
"label": "塞尔维亚语(西里尔)",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "st",
|
||||
"label": "塞索托语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "sm",
|
||||
"label": "萨摩亚语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "si",
|
||||
"label": "僧伽罗语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "eo",
|
||||
"label": "世界语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "sk",
|
||||
"label": "斯洛伐克语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "sl",
|
||||
"label": "斯洛语尼亚语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "sw",
|
||||
"label": "斯瓦希里语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "gd",
|
||||
"label": "苏格兰盖尔语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "so",
|
||||
"label": "索马里语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "ceb",
|
||||
"label": "宿务语",
|
||||
"alphabet": "S"
|
||||
},
|
||||
{
|
||||
"code": "te",
|
||||
"label": "泰卢固语",
|
||||
"alphabet": "T"
|
||||
},
|
||||
{
|
||||
"code": "ta",
|
||||
"label": "泰米尔语",
|
||||
"alphabet": "T"
|
||||
},
|
||||
{
|
||||
"code": "th",
|
||||
"label": "泰语",
|
||||
"alphabet": "T"
|
||||
},
|
||||
{
|
||||
"code": "tg",
|
||||
"label": "塔吉克语",
|
||||
"alphabet": "T"
|
||||
},
|
||||
{
|
||||
"code": "tr",
|
||||
"label": "土耳其语",
|
||||
"alphabet": "T"
|
||||
},
|
||||
{
|
||||
"code": "cy",
|
||||
"label": "威尔士语",
|
||||
"alphabet": "W"
|
||||
},
|
||||
{
|
||||
"code": "zh-lzh",
|
||||
"label": "文言文",
|
||||
"alphabet": "W"
|
||||
},
|
||||
{
|
||||
"code": "ur",
|
||||
"label": "乌尔都语",
|
||||
"alphabet": "W"
|
||||
},
|
||||
{
|
||||
"code": "uk",
|
||||
"label": "乌克兰语",
|
||||
"alphabet": "W"
|
||||
},
|
||||
{
|
||||
"code": "uz",
|
||||
"label": "乌兹别克语",
|
||||
"alphabet": "W"
|
||||
},
|
||||
{
|
||||
"code": "haw",
|
||||
"label": "夏威夷语",
|
||||
"alphabet": "X"
|
||||
},
|
||||
{
|
||||
"code": "es",
|
||||
"label": "西班牙语",
|
||||
"alphabet": "X"
|
||||
},
|
||||
{
|
||||
"code": "he",
|
||||
"label": "希伯来语",
|
||||
"alphabet": "X"
|
||||
},
|
||||
{
|
||||
"code": "el",
|
||||
"label": "希腊语",
|
||||
"alphabet": "X"
|
||||
},
|
||||
{
|
||||
"code": "sd",
|
||||
"label": "信德语",
|
||||
"alphabet": "X"
|
||||
},
|
||||
{
|
||||
"code": "hu",
|
||||
"label": "匈牙利语",
|
||||
"alphabet": "X"
|
||||
},
|
||||
{
|
||||
"code": "sn",
|
||||
"label": "修纳语",
|
||||
"alphabet": "X"
|
||||
},
|
||||
{
|
||||
"code": "en",
|
||||
"label": "英语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "hy",
|
||||
"label": "亚美尼亚语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "ig",
|
||||
"label": "伊博语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "it",
|
||||
"label": "意大利语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "yi",
|
||||
"label": "意第绪语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "hi",
|
||||
"label": "印地语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "id",
|
||||
"label": "印度尼西亚语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "su",
|
||||
"label": "印尼巽他语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "jw",
|
||||
"label": "印尼爪哇语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "yua",
|
||||
"label": "尤卡坦玛雅语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "yo",
|
||||
"label": "约鲁巴语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "vi",
|
||||
"label": "越南语",
|
||||
"alphabet": "Y"
|
||||
},
|
||||
{
|
||||
"code": "zh-CHS",
|
||||
"label": "中文",
|
||||
"alphabet": "Z"
|
||||
},
|
||||
{
|
||||
"code": "zh-CHT",
|
||||
"label": "中文(繁体)",
|
||||
"alphabet": "Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/返回接口.json
Normal file
1
InfoGenie-frontend/public/60sapi/实用功能/在线翻译/返回接口.json
Normal file
@@ -0,0 +1 @@
|
||||
{"code":200,"message":"所有数据均来自官方,确保稳定与实时,用户群: 595941841,开源地址: https://github.com/vikiboss/60s","data":{"source":{"text":"こんにちは","type":"ja","type_desc":"日语","pronounce":"Konnitiha"},"target":{"text":"你好","type":"zh-CHS","type_desc":"中文","pronounce":"nĭhăo"}}}
|
||||
145
InfoGenie-frontend/public/60sapi/实用功能/天气预报/css/background.css
Normal file
145
InfoGenie-frontend/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;
|
||||
}
|
||||
}
|
||||
412
InfoGenie-frontend/public/60sapi/实用功能/天气预报/css/style.css
Normal file
412
InfoGenie-frontend/public/60sapi/实用功能/天气预报/css/style.css
Normal file
@@ -0,0 +1,412 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
color: #2d5a3d;
|
||||
font-size: 2.5rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 搜索区域 */
|
||||
.search-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#cityInput {
|
||||
flex: 1;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #a8d5ba;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
outline: none;
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
#cityInput:focus {
|
||||
border-color: #6bb77b;
|
||||
box-shadow: 0 0 10px rgba(107, 183, 123, 0.3);
|
||||
}
|
||||
|
||||
#searchBtn {
|
||||
padding: 12px 24px;
|
||||
background: linear-gradient(135deg, #6bb77b, #5a9f6a);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#searchBtn:hover {
|
||||
background: linear-gradient(135deg, #5a9f6a, #4a8759);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(107, 183, 123, 0.4);
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #e8f5e8;
|
||||
border-top: 4px solid #6bb77b;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 天气容器 */
|
||||
.weather-container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(168, 213, 186, 0.3);
|
||||
}
|
||||
|
||||
/* 位置信息 */
|
||||
.location-info {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #e8f5e8;
|
||||
}
|
||||
|
||||
.location-info h2 {
|
||||
color: #2d5a3d;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.location-info p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 当前天气 */
|
||||
.current-weather {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.weather-main {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 4rem;
|
||||
font-weight: 300;
|
||||
color: #2d5a3d;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 2rem;
|
||||
color: #6bb77b;
|
||||
}
|
||||
|
||||
.weather-desc p:first-child {
|
||||
font-size: 1.5rem;
|
||||
color: #2d5a3d;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.weather-desc p:last-child {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 更新时间 */
|
||||
.update-time {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e8f5e8;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 错误信息 */
|
||||
.error-message {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
background: rgba(255, 107, 107, 0.1);
|
||||
border-radius: 15px;
|
||||
border: 1px solid rgba(255, 107, 107, 0.2);
|
||||
color: #d63031;
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
.index-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* 电脑端适配 (1024px+) */
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.weather-container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.weather-main {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.index-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.search-box {
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (768px以下) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
#searchBtn {
|
||||
padding: 14px 24px;
|
||||
}
|
||||
|
||||
.weather-container {
|
||||
padding: 20px;
|
||||
margin: 0 -5px;
|
||||
}
|
||||
|
||||
.weather-main {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.index-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.index-item {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.index-icon {
|
||||
font-size: 1.5rem;
|
||||
width: 40px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕适配 (480px以下) */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.weather-container {
|
||||
padding: 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 预报区域样式 */
|
||||
.forecast-section {
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.forecast-section h3 {
|
||||
color: #2d5a3d;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.forecast-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.forecast-item {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 15px;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
border: 1px solid rgba(168, 213, 186, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.forecast-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.forecast-date {
|
||||
font-weight: bold;
|
||||
color: #2d5a3d;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.forecast-weather {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.weather-day {
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.weather-night {
|
||||
color: #888;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.forecast-temp {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.temp-high {
|
||||
color: #ff6b6b;
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.temp-low {
|
||||
color: #4ecdc4;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.forecast-wind {
|
||||
color: #666;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.forecast-humidity {
|
||||
color: #888;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* 预报区域响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.forecast-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.forecast-item {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.forecast-date {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.temp-high {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.forecast-section {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.forecast-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
66
InfoGenie-frontend/public/60sapi/实用功能/天气预报/index.html
Normal file
66
InfoGenie-frontend/public/60sapi/实用功能/天气预报/index.html
Normal file
@@ -0,0 +1,66 @@
|
||||
<!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>天气预报</h1>
|
||||
</header>
|
||||
|
||||
<div class="search-section">
|
||||
<div class="search-box">
|
||||
<input type="text" id="cityInput" placeholder="请输入城市名称(如:北京)" value="北京">
|
||||
<button id="searchBtn">查询天气</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="loading" id="loading" style="display: none;">
|
||||
<div class="spinner"></div>
|
||||
<p>正在获取天气信息...</p>
|
||||
</div>
|
||||
|
||||
<div class="weather-container" id="weatherContainer" style="display: none;">
|
||||
<div class="location-info">
|
||||
<h2 id="locationName"></h2>
|
||||
<p id="locationDetail"></p>
|
||||
</div>
|
||||
|
||||
<div class="current-weather">
|
||||
<div class="weather-main">
|
||||
<div class="temperature">
|
||||
<span id="temperature"></span>
|
||||
<span class="unit">°C</span>
|
||||
</div>
|
||||
<div class="weather-desc">
|
||||
<p id="weatherCondition"></p>
|
||||
<p id="feelsLike"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="forecast-section">
|
||||
<h3>未来天气预报</h3>
|
||||
<div class="forecast-grid" id="forecastGrid">
|
||||
<!-- 预报数据将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="update-time">
|
||||
<p>更新时间:<span id="updateTime"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error-message" id="errorMessage" style="display: none;">
|
||||
<p>获取天气信息失败,请稍后重试</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
211
InfoGenie-frontend/public/60sapi/实用功能/天气预报/js/script.js
Normal file
211
InfoGenie-frontend/public/60sapi/实用功能/天气预报/js/script.js
Normal file
@@ -0,0 +1,211 @@
|
||||
// 天气查询应用
|
||||
class WeatherApp {
|
||||
constructor() {
|
||||
this.apiEndpoints = [
|
||||
"https://60s.api.shumengya.top/v2/weather/forecast"
|
||||
];
|
||||
this.currentEndpointIndex = 0;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
// 页面加载时自动查询北京天气
|
||||
this.searchWeather('北京');
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
const searchBtn = document.getElementById('searchBtn');
|
||||
const cityInput = document.getElementById('cityInput');
|
||||
|
||||
searchBtn.addEventListener('click', () => {
|
||||
const city = cityInput.value.trim();
|
||||
if (city) {
|
||||
this.searchWeather(city);
|
||||
} else {
|
||||
this.showError('请输入城市名称');
|
||||
}
|
||||
});
|
||||
|
||||
cityInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
const city = cityInput.value.trim();
|
||||
if (city) {
|
||||
this.searchWeather(city);
|
||||
} else {
|
||||
this.showError('请输入城市名称');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 防止输入框为空时查询
|
||||
cityInput.addEventListener('input', () => {
|
||||
const searchBtn = document.getElementById('searchBtn');
|
||||
searchBtn.disabled = !cityInput.value.trim();
|
||||
});
|
||||
}
|
||||
|
||||
async searchWeather(city) {
|
||||
this.showLoading();
|
||||
|
||||
for (let i = 0; i < this.apiEndpoints.length; i++) {
|
||||
try {
|
||||
const endpoint = this.apiEndpoints[this.currentEndpointIndex];
|
||||
const response = await this.fetchWeatherData(endpoint, city);
|
||||
|
||||
if (response && response.code === 200) {
|
||||
this.displayWeatherData(response.data);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`API ${this.apiEndpoints[this.currentEndpointIndex]} 请求失败:`, error);
|
||||
}
|
||||
|
||||
// 切换到下一个API端点
|
||||
this.currentEndpointIndex = (this.currentEndpointIndex + 1) % this.apiEndpoints.length;
|
||||
}
|
||||
|
||||
// 所有API都失败了
|
||||
this.showError('获取天气信息失败,请检查网络连接或稍后重试');
|
||||
}
|
||||
|
||||
async fetchWeatherData(endpoint, city) {
|
||||
const url = `${endpoint}?query=${encodeURIComponent(city)}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
displayWeatherData(data) {
|
||||
const { location, forecast } = data;
|
||||
|
||||
// 显示位置信息
|
||||
document.getElementById('locationName').textContent = location.formatted;
|
||||
document.getElementById('locationDetail').textContent =
|
||||
`${location.province} ${location.city} | 邮编: ${location.zip_code}`;
|
||||
|
||||
// 使用第一天的预报数据作为当前天气(今天的天气)
|
||||
const todayWeather = forecast[0];
|
||||
|
||||
// 显示当前天气(使用今天的最高温度)
|
||||
document.getElementById('temperature').textContent = todayWeather.temperature_high;
|
||||
document.getElementById('weatherCondition').textContent =
|
||||
`${todayWeather.weather_day} 转 ${todayWeather.weather_night}`;
|
||||
|
||||
// 体感温度(使用温度范围)
|
||||
document.getElementById('feelsLike').textContent =
|
||||
`温度范围 ${todayWeather.temperature_low}°C - ${todayWeather.temperature_high}°C`;
|
||||
|
||||
// 显示更新时间(使用当前时间)
|
||||
document.getElementById('updateTime').textContent =
|
||||
`${this.formatDate(new Date())} (基于预报数据)`;
|
||||
|
||||
// 显示天气预报
|
||||
this.displayForecast(forecast);
|
||||
|
||||
this.showWeatherContainer();
|
||||
}
|
||||
|
||||
displayForecast(forecast) {
|
||||
const forecastGrid = document.getElementById('forecastGrid');
|
||||
forecastGrid.innerHTML = '';
|
||||
|
||||
forecast.forEach((day, index) => {
|
||||
const forecastItem = document.createElement('div');
|
||||
forecastItem.className = 'forecast-item';
|
||||
|
||||
forecastItem.innerHTML = `
|
||||
<div class="forecast-date">${day.date_desc}</div>
|
||||
<div class="forecast-weather">
|
||||
<div class="weather-day">${day.weather_day}</div>
|
||||
<div class="weather-night">${day.weather_night}</div>
|
||||
</div>
|
||||
<div class="forecast-temp">
|
||||
<span class="temp-high">${day.temperature_high}°</span>
|
||||
<span class="temp-low">${day.temperature_low}°</span>
|
||||
</div>
|
||||
<div class="forecast-wind">
|
||||
<div>${day.wind_direction_day} ${day.wind_strength_day}</div>
|
||||
</div>
|
||||
<div class="forecast-humidity">湿度: ${day.humidity}%</div>
|
||||
`;
|
||||
|
||||
forecastGrid.appendChild(forecastItem);
|
||||
});
|
||||
}
|
||||
|
||||
// 华氏度转摄氏度
|
||||
fahrenheitToCelsius(fahrenheit) {
|
||||
const celsius = (fahrenheit - 32) * 5 / 9;
|
||||
return Math.round(celsius * 10) / 10; // 保留一位小数
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
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}`;
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
document.getElementById('loading').style.display = 'block';
|
||||
document.getElementById('weatherContainer').style.display = 'none';
|
||||
document.getElementById('errorMessage').style.display = 'none';
|
||||
}
|
||||
|
||||
showWeatherContainer() {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('weatherContainer').style.display = 'block';
|
||||
document.getElementById('errorMessage').style.display = 'none';
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('weatherContainer').style.display = 'none';
|
||||
const errorElement = document.getElementById('errorMessage');
|
||||
errorElement.style.display = 'block';
|
||||
errorElement.querySelector('p').textContent = message;
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化应用
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new WeatherApp();
|
||||
});
|
||||
|
||||
// 添加页面可见性检测,当页面重新可见时刷新数据
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) {
|
||||
const cityInput = document.getElementById('cityInput');
|
||||
const city = cityInput.value.trim() || '北京';
|
||||
// 延迟1秒刷新,避免频繁请求
|
||||
setTimeout(() => {
|
||||
if (window.weatherApp) {
|
||||
window.weatherApp.searchWeather(city);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
// 将应用实例暴露到全局,方便调试和其他功能调用
|
||||
window.weatherApp = null;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.weatherApp = new WeatherApp();
|
||||
});
|
||||
3
InfoGenie-frontend/public/60sapi/实用功能/天气预报/接口集合.json
Normal file
3
InfoGenie-frontend/public/60sapi/实用功能/天气预报/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top/v2/weather/forecast"
|
||||
]
|
||||
101
InfoGenie-frontend/public/60sapi/实用功能/天气预报/返回接口.json
Normal file
101
InfoGenie-frontend/public/60sapi/实用功能/天气预报/返回接口.json
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"location": {
|
||||
"province": "北京",
|
||||
"city": "北京",
|
||||
"town": "北京",
|
||||
"formatted": "北京",
|
||||
"location_id": "101010100",
|
||||
"detail_url": "http://www.weather.com.cn/weather/101010100.shtml",
|
||||
"is_province": true,
|
||||
"is_city": false,
|
||||
"is_town": false,
|
||||
"area_code": "10",
|
||||
"zip_code": "100000"
|
||||
},
|
||||
"forecast": [
|
||||
{
|
||||
"date": "9/4",
|
||||
"date_desc": "今天",
|
||||
"weather_day": "多云",
|
||||
"weather_night": "阴",
|
||||
"weather_code_day": "01",
|
||||
"weather_code_night": "02",
|
||||
"temperature_high": 31,
|
||||
"temperature_low": 21,
|
||||
"wind_direction_day": "南风",
|
||||
"wind_direction_night": "南风",
|
||||
"wind_strength_day": "\u003C3级",
|
||||
"wind_strength_night": "\u003C3级",
|
||||
"rainfall": 96.1,
|
||||
"humidity": 83
|
||||
},
|
||||
{
|
||||
"date": "9/5",
|
||||
"date_desc": "星期五",
|
||||
"weather_day": "中雨",
|
||||
"weather_night": "多云",
|
||||
"weather_code_day": "08",
|
||||
"weather_code_night": "01",
|
||||
"temperature_high": 23,
|
||||
"temperature_low": 19,
|
||||
"wind_direction_day": "西南风",
|
||||
"wind_direction_night": "北风",
|
||||
"wind_strength_day": "\u003C3级",
|
||||
"wind_strength_night": "\u003C3级",
|
||||
"rainfall": 100,
|
||||
"humidity": 68
|
||||
},
|
||||
{
|
||||
"date": "9/6",
|
||||
"date_desc": "星期六",
|
||||
"weather_day": "多云",
|
||||
"weather_night": "晴",
|
||||
"weather_code_day": "01",
|
||||
"weather_code_night": "00",
|
||||
"temperature_high": 30,
|
||||
"temperature_low": 19,
|
||||
"wind_direction_day": "南风",
|
||||
"wind_direction_night": "西南风",
|
||||
"wind_strength_day": "\u003C3级",
|
||||
"wind_strength_night": "\u003C3级",
|
||||
"rainfall": 85.2,
|
||||
"humidity": 36
|
||||
},
|
||||
{
|
||||
"date": "9/7",
|
||||
"date_desc": "星期日",
|
||||
"weather_day": "多云",
|
||||
"weather_night": "晴",
|
||||
"weather_code_day": "01",
|
||||
"weather_code_night": "00",
|
||||
"temperature_high": 29,
|
||||
"temperature_low": 20,
|
||||
"wind_direction_day": "北风",
|
||||
"wind_direction_night": "北风",
|
||||
"wind_strength_day": "\u003C3级",
|
||||
"wind_strength_night": "\u003C3级",
|
||||
"rainfall": 87.3,
|
||||
"humidity": 27
|
||||
},
|
||||
{
|
||||
"date": "9/8",
|
||||
"date_desc": "星期一",
|
||||
"weather_day": "多云",
|
||||
"weather_night": "多云",
|
||||
"weather_code_day": "01",
|
||||
"weather_code_night": "01",
|
||||
"temperature_high": 28,
|
||||
"temperature_low": 20,
|
||||
"wind_direction_day": "南风",
|
||||
"wind_direction_night": "南风",
|
||||
"wind_strength_day": "\u003C3级",
|
||||
"wind_strength_night": "\u003C3级",
|
||||
"rainfall": 84.8,
|
||||
"humidity": 41
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
202
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/background.css
Normal file
202
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/background.css
Normal file
@@ -0,0 +1,202 @@
|
||||
/* 背景样式文件 - 独立管理背景相关CSS */
|
||||
|
||||
/* 主体背景 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #a8e6cf 0%, #dcedc8 50%, #f0f4c3 100%);
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 背景装饰元素 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 80%, rgba(120, 219, 226, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(168, 230, 207, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(220, 237, 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:
|
||||
linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.05) 50%, transparent 70%),
|
||||
linear-gradient(-45deg, transparent 30%, rgba(168, 230, 207, 0.05) 50%, transparent 70%);
|
||||
background-size: 200px 200px;
|
||||
animation: backgroundMove 20s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* 背景动画 */
|
||||
@keyframes backgroundMove {
|
||||
0% {
|
||||
background-position: 0 0, 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 200px 200px, -200px -200px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 容器背景 */
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(5px);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 卡片背景增强 */
|
||||
.weather-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(15px);
|
||||
border: 1px solid rgba(168, 230, 207, 0.3);
|
||||
box-shadow:
|
||||
0 10px 30px rgba(0, 0, 0, 0.1),
|
||||
0 1px 8px rgba(168, 230, 207, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* 当前天气区域背景 */
|
||||
.current-weather {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(168, 230, 207, 0.8) 0%,
|
||||
rgba(220, 237, 200, 0.8) 50%,
|
||||
rgba(240, 244, 195, 0.8) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
box-shadow:
|
||||
0 4px 15px rgba(39, 174, 96, 0.1),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
/* 详情项背景 */
|
||||
.detail-item {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(168, 230, 207, 0.1) 0%,
|
||||
rgba(255, 255, 255, 0.1) 100%);
|
||||
backdrop-filter: blur(5px);
|
||||
border: 1px solid rgba(168, 230, 207, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(39, 174, 96, 0.05);
|
||||
}
|
||||
|
||||
/* 生活指数项背景 */
|
||||
.index-item {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(168, 230, 207, 0.05) 0%,
|
||||
rgba(255, 255, 255, 0.1) 100%);
|
||||
backdrop-filter: blur(5px);
|
||||
border: 1px solid rgba(168, 230, 207, 0.15);
|
||||
box-shadow: 0 2px 10px rgba(39, 174, 96, 0.05);
|
||||
}
|
||||
|
||||
.index-item:hover {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(168, 230, 207, 0.1) 0%,
|
||||
rgba(255, 255, 255, 0.15) 100%);
|
||||
box-shadow: 0 5px 20px rgba(39, 174, 96, 0.1);
|
||||
}
|
||||
|
||||
/* 输入框背景 */
|
||||
#cityInput {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 2px solid rgba(168, 230, 207, 0.6);
|
||||
box-shadow: 0 2px 10px rgba(39, 174, 96, 0.1);
|
||||
}
|
||||
|
||||
#cityInput:focus {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
box-shadow:
|
||||
0 0 15px rgba(39, 174, 96, 0.2),
|
||||
0 2px 10px rgba(39, 174, 96, 0.1);
|
||||
}
|
||||
|
||||
/* 按钮背景 */
|
||||
#searchBtn {
|
||||
background: linear-gradient(135deg,
|
||||
#27ae60 0%,
|
||||
#2ecc71 50%,
|
||||
#58d68d 100%);
|
||||
box-shadow:
|
||||
0 4px 15px rgba(39, 174, 96, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
#searchBtn:hover {
|
||||
background: linear-gradient(135deg,
|
||||
#229954 0%,
|
||||
#27ae60 50%,
|
||||
#52c370 100%);
|
||||
box-shadow:
|
||||
0 6px 20px rgba(39, 174, 96, 0.4),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 错误消息背景 */
|
||||
.error-message {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(231, 76, 60, 0.1) 0%,
|
||||
rgba(255, 255, 255, 0.1) 100%);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(231, 76, 60, 0.2);
|
||||
box-shadow: 0 4px 15px rgba(231, 76, 60, 0.1);
|
||||
}
|
||||
|
||||
/* 加载状态背景 */
|
||||
.loading {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 15px;
|
||||
border: 1px solid rgba(168, 230, 207, 0.3);
|
||||
box-shadow: 0 4px 15px rgba(39, 174, 96, 0.1);
|
||||
}
|
||||
|
||||
/* 移动端背景优化 */
|
||||
@media (max-width: 767px) {
|
||||
body::after {
|
||||
background-size: 100px 100px;
|
||||
animation-duration: 15s;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(3px);
|
||||
}
|
||||
|
||||
.weather-card {
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 高性能设备背景增强 */
|
||||
@media (min-width: 1024px) {
|
||||
body::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 80%, rgba(120, 219, 226, 0.15) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(168, 230, 207, 0.15) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(220, 237, 200, 0.15) 0%, transparent 50%),
|
||||
radial-gradient(circle at 60% 70%, rgba(240, 244, 195, 0.1) 0%, transparent 50%);
|
||||
}
|
||||
|
||||
.weather-card {
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.current-weather {
|
||||
backdrop-filter: blur(15px);
|
||||
}
|
||||
}
|
||||
143
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/index.html
Normal file
143
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/index.html
Normal file
@@ -0,0 +1,143 @@
|
||||
<!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="styles.css">
|
||||
<link rel="stylesheet" href="background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>实时天气</h1>
|
||||
<div class="search-box">
|
||||
<input type="text" id="cityInput" placeholder="请输入城市名称..." value="北京">
|
||||
<button id="searchBtn">查询</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="loading" id="loading">正在加载天气数据...</div>
|
||||
|
||||
<div class="weather-card" id="weatherCard" style="display: none;">
|
||||
<div class="location-info">
|
||||
<h2 id="locationName">北京</h2>
|
||||
<p id="updateTime">更新时间: --</p>
|
||||
</div>
|
||||
|
||||
<div class="current-weather">
|
||||
<div class="temperature-section">
|
||||
<span class="temperature" id="temperature">--°C</span>
|
||||
<span class="weather-desc" id="weatherDesc">--</span>
|
||||
</div>
|
||||
<div class="weather-icon" id="weatherIcon">🌤️</div>
|
||||
</div>
|
||||
|
||||
<div class="weather-details">
|
||||
<div class="detail-item">
|
||||
<span class="label">体感温度</span>
|
||||
<span class="value" id="feelsLike">--°C</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">湿度</span>
|
||||
<span class="value" id="humidity">--%</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">风向</span>
|
||||
<span class="value" id="windDirection">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">风力</span>
|
||||
<span class="value" id="windStrength">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">气压</span>
|
||||
<span class="value" id="pressure">-- hPa</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">能见度</span>
|
||||
<span class="value" id="visibility">--</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">空气质量</span>
|
||||
<span class="value" id="aqi">AQI --</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">PM2.5</span>
|
||||
<span class="value" id="pm25">-- μg/m³</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="life-index">
|
||||
<h3>生活指数</h3>
|
||||
<div class="index-grid">
|
||||
<div class="index-item">
|
||||
<div class="index-icon">🌡️</div>
|
||||
<div class="index-content">
|
||||
<div class="index-title">舒适度</div>
|
||||
<div class="index-level" id="comfortLevel">--</div>
|
||||
<div class="index-desc" id="comfortDesc">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="index-item">
|
||||
<div class="index-icon">👕</div>
|
||||
<div class="index-content">
|
||||
<div class="index-title">穿衣指数</div>
|
||||
<div class="index-level" id="clothingLevel">--</div>
|
||||
<div class="index-desc" id="clothingDesc">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="index-item">
|
||||
<div class="index-icon">☂️</div>
|
||||
<div class="index-content">
|
||||
<div class="index-title">雨伞指数</div>
|
||||
<div class="index-level" id="umbrellaLevel">--</div>
|
||||
<div class="index-desc" id="umbrellaDesc">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="index-item">
|
||||
<div class="index-icon">☀️</div>
|
||||
<div class="index-content">
|
||||
<div class="index-title">紫外线</div>
|
||||
<div class="index-level" id="uvLevel">--</div>
|
||||
<div class="index-desc" id="uvDesc">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="index-item">
|
||||
<div class="index-icon">🚗</div>
|
||||
<div class="index-content">
|
||||
<div class="index-title">洗车指数</div>
|
||||
<div class="index-level" id="carWashLevel">--</div>
|
||||
<div class="index-desc" id="carWashDesc">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="index-item">
|
||||
<div class="index-icon">🎒</div>
|
||||
<div class="index-content">
|
||||
<div class="index-title">旅游指数</div>
|
||||
<div class="index-level" id="travelLevel">--</div>
|
||||
<div class="index-desc" id="travelDesc">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="index-item">
|
||||
<div class="index-icon">🏃</div>
|
||||
<div class="index-content">
|
||||
<div class="index-title">运动指数</div>
|
||||
<div class="index-level" id="sportLevel">--</div>
|
||||
<div class="index-desc" id="sportDesc">--</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error-message" id="errorMessage" style="display: none;">
|
||||
<p>获取天气数据失败,请检查网络连接或稍后重试</p>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
354
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/script.js
Normal file
354
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/script.js
Normal file
@@ -0,0 +1,354 @@
|
||||
// 天气应用主要功能
|
||||
class WeatherApp {
|
||||
constructor() {
|
||||
this.apiUrl = 'https://60s.api.shumengya.top/v2/weather';
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.loadWeather('北京'); // 默认加载北京天气
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
const searchBtn = document.getElementById('searchBtn');
|
||||
const cityInput = document.getElementById('cityInput');
|
||||
|
||||
// 搜索按钮点击事件
|
||||
searchBtn.addEventListener('click', () => {
|
||||
const city = cityInput.value.trim();
|
||||
if (city) {
|
||||
this.loadWeather(city);
|
||||
}
|
||||
});
|
||||
|
||||
// 输入框回车事件
|
||||
cityInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
const city = cityInput.value.trim();
|
||||
if (city) {
|
||||
this.loadWeather(city);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async loadWeather(city) {
|
||||
this.showLoading();
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.apiUrl}?query=${encodeURIComponent(city)}`);
|
||||
|
||||
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.displayWeather(data.data);
|
||||
this.hideLoading();
|
||||
} else {
|
||||
throw new Error(data.message || `API返回错误: code=${data.code}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取天气数据失败:', error);
|
||||
console.error('错误详情:', {
|
||||
message: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
this.showError(error.message);
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
document.getElementById('loading').style.display = 'block';
|
||||
document.getElementById('weatherCard').style.display = 'none';
|
||||
document.getElementById('errorMessage').style.display = 'none';
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
}
|
||||
|
||||
showError(message = '获取天气数据失败,请检查网络连接或稍后重试') {
|
||||
const errorElement = document.getElementById('errorMessage');
|
||||
const errorText = errorElement.querySelector('p');
|
||||
|
||||
if (errorText) {
|
||||
errorText.textContent = message;
|
||||
}
|
||||
|
||||
errorElement.style.display = 'block';
|
||||
document.getElementById('weatherCard').style.display = 'none';
|
||||
}
|
||||
|
||||
displayWeather(data) {
|
||||
console.log('API返回数据:', data); // 调试日志
|
||||
|
||||
// 根据实际API结构解构数据
|
||||
const location = data.location || {};
|
||||
const realtime = data.realtime || {};
|
||||
const air_quality = realtime.air_quality || {};
|
||||
const life_indices = realtime.life_indices || [];
|
||||
|
||||
// 显示位置信息
|
||||
const locationName = location.formatted || location.city || location.name || '未知位置';
|
||||
document.getElementById('locationName').textContent = locationName;
|
||||
|
||||
const updateTime = realtime.updated || '未知时间';
|
||||
document.getElementById('updateTime').textContent = `更新时间: ${updateTime}`;
|
||||
|
||||
// 显示当前天气
|
||||
const temperature = realtime.temperature !== undefined ? realtime.temperature : '--';
|
||||
document.getElementById('temperature').textContent = `${temperature}°C`;
|
||||
|
||||
const condition = realtime.weather || realtime.weather_desc || '未知';
|
||||
document.getElementById('weatherDesc').textContent = condition;
|
||||
document.getElementById('weatherIcon').textContent = this.getWeatherIcon(condition);
|
||||
|
||||
// 显示天气详情
|
||||
const feelsLike = realtime.temperature_feels_like !== undefined ? realtime.temperature_feels_like : temperature;
|
||||
document.getElementById('feelsLike').textContent = `${feelsLike}°C`;
|
||||
|
||||
const humidity = realtime.humidity !== undefined ? realtime.humidity : '--';
|
||||
document.getElementById('humidity').textContent = `${humidity}%`;
|
||||
|
||||
const windDirection = realtime.wind_direction || '--';
|
||||
document.getElementById('windDirection').textContent = windDirection;
|
||||
|
||||
const windPower = realtime.wind_power || realtime.wind_strength || '--';
|
||||
document.getElementById('windStrength').textContent = windPower;
|
||||
|
||||
const pressure = realtime.pressure !== undefined ? realtime.pressure : '--';
|
||||
document.getElementById('pressure').textContent = `${pressure} hPa`;
|
||||
|
||||
document.getElementById('visibility').textContent = '--'; // API中没有能见度数据
|
||||
|
||||
const aqi = air_quality.aqi !== undefined ? air_quality.aqi : '--';
|
||||
document.getElementById('aqi').textContent = `AQI ${aqi}`;
|
||||
|
||||
const pm25 = air_quality.pm25 !== undefined ? air_quality.pm25 : '--';
|
||||
document.getElementById('pm25').textContent = `${pm25} μg/m³`;
|
||||
|
||||
// 显示生活指数
|
||||
if (life_indices && life_indices.length > 0) {
|
||||
this.displayLifeIndex(life_indices);
|
||||
} else {
|
||||
// 如果没有生活指数数据,重置显示
|
||||
this.resetLifeIndex();
|
||||
}
|
||||
|
||||
// 显示天气卡片
|
||||
document.getElementById('weatherCard').style.display = 'block';
|
||||
}
|
||||
|
||||
displayLifeIndex(lifeIndices) {
|
||||
const indexMap = {
|
||||
comfort: { level: 'comfortLevel', desc: 'comfortDesc' },
|
||||
clothes: { level: 'clothingLevel', desc: 'clothingDesc' },
|
||||
umbrella: { level: 'umbrellaLevel', desc: 'umbrellaDesc' },
|
||||
ultraviolet: { level: 'uvLevel', desc: 'uvDesc' },
|
||||
carwash: { level: 'carWashLevel', desc: 'carWashDesc' },
|
||||
tourism: { level: 'travelLevel', desc: 'travelDesc' },
|
||||
sports: { level: 'sportLevel', desc: 'sportDesc' }
|
||||
};
|
||||
|
||||
// 重置所有指数显示
|
||||
this.resetLifeIndex();
|
||||
|
||||
// 根据新的API数据结构更新生活指数
|
||||
if (Array.isArray(lifeIndices)) {
|
||||
lifeIndices.forEach(index => {
|
||||
if (index && index.key && indexMap[index.key]) {
|
||||
const { level, desc } = indexMap[index.key];
|
||||
const levelElement = document.getElementById(level);
|
||||
const descElement = document.getElementById(desc);
|
||||
|
||||
if (levelElement) levelElement.textContent = index.level || '--';
|
||||
if (descElement) descElement.textContent = index.description || '--';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resetLifeIndex() {
|
||||
const indexMap = {
|
||||
comfort: { level: 'comfortLevel', desc: 'comfortDesc' },
|
||||
clothes: { level: 'clothingLevel', desc: 'clothingDesc' },
|
||||
umbrella: { level: 'umbrellaLevel', desc: 'umbrellaDesc' },
|
||||
ultraviolet: { level: 'uvLevel', desc: 'uvDesc' },
|
||||
carwash: { level: 'carWashLevel', desc: 'carWashDesc' },
|
||||
tourism: { level: 'travelLevel', desc: 'travelDesc' },
|
||||
sports: { level: 'sportLevel', desc: 'sportDesc' }
|
||||
};
|
||||
|
||||
Object.values(indexMap).forEach(({ level, desc }) => {
|
||||
const levelElement = document.getElementById(level);
|
||||
const descElement = document.getElementById(desc);
|
||||
|
||||
if (levelElement) levelElement.textContent = '--';
|
||||
if (descElement) descElement.textContent = '--';
|
||||
});
|
||||
}
|
||||
|
||||
getWeatherIcon(weather) {
|
||||
const iconMap = {
|
||||
'晴': '☀️',
|
||||
'多云': '⛅',
|
||||
'阴': '☁️',
|
||||
'小雨': '🌦️',
|
||||
'中雨': '🌧️',
|
||||
'大雨': '⛈️',
|
||||
'雷阵雨': '⛈️',
|
||||
'雪': '❄️',
|
||||
'小雪': '🌨️',
|
||||
'中雪': '❄️',
|
||||
'大雪': '❄️',
|
||||
'雾': '🌫️',
|
||||
'霾': '😷',
|
||||
'沙尘暴': '🌪️'
|
||||
};
|
||||
|
||||
// 查找匹配的天气图标
|
||||
for (const [key, icon] of Object.entries(iconMap)) {
|
||||
if (weather.includes(key)) {
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认图标
|
||||
return '🌤️';
|
||||
}
|
||||
|
||||
// 获取空气质量等级颜色
|
||||
getAQIColor(aqi) {
|
||||
if (aqi <= 50) return '#00e400';
|
||||
if (aqi <= 100) return '#ffff00';
|
||||
if (aqi <= 150) return '#ff7e00';
|
||||
if (aqi <= 200) return '#ff0000';
|
||||
if (aqi <= 300) return '#8f3f97';
|
||||
return '#7e0023';
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
formatTime(timeString) {
|
||||
try {
|
||||
const date = new Date(timeString);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
} catch (error) {
|
||||
return timeString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化应用
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new WeatherApp();
|
||||
});
|
||||
|
||||
// 添加一些实用的工具函数
|
||||
const utils = {
|
||||
// 防抖函数
|
||||
debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
},
|
||||
|
||||
// 节流函数
|
||||
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);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// 检查网络状态
|
||||
checkNetworkStatus() {
|
||||
return navigator.onLine;
|
||||
},
|
||||
|
||||
// 显示提示消息
|
||||
showToast(message, type = 'info') {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.textContent = message;
|
||||
toast.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 12px 20px;
|
||||
background: ${type === 'error' ? '#e74c3c' : '#27ae60'};
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
z-index: 1000;
|
||||
animation: slideIn 0.3s ease;
|
||||
`;
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'slideOut 0.3s ease';
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(toast);
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
|
||||
// 添加CSS动画
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOut {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// 网络状态监听
|
||||
window.addEventListener('online', () => {
|
||||
utils.showToast('网络连接已恢复', 'success');
|
||||
});
|
||||
|
||||
window.addEventListener('offline', () => {
|
||||
utils.showToast('网络连接已断开', 'error');
|
||||
});
|
||||
442
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/styles.css
Normal file
442
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/styles.css
Normal file
@@ -0,0 +1,442 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
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;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
color: #27ae60;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 300;
|
||||
text-shadow: 0 2px 4px rgba(39, 174, 96, 0.1);
|
||||
}
|
||||
|
||||
.search-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#cityInput {
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #a8e6cf;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
outline: none;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
transition: all 0.3s ease;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
#cityInput:focus {
|
||||
border-color: #27ae60;
|
||||
box-shadow: 0 0 10px rgba(39, 174, 96, 0.2);
|
||||
}
|
||||
|
||||
#searchBtn {
|
||||
padding: 12px 24px;
|
||||
background: linear-gradient(135deg, #27ae60, #2ecc71);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(39, 174, 96, 0.3);
|
||||
}
|
||||
|
||||
#searchBtn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(39, 174, 96, 0.4);
|
||||
}
|
||||
|
||||
#searchBtn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: #27ae60;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.weather-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(168, 230, 207, 0.3);
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
/* 位置信息 */
|
||||
.location-info {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #a8e6cf;
|
||||
}
|
||||
|
||||
.location-info h2 {
|
||||
font-size: 2rem;
|
||||
color: #27ae60;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.location-info p {
|
||||
color: #7f8c8d;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 当前天气 */
|
||||
.current-weather {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #a8e6cf, #dcedc8);
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.temperature-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 300;
|
||||
color: #27ae60;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.weather-desc {
|
||||
font-size: 1.2rem;
|
||||
color: #2c3e50;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.weather-icon {
|
||||
font-size: 4rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 天气详情 */
|
||||
.weather-details {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
background: rgba(168, 230, 207, 0.1);
|
||||
border-radius: 10px;
|
||||
border-left: 4px solid #27ae60;
|
||||
}
|
||||
|
||||
.detail-item .label {
|
||||
color: #7f8c8d;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-item .value {
|
||||
color: #2c3e50;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 生活指数 */
|
||||
.life-index h3 {
|
||||
color: #27ae60;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.index-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.index-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 20px;
|
||||
background: rgba(168, 230, 207, 0.05);
|
||||
border-radius: 15px;
|
||||
border: 1px solid rgba(168, 230, 207, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.index-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(39, 174, 96, 0.1);
|
||||
}
|
||||
|
||||
.index-icon {
|
||||
font-size: 2rem;
|
||||
margin-right: 15px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.index-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.index-title {
|
||||
font-weight: 500;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.index-level {
|
||||
color: #27ae60;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.index-desc {
|
||||
color: #7f8c8d;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #e74c3c;
|
||||
background: rgba(231, 76, 60, 0.1);
|
||||
border-radius: 15px;
|
||||
border: 1px solid rgba(231, 76, 60, 0.2);
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
max-width: 500px;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
.weather-details {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.index-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.current-weather {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 电脑端适配 (1024px+) */
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 3.2rem;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
max-width: 600px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
.weather-card {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.weather-details {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
.index-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.current-weather {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 4.5rem;
|
||||
}
|
||||
|
||||
.index-item {
|
||||
padding: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (优先优化) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
#cityInput {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#searchBtn {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
padding: 14px 24px;
|
||||
}
|
||||
|
||||
.weather-card {
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.current-weather {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.weather-icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.weather-details {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.index-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.index-item {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.index-icon {
|
||||
font-size: 1.5rem;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.life-index h3 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.location-info h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕适配 */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.weather-card {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.current-weather {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.index-item {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.index-desc {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
68
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/返回接口.json
Normal file
68
InfoGenie-frontend/public/60sapi/实用功能/实时天气[目前有问题]/返回接口.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"location": {
|
||||
"province": "北京",
|
||||
"city": "北京",
|
||||
"town": "北京",
|
||||
"formatted": "北京",
|
||||
"location_id": "101010100",
|
||||
"detail_url": "http://www.weather.com.cn/weather/101010100.shtml",
|
||||
"is_province": true,
|
||||
"is_city": false,
|
||||
"is_town": false,
|
||||
"area_code": "10",
|
||||
"zip_code": "100000"
|
||||
},
|
||||
"realtime": {
|
||||
"weather": "晴转雷阵雨",
|
||||
"weather_desc": "未知",
|
||||
"weather_code": "d0",
|
||||
"temperature": 999,
|
||||
"temperature_feels_like": 75.6,
|
||||
"humidity": 63,
|
||||
"wind_direction": "南风",
|
||||
"wind_strength": "3-4级转\u003C3级",
|
||||
"wind_speed": "1km/h",
|
||||
"pressure": 1006,
|
||||
"visibility": "21km",
|
||||
"aqi": 41,
|
||||
"pm25": 41,
|
||||
"rainfall": 0,
|
||||
"rainfall_24h": 0,
|
||||
"updated": "2025-09-08 08:00:00",
|
||||
"updated_at": "20:30",
|
||||
"life_index": {
|
||||
"comfort": {
|
||||
"level": "舒适",
|
||||
"desc": "白天温度宜人,风力不大。"
|
||||
},
|
||||
"clothing": {
|
||||
"level": "舒适",
|
||||
"desc": "建议穿长袖衬衫单裤等服装。"
|
||||
},
|
||||
"umbrella": {
|
||||
"level": "带伞",
|
||||
"desc": "有降水,短时间出行不必带伞。"
|
||||
},
|
||||
"uv": {
|
||||
"level": "最弱",
|
||||
"desc": "辐射弱,涂擦SPF8-12防晒护肤品。"
|
||||
},
|
||||
"car_wash": {
|
||||
"level": "不宜",
|
||||
"desc": "有雨,雨水和泥水会弄脏爱车。"
|
||||
},
|
||||
"travel": {
|
||||
"level": "一般",
|
||||
"desc": "可能有雷暴,外出请尽量避开降雨时段。"
|
||||
},
|
||||
"sport": {
|
||||
"level": "较不宜",
|
||||
"desc": "有降水,推荐您在室内进行休闲运动。"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
878
InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/css/style.css
Normal file
878
InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/css/style.css
Normal file
@@ -0,0 +1,878 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
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: #2c3e50;
|
||||
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: 40px 20px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.8rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 15px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.9;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* 输入容器 */
|
||||
.input-container {
|
||||
background: #ffffff;
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e8ecf4;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
display: block;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.password-input-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
width: 100%;
|
||||
padding: 18px 60px 18px 20px;
|
||||
border: 2px solid #e8ecf4;
|
||||
border-radius: 12px;
|
||||
font-size: 1.1rem;
|
||||
font-family: 'Courier New', monospace;
|
||||
background: #f8fafc;
|
||||
transition: all 0.3s ease;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.password-input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.password-input::placeholder {
|
||||
color: #94a3b8;
|
||||
letter-spacing: normal;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
.toggle-visibility {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
color: #64748b;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.toggle-visibility:hover {
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.input-hint {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #64748b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.hint-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* 检测按钮 */
|
||||
.check-btn {
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 18px 32px;
|
||||
border-radius: 12px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.check-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 25px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.check-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.check-btn:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
/* 结果容器 */
|
||||
.result-container {
|
||||
background: #ffffff;
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e8ecf4;
|
||||
animation: slideIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 强度概览 */
|
||||
.strength-overview {
|
||||
margin-bottom: 40px;
|
||||
padding: 30px;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.strength-score {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.score-circle {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(from 0deg, #e2e8f0 0deg, #e2e8f0 360deg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.score-circle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.score-label {
|
||||
font-size: 0.9rem;
|
||||
color: #64748b;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.strength-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.strength-level {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.strength-description {
|
||||
font-size: 1.1rem;
|
||||
color: #64748b;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.strength-bar {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.bar-background {
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
background: #e2e8f0;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bar-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #ef4444, #f97316, #eab308, #22c55e);
|
||||
border-radius: 6px;
|
||||
width: 0%;
|
||||
transition: width 0.8s ease;
|
||||
}
|
||||
|
||||
.bar-labels {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.85rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/* 详细信息网格 */
|
||||
.details-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 25px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
background: #f8fafc;
|
||||
border-radius: 16px;
|
||||
padding: 25px;
|
||||
border: 1px solid #e2e8f0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.detail-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card-header h3 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 500;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
/* 字符类型分析 */
|
||||
.character-types {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.char-type {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.char-type.has-type {
|
||||
background: #dcfce7;
|
||||
border-color: #bbf7d0;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.char-type.has-type .type-icon {
|
||||
color: #22c55e;
|
||||
}
|
||||
|
||||
.type-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.character-issues {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.issue-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
background: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 8px;
|
||||
color: #dc2626;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.issue-item.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.issue-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* 建议和提示区域 */
|
||||
.recommendations-section {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.recommendations-card,
|
||||
.security-tips-card {
|
||||
background: #f8fafc;
|
||||
border-radius: 16px;
|
||||
padding: 25px;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.recommendations-list {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.recommendations-list li {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
color: #2c3e50;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.recommendations-list li::before {
|
||||
content: '💡';
|
||||
font-size: 1rem;
|
||||
margin-top: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tips-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tip-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
color: #2c3e50;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
font-size: 1rem;
|
||||
margin-top: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 错误容器 */
|
||||
.error-container {
|
||||
background: #ffffff;
|
||||
border-radius: 20px;
|
||||
padding: 50px 40px;
|
||||
text-align: center;
|
||||
box-shadow: 0 10px 40px rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.error-container h3 {
|
||||
color: #dc2626;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.error-container p {
|
||||
color: #64748b;
|
||||
margin-bottom: 25px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
background: #dc2626;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 14px 28px;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background: #b91c1c;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
color: #64748b;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.footer p {
|
||||
margin-bottom: 8px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.footer-note {
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 提示框 */
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #22c55e;
|
||||
color: white;
|
||||
padding: 16px 24px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 20px rgba(34, 197, 94, 0.3);
|
||||
z-index: 1000;
|
||||
animation: toastSlide 0.3s ease-out;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@keyframes toastSlide {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 强度等级颜色 */
|
||||
.strength-weak {
|
||||
color: #dc2626 !important;
|
||||
}
|
||||
|
||||
.strength-medium {
|
||||
color: #f59e0b !important;
|
||||
}
|
||||
|
||||
.strength-strong {
|
||||
color: #059669 !important;
|
||||
}
|
||||
|
||||
.strength-very-strong {
|
||||
color: #047857 !important;
|
||||
}
|
||||
|
||||
/* 分数圆圈颜色 */
|
||||
.score-weak {
|
||||
background: conic-gradient(from 0deg, #dc2626 0deg, #dc2626 var(--score-deg), #e2e8f0 var(--score-deg), #e2e8f0 360deg) !important;
|
||||
}
|
||||
|
||||
.score-medium {
|
||||
background: conic-gradient(from 0deg, #f59e0b 0deg, #f59e0b var(--score-deg), #e2e8f0 var(--score-deg), #e2e8f0 360deg) !important;
|
||||
}
|
||||
|
||||
.score-strong {
|
||||
background: conic-gradient(from 0deg, #059669 0deg, #059669 var(--score-deg), #e2e8f0 var(--score-deg), #e2e8f0 360deg) !important;
|
||||
}
|
||||
|
||||
.score-very-strong {
|
||||
background: conic-gradient(from 0deg, #047857 0deg, #047857 var(--score-deg), #e2e8f0 var(--score-deg), #e2e8f0 360deg) !important;
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container {
|
||||
max-width: 900px;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
|
||||
.input-container,
|
||||
.result-container {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.details-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.recommendations-section {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.strength-score {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大767px) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 25px 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.input-container,
|
||||
.result-container {
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
padding: 16px 50px 16px 16px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.check-btn {
|
||||
padding: 16px 28px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.strength-overview {
|
||||
padding: 20px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.strength-score {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.score-circle {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.score-circle::before {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.strength-level {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.details-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.character-types {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.recommendations-section {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.recommendations-card,
|
||||
.security-tips-card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.toast {
|
||||
right: 15px;
|
||||
left: 15px;
|
||||
top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 20px 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.input-container,
|
||||
.result-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
padding: 14px 45px 14px 14px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.check-btn {
|
||||
padding: 14px 24px;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.card-header h3 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 触摸设备优化 */
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
.check-btn,
|
||||
.retry-btn,
|
||||
.toggle-visibility {
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.toggle-visibility {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 高对比度模式支持 */
|
||||
@media (prefers-contrast: high) {
|
||||
.input-container,
|
||||
.result-container,
|
||||
.detail-card {
|
||||
border: 2px solid #2c3e50;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
border: 2px solid #2c3e50;
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画模式支持 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 深色模式支持 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.input-container,
|
||||
.result-container,
|
||||
.detail-card,
|
||||
.recommendations-card,
|
||||
.security-tips-card {
|
||||
background: #1e293b;
|
||||
border-color: #334155;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
background: #334155;
|
||||
border-color: #475569;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.password-input:focus {
|
||||
background: #1e293b;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.strength-overview {
|
||||
background: #1e293b;
|
||||
border-color: #334155;
|
||||
}
|
||||
|
||||
.char-type,
|
||||
.recommendations-list li,
|
||||
.tip-item {
|
||||
background: #334155;
|
||||
border-color: #475569;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 打印样式 */
|
||||
@media print {
|
||||
.header {
|
||||
background: none !important;
|
||||
color: black !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.check-btn,
|
||||
.retry-btn,
|
||||
.toggle-visibility,
|
||||
.toast {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.input-container,
|
||||
.result-container {
|
||||
box-shadow: none !important;
|
||||
border: 1px solid #ccc !important;
|
||||
}
|
||||
}
|
||||
218
InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/index.html
Normal file
218
InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/index.html
Normal file
@@ -0,0 +1,218 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="在线密码强度检测工具,实时分析密码安全性,提供专业的密码安全建议">
|
||||
<meta name="keywords" content="密码强度检测,密码安全,密码分析,在线工具">
|
||||
<title>🔒 密码强度检测器</title>
|
||||
<link rel="preconnect" href="https://60s.api.shumengya.top">
|
||||
<link rel="dns-prefetch" href="https://60s.api.shumengya.top">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>🔒 密码强度检测器</h1>
|
||||
<p class="subtitle">实时分析密码安全性,保护您的数字生活</p>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<!-- 密码输入区域 -->
|
||||
<div class="input-container">
|
||||
<div class="input-group">
|
||||
<label for="passwordInput" class="input-label">请输入要检测的密码</label>
|
||||
<div class="password-input-wrapper">
|
||||
<input
|
||||
type="password"
|
||||
id="passwordInput"
|
||||
class="password-input"
|
||||
placeholder="输入您的密码进行安全性检测..."
|
||||
autocomplete="new-password"
|
||||
spellcheck="false"
|
||||
>
|
||||
<button type="button" class="toggle-visibility" id="toggleVisibility" title="显示/隐藏密码">
|
||||
<svg class="eye-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
<svg class="eye-off-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: none;">
|
||||
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
|
||||
<line x1="1" y1="1" x2="23" y2="23"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="input-hint">
|
||||
<span class="hint-icon">💡</span>
|
||||
<span class="hint-text">输入密码后将实时显示安全性分析结果</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="check-btn" id="checkBtn">
|
||||
<span class="btn-icon">🔍</span>
|
||||
<span class="btn-text">检测密码强度</span>
|
||||
<span class="btn-loading" style="display: none;">检测中...</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 结果显示区域 -->
|
||||
<div class="result-container" id="resultContainer" style="display: none;">
|
||||
<!-- 密码强度概览 -->
|
||||
<div class="strength-overview">
|
||||
<div class="strength-score">
|
||||
<div class="score-circle" id="scoreCircle">
|
||||
<div class="score-value" id="scoreValue">0</div>
|
||||
<div class="score-label">分</div>
|
||||
</div>
|
||||
<div class="strength-info">
|
||||
<div class="strength-level" id="strengthLevel">未知</div>
|
||||
<div class="strength-description" id="strengthDescription">请输入密码进行检测</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="strength-bar">
|
||||
<div class="bar-background">
|
||||
<div class="bar-fill" id="strengthBar"></div>
|
||||
</div>
|
||||
<div class="bar-labels">
|
||||
<span>弱</span>
|
||||
<span>中等</span>
|
||||
<span>强</span>
|
||||
<span>非常强</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详细信息 -->
|
||||
<div class="details-grid">
|
||||
<div class="detail-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon">📏</span>
|
||||
<h3>基本信息</h3>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="info-row">
|
||||
<span class="info-label">密码长度:</span>
|
||||
<span class="info-value" id="passwordLength">-</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">熵值:</span>
|
||||
<span class="info-value" id="entropyValue">-</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">破解时间:</span>
|
||||
<span class="info-value" id="crackTime">-</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">字符种类:</span>
|
||||
<span class="info-value" id="characterVariety">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon">🔤</span>
|
||||
<h3>字符分析</h3>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="character-types" id="characterTypes">
|
||||
<div class="char-type" id="hasLowercase">
|
||||
<span class="type-icon">❌</span>
|
||||
<span class="type-text">小写字母</span>
|
||||
</div>
|
||||
<div class="char-type" id="hasUppercase">
|
||||
<span class="type-icon">❌</span>
|
||||
<span class="type-text">大写字母</span>
|
||||
</div>
|
||||
<div class="char-type" id="hasNumbers">
|
||||
<span class="type-icon">❌</span>
|
||||
<span class="type-text">数字</span>
|
||||
</div>
|
||||
<div class="char-type" id="hasSymbols">
|
||||
<span class="type-icon">❌</span>
|
||||
<span class="type-text">特殊符号</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="character-issues" id="characterIssues">
|
||||
<div class="issue-item" id="hasRepeated">
|
||||
<span class="issue-icon">⚠️</span>
|
||||
<span class="issue-text">包含重复字符</span>
|
||||
</div>
|
||||
<div class="issue-item" id="hasSequential">
|
||||
<span class="issue-icon">⚠️</span>
|
||||
<span class="issue-text">包含连续字符</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 建议和提示 -->
|
||||
<div class="recommendations-section">
|
||||
<div class="recommendations-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon">💡</span>
|
||||
<h3>改进建议</h3>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<ul class="recommendations-list" id="recommendationsList">
|
||||
<li>请输入密码进行分析</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="security-tips-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon">🛡️</span>
|
||||
<h3>安全提示</h3>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="tips-container" id="securityTips">
|
||||
<div class="tip-item">
|
||||
<span class="tip-icon">🔐</span>
|
||||
<span class="tip-text">使用密码管理器生成和存储复杂密码</span>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<span class="tip-icon">🔄</span>
|
||||
<span class="tip-text">为不同账户使用不同的密码</span>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<span class="tip-icon">⏰</span>
|
||||
<span class="tip-text">定期更换重要账户的密码</span>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<span class="tip-icon">🔒</span>
|
||||
<span class="tip-text">启用双因素认证(2FA)增强安全性</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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>🔒 保护您的数字安全,从强密码开始</p>
|
||||
<p class="footer-note">本工具不会存储您的密码信息</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- 提示框 -->
|
||||
<div class="toast" id="toast" style="display: none;">
|
||||
<span id="toastMessage">操作成功</span>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
516
InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/js/script.js
Normal file
516
InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/js/script.js
Normal file
@@ -0,0 +1,516 @@
|
||||
/**
|
||||
* 密码强度检测器
|
||||
* 提供密码强度分析和安全建议
|
||||
*/
|
||||
class PasswordStrengthChecker {
|
||||
constructor() {
|
||||
this.apiUrl = 'https://60s.api.shumengya.top/v2/password/check';
|
||||
this.isChecking = false;
|
||||
this.currentPassword = '';
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.setupFormValidation();
|
||||
this.hideResultContainer();
|
||||
this.hideErrorContainer();
|
||||
console.log('密码强度检测器初始化完成');
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定事件监听器
|
||||
*/
|
||||
bindEvents() {
|
||||
// 密码输入框事件
|
||||
const passwordInput = document.getElementById('passwordInput');
|
||||
if (passwordInput) {
|
||||
passwordInput.addEventListener('input', this.handlePasswordInput.bind(this));
|
||||
passwordInput.addEventListener('keypress', this.handleKeyPress.bind(this));
|
||||
}
|
||||
|
||||
// 显示/隐藏密码按钮
|
||||
const toggleBtn = document.getElementById('toggleVisibility');
|
||||
if (toggleBtn) {
|
||||
toggleBtn.addEventListener('click', this.togglePasswordVisibility.bind(this));
|
||||
}
|
||||
|
||||
// 检测按钮
|
||||
const checkBtn = document.getElementById('checkBtn');
|
||||
if (checkBtn) {
|
||||
checkBtn.addEventListener('click', this.handleCheckPassword.bind(this));
|
||||
}
|
||||
|
||||
// 重试按钮
|
||||
const retryBtn = document.getElementById('retryBtn');
|
||||
if (retryBtn) {
|
||||
retryBtn.addEventListener('click', this.handleRetry.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表单验证
|
||||
*/
|
||||
setupFormValidation() {
|
||||
const form = document.querySelector('.input-container');
|
||||
if (form) {
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.handleCheckPassword();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理密码输入
|
||||
*/
|
||||
handlePasswordInput(event) {
|
||||
const password = event.target.value;
|
||||
this.currentPassword = password;
|
||||
|
||||
// 更新按钮状态
|
||||
this.updateCheckButtonState();
|
||||
|
||||
// 如果密码为空,隐藏结果
|
||||
if (!password.trim()) {
|
||||
this.hideResultContainer();
|
||||
this.hideErrorContainer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理键盘事件
|
||||
*/
|
||||
handleKeyPress(event) {
|
||||
if (event.key === 'Enter' && !this.isChecking) {
|
||||
event.preventDefault();
|
||||
this.handleCheckPassword();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换密码可见性
|
||||
*/
|
||||
togglePasswordVisibility() {
|
||||
const passwordInput = document.getElementById('passwordInput');
|
||||
const toggleBtn = document.getElementById('toggleVisibility');
|
||||
|
||||
if (passwordInput && toggleBtn) {
|
||||
const isPassword = passwordInput.type === 'password';
|
||||
passwordInput.type = isPassword ? 'text' : 'password';
|
||||
toggleBtn.innerHTML = isPassword ? '🙈' : '👁️';
|
||||
toggleBtn.title = isPassword ? '隐藏密码' : '显示密码';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新检测按钮状态
|
||||
*/
|
||||
updateCheckButtonState() {
|
||||
const checkBtn = document.getElementById('checkBtn');
|
||||
const hasPassword = this.currentPassword.trim().length > 0;
|
||||
|
||||
if (checkBtn) {
|
||||
checkBtn.disabled = !hasPassword || this.isChecking;
|
||||
|
||||
if (this.isChecking) {
|
||||
checkBtn.innerHTML = '<span class="btn-icon">⏳</span>检测中...';
|
||||
} else if (hasPassword) {
|
||||
checkBtn.innerHTML = '<span class="btn-icon">🔍</span>检测密码强度';
|
||||
} else {
|
||||
checkBtn.innerHTML = '<span class="btn-icon">🔍</span>请输入密码';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理密码检测
|
||||
*/
|
||||
async handleCheckPassword() {
|
||||
const password = this.currentPassword.trim();
|
||||
|
||||
if (!password) {
|
||||
this.showToast('请输入要检测的密码', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isChecking) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.setLoadingState(true);
|
||||
this.hideErrorContainer();
|
||||
|
||||
const result = await this.checkPasswordStrength(password);
|
||||
|
||||
if (result.code === 200 && result.data) {
|
||||
this.displayResults(result.data);
|
||||
this.showResultContainer();
|
||||
this.showToast('密码强度检测完成', 'success');
|
||||
} else {
|
||||
throw new Error(result.message || '检测失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('密码检测错误:', error);
|
||||
this.showError(error.message || '检测服务暂时不可用,请稍后重试');
|
||||
} finally {
|
||||
this.setLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用API检测密码强度
|
||||
*/
|
||||
async checkPasswordStrength(password) {
|
||||
const url = new URL(this.apiUrl);
|
||||
url.searchParams.append('password', password);
|
||||
url.searchParams.append('encoding', 'utf-8');
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示检测结果
|
||||
*/
|
||||
displayResults(data) {
|
||||
this.updateStrengthOverview(data);
|
||||
this.updateDetailedInfo(data);
|
||||
this.updateRecommendations(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新强度概览
|
||||
*/
|
||||
updateStrengthOverview(data) {
|
||||
// 更新分数圆圈
|
||||
const scoreCircle = document.getElementById('scoreCircle');
|
||||
const scoreValue = document.getElementById('scoreValue');
|
||||
const strengthLevel = document.getElementById('strengthLevel');
|
||||
const strengthDescription = document.getElementById('strengthDescription');
|
||||
const barFill = document.getElementById('strengthBar');
|
||||
|
||||
if (scoreValue) {
|
||||
scoreValue.textContent = data.score || 0;
|
||||
}
|
||||
|
||||
if (strengthLevel) {
|
||||
strengthLevel.textContent = this.getStrengthText(data.strength);
|
||||
const strengthClass = this.getStrengthClass(data.strength);
|
||||
strengthLevel.className = `strength-level strength-${strengthClass}`;
|
||||
}
|
||||
|
||||
if (strengthDescription) {
|
||||
strengthDescription.textContent = this.getStrengthDescription(data.strength);
|
||||
}
|
||||
|
||||
// 更新分数圆圈
|
||||
if (scoreCircle) {
|
||||
const percentage = (data.score / 100) * 360;
|
||||
scoreCircle.style.setProperty('--score-deg', `${percentage}deg`);
|
||||
// 将中文强度转换为CSS类名
|
||||
const strengthClass = this.getStrengthClass(data.strength);
|
||||
scoreCircle.className = `score-circle score-${strengthClass}`;
|
||||
}
|
||||
|
||||
// 更新强度条
|
||||
if (barFill) {
|
||||
setTimeout(() => {
|
||||
barFill.style.width = `${data.score}%`;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新详细信息
|
||||
*/
|
||||
updateDetailedInfo(data) {
|
||||
// 基本信息
|
||||
this.updateElement('passwordLength', data.length || 0);
|
||||
this.updateElement('entropyValue', data.entropy ? data.entropy.toFixed(2) : '0.00');
|
||||
this.updateElement('crackTime', data.time_to_crack || '未知');
|
||||
|
||||
// 字符类型分析
|
||||
this.updateCharacterAnalysis(data.character_analysis || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新字符类型分析
|
||||
*/
|
||||
updateCharacterAnalysis(analysis) {
|
||||
const types = {
|
||||
'has_lowercase': { element: 'hasLowercase', label: '小写字母', icon: '🔤' },
|
||||
'has_uppercase': { element: 'hasUppercase', label: '大写字母', icon: '🔠' },
|
||||
'has_numbers': { element: 'hasNumbers', label: '数字', icon: '🔢' },
|
||||
'has_symbols': { element: 'hasSymbols', label: '特殊符号', icon: '🔣' }
|
||||
};
|
||||
|
||||
Object.keys(types).forEach(key => {
|
||||
const element = document.getElementById(types[key].element);
|
||||
if (element) {
|
||||
const hasType = analysis[key] || false;
|
||||
element.className = `char-type ${hasType ? 'has-type' : ''}`;
|
||||
element.innerHTML = `
|
||||
<span class="type-icon">${hasType ? '✅' : '❌'}</span>
|
||||
<span>${types[key].label}</span>
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
// 更新字符种类数量
|
||||
this.updateElement('characterVariety', analysis.character_variety || 0);
|
||||
|
||||
// 更新问题提示
|
||||
this.updateCharacterIssues(analysis);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新字符问题提示
|
||||
*/
|
||||
updateCharacterIssues(analysis) {
|
||||
const issues = [
|
||||
{ id: 'hasRepeated', condition: analysis.has_repeated, text: '包含重复字符' },
|
||||
{ id: 'hasSequential', condition: analysis.has_sequential, text: '包含连续字符' }
|
||||
];
|
||||
|
||||
issues.forEach(issue => {
|
||||
const element = document.getElementById(issue.id);
|
||||
if (element) {
|
||||
if (issue.condition) {
|
||||
element.style.display = 'flex';
|
||||
element.innerHTML = `
|
||||
<span class="issue-icon">⚠️</span>
|
||||
<span class="issue-text">${issue.text}</span>
|
||||
`;
|
||||
} else {
|
||||
element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新建议和提示
|
||||
*/
|
||||
updateRecommendations(data) {
|
||||
// 更新建议列表
|
||||
const recommendationsList = document.getElementById('recommendationsList');
|
||||
if (recommendationsList && data.recommendations) {
|
||||
recommendationsList.innerHTML = '';
|
||||
data.recommendations.forEach(recommendation => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = recommendation;
|
||||
recommendationsList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
// 更新安全提示
|
||||
const tipsContainer = document.getElementById('securityTips');
|
||||
if (tipsContainer && data.security_tips) {
|
||||
tipsContainer.innerHTML = '';
|
||||
data.security_tips.forEach((tip, index) => {
|
||||
const tipElement = document.createElement('div');
|
||||
tipElement.className = 'tip-item';
|
||||
tipElement.innerHTML = `
|
||||
<span class="tip-icon">${this.getTipIcon(index)}</span>
|
||||
<span class="tip-text">${tip}</span>
|
||||
`;
|
||||
tipsContainer.appendChild(tipElement);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提示图标
|
||||
*/
|
||||
getTipIcon(index) {
|
||||
const icons = ['🛡️', '🔐', '⚡', '🎯', '💡', '🔄'];
|
||||
return icons[index % icons.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取强度文本
|
||||
*/
|
||||
getStrengthText(strength) {
|
||||
// API直接返回中文强度,无需映射
|
||||
return strength || '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取强度CSS类名
|
||||
*/
|
||||
getStrengthClass(strength) {
|
||||
const classMap = {
|
||||
'弱': 'weak',
|
||||
'中等': 'medium',
|
||||
'强': 'strong',
|
||||
'非常强': 'very-strong'
|
||||
};
|
||||
return classMap[strength] || 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取强度描述
|
||||
*/
|
||||
getStrengthDescription(strength) {
|
||||
const descriptions = {
|
||||
'弱': '密码强度较弱,建议增加复杂度',
|
||||
'中等': '密码强度中等,可以进一步优化',
|
||||
'强': '密码强度良好,安全性较高',
|
||||
'非常强': '密码强度非常好,安全性很高'
|
||||
};
|
||||
return descriptions[strength] || '无法评估密码强度';
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置加载状态
|
||||
*/
|
||||
setLoadingState(loading) {
|
||||
this.isChecking = loading;
|
||||
this.updateCheckButtonState();
|
||||
|
||||
const passwordInput = document.getElementById('passwordInput');
|
||||
if (passwordInput) {
|
||||
passwordInput.disabled = loading;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示结果容器
|
||||
*/
|
||||
showResultContainer() {
|
||||
const container = document.getElementById('resultContainer');
|
||||
if (container) {
|
||||
container.style.display = 'block';
|
||||
container.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏结果容器
|
||||
*/
|
||||
hideResultContainer() {
|
||||
const container = document.getElementById('resultContainer');
|
||||
if (container) {
|
||||
container.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误
|
||||
*/
|
||||
showError(message) {
|
||||
const errorContainer = document.getElementById('errorContainer');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
|
||||
if (errorContainer && errorMessage) {
|
||||
errorMessage.textContent = message;
|
||||
errorContainer.style.display = 'block';
|
||||
this.hideResultContainer();
|
||||
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏错误容器
|
||||
*/
|
||||
hideErrorContainer() {
|
||||
const container = document.getElementById('errorContainer');
|
||||
if (container) {
|
||||
container.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理重试
|
||||
*/
|
||||
handleRetry() {
|
||||
this.hideErrorContainer();
|
||||
const passwordInput = document.getElementById('passwordInput');
|
||||
if (passwordInput) {
|
||||
passwordInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新元素内容
|
||||
*/
|
||||
updateElement(id, content) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.textContent = content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示提示消息
|
||||
*/
|
||||
showToast(message, type = 'success') {
|
||||
const toast = document.getElementById('toast');
|
||||
const toastMessage = document.getElementById('toastMessage');
|
||||
|
||||
if (toast && toastMessage) {
|
||||
toastMessage.textContent = message;
|
||||
toast.className = `toast toast-${type}`;
|
||||
toast.style.display = 'block';
|
||||
|
||||
// 3秒后自动隐藏
|
||||
setTimeout(() => {
|
||||
toast.style.display = 'none';
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
try {
|
||||
window.passwordChecker = new PasswordStrengthChecker();
|
||||
console.log('密码强度检测器已启动');
|
||||
} catch (error) {
|
||||
console.error('初始化失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 页面可见性变化处理
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible' && window.passwordChecker) {
|
||||
console.log('页面重新激活');
|
||||
}
|
||||
});
|
||||
|
||||
// 全局错误处理
|
||||
window.addEventListener('error', (event) => {
|
||||
console.error('全局错误:', event.error);
|
||||
if (window.passwordChecker) {
|
||||
window.passwordChecker.showToast('发生了意外错误,请刷新页面重试', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
// 网络状态监听
|
||||
window.addEventListener('online', () => {
|
||||
if (window.passwordChecker) {
|
||||
window.passwordChecker.showToast('网络连接已恢复', 'success');
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('offline', () => {
|
||||
if (window.passwordChecker) {
|
||||
window.passwordChecker.showToast('网络连接已断开', 'error');
|
||||
}
|
||||
});
|
||||
37
InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/返回接口.json
Normal file
37
InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/返回接口.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"password": "adasdasdasdadasd",
|
||||
"length": 16,
|
||||
"score": 68,
|
||||
"strength": "中等",
|
||||
"entropy": 75.21,
|
||||
"time_to_crack": "数百万年",
|
||||
"character_analysis": {
|
||||
"has_lowercase": true,
|
||||
"has_uppercase": false,
|
||||
"has_numbers": false,
|
||||
"has_symbols": false,
|
||||
"has_repeated": false,
|
||||
"has_sequential": true,
|
||||
"character_variety": 26
|
||||
},
|
||||
"recommendations": [
|
||||
"建议包含大写字母",
|
||||
"建议包含数字",
|
||||
"建议包含特殊符号",
|
||||
"避免使用连续序列字符"
|
||||
],
|
||||
"security_tips": [
|
||||
"使用密码管理器生成和存储复杂密码",
|
||||
"为不同账户使用不同的密码",
|
||||
"定期更换重要账户的密码",
|
||||
"启用双因素认证(2FA)增强安全性",
|
||||
"避免在公共场合输入密码",
|
||||
"不要将密码保存在浏览器中(除非使用可信的密码管理器)",
|
||||
"避免使用个人信息作为密码",
|
||||
"长密码比复杂密码更安全"
|
||||
]
|
||||
}
|
||||
}
|
||||
132
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/css/background.css
Normal file
132
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/css/background.css
Normal file
@@ -0,0 +1,132 @@
|
||||
/* 背景样式文件 - 独立分离便于迁移 */
|
||||
|
||||
/* 主背景渐变 */
|
||||
body {
|
||||
background: linear-gradient(135deg,
|
||||
#e8f5e8 0%,
|
||||
#f1f8e9 25%,
|
||||
#e8f5e8 50%,
|
||||
#c8e6c9 75%,
|
||||
#e8f5e8 100%);
|
||||
background-size: 400% 400%;
|
||||
animation: backgroundShift 15s ease-in-out infinite;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 背景动画 */
|
||||
@keyframes backgroundShift {
|
||||
0%, 100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
25% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 50% 100%;
|
||||
}
|
||||
75% {
|
||||
background-position: 50% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 背景装饰元素 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 20%, rgba(76, 175, 80, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 80%, rgba(129, 199, 132, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 60%, rgba(165, 214, 167, 0.08) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
animation: floatingBubbles 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes floatingBubbles {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
33% {
|
||||
transform: translateY(-20px) rotate(120deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
66% {
|
||||
transform: translateY(10px) rotate(240deg);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
/* 背景粒子效果 */
|
||||
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;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
animation: particleFloat 25s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes particleFloat {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 768px) {
|
||||
body::after {
|
||||
background-size: 150px 75px;
|
||||
animation-duration: 20s;
|
||||
}
|
||||
|
||||
body::before {
|
||||
animation-duration: 15s;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body::after {
|
||||
background-size: 100px 50px;
|
||||
animation-duration: 15s;
|
||||
}
|
||||
|
||||
body::before {
|
||||
animation-duration: 12s;
|
||||
}
|
||||
|
||||
body {
|
||||
animation-duration: 12s;
|
||||
}
|
||||
}
|
||||
|
||||
/* 高性能模式 - 减少动画 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
body,
|
||||
body::before,
|
||||
body::after {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f1f8e9 50%, #c8e6c9 100%);
|
||||
}
|
||||
}
|
||||
468
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/css/style.css
Normal file
468
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/css/style.css
Normal file
@@ -0,0 +1,468 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #2d5a3d;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 容器样式 */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding: 40px 20px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 8px 32px rgba(76, 175, 80, 0.1);
|
||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, transparent, #4caf50, transparent);
|
||||
animation: headerGlow 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes headerGlow {
|
||||
0% { left: -100%; }
|
||||
50% { left: 100%; }
|
||||
100% { left: 100%; }
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #4caf50, #81c784, #4caf50);
|
||||
background-size: 200% 200%;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 10px;
|
||||
animation: titleGradient 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes titleGradient {
|
||||
0%, 100% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
}
|
||||
|
||||
.header p {
|
||||
font-size: 1.1rem;
|
||||
color: #66bb6a;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 40px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* 表单容器 */
|
||||
.form-container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 35px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 8px 32px rgba(76, 175, 80, 0.1);
|
||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-container:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 40px rgba(76, 175, 80, 0.15);
|
||||
}
|
||||
|
||||
/* 表单样式 */
|
||||
.qr-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
font-weight: 600;
|
||||
color: #2d5a3d;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
textarea, select {
|
||||
padding: 12px 16px;
|
||||
border: 2px solid rgba(76, 175, 80, 0.3);
|
||||
border-radius: 12px;
|
||||
font-size: 1rem;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
transition: all 0.3s ease;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 100px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
textarea:focus, select:focus {
|
||||
outline: none;
|
||||
border-color: #4caf50;
|
||||
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1);
|
||||
background: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.options-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.generate-btn {
|
||||
padding: 15px 30px;
|
||||
background: linear-gradient(135deg, #4caf50, #66bb6a);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.generate-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 ease;
|
||||
}
|
||||
|
||||
.generate-btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.generate-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.generate-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.generate-btn.loading .btn-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.generate-btn.loading .btn-loading {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.btn-loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 结果容器 */
|
||||
.result-container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 35px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 8px 32px rgba(76, 175, 80, 0.1);
|
||||
border: 1px solid rgba(76, 175, 80, 0.2);
|
||||
min-height: 400px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading {
|
||||
text-align: center;
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 4px solid rgba(76, 175, 80, 0.2);
|
||||
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); }
|
||||
}
|
||||
|
||||
/* 错误样式 */
|
||||
.error {
|
||||
text-align: center;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
padding: 10px 20px;
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background: #45a049;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 结果显示 */
|
||||
.result {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qr-display {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.qr-display img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(76, 175, 80, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.qr-display img:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 8px 30px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.result-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.result-text {
|
||||
font-size: 1rem;
|
||||
color: #666;
|
||||
word-break: break-all;
|
||||
background: rgba(76, 175, 80, 0.1);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #4caf50;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.download-btn, .copy-btn, .new-btn {
|
||||
padding: 10px 16px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: #2196f3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.new-btn {
|
||||
background: #ff9800;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.download-btn:hover, .copy-btn:hover, .new-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 隐藏类 */
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 页脚样式 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding: 25px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(15px);
|
||||
border-radius: 15px;
|
||||
color: #66bb6a;
|
||||
font-size: 0.9rem;
|
||||
border: 1px solid rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container {
|
||||
max-width: 95%;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.main {
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.form-container, .result-container {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.options-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大768px) */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 25px;
|
||||
padding: 25px 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.header p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.main {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.form-container, .result-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.options-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.download-btn, .copy-btn, .new-btn {
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 20px 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.form-container, .result-container {
|
||||
padding: 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.generate-btn {
|
||||
padding: 12px 20px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.qr-display img {
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
98
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/index.html
Normal file
98
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/index.html
Normal file
@@ -0,0 +1,98 @@
|
||||
<!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>🔗 二维码生成器</h1>
|
||||
<p>快速生成高质量二维码</p>
|
||||
</header>
|
||||
|
||||
<main class="main">
|
||||
<div class="form-container">
|
||||
<form id="qrForm" class="qr-form">
|
||||
<div class="input-group">
|
||||
<label for="text">文本内容</label>
|
||||
<textarea id="text" name="text" placeholder="请输入要生成二维码的文本或URL" required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="options-grid">
|
||||
<div class="input-group">
|
||||
<label for="size">尺寸大小</label>
|
||||
<select id="size" name="size">
|
||||
<option value="128">128x128</option>
|
||||
<option value="256" selected>256x256</option>
|
||||
<option value="512">512x512</option>
|
||||
<option value="1024">1024x1024</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="level">容错级别</label>
|
||||
<select id="level" name="level">
|
||||
<option value="L">L - 低 (7%)</option>
|
||||
<option value="M" selected>M - 中 (15%)</option>
|
||||
<option value="Q">Q - 高 (25%)</option>
|
||||
<option value="H">H - 最高 (30%)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="encoding">返回格式</label>
|
||||
<select id="encoding" name="encoding">
|
||||
<option value="image" selected>图片</option>
|
||||
<option value="json">JSON</option>
|
||||
<option value="text">文本</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="generate-btn">
|
||||
<span class="btn-text">生成二维码</span>
|
||||
<span class="btn-loading">生成中...</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="result-container">
|
||||
<div id="loading" class="loading hidden">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>正在生成二维码...</p>
|
||||
</div>
|
||||
|
||||
<div id="error" class="error hidden">
|
||||
<div class="error-icon">⚠️</div>
|
||||
<p class="error-message"></p>
|
||||
<button class="retry-btn">重试</button>
|
||||
</div>
|
||||
|
||||
<div id="result" class="result hidden">
|
||||
<div class="qr-display">
|
||||
<img id="qrImage" src="" alt="生成的二维码">
|
||||
</div>
|
||||
<div class="result-info">
|
||||
<p class="result-text"></p>
|
||||
<div class="result-actions">
|
||||
<button class="download-btn">下载二维码</button>
|
||||
<button class="copy-btn">复制链接</button>
|
||||
<button class="new-btn">生成新的</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>© 2024 二维码生成器 - 简单快捷的二维码生成工具</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
453
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/js/script.js
Normal file
453
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/js/script.js
Normal file
@@ -0,0 +1,453 @@
|
||||
// 二维码生成器 JavaScript
|
||||
class QRCodeGenerator {
|
||||
constructor() {
|
||||
this.apiEndpoints = [];
|
||||
this.currentApiIndex = 0;
|
||||
this.init();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
async init() {
|
||||
await this.loadApiEndpoints();
|
||||
this.bindEvents();
|
||||
this.setupFormValidation();
|
||||
}
|
||||
|
||||
// 加载API接口列表
|
||||
async loadApiEndpoints() {
|
||||
try {
|
||||
// 直接在代码中配置API接口,避免CORS问题
|
||||
this.apiEndpoints = [
|
||||
"https://60s.api.shumengya.top"
|
||||
];
|
||||
console.log('已加载API接口:', this.apiEndpoints);
|
||||
} catch (error) {
|
||||
console.error('加载API接口失败:', error);
|
||||
this.showError('加载配置失败,请刷新页面重试');
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
const form = document.getElementById('qrForm');
|
||||
const retryBtn = document.querySelector('.retry-btn');
|
||||
const downloadBtn = document.querySelector('.download-btn');
|
||||
const copyBtn = document.querySelector('.copy-btn');
|
||||
const newBtn = document.querySelector('.new-btn');
|
||||
const textArea = document.getElementById('text');
|
||||
|
||||
if (form) {
|
||||
form.addEventListener('submit', (e) => this.handleSubmit(e));
|
||||
}
|
||||
if (retryBtn) {
|
||||
retryBtn.addEventListener('click', () => this.retryGeneration());
|
||||
}
|
||||
if (downloadBtn) {
|
||||
downloadBtn.addEventListener('click', () => this.downloadQRCode());
|
||||
}
|
||||
if (copyBtn) {
|
||||
copyBtn.addEventListener('click', () => this.copyImageLink());
|
||||
}
|
||||
if (newBtn) {
|
||||
newBtn.addEventListener('click', () => this.resetForm());
|
||||
}
|
||||
if (textArea) {
|
||||
textArea.addEventListener('input', () => this.updateCharCount());
|
||||
}
|
||||
}
|
||||
|
||||
// 设置表单验证
|
||||
setupFormValidation() {
|
||||
const textArea = document.getElementById('text');
|
||||
const form = document.getElementById('qrForm');
|
||||
|
||||
textArea.addEventListener('blur', () => {
|
||||
if (textArea.value.trim() === '') {
|
||||
this.showFieldError(textArea, '请输入要生成二维码的内容');
|
||||
} else {
|
||||
this.clearFieldError(textArea);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 显示字段错误
|
||||
showFieldError(field, message) {
|
||||
this.clearFieldError(field);
|
||||
field.style.borderColor = '#d32f2f';
|
||||
const errorDiv = document.createElement('div');
|
||||
errorDiv.className = 'field-error';
|
||||
errorDiv.style.color = '#d32f2f';
|
||||
errorDiv.style.fontSize = '0.8rem';
|
||||
errorDiv.style.marginTop = '5px';
|
||||
errorDiv.textContent = message;
|
||||
field.parentNode.appendChild(errorDiv);
|
||||
}
|
||||
|
||||
// 清除字段错误
|
||||
clearFieldError(field) {
|
||||
field.style.borderColor = '';
|
||||
const errorDiv = field.parentNode.querySelector('.field-error');
|
||||
if (errorDiv) {
|
||||
errorDiv.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新字符计数
|
||||
updateCharCount() {
|
||||
const textArea = document.getElementById('text');
|
||||
const text = textArea.value;
|
||||
const length = text.length;
|
||||
|
||||
// 移除旧的计数显示
|
||||
const oldCounter = textArea.parentNode.querySelector('.char-counter');
|
||||
if (oldCounter) oldCounter.remove();
|
||||
|
||||
// 添加新的计数显示
|
||||
if (length > 0) {
|
||||
const counter = document.createElement('div');
|
||||
counter.className = 'char-counter';
|
||||
counter.style.fontSize = '0.8rem';
|
||||
counter.style.color = '#666';
|
||||
counter.style.textAlign = 'right';
|
||||
counter.style.marginTop = '5px';
|
||||
counter.textContent = `${length} 个字符`;
|
||||
textArea.parentNode.appendChild(counter);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理表单提交
|
||||
async handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
const params = {
|
||||
text: formData.get('text').trim(),
|
||||
size: formData.get('size'),
|
||||
level: formData.get('level'),
|
||||
encoding: formData.get('encoding')
|
||||
};
|
||||
|
||||
// 验证输入
|
||||
if (!params.text) {
|
||||
this.showFieldError(document.getElementById('text'), '请输入要生成二维码的内容');
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading();
|
||||
await this.generateQRCode(params);
|
||||
}
|
||||
|
||||
// 生成二维码
|
||||
async generateQRCode(params) {
|
||||
let success = false;
|
||||
let lastError = null;
|
||||
|
||||
// 尝试所有API接口
|
||||
for (let i = 0; i < this.apiEndpoints.length; i++) {
|
||||
const apiIndex = (this.currentApiIndex + i) % this.apiEndpoints.length;
|
||||
const apiUrl = this.apiEndpoints[apiIndex];
|
||||
|
||||
try {
|
||||
console.log(`尝试API ${apiIndex + 1}:`, apiUrl);
|
||||
const result = await this.callAPI(apiUrl, params);
|
||||
|
||||
if (result.success) {
|
||||
this.currentApiIndex = apiIndex; // 记录成功的API
|
||||
this.showResult(result.data, params);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`API ${apiIndex + 1} 失败:`, error);
|
||||
lastError = error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
this.showError(lastError?.message || '所有API接口都无法访问,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
// 调用API
|
||||
async callAPI(baseUrl, params) {
|
||||
const url = new URL('/v2/qrcode', baseUrl);
|
||||
|
||||
// 添加查询参数
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
url.searchParams.append(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('请求URL:', url.toString());
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), {
|
||||
method: 'GET',
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Accept': 'application/json, image/*'
|
||||
}
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
// 根据返回格式处理
|
||||
if (params.encoding === 'image' || !params.encoding) {
|
||||
// 默认返回图片格式
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (contentType && contentType.startsWith('image/')) {
|
||||
const blob = await response.blob();
|
||||
const imageUrl = URL.createObjectURL(blob);
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
imageUrl: imageUrl,
|
||||
text: params.text,
|
||||
size: params.size,
|
||||
level: params.level,
|
||||
format: 'image'
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// 如果返回的不是图片,尝试解析JSON
|
||||
const jsonData = await response.json();
|
||||
if (jsonData.code === 0 && jsonData.data && jsonData.data.data_uri) {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
imageUrl: jsonData.data.data_uri,
|
||||
text: params.text,
|
||||
size: params.size,
|
||||
level: params.level,
|
||||
format: 'json',
|
||||
base64: jsonData.data.base64,
|
||||
mimeType: jsonData.data.mime_type
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new Error(jsonData.message || '生成失败');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// JSON或text格式
|
||||
const jsonData = await response.json();
|
||||
if (jsonData.code === 0 && jsonData.data) {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
imageUrl: jsonData.data.data_uri,
|
||||
text: params.text,
|
||||
size: params.size,
|
||||
level: params.level,
|
||||
format: params.encoding,
|
||||
base64: jsonData.data.base64,
|
||||
mimeType: jsonData.data.mime_type
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new Error(jsonData.message || '生成失败');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
if (error.name === 'AbortError') {
|
||||
throw new Error('请求超时,请重试');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
showLoading() {
|
||||
this.hideAllStates();
|
||||
document.getElementById('loading').classList.remove('hidden');
|
||||
|
||||
const btn = document.querySelector('.generate-btn');
|
||||
btn.classList.add('loading');
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
// 显示错误
|
||||
showError(message) {
|
||||
this.hideAllStates();
|
||||
const errorDiv = document.getElementById('error');
|
||||
const errorMessage = errorDiv.querySelector('.error-message');
|
||||
errorMessage.textContent = message;
|
||||
errorDiv.classList.remove('hidden');
|
||||
|
||||
this.resetButton();
|
||||
}
|
||||
|
||||
// 显示结果
|
||||
showResult(data, params) {
|
||||
this.hideAllStates();
|
||||
|
||||
const resultDiv = document.getElementById('result');
|
||||
const qrImage = document.getElementById('qrImage');
|
||||
const resultText = document.querySelector('.result-text');
|
||||
|
||||
qrImage.src = data.imageUrl;
|
||||
qrImage.alt = `二维码: ${data.text}`;
|
||||
|
||||
resultText.innerHTML = `
|
||||
<strong>内容:</strong> ${this.escapeHtml(data.text)}<br>
|
||||
<strong>尺寸:</strong> ${data.size}x${data.size}<br>
|
||||
<strong>容错级别:</strong> ${data.level}<br>
|
||||
<strong>格式:</strong> ${data.format.toUpperCase()}
|
||||
`;
|
||||
|
||||
resultDiv.classList.remove('hidden');
|
||||
this.resetButton();
|
||||
|
||||
// 保存数据供下载使用
|
||||
this.currentQRData = data;
|
||||
}
|
||||
|
||||
// 隐藏所有状态
|
||||
hideAllStates() {
|
||||
document.getElementById('loading').classList.add('hidden');
|
||||
document.getElementById('error').classList.add('hidden');
|
||||
document.getElementById('result').classList.add('hidden');
|
||||
}
|
||||
|
||||
// 重置按钮状态
|
||||
resetButton() {
|
||||
const btn = document.querySelector('.generate-btn');
|
||||
btn.classList.remove('loading');
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
// 重试生成
|
||||
async retryGeneration() {
|
||||
const form = document.getElementById('qrForm');
|
||||
const formData = new FormData(form);
|
||||
const params = {
|
||||
text: formData.get('text').trim(),
|
||||
size: formData.get('size'),
|
||||
level: formData.get('level'),
|
||||
encoding: formData.get('encoding')
|
||||
};
|
||||
|
||||
this.showLoading();
|
||||
await this.generateQRCode(params);
|
||||
}
|
||||
|
||||
// 下载二维码
|
||||
downloadQRCode() {
|
||||
if (!this.currentQRData) return;
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = this.currentQRData.imageUrl;
|
||||
link.download = `qrcode_${Date.now()}.png`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
this.showToast('二维码已下载');
|
||||
}
|
||||
|
||||
// 复制图片链接
|
||||
async copyImageLink() {
|
||||
if (!this.currentQRData) return;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(this.currentQRData.imageUrl);
|
||||
this.showToast('链接已复制到剪贴板');
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error);
|
||||
this.showToast('复制失败,请手动复制');
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
resetForm() {
|
||||
document.getElementById('qrForm').reset();
|
||||
this.hideAllStates();
|
||||
this.currentQRData = null;
|
||||
|
||||
// 清除字符计数
|
||||
const counter = document.querySelector('.char-counter');
|
||||
if (counter) counter.remove();
|
||||
|
||||
// 清除字段错误
|
||||
document.querySelectorAll('input, textarea, select').forEach(field => {
|
||||
this.clearFieldError(field);
|
||||
});
|
||||
|
||||
// 聚焦到文本框
|
||||
document.getElementById('text').focus();
|
||||
}
|
||||
|
||||
// 显示提示消息
|
||||
showToast(message) {
|
||||
// 移除旧的toast
|
||||
const oldToast = document.querySelector('.toast');
|
||||
if (oldToast) oldToast.remove();
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast';
|
||||
toast.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
z-index: 1000;
|
||||
animation: slideIn 0.3s ease;
|
||||
`;
|
||||
toast.textContent = message;
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'slideOut 0.3s ease';
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// HTML转义
|
||||
escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加CSS动画
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes slideIn {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
@keyframes slideOut {
|
||||
from { transform: translateX(0); opacity: 1; }
|
||||
to { transform: translateX(100%); opacity: 0; }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new QRCodeGenerator();
|
||||
});
|
||||
|
||||
// 错误处理
|
||||
window.addEventListener('error', (e) => {
|
||||
console.error('全局错误:', e.error);
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', (e) => {
|
||||
console.error('未处理的Promise拒绝:', e.reason);
|
||||
});
|
||||
3
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/接口集合.json
Normal file
3
InfoGenie-frontend/public/60sapi/实用功能/生成二维码/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
192
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/css/background.css
Normal file
192
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/css/background.css
Normal file
@@ -0,0 +1,192 @@
|
||||
/* 彩虹渐变背景样式 */
|
||||
|
||||
/* 主背景渐变 */
|
||||
body {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 107, 107, 0.3) 0%,
|
||||
rgba(255, 165, 0, 0.3) 14.28%,
|
||||
rgba(255, 255, 0, 0.25) 28.56%,
|
||||
rgba(50, 205, 50, 0.3) 42.84%,
|
||||
rgba(0, 191, 255, 0.3) 57.12%,
|
||||
rgba(65, 105, 225, 0.3) 71.4%,
|
||||
rgba(147, 112, 219, 0.3) 85.68%,
|
||||
rgba(255, 105, 180, 0.3) 100%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: rainbowShift 20s ease infinite;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 彩虹渐变动画 */
|
||||
@keyframes rainbowShift {
|
||||
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: rgba(255, 255, 255, 0.4);
|
||||
backdrop-filter: blur(2px);
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 搜索按钮彩虹渐变 */
|
||||
.search-btn {
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
rgba(255, 107, 107, 0.8),
|
||||
rgba(255, 165, 0, 0.8),
|
||||
rgba(255, 255, 0, 0.7),
|
||||
rgba(50, 205, 50, 0.8),
|
||||
rgba(0, 191, 255, 0.8),
|
||||
rgba(65, 105, 225, 0.8),
|
||||
rgba(147, 112, 219, 0.8)
|
||||
);
|
||||
background-size: 300% 300%;
|
||||
animation: buttonRainbow 12s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes buttonRainbow {
|
||||
0%, 100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 结果卡片边框彩虹渐变 */
|
||||
.result-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.result-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
rgba(255, 107, 107, 0.4),
|
||||
rgba(255, 165, 0, 0.4),
|
||||
rgba(255, 255, 0, 0.3),
|
||||
rgba(50, 205, 50, 0.4),
|
||||
rgba(0, 191, 255, 0.4),
|
||||
rgba(65, 105, 225, 0.4),
|
||||
rgba(147, 112, 219, 0.4),
|
||||
rgba(255, 107, 107, 0.4)
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: borderRainbow 15s linear infinite;
|
||||
border-radius: inherit;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes borderRainbow {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 400% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载动画彩虹效果 */
|
||||
.loading-spinner {
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 4px solid transparent;
|
||||
border-image: linear-gradient(
|
||||
45deg,
|
||||
#ff6b6b,
|
||||
#ffa500,
|
||||
#ffff00,
|
||||
#32cd32,
|
||||
#00bfff,
|
||||
#4169e1,
|
||||
#9370db
|
||||
) 1;
|
||||
animation: spin 1s linear infinite, colorShift 3s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes colorShift {
|
||||
0%, 100% {
|
||||
filter: hue-rotate(0deg);
|
||||
}
|
||||
50% {
|
||||
filter: hue-rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 链接悬停彩虹效果 */
|
||||
.result-link:hover {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 107, 107, 0.7),
|
||||
rgba(255, 165, 0, 0.7),
|
||||
rgba(255, 255, 0, 0.6),
|
||||
rgba(50, 205, 50, 0.7),
|
||||
rgba(0, 191, 255, 0.7),
|
||||
rgba(65, 105, 225, 0.7),
|
||||
rgba(147, 112, 219, 0.7)
|
||||
);
|
||||
background-size: 200% 200%;
|
||||
animation: linkRainbow 3s ease infinite;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
@keyframes linkRainbow {
|
||||
0%, 100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 标题彩虹文字效果 */
|
||||
.title {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 107, 107, 0.8),
|
||||
rgba(255, 165, 0, 0.8),
|
||||
rgba(255, 255, 0, 0.7),
|
||||
rgba(50, 205, 50, 0.8),
|
||||
rgba(0, 191, 255, 0.8),
|
||||
rgba(65, 105, 225, 0.8),
|
||||
rgba(147, 112, 219, 0.8)
|
||||
);
|
||||
background-size: 200% 200%;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
animation: titleRainbow 8s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes titleRainbow {
|
||||
0%, 100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
}
|
||||
530
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/css/style.css
Normal file
530
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/css/style.css
Normal file
@@ -0,0 +1,530 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
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: #333;
|
||||
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: 20px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-weight: 300;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* 搜索区域 */
|
||||
.search-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 50px;
|
||||
padding: 8px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-container:focus-within {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 15px 25px;
|
||||
font-size: 1.1rem;
|
||||
background: transparent;
|
||||
color: #333;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: #999;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 15px 25px;
|
||||
border-radius: 50px;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
min-width: 120px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.search-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
/* 结果区域 */
|
||||
.result-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 40px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 结果卡片 */
|
||||
.result-card {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
animation: slideUp 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.result-header {
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.result-description {
|
||||
font-size: 1.1rem;
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 30px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.result-image-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.result-image {
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
height: auto;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.result-image:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.result-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.result-abstract h3 {
|
||||
font-size: 1.3rem;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.result-abstract p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.8;
|
||||
color: #555;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.result-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 24px;
|
||||
background: rgba(0, 123, 255, 0.1);
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
border-radius: 25px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.result-link:hover {
|
||||
background: rgba(0, 123, 255, 0.2);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0, 123, 255, 0.3);
|
||||
}
|
||||
|
||||
.link-icon {
|
||||
font-size: 1.2rem;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.result-link:hover .link-icon {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
/* 错误消息 */
|
||||
.error-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 40px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
text-align: center;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.error-message h3 {
|
||||
color: #e74c3c;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.error-message p {
|
||||
color: #666;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
padding: 12px 24px;
|
||||
background: #e74c3c;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background: #c0392b;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(231, 76, 60, 0.3);
|
||||
}
|
||||
|
||||
/* 欢迎消息 */
|
||||
.welcome-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 60px 40px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.welcome-icon {
|
||||
font-size: 4rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.welcome-message h3 {
|
||||
color: #333;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.welcome-message p {
|
||||
color: #666;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.6;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
margin-top: 40px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
grid-template-columns: 1fr 1.5fr;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
padding: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大768px) */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 30px;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
max-width: 100%;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
padding: 12px 20px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
padding: 12px 20px;
|
||||
min-width: 100px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.search-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.result-description {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.result-image {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.result-abstract h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.result-abstract p {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.welcome-message {
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.welcome-icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.welcome-message h3 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.welcome-message p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.error-message h3 {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 15px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
border-radius: 15px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-text {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.result-image {
|
||||
max-width: 150px;
|
||||
}
|
||||
}
|
||||
83
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/index.html
Normal file
83
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/index.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<!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">
|
||||
<h1 class="title">百度百科词条查询</h1>
|
||||
<p class="subtitle">探索知识的彩虹世界</p>
|
||||
</header>
|
||||
|
||||
<main class="main">
|
||||
<div class="search-section">
|
||||
<div class="search-container">
|
||||
<input type="text" id="searchInput" class="search-input" placeholder="请输入要查询的词条..." autocomplete="off">
|
||||
<button id="searchBtn" class="search-btn">
|
||||
<span class="search-icon">🔍</span>
|
||||
<span class="search-text">搜索</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section" id="resultSection">
|
||||
<div class="loading" id="loading" style="display: none;">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>正在搜索中...</p>
|
||||
</div>
|
||||
|
||||
<div class="result-card" id="resultCard" style="display: none;">
|
||||
<div class="result-header">
|
||||
<h2 class="result-title" id="resultTitle"></h2>
|
||||
<p class="result-description" id="resultDescription"></p>
|
||||
</div>
|
||||
|
||||
<div class="result-content">
|
||||
<div class="result-image-container">
|
||||
<img id="resultImage" class="result-image" alt="词条图片">
|
||||
</div>
|
||||
|
||||
<div class="result-text">
|
||||
<div class="result-abstract">
|
||||
<h3>摘要</h3>
|
||||
<p id="resultAbstract"></p>
|
||||
</div>
|
||||
|
||||
<div class="result-actions">
|
||||
<a id="resultLink" class="result-link" target="_blank">
|
||||
<span>查看完整词条</span>
|
||||
<span class="link-icon">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error-message" id="errorMessage" style="display: none;">
|
||||
<div class="error-icon">⚠️</div>
|
||||
<h3>查询失败</h3>
|
||||
<p id="errorText"></p>
|
||||
<button id="retryBtn" class="retry-btn">重试</button>
|
||||
</div>
|
||||
|
||||
<div class="welcome-message" id="welcomeMessage">
|
||||
<div class="welcome-icon">📚</div>
|
||||
<h3>欢迎使用百度百科词条查询</h3>
|
||||
<p>在上方搜索框中输入您想了解的词条,开始探索知识的海洋</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>数据来源:百度百科</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
320
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/js/script.js
Normal file
320
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/js/script.js
Normal file
@@ -0,0 +1,320 @@
|
||||
// 百度百科词条查询应用
|
||||
class BaikeApp {
|
||||
constructor() {
|
||||
// API接口列表
|
||||
this.apiEndpoints = [
|
||||
'https://60s.api.shumengya.top',
|
||||
];
|
||||
|
||||
this.currentApiIndex = 0;
|
||||
this.isLoading = false;
|
||||
|
||||
this.initElements();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
// 初始化DOM元素
|
||||
initElements() {
|
||||
this.searchInput = document.getElementById('searchInput');
|
||||
this.searchBtn = document.getElementById('searchBtn');
|
||||
this.resultSection = document.getElementById('resultSection');
|
||||
this.loading = document.getElementById('loading');
|
||||
this.resultCard = document.getElementById('resultCard');
|
||||
this.errorMessage = document.getElementById('errorMessage');
|
||||
this.welcomeMessage = document.getElementById('welcomeMessage');
|
||||
this.retryBtn = document.getElementById('retryBtn');
|
||||
|
||||
// 结果显示元素
|
||||
this.resultTitle = document.getElementById('resultTitle');
|
||||
this.resultDescription = document.getElementById('resultDescription');
|
||||
this.resultImage = document.getElementById('resultImage');
|
||||
this.resultAbstract = document.getElementById('resultAbstract');
|
||||
this.resultLink = document.getElementById('resultLink');
|
||||
this.errorText = document.getElementById('errorText');
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
// 搜索按钮点击事件
|
||||
this.searchBtn.addEventListener('click', () => {
|
||||
this.handleSearch();
|
||||
});
|
||||
|
||||
// 输入框回车事件
|
||||
this.searchInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleSearch();
|
||||
}
|
||||
});
|
||||
|
||||
// 重试按钮事件
|
||||
this.retryBtn.addEventListener('click', () => {
|
||||
this.handleSearch();
|
||||
});
|
||||
|
||||
// 输入框焦点事件
|
||||
this.searchInput.addEventListener('focus', () => {
|
||||
this.searchInput.select();
|
||||
});
|
||||
}
|
||||
|
||||
// 处理搜索
|
||||
async handleSearch() {
|
||||
const query = this.searchInput.value.trim();
|
||||
|
||||
if (!query) {
|
||||
this.showError('请输入要查询的词条');
|
||||
this.searchInput.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.searchBaike(query);
|
||||
}
|
||||
|
||||
// 搜索百科词条
|
||||
async searchBaike(query) {
|
||||
this.showLoading();
|
||||
this.isLoading = true;
|
||||
|
||||
// 重置API索引
|
||||
this.currentApiIndex = 0;
|
||||
|
||||
const success = await this.tryApiCall(query);
|
||||
|
||||
if (!success) {
|
||||
this.showError('所有API接口都无法访问,请稍后重试');
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
// 尝试API调用
|
||||
async tryApiCall(query) {
|
||||
for (let i = 0; i < this.apiEndpoints.length; i++) {
|
||||
const endpoint = this.apiEndpoints[this.currentApiIndex];
|
||||
|
||||
try {
|
||||
const result = await this.callApi(endpoint, query);
|
||||
if (result) {
|
||||
this.showResult(result);
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`API ${endpoint} 调用失败:`, error.message);
|
||||
}
|
||||
|
||||
// 切换到下一个API
|
||||
this.currentApiIndex = (this.currentApiIndex + 1) % this.apiEndpoints.length;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 调用API
|
||||
async callApi(endpoint, query) {
|
||||
const url = `${endpoint}/v2/baike?word=${encodeURIComponent(query)}`;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200 && data.data) {
|
||||
return data.data;
|
||||
} else {
|
||||
throw new Error(data.message || '未找到相关词条');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (error.name === 'AbortError') {
|
||||
throw new Error('请求超时');
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
showLoading() {
|
||||
this.hideAllSections();
|
||||
this.loading.style.display = 'flex';
|
||||
}
|
||||
|
||||
// 显示搜索结果
|
||||
showResult(data) {
|
||||
this.hideAllSections();
|
||||
|
||||
// 填充数据
|
||||
this.resultTitle.textContent = data.title || '未知标题';
|
||||
this.resultDescription.textContent = data.description || '暂无描述';
|
||||
this.resultAbstract.textContent = data.abstract || '暂无摘要信息';
|
||||
|
||||
// 处理图片
|
||||
if (data.cover) {
|
||||
this.resultImage.src = data.cover;
|
||||
this.resultImage.style.display = 'block';
|
||||
this.resultImage.onerror = () => {
|
||||
this.resultImage.style.display = 'none';
|
||||
};
|
||||
} else {
|
||||
this.resultImage.style.display = 'none';
|
||||
}
|
||||
|
||||
// 处理链接
|
||||
if (data.link) {
|
||||
this.resultLink.href = data.link;
|
||||
this.resultLink.style.display = 'inline-flex';
|
||||
} else {
|
||||
this.resultLink.style.display = 'none';
|
||||
}
|
||||
|
||||
this.resultCard.style.display = 'block';
|
||||
|
||||
// 滚动到结果区域
|
||||
this.resultCard.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
showError(message) {
|
||||
this.hideAllSections();
|
||||
this.errorText.textContent = message;
|
||||
this.errorMessage.style.display = 'flex';
|
||||
}
|
||||
|
||||
// 隐藏所有区域
|
||||
hideAllSections() {
|
||||
this.loading.style.display = 'none';
|
||||
this.resultCard.style.display = 'none';
|
||||
this.errorMessage.style.display = 'none';
|
||||
this.welcomeMessage.style.display = 'none';
|
||||
}
|
||||
|
||||
// 显示欢迎信息
|
||||
showWelcome() {
|
||||
this.hideAllSections();
|
||||
this.welcomeMessage.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
class Utils {
|
||||
// 防抖函数
|
||||
static debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// 节流函数
|
||||
static 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 格式化文本长度
|
||||
static truncateText(text, maxLength) {
|
||||
if (text.length <= maxLength) {
|
||||
return text;
|
||||
}
|
||||
return text.substring(0, maxLength) + '...';
|
||||
}
|
||||
|
||||
// 检查是否为移动设备
|
||||
static isMobile() {
|
||||
return window.innerWidth <= 768;
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化应用
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 初始化应用
|
||||
const app = new BaikeApp();
|
||||
|
||||
// 添加页面可见性变化监听
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
// 页面重新可见时,聚焦搜索框
|
||||
if (!app.isLoading) {
|
||||
app.searchInput.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 添加窗口大小变化监听
|
||||
window.addEventListener('resize', Utils.throttle(() => {
|
||||
// 响应式调整
|
||||
if (Utils.isMobile()) {
|
||||
// 移动端特殊处理
|
||||
document.body.classList.add('mobile');
|
||||
} else {
|
||||
document.body.classList.remove('mobile');
|
||||
}
|
||||
}, 250));
|
||||
|
||||
// 初始检查设备类型
|
||||
if (Utils.isMobile()) {
|
||||
document.body.classList.add('mobile');
|
||||
}
|
||||
|
||||
// 页面加载完成后聚焦搜索框
|
||||
setTimeout(() => {
|
||||
app.searchInput.focus();
|
||||
}, 500);
|
||||
|
||||
// 添加键盘快捷键支持
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// Ctrl/Cmd + K 聚焦搜索框
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
app.searchInput.focus();
|
||||
app.searchInput.select();
|
||||
}
|
||||
|
||||
// ESC 清空搜索框
|
||||
if (e.key === 'Escape') {
|
||||
app.searchInput.value = '';
|
||||
app.showWelcome();
|
||||
app.searchInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
console.log('百度百科词条查询应用已初始化');
|
||||
});
|
||||
3
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/接口集合.json
Normal file
3
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
12
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/返回接口.json
Normal file
12
InfoGenie-frontend/public/60sapi/实用功能/百度百科词条/返回接口.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"title": "西游记",
|
||||
"description": "明代吴承恩创作的章回体长篇神魔小说",
|
||||
"abstract": "《西游记》又名《西游释厄传》,是中国古代第一部浪漫主义章回体长篇神魔小说。最早的《西游记》版本是明代万历二十年金陵世德堂《新刻出像官板大字西游记》,未署作者姓名。鲁迅、董作宾等人根据《淮安府志》“吴承恩《西游记》”的记载予以最终论定“吴承恩原著”。该小说主要讲述了孙悟空出世,并寻菩提祖师学艺及大闹天宫后,与猪八戒、沙僧和白龙马一同护送唐僧西天取经,于路上历经险阻,降妖除魔,渡过了九九八十一难,成功...",
|
||||
"cover": "https://bkimg.cdn.bcebos.com/pic/b7fd5266d01609248d763e43db0735fae6cd3412?x-bce-process=image/format,f_auto",
|
||||
"has_other": true,
|
||||
"link": "http://baike.baidu.com/subview/2583/5315045.htm"
|
||||
}
|
||||
}
|
||||
243
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/background.css
Normal file
243
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/background.css
Normal file
@@ -0,0 +1,243 @@
|
||||
/* 背景样式文件 - 独立管理背景相关样式 */
|
||||
|
||||
/* 主体背景 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f0f8f0 25%, #e1f5e1 50%, #f5f9f5 75%, #e8f5e8 100%);
|
||||
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(144, 238, 144, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(152, 251, 152, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(173, 255, 173, 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(76, 175, 80, 0.3), transparent),
|
||||
radial-gradient(2px 2px at 40px 70px, rgba(129, 199, 132, 0.3), transparent),
|
||||
radial-gradient(1px 1px at 90px 40px, rgba(165, 214, 167, 0.3), transparent),
|
||||
radial-gradient(1px 1px at 130px 80px, rgba(200, 230, 201, 0.3), transparent),
|
||||
radial-gradient(2px 2px at 160px 30px, rgba(76, 175, 80, 0.2), 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 容器背景增强 */
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 表单区域背景 */
|
||||
.form-section {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(144, 238, 144, 0.3);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.form-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: linear-gradient(45deg, transparent, rgba(144, 238, 144, 0.05), transparent);
|
||||
animation: shimmer 3s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%) translateY(-100%) rotate(45deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(100%) translateY(100%) rotate(45deg);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%) translateY(-100%) rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 结果卡片背景 */
|
||||
.basic-info-card,
|
||||
.bmi-card,
|
||||
.weight-card,
|
||||
.metabolism-card,
|
||||
.body-fat-card,
|
||||
.measurements-card,
|
||||
.advice-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(15px);
|
||||
border: 1px solid rgba(144, 238, 144, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 卡片悬停背景效果 */
|
||||
.basic-info-card:hover,
|
||||
.bmi-card:hover,
|
||||
.weight-card:hover,
|
||||
.metabolism-card:hover,
|
||||
.body-fat-card:hover,
|
||||
.measurements-card:hover,
|
||||
.advice-card:hover {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-color: rgba(76, 175, 80, 0.4);
|
||||
}
|
||||
|
||||
/* 免责声明卡片背景 */
|
||||
.disclaimer-card {
|
||||
background: rgba(255, 243, 205, 0.95);
|
||||
backdrop-filter: blur(15px);
|
||||
border: 1px solid rgba(255, 234, 167, 0.5);
|
||||
}
|
||||
|
||||
/* 错误区域背景 */
|
||||
.error-content {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(220, 53, 69, 0.2);
|
||||
}
|
||||
|
||||
/* 输入框背景 */
|
||||
.form-input,
|
||||
.form-select {
|
||||
background: rgba(248, 255, 248, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-select:focus {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(15px);
|
||||
}
|
||||
|
||||
/* 信息项背景 */
|
||||
.info-item {
|
||||
background: rgba(248, 255, 248, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* BMI分类背景 */
|
||||
.bmi-category {
|
||||
background: rgba(232, 245, 232, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
/* 健康建议列表项背景 */
|
||||
.health-tips li {
|
||||
background: rgba(248, 255, 248, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* 按钮背景增强 */
|
||||
.submit-btn {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.submit-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;
|
||||
}
|
||||
|
||||
.submit-btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
/* 重置按钮背景 */
|
||||
.reset-btn {
|
||||
background: rgba(232, 245, 232, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.reset-btn:hover {
|
||||
background: rgba(212, 237, 218, 0.95);
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 767px) {
|
||||
body::after {
|
||||
background-size: 150px 75px;
|
||||
animation-duration: 15s;
|
||||
}
|
||||
|
||||
.form-section::before {
|
||||
animation-duration: 2s;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
body::after {
|
||||
background-size: 180px 90px;
|
||||
animation-duration: 18s;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body::after {
|
||||
background-size: 220px 110px;
|
||||
animation-duration: 25s;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
}
|
||||
115
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/index.html
Normal file
115
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/index.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<!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="styles.css">
|
||||
<link rel="stylesheet" href="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="form-section">
|
||||
<form id="healthForm" class="health-form">
|
||||
<div class="form-group">
|
||||
<label for="height" class="form-label">身高 (cm)</label>
|
||||
<input type="number" id="height" name="height" class="form-input" placeholder="请输入身高" min="100" max="250" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="weight" class="form-label">体重 (kg)</label>
|
||||
<input type="number" id="weight" name="weight" class="form-input" placeholder="请输入体重" min="30" max="200" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="age" class="form-label">年龄</label>
|
||||
<input type="number" id="age" name="age" class="form-input" placeholder="请输入年龄" min="1" max="120" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="gender" class="form-label">性别</label>
|
||||
<select id="gender" name="gender" class="form-select" required>
|
||||
<option value="">请选择性别</option>
|
||||
<option value="male">男性</option>
|
||||
<option value="female">女性</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="submit-btn" id="analyzeBtn">
|
||||
<span class="btn-text">开始分析</span>
|
||||
<div class="loading-spinner" style="display: none;"></div>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="result-section" id="resultSection" style="display: none;">
|
||||
<div class="result-header">
|
||||
<h2 class="result-title">分析结果</h2>
|
||||
<button class="reset-btn" id="resetBtn">重新分析</button>
|
||||
</div>
|
||||
|
||||
<div class="result-content">
|
||||
<div class="basic-info-card">
|
||||
<h3 class="card-title">基本信息</h3>
|
||||
<div class="info-grid" id="basicInfo"></div>
|
||||
</div>
|
||||
|
||||
<div class="bmi-card">
|
||||
<h3 class="card-title">BMI 分析</h3>
|
||||
<div class="bmi-content" id="bmiContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="weight-card">
|
||||
<h3 class="card-title">体重评估</h3>
|
||||
<div class="weight-content" id="weightContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="metabolism-card">
|
||||
<h3 class="card-title">代谢分析</h3>
|
||||
<div class="metabolism-content" id="metabolismContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="body-fat-card">
|
||||
<h3 class="card-title">体脂分析</h3>
|
||||
<div class="body-fat-content" id="bodyFatContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="measurements-card">
|
||||
<h3 class="card-title">理想三围</h3>
|
||||
<div class="measurements-content" id="measurementsContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="advice-card">
|
||||
<h3 class="card-title">健康建议</h3>
|
||||
<div class="advice-content" id="adviceContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="disclaimer-card">
|
||||
<p class="disclaimer" id="disclaimer"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error-section" id="errorSection" style="display: none;">
|
||||
<div class="error-content">
|
||||
<h3 class="error-title">分析失败</h3>
|
||||
<p class="error-message" id="errorMessage"></p>
|
||||
<button class="retry-btn" id="retryBtn">重试</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p class="footer-text">数据来源:60s API</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
515
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/script.js
Normal file
515
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/script.js
Normal file
@@ -0,0 +1,515 @@
|
||||
// 身体健康分析 JavaScript 功能
|
||||
|
||||
// DOM 元素获取
|
||||
const healthForm = document.getElementById('healthForm');
|
||||
const analyzeBtn = document.getElementById('analyzeBtn');
|
||||
const btnText = analyzeBtn.querySelector('.btn-text');
|
||||
const loadingSpinner = analyzeBtn.querySelector('.loading-spinner');
|
||||
const resultSection = document.getElementById('resultSection');
|
||||
const errorSection = document.getElementById('errorSection');
|
||||
const resetBtn = document.getElementById('resetBtn');
|
||||
const retryBtn = document.getElementById('retryBtn');
|
||||
|
||||
// API 配置
|
||||
const API_BASE_URL = 'https://60s.api.shumengya.top/v2/health';
|
||||
|
||||
// 表单验证规则
|
||||
const validationRules = {
|
||||
height: {
|
||||
min: 100,
|
||||
max: 250,
|
||||
message: '身高应在100-250cm之间'
|
||||
},
|
||||
weight: {
|
||||
min: 30,
|
||||
max: 200,
|
||||
message: '体重应在30-200kg之间'
|
||||
},
|
||||
age: {
|
||||
min: 1,
|
||||
max: 120,
|
||||
message: '年龄应在1-120岁之间'
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializeEventListeners();
|
||||
setupFormValidation();
|
||||
});
|
||||
|
||||
// 事件监听器初始化
|
||||
function initializeEventListeners() {
|
||||
healthForm.addEventListener('submit', handleFormSubmit);
|
||||
resetBtn.addEventListener('click', resetForm);
|
||||
retryBtn.addEventListener('click', retryAnalysis);
|
||||
|
||||
// 输入框实时验证
|
||||
const inputs = healthForm.querySelectorAll('input, select');
|
||||
inputs.forEach(input => {
|
||||
input.addEventListener('blur', validateField);
|
||||
input.addEventListener('input', clearFieldError);
|
||||
});
|
||||
}
|
||||
|
||||
// 表单验证设置
|
||||
function setupFormValidation() {
|
||||
const inputs = healthForm.querySelectorAll('input[type="number"]');
|
||||
inputs.forEach(input => {
|
||||
input.addEventListener('input', function() {
|
||||
// 移除非数字字符
|
||||
this.value = this.value.replace(/[^0-9.]/g, '');
|
||||
|
||||
// 防止多个小数点
|
||||
const parts = this.value.split('.');
|
||||
if (parts.length > 2) {
|
||||
this.value = parts[0] + '.' + parts.slice(1).join('');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 表单提交处理
|
||||
async function handleFormSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = getFormData();
|
||||
|
||||
try {
|
||||
setLoadingState(true);
|
||||
hideAllSections();
|
||||
|
||||
const result = await callHealthAPI(formData);
|
||||
displayResults(result);
|
||||
|
||||
} catch (error) {
|
||||
console.error('分析失败:', error);
|
||||
displayError(error.message || '分析失败,请稍后重试');
|
||||
} finally {
|
||||
setLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取表单数据
|
||||
function getFormData() {
|
||||
return {
|
||||
height: parseInt(document.getElementById('height').value),
|
||||
weight: parseInt(document.getElementById('weight').value),
|
||||
age: parseInt(document.getElementById('age').value),
|
||||
gender: document.getElementById('gender').value
|
||||
};
|
||||
}
|
||||
|
||||
// 表单验证
|
||||
function validateForm() {
|
||||
let isValid = true;
|
||||
const inputs = healthForm.querySelectorAll('input, select');
|
||||
|
||||
inputs.forEach(input => {
|
||||
if (!validateField({ target: input })) {
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// 单个字段验证
|
||||
function validateField(event) {
|
||||
const field = event.target;
|
||||
const value = field.value.trim();
|
||||
const fieldName = field.name;
|
||||
|
||||
// 清除之前的错误状态
|
||||
clearFieldError(event);
|
||||
|
||||
// 必填验证
|
||||
if (!value) {
|
||||
showFieldError(field, '此字段为必填项');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 数值范围验证
|
||||
if (validationRules[fieldName]) {
|
||||
const numValue = parseFloat(value);
|
||||
const rule = validationRules[fieldName];
|
||||
|
||||
if (numValue < rule.min || numValue > rule.max) {
|
||||
showFieldError(field, rule.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 显示字段错误
|
||||
function showFieldError(field, message) {
|
||||
field.classList.add('error');
|
||||
|
||||
// 移除已存在的错误消息
|
||||
const existingError = field.parentNode.querySelector('.error-message');
|
||||
if (existingError) {
|
||||
existingError.remove();
|
||||
}
|
||||
|
||||
// 添加错误消息
|
||||
const errorDiv = document.createElement('div');
|
||||
errorDiv.className = 'error-message';
|
||||
errorDiv.textContent = message;
|
||||
errorDiv.style.color = '#dc3545';
|
||||
errorDiv.style.fontSize = '0.875rem';
|
||||
errorDiv.style.marginTop = '5px';
|
||||
|
||||
field.parentNode.appendChild(errorDiv);
|
||||
}
|
||||
|
||||
// 清除字段错误
|
||||
function clearFieldError(event) {
|
||||
const field = event.target;
|
||||
field.classList.remove('error');
|
||||
|
||||
const errorMessage = field.parentNode.querySelector('.error-message');
|
||||
if (errorMessage) {
|
||||
errorMessage.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 调用健康分析API
|
||||
async function callHealthAPI(data) {
|
||||
const params = new URLSearchParams({
|
||||
height: data.height,
|
||||
weight: data.weight,
|
||||
age: data.age,
|
||||
gender: data.gender
|
||||
});
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}?${params}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code !== 200) {
|
||||
throw new Error(result.message || '分析失败');
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
|
||||
// 显示分析结果
|
||||
function displayResults(data) {
|
||||
// 基本信息
|
||||
displayBasicInfo(data.basic_info);
|
||||
|
||||
// BMI 分析
|
||||
displayBMIInfo(data.bmi);
|
||||
|
||||
// 体重评估
|
||||
displayWeightAssessment(data.weight_assessment);
|
||||
|
||||
// 代谢分析
|
||||
displayMetabolism(data.metabolism);
|
||||
|
||||
// 体脂分析
|
||||
displayBodyFat(data.body_fat);
|
||||
|
||||
// 理想三围
|
||||
displayMeasurements(data.ideal_measurements);
|
||||
|
||||
// 健康建议
|
||||
displayHealthAdvice(data.health_advice);
|
||||
|
||||
// 免责声明
|
||||
displayDisclaimer(data.disclaimer);
|
||||
|
||||
// 显示结果区域
|
||||
resultSection.style.display = 'block';
|
||||
resultSection.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// 显示基本信息
|
||||
function displayBasicInfo(basicInfo) {
|
||||
const container = document.getElementById('basicInfo');
|
||||
container.innerHTML = '';
|
||||
|
||||
const infoItems = [
|
||||
{ label: basicInfo.height_desc, value: basicInfo.height },
|
||||
{ label: basicInfo.weight_desc, value: basicInfo.weight },
|
||||
{ label: basicInfo.age_desc, value: basicInfo.age },
|
||||
{ label: basicInfo.gender_desc, value: basicInfo.gender }
|
||||
];
|
||||
|
||||
infoItems.forEach(item => {
|
||||
const itemDiv = createInfoItem(item.label, item.value);
|
||||
container.appendChild(itemDiv);
|
||||
});
|
||||
}
|
||||
|
||||
// 显示BMI信息
|
||||
function displayBMIInfo(bmiData) {
|
||||
const container = document.getElementById('bmiContent');
|
||||
container.innerHTML = `
|
||||
<div class="bmi-value">${bmiData.value}</div>
|
||||
<div class="bmi-category">${bmiData.category}</div>
|
||||
<div class="info-grid">
|
||||
${createInfoItem(bmiData.evaluation_desc, bmiData.evaluation).outerHTML}
|
||||
${createInfoItem(bmiData.risk_desc, bmiData.risk).outerHTML}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 显示体重评估
|
||||
function displayWeightAssessment(weightData) {
|
||||
const container = document.getElementById('weightContent');
|
||||
container.innerHTML = '';
|
||||
|
||||
const items = [
|
||||
{ label: weightData.ideal_weight_range_desc, value: weightData.ideal_weight_range },
|
||||
{ label: weightData.standard_weight_desc, value: weightData.standard_weight },
|
||||
{ label: weightData.status_desc, value: weightData.status },
|
||||
{ label: weightData.adjustment_desc, value: weightData.adjustment }
|
||||
];
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'info-grid';
|
||||
|
||||
items.forEach(item => {
|
||||
const itemDiv = createInfoItem(item.label, item.value);
|
||||
grid.appendChild(itemDiv);
|
||||
});
|
||||
|
||||
container.appendChild(grid);
|
||||
}
|
||||
|
||||
// 显示代谢分析
|
||||
function displayMetabolism(metabolismData) {
|
||||
const container = document.getElementById('metabolismContent');
|
||||
container.innerHTML = '';
|
||||
|
||||
const items = [
|
||||
{ label: metabolismData.bmr_desc, value: metabolismData.bmr },
|
||||
{ label: metabolismData.tdee_desc, value: metabolismData.tdee },
|
||||
{ label: metabolismData.recommended_calories_desc, value: metabolismData.recommended_calories },
|
||||
{ label: metabolismData.weight_loss_calories_desc, value: metabolismData.weight_loss_calories },
|
||||
{ label: metabolismData.weight_gain_calories_desc, value: metabolismData.weight_gain_calories }
|
||||
];
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'info-grid';
|
||||
|
||||
items.forEach(item => {
|
||||
const itemDiv = createInfoItem(item.label, item.value);
|
||||
grid.appendChild(itemDiv);
|
||||
});
|
||||
|
||||
container.appendChild(grid);
|
||||
}
|
||||
|
||||
// 显示体脂分析
|
||||
function displayBodyFat(bodyFatData) {
|
||||
const container = document.getElementById('bodyFatContent');
|
||||
container.innerHTML = '';
|
||||
|
||||
const items = [
|
||||
{ label: bodyFatData.percentage_desc, value: bodyFatData.percentage },
|
||||
{ label: bodyFatData.category_desc, value: bodyFatData.category },
|
||||
{ label: bodyFatData.fat_weight_desc, value: bodyFatData.fat_weight },
|
||||
{ label: bodyFatData.lean_weight_desc, value: bodyFatData.lean_weight }
|
||||
];
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'info-grid';
|
||||
|
||||
items.forEach(item => {
|
||||
const itemDiv = createInfoItem(item.label, item.value);
|
||||
grid.appendChild(itemDiv);
|
||||
});
|
||||
|
||||
container.appendChild(grid);
|
||||
}
|
||||
|
||||
// 显示理想三围
|
||||
function displayMeasurements(measurementsData) {
|
||||
const container = document.getElementById('measurementsContent');
|
||||
container.innerHTML = '';
|
||||
|
||||
const items = [
|
||||
{ label: measurementsData.chest_desc, value: measurementsData.chest },
|
||||
{ label: measurementsData.waist_desc, value: measurementsData.waist },
|
||||
{ label: measurementsData.hip_desc, value: measurementsData.hip }
|
||||
];
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'info-grid';
|
||||
|
||||
items.forEach(item => {
|
||||
const itemDiv = createInfoItem(item.label, item.value);
|
||||
grid.appendChild(itemDiv);
|
||||
});
|
||||
|
||||
// 添加说明
|
||||
const note = document.createElement('p');
|
||||
note.style.marginTop = '15px';
|
||||
note.style.fontSize = '0.9rem';
|
||||
note.style.color = '#4a7c59';
|
||||
note.style.textAlign = 'center';
|
||||
note.textContent = measurementsData.note;
|
||||
|
||||
container.appendChild(grid);
|
||||
container.appendChild(note);
|
||||
}
|
||||
|
||||
// 显示健康建议
|
||||
function displayHealthAdvice(adviceData) {
|
||||
const container = document.getElementById('adviceContent');
|
||||
container.innerHTML = '';
|
||||
|
||||
// 饮水量建议
|
||||
const waterDiv = createAdviceSection(adviceData.daily_water_intake_desc, adviceData.daily_water_intake);
|
||||
container.appendChild(waterDiv);
|
||||
|
||||
// 运动建议
|
||||
const exerciseDiv = createAdviceSection(adviceData.exercise_recommendation_desc, adviceData.exercise_recommendation);
|
||||
container.appendChild(exerciseDiv);
|
||||
|
||||
// 营养建议
|
||||
const nutritionDiv = createAdviceSection(adviceData.nutrition_advice_desc, adviceData.nutrition_advice);
|
||||
container.appendChild(nutritionDiv);
|
||||
|
||||
// 健康提示
|
||||
const tipsDiv = document.createElement('div');
|
||||
tipsDiv.innerHTML = `
|
||||
<h4 style="color: #2d5a3d; margin-bottom: 10px;">${adviceData.health_tips_desc}</h4>
|
||||
<ul class="health-tips"></ul>
|
||||
`;
|
||||
|
||||
const tipsList = tipsDiv.querySelector('.health-tips');
|
||||
adviceData.health_tips.forEach(tip => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = tip;
|
||||
tipsList.appendChild(li);
|
||||
});
|
||||
|
||||
container.appendChild(tipsDiv);
|
||||
}
|
||||
|
||||
// 创建建议区块
|
||||
function createAdviceSection(title, content) {
|
||||
const div = document.createElement('div');
|
||||
div.style.marginBottom = '20px';
|
||||
div.innerHTML = `
|
||||
<h4 style="color: #2d5a3d; margin-bottom: 8px;">${title}</h4>
|
||||
<p style="background: #f8fff8; padding: 12px; border-radius: 8px; border-left: 4px solid #4caf50; line-height: 1.6;">${content}</p>
|
||||
`;
|
||||
return div;
|
||||
}
|
||||
|
||||
// 显示免责声明
|
||||
function displayDisclaimer(disclaimer) {
|
||||
const container = document.getElementById('disclaimer');
|
||||
container.textContent = disclaimer;
|
||||
}
|
||||
|
||||
// 创建信息项
|
||||
function createInfoItem(label, value) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'info-item';
|
||||
div.innerHTML = `
|
||||
<div class="info-label">${label}</div>
|
||||
<div class="info-value">${value}</div>
|
||||
`;
|
||||
return div;
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
function displayError(message) {
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
errorMessage.textContent = message;
|
||||
errorSection.style.display = 'block';
|
||||
errorSection.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// 设置加载状态
|
||||
function setLoadingState(isLoading) {
|
||||
if (isLoading) {
|
||||
analyzeBtn.disabled = true;
|
||||
btnText.style.display = 'none';
|
||||
loadingSpinner.style.display = 'block';
|
||||
} else {
|
||||
analyzeBtn.disabled = false;
|
||||
btnText.style.display = 'block';
|
||||
loadingSpinner.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏所有结果区域
|
||||
function hideAllSections() {
|
||||
resultSection.style.display = 'none';
|
||||
errorSection.style.display = 'none';
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
function resetForm() {
|
||||
healthForm.reset();
|
||||
hideAllSections();
|
||||
|
||||
// 清除所有错误状态
|
||||
const errorInputs = healthForm.querySelectorAll('.error');
|
||||
errorInputs.forEach(input => {
|
||||
input.classList.remove('error');
|
||||
});
|
||||
|
||||
const errorMessages = healthForm.querySelectorAll('.error-message');
|
||||
errorMessages.forEach(msg => msg.remove());
|
||||
|
||||
// 滚动到表单顶部
|
||||
healthForm.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// 重试分析
|
||||
function retryAnalysis() {
|
||||
hideAllSections();
|
||||
healthForm.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// 工具函数:防抖
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// 添加CSS样式到错误输入框
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.form-input.error,
|
||||
.form-select.error {
|
||||
border-color: #dc3545 !important;
|
||||
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1) !important;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// 页面可见性变化处理(用户切换标签页时暂停动画等)
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (document.hidden) {
|
||||
// 页面隐藏时的处理
|
||||
document.body.style.animationPlayState = 'paused';
|
||||
} else {
|
||||
// 页面显示时的处理
|
||||
document.body.style.animationPlayState = 'running';
|
||||
}
|
||||
});
|
||||
697
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/styles.css
Normal file
697
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/styles.css
Normal file
@@ -0,0 +1,697 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
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: #2d5a3d;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 容器布局 */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: #1a4d2e;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 0 2px 4px rgba(26, 77, 46, 0.1);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: #4a7c59;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* 表单区域 */
|
||||
.form-section {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 8px 32px rgba(26, 77, 46, 0.1);
|
||||
border: 1px solid rgba(144, 238, 144, 0.3);
|
||||
}
|
||||
|
||||
.health-form {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 600;
|
||||
color: #2d5a3d;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-select {
|
||||
padding: 15px 20px;
|
||||
border: 2px solid #a8e6a3;
|
||||
border-radius: 12px;
|
||||
font-size: 1rem;
|
||||
background: #f8fff8;
|
||||
color: #2d5a3d;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-select:focus {
|
||||
outline: none;
|
||||
border-color: #4caf50;
|
||||
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.form-input::placeholder {
|
||||
color: #81c784;
|
||||
}
|
||||
|
||||
/* 提交按钮 */
|
||||
.submit-btn {
|
||||
background: linear-gradient(135deg, #4caf50, #66bb6a);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 18px 30px;
|
||||
border-radius: 12px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
background: linear-gradient(135deg, #45a049, #5cb85c);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.submit-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
background: #c8e6c9;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 2px solid white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 结果区域 */
|
||||
.result-section {
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 25px;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 2rem;
|
||||
color: #1a4d2e;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
background: #e8f5e8;
|
||||
color: #2d5a3d;
|
||||
border: 2px solid #a8e6a3;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.reset-btn:hover {
|
||||
background: #d4edda;
|
||||
border-color: #4caf50;
|
||||
}
|
||||
|
||||
/* 结果卡片 */
|
||||
.result-content {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.basic-info-card,
|
||||
.bmi-card,
|
||||
.weight-card,
|
||||
.metabolism-card,
|
||||
.body-fat-card,
|
||||
.measurements-card,
|
||||
.advice-card,
|
||||
.disclaimer-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 16px;
|
||||
padding: 25px;
|
||||
box-shadow: 0 6px 20px rgba(26, 77, 46, 0.08);
|
||||
border: 1px solid rgba(144, 238, 144, 0.2);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.basic-info-card:hover,
|
||||
.bmi-card:hover,
|
||||
.weight-card:hover,
|
||||
.metabolism-card:hover,
|
||||
.body-fat-card:hover,
|
||||
.measurements-card:hover,
|
||||
.advice-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(26, 77, 46, 0.12);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.4rem;
|
||||
color: #1a4d2e;
|
||||
font-weight: 700;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 2px solid #e8f5e8;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 信息网格 */
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
background: #f8fff8;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
border-left: 4px solid #4caf50;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 0.9rem;
|
||||
color: #4a7c59;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 1.2rem;
|
||||
color: #2d5a3d;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* BMI 特殊样式 */
|
||||
.bmi-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
color: #4caf50;
|
||||
text-align: center;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.bmi-category {
|
||||
text-align: center;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
color: #2d5a3d;
|
||||
background: #e8f5e8;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* 健康建议列表 */
|
||||
.health-tips {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.health-tips li {
|
||||
background: #f8fff8;
|
||||
margin: 10px 0;
|
||||
padding: 12px 15px;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #81c784;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.health-tips li::before {
|
||||
content: "✓";
|
||||
color: #4caf50;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* 免责声明 */
|
||||
.disclaimer {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
color: #856404;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 错误区域 */
|
||||
.error-section {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.error-content {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 16px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 6px 20px rgba(220, 53, 69, 0.1);
|
||||
border: 1px solid rgba(220, 53, 69, 0.2);
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
color: #dc3545;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #6c757d;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
/* 底部 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
margin-top: 30px;
|
||||
border-top: 1px solid rgba(144, 238, 144, 0.3);
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
color: #4a7c59;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
padding: 35px;
|
||||
}
|
||||
|
||||
.health-form {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.form-group:last-child {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.advice-card,
|
||||
.disclaimer-card {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 电脑端适配 (1024px+) */
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
padding: 40px;
|
||||
max-width: 1400px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 3.2rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex-direction: row;
|
||||
gap: 40px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
flex: 0 0 380px;
|
||||
position: sticky;
|
||||
top: 20px;
|
||||
max-height: calc(100vh - 40px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.result-section,
|
||||
.error-section {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 桌面端结果区域重新设计 - 使用更清晰的布局 */
|
||||
.result-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 25px;
|
||||
grid-auto-rows: min-content;
|
||||
}
|
||||
|
||||
/* 基本信息卡片 - 占满第一行 */
|
||||
.basic-info-card {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
/* 第二行:BMI、体重评估、代谢分析 */
|
||||
.bmi-card,
|
||||
.weight-card,
|
||||
.metabolism-card {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
/* 第三行:体脂分析和理想三围 */
|
||||
.body-fat-card {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.measurements-card {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
/* 第四行:健康建议 - 占满整行 */
|
||||
.advice-card {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
/* 第五行:免责声明 - 占满整行 */
|
||||
.disclaimer-card {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
/* 基本信息网格优化 */
|
||||
.basic-info-card .info-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* BMI卡片特殊布局 */
|
||||
.bmi-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 280px;
|
||||
}
|
||||
|
||||
.bmi-card .bmi-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bmi-value {
|
||||
font-size: 3rem;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
/* 体重评估卡片布局优化 */
|
||||
.weight-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 280px;
|
||||
}
|
||||
|
||||
.weight-card .weight-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.weight-card .info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* 代谢分析卡片布局优化 */
|
||||
.metabolism-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 280px;
|
||||
}
|
||||
|
||||
.metabolism-card .metabolism-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.metabolism-card .info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* 体脂分析卡片网格优化 */
|
||||
.body-fat-card .info-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* 理想三围卡片网格优化 */
|
||||
.measurements-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.measurements-card .measurements-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.measurements-card .info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* 健康建议卡片布局优化 */
|
||||
.advice-card {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.advice-card .advice-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.advice-card .health-tips {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 表单区域优化 */
|
||||
.health-form {
|
||||
display: grid;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
margin-top: 20px;
|
||||
padding: 20px 30px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最高优先级) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
padding: 20px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-select {
|
||||
padding: 12px 16px;
|
||||
font-size: 16px; /* 防止iOS缩放 */
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding: 16px 24px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 1.6rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
align-self: center;
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.basic-info-card,
|
||||
.bmi-card,
|
||||
.weight-card,
|
||||
.metabolism-card,
|
||||
.body-fat-card,
|
||||
.measurements-card,
|
||||
.advice-card,
|
||||
.disclaimer-card {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.bmi-value {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.bmi-category {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.health-tips li {
|
||||
padding: 10px 12px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.error-content {
|
||||
padding: 25px 20px;
|
||||
}
|
||||
}
|
||||
93
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/返回接口.json
Normal file
93
InfoGenie-frontend/public/60sapi/实用功能/身体健康分析/返回接口.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"basic_info": {
|
||||
"height": "176cm",
|
||||
"height_desc": "身高",
|
||||
"weight": "60kg",
|
||||
"weight_desc": "体重",
|
||||
"gender": "男性",
|
||||
"gender_desc": "性别",
|
||||
"age": "24岁",
|
||||
"age_desc": "年龄"
|
||||
},
|
||||
"bmi": {
|
||||
"value": 19.37,
|
||||
"value_desc": "BMI 值",
|
||||
"category": "正常体重",
|
||||
"category_desc": "BMI 分类",
|
||||
"evaluation": "体重正常,保持良好",
|
||||
"evaluation_desc": "BMI 评价",
|
||||
"risk": "健康风险较低",
|
||||
"risk_desc": "健康风险"
|
||||
},
|
||||
"weight_assessment": {
|
||||
"ideal_weight_range": "57.3-74.3kg",
|
||||
"ideal_weight_range_desc": "理想体重范围",
|
||||
"standard_weight": "71kg",
|
||||
"standard_weight_desc": "标准体重",
|
||||
"status": "体重正常",
|
||||
"status_desc": "体重状态",
|
||||
"adjustment": "保持当前体重",
|
||||
"adjustment_desc": "调整建议"
|
||||
},
|
||||
"metabolism": {
|
||||
"bmr": "1601 卡路里/天",
|
||||
"bmr_desc": "基础代谢率",
|
||||
"tdee": "2561 卡路里/天",
|
||||
"tdee_desc": "每日总消耗",
|
||||
"recommended_calories": "2561 卡路里/天",
|
||||
"recommended_calories_desc": "推荐卡路里摄入",
|
||||
"weight_loss_calories": "2061 卡路里/天",
|
||||
"weight_loss_calories_desc": "减重卡路里",
|
||||
"weight_gain_calories": "2861 卡路里/天",
|
||||
"weight_gain_calories_desc": "增重卡路里"
|
||||
},
|
||||
"body_surface_area": {
|
||||
"value": "1.74m²",
|
||||
"value_desc": "体表面积",
|
||||
"formula": "Du Bois 公式",
|
||||
"formula_desc": "计算公式"
|
||||
},
|
||||
"body_fat": {
|
||||
"percentage": "12.6%",
|
||||
"percentage_desc": "体脂率",
|
||||
"category": "正常",
|
||||
"category_desc": "体脂分类",
|
||||
"fat_weight": "7.6kg",
|
||||
"fat_weight_desc": "脂肪重量",
|
||||
"lean_weight": "52.4kg",
|
||||
"lean_weight_desc": "瘦体重"
|
||||
},
|
||||
"health_advice": {
|
||||
"daily_water_intake": "2000ml (约 8 杯水),运动时需额外补充 500-1000ml",
|
||||
"daily_water_intake_desc": "每日饮水量",
|
||||
"exercise_recommendation": "继续保持运动习惯,有氧运动和力量训练相结合效果更佳。年轻人可选择多样化的运动方式,建议每周运动 3-5 次",
|
||||
"exercise_recommendation_desc": "运动建议",
|
||||
"nutrition_advice": "保持均衡饮食,三大营养素合理搭配,定时定量进餐。年轻人新陈代谢较快,可适当增加能量摄入,男性可适当增加蛋白质摄入",
|
||||
"nutrition_advice_desc": "营养建议",
|
||||
"health_tips": [
|
||||
"保持充足睡眠,成年人建议每天 7-9 小时",
|
||||
"定期体检有助于早期发现健康问题",
|
||||
"保持良好心态,适当释放压力",
|
||||
"年轻人要注意作息规律,合理安排工作与休息",
|
||||
"长时间用眼后适当休息,保护视力",
|
||||
"培养兴趣爱好,保持积极的生活态度",
|
||||
"多饮水,成年人每天 1500-2000ml 为宜"
|
||||
],
|
||||
"health_tips_desc": "健康提示"
|
||||
},
|
||||
"ideal_measurements": {
|
||||
"chest": "84cm",
|
||||
"chest_desc": "胸围",
|
||||
"waist": "74cm",
|
||||
"waist_desc": "腰围",
|
||||
"hip": "83cm",
|
||||
"hip_desc": "臀围",
|
||||
"note": "男性理想三围参考标准",
|
||||
"note_desc": "说明"
|
||||
},
|
||||
"disclaimer": "结果基于通用公式和统计数据,仅供参考,不能替代专业医疗建议。如有健康问题,请咨询医生。"
|
||||
}
|
||||
}
|
||||
187
InfoGenie-frontend/public/60sapi/实用功能/配色方案/background.css
Normal file
187
InfoGenie-frontend/public/60sapi/实用功能/配色方案/background.css
Normal file
@@ -0,0 +1,187 @@
|
||||
/* 背景样式文件 - 单独管理所有背景相关样式 */
|
||||
|
||||
/* 主体背景 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #f0fff4 0%, #e6fffa 50%, #f0fff4 100%);
|
||||
background-attachment: fixed;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 背景装饰元素 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 80%, rgba(104, 211, 145, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(72, 187, 120, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(56, 161, 105, 0.05) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* 容器背景 */
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 输入区域背景 */
|
||||
.input-section {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(104, 211, 145, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.input-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, #48bb78, #68d391, #9ae6b4);
|
||||
}
|
||||
|
||||
/* 配色方案卡片背景 */
|
||||
.palette {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(15px);
|
||||
border: 1px solid rgba(104, 211, 145, 0.15);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.palette::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, #68d391, transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.palette:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 颜色信息背景 */
|
||||
.color-info {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(104, 211, 145, 0.2);
|
||||
}
|
||||
|
||||
/* 颜色项背景 */
|
||||
.color-item {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
border: 1px solid rgba(104, 211, 145, 0.15);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.color-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(45deg, transparent 48%, rgba(104, 211, 145, 0.05) 50%, transparent 52%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.color-item:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 颜色详情背景 */
|
||||
.color-detail {
|
||||
background: rgba(104, 211, 145, 0.08);
|
||||
border: 1px solid rgba(104, 211, 145, 0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.color-detail::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 按钮背景 */
|
||||
.generate-btn {
|
||||
background: linear-gradient(135deg, #48bb78 0%, #68d391 50%, #9ae6b4 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.generate-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 ease;
|
||||
}
|
||||
|
||||
.generate-btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
/* 加载动画背景 */
|
||||
.loading {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(104, 211, 145, 0.2);
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
background: linear-gradient(180deg, #f0fff4 0%, #e6fffa 100%);
|
||||
}
|
||||
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.input-section,
|
||||
.palette,
|
||||
.color-info {
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 50% 50%, rgba(104, 211, 145, 0.08) 0%, transparent 70%);
|
||||
}
|
||||
|
||||
.container {
|
||||
background: transparent;
|
||||
backdrop-filter: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
64
InfoGenie-frontend/public/60sapi/实用功能/配色方案/index.html
Normal file
64
InfoGenie-frontend/public/60sapi/实用功能/配色方案/index.html
Normal file
@@ -0,0 +1,64 @@
|
||||
<!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="styles.css">
|
||||
<link rel="stylesheet" href="background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>配色方案生成器</h1>
|
||||
<p class="subtitle">输入颜色值,获取专业的配色方案</p>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<section class="input-section">
|
||||
<div class="color-input-group">
|
||||
<label for="colorInput">颜色值</label>
|
||||
<div class="input-wrapper">
|
||||
<input type="text" id="colorInput" placeholder="#33AAFF" value="#DE4F99">
|
||||
<input type="color" id="colorPicker" value="#DE4F99">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="format-select">
|
||||
<label for="formatSelect">输出格式</label>
|
||||
<select id="formatSelect">
|
||||
<option value="json">JSON</option>
|
||||
<option value="text">文本</option>
|
||||
<option value="html">HTML</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button id="generateBtn" class="generate-btn">生成配色方案</button>
|
||||
</section>
|
||||
|
||||
<section class="result-section" id="resultSection">
|
||||
<div class="loading" id="loading" style="display: none;">
|
||||
<div class="spinner"></div>
|
||||
<p>正在生成配色方案...</p>
|
||||
</div>
|
||||
|
||||
<div class="color-info" id="colorInfo" style="display: none;">
|
||||
<h3>输入颜色信息</h3>
|
||||
<div class="color-preview" id="colorPreview"></div>
|
||||
<div class="color-details" id="colorDetails"></div>
|
||||
</div>
|
||||
|
||||
<div class="palettes-container" id="palettesContainer">
|
||||
<!-- 配色方案将在这里动态生成 -->
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>基于色彩理论的专业配色方案生成</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
315
InfoGenie-frontend/public/60sapi/实用功能/配色方案/script.js
Normal file
315
InfoGenie-frontend/public/60sapi/实用功能/配色方案/script.js
Normal file
@@ -0,0 +1,315 @@
|
||||
// 配色方案生成器 JavaScript
|
||||
class ColorPaletteGenerator {
|
||||
constructor() {
|
||||
this.apiUrl = 'https://60s.api.shumengya.top/v2/color/palette';
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.loadDefaultPalette();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
const colorInput = document.getElementById('colorInput');
|
||||
const colorPicker = document.getElementById('colorPicker');
|
||||
const generateBtn = document.getElementById('generateBtn');
|
||||
const formatSelect = document.getElementById('formatSelect');
|
||||
|
||||
// 颜色输入框事件
|
||||
colorInput.addEventListener('input', (e) => {
|
||||
const color = e.target.value;
|
||||
if (this.isValidColor(color)) {
|
||||
colorPicker.value = color;
|
||||
}
|
||||
});
|
||||
|
||||
// 颜色选择器事件
|
||||
colorPicker.addEventListener('change', (e) => {
|
||||
colorInput.value = e.target.value;
|
||||
});
|
||||
|
||||
// 生成按钮事件
|
||||
generateBtn.addEventListener('click', () => {
|
||||
this.generatePalette();
|
||||
});
|
||||
|
||||
// 回车键生成
|
||||
colorInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.generatePalette();
|
||||
}
|
||||
});
|
||||
|
||||
// 格式选择事件
|
||||
formatSelect.addEventListener('change', () => {
|
||||
const currentColor = colorInput.value;
|
||||
if (currentColor && this.isValidColor(currentColor)) {
|
||||
this.generatePalette();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 验证颜色格式
|
||||
isValidColor(color) {
|
||||
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
||||
return hexRegex.test(color);
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
showLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
const colorInfo = document.getElementById('colorInfo');
|
||||
const palettesContainer = document.getElementById('palettesContainer');
|
||||
|
||||
loading.style.display = 'block';
|
||||
colorInfo.style.display = 'none';
|
||||
palettesContainer.innerHTML = '';
|
||||
}
|
||||
|
||||
// 隐藏加载状态
|
||||
hideLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
loading.style.display = 'none';
|
||||
}
|
||||
|
||||
// 生成配色方案
|
||||
async generatePalette() {
|
||||
const colorInput = document.getElementById('colorInput');
|
||||
const formatSelect = document.getElementById('formatSelect');
|
||||
const color = colorInput.value.trim();
|
||||
const format = formatSelect.value;
|
||||
|
||||
if (!color) {
|
||||
this.showError('请输入颜色值');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isValidColor(color)) {
|
||||
this.showError('请输入有效的十六进制颜色值(如:#33AAFF)');
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading();
|
||||
|
||||
try {
|
||||
const url = new URL(this.apiUrl);
|
||||
url.searchParams.append('color', color);
|
||||
url.searchParams.append('encoding', format);
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200) {
|
||||
this.displayResults(data.data);
|
||||
} else {
|
||||
throw new Error(data.message || '获取配色方案失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
this.showError('获取配色方案失败,请检查网络连接或稍后重试');
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
showError(message) {
|
||||
const palettesContainer = document.getElementById('palettesContainer');
|
||||
palettesContainer.innerHTML = `
|
||||
<div class="error-message" style="
|
||||
background: rgba(254, 226, 226, 0.9);
|
||||
border: 1px solid #feb2b2;
|
||||
color: #c53030;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
">
|
||||
<p>❌ ${message}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 显示结果
|
||||
displayResults(data) {
|
||||
this.displayColorInfo(data.input);
|
||||
this.displayPalettes(data.palettes);
|
||||
}
|
||||
|
||||
// 显示颜色信息
|
||||
displayColorInfo(inputData) {
|
||||
const colorInfo = document.getElementById('colorInfo');
|
||||
const colorPreview = document.getElementById('colorPreview');
|
||||
const colorDetails = document.getElementById('colorDetails');
|
||||
|
||||
colorPreview.style.backgroundColor = inputData.hex;
|
||||
|
||||
colorDetails.innerHTML = `
|
||||
<div class="color-detail">
|
||||
<strong>HEX</strong>
|
||||
<span>${inputData.hex}</span>
|
||||
</div>
|
||||
<div class="color-detail">
|
||||
<strong>RGB</strong>
|
||||
<span>rgb(${inputData.rgb.r}, ${inputData.rgb.g}, ${inputData.rgb.b})</span>
|
||||
</div>
|
||||
<div class="color-detail">
|
||||
<strong>HSL</strong>
|
||||
<span>hsl(${inputData.hsl.h}°, ${inputData.hsl.s}%, ${inputData.hsl.l}%)</span>
|
||||
</div>
|
||||
<div class="color-detail">
|
||||
<strong>色系</strong>
|
||||
<span>${inputData.name}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
colorInfo.style.display = 'block';
|
||||
}
|
||||
|
||||
// 显示配色方案
|
||||
displayPalettes(palettes) {
|
||||
const palettesContainer = document.getElementById('palettesContainer');
|
||||
|
||||
palettesContainer.innerHTML = palettes.map(palette => `
|
||||
<div class="palette">
|
||||
<div class="palette-header">
|
||||
<h3 class="palette-name">${palette.name}</h3>
|
||||
<p class="palette-description">${palette.description}</p>
|
||||
</div>
|
||||
<div class="colors-grid">
|
||||
${palette.colors.map(color => `
|
||||
<div class="color-item">
|
||||
<div class="color-swatch"
|
||||
style="background-color: ${color.hex}"
|
||||
onclick="copyToClipboard('${color.hex}')"
|
||||
title="点击复制 ${color.hex}">
|
||||
</div>
|
||||
<div class="color-name">${color.name}</div>
|
||||
<div class="color-hex">${color.hex}</div>
|
||||
<div class="color-role">${color.role} • ${color.theory}</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 加载默认配色方案
|
||||
async loadDefaultPalette() {
|
||||
const colorInput = document.getElementById('colorInput');
|
||||
const defaultColor = colorInput.value;
|
||||
|
||||
if (defaultColor && this.isValidColor(defaultColor)) {
|
||||
await this.generatePalette();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 复制到剪贴板功能
|
||||
function copyToClipboard(text) {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showToast(`已复制 ${text} 到剪贴板`);
|
||||
}).catch(err => {
|
||||
console.error('复制失败:', err);
|
||||
fallbackCopyTextToClipboard(text);
|
||||
});
|
||||
} else {
|
||||
fallbackCopyTextToClipboard(text);
|
||||
}
|
||||
}
|
||||
|
||||
// 备用复制方法
|
||||
function fallbackCopyTextToClipboard(text) {
|
||||
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();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
showToast(`已复制 ${text} 到剪贴板`);
|
||||
} catch (err) {
|
||||
console.error('复制失败:', err);
|
||||
showToast('复制失败,请手动复制');
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
// 显示提示信息
|
||||
function showToast(message) {
|
||||
// 移除已存在的toast
|
||||
const existingToast = document.querySelector('.toast');
|
||||
if (existingToast) {
|
||||
existingToast.remove();
|
||||
}
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast';
|
||||
toast.textContent = message;
|
||||
toast.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: rgba(45, 90, 39, 0.95);
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
z-index: 10000;
|
||||
box-shadow: 0 4px 12px rgba(45, 90, 39, 0.3);
|
||||
transform: translateX(100%);
|
||||
transition: transform 0.3s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
`;
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
// 动画显示
|
||||
setTimeout(() => {
|
||||
toast.style.transform = 'translateX(0)';
|
||||
}, 100);
|
||||
|
||||
// 3秒后隐藏
|
||||
setTimeout(() => {
|
||||
toast.style.transform = 'translateX(100%)';
|
||||
setTimeout(() => {
|
||||
if (toast.parentNode) {
|
||||
toast.parentNode.removeChild(toast);
|
||||
}
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new ColorPaletteGenerator();
|
||||
});
|
||||
|
||||
// 添加移动端优化
|
||||
if ('ontouchstart' in window) {
|
||||
// 移动端触摸优化
|
||||
document.addEventListener('touchstart', function() {}, {passive: true});
|
||||
|
||||
// 防止双击缩放
|
||||
let lastTouchEnd = 0;
|
||||
document.addEventListener('touchend', function (event) {
|
||||
const now = (new Date()).getTime();
|
||||
if (now - lastTouchEnd <= 300) {
|
||||
event.preventDefault();
|
||||
}
|
||||
lastTouchEnd = now;
|
||||
}, false);
|
||||
}
|
||||
422
InfoGenie-frontend/public/60sapi/实用功能/配色方案/styles.css
Normal file
422
InfoGenie-frontend/public/60sapi/实用功能/配色方案/styles.css
Normal file
@@ -0,0 +1,422 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
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: #2d3748;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.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: #2d5a27;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: #68d391;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* 输入区域 */
|
||||
.input-section {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 30px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 20px rgba(45, 90, 39, 0.1);
|
||||
border: 1px solid rgba(104, 211, 145, 0.2);
|
||||
}
|
||||
|
||||
.color-input-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.color-input-group label,
|
||||
.format-select label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #2d5a27;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#colorInput {
|
||||
flex: 1;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#colorInput:focus {
|
||||
outline: none;
|
||||
border-color: #68d391;
|
||||
box-shadow: 0 0 0 3px rgba(104, 211, 145, 0.1);
|
||||
}
|
||||
|
||||
#colorPicker {
|
||||
width: 50px;
|
||||
height: 44px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.format-select {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
#formatSelect {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#formatSelect:focus {
|
||||
outline: none;
|
||||
border-color: #68d391;
|
||||
box-shadow: 0 0 0 3px rgba(104, 211, 145, 0.1);
|
||||
}
|
||||
|
||||
.generate-btn {
|
||||
width: 100%;
|
||||
padding: 14px 24px;
|
||||
background: linear-gradient(135deg, #48bb78, #68d391);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.3);
|
||||
}
|
||||
|
||||
.generate-btn:hover {
|
||||
background: linear-gradient(135deg, #38a169, #48bb78);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(72, 187, 120, 0.4);
|
||||
}
|
||||
|
||||
.generate-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 结果区域 */
|
||||
.result-section {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #e2e8f0;
|
||||
border-top: 4px solid #68d391;
|
||||
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: #68d391;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 颜色信息 */
|
||||
.color-info {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 25px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 2px 10px rgba(45, 90, 39, 0.1);
|
||||
border: 1px solid rgba(104, 211, 145, 0.2);
|
||||
}
|
||||
|
||||
.color-info h3 {
|
||||
color: #2d5a27;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
border: 2px solid rgba(104, 211, 145, 0.3);
|
||||
}
|
||||
|
||||
.color-details {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.color-detail {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
background: rgba(104, 211, 145, 0.1);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.color-detail strong {
|
||||
display: block;
|
||||
color: #2d5a27;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.color-detail span {
|
||||
color: #4a5568;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* 配色方案容器 */
|
||||
.palettes-container {
|
||||
display: grid;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.palette {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
box-shadow: 0 4px 15px rgba(45, 90, 39, 0.1);
|
||||
border: 1px solid rgba(104, 211, 145, 0.2);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.palette:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(45, 90, 39, 0.15);
|
||||
}
|
||||
|
||||
.palette-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.palette-name {
|
||||
font-size: 1.4rem;
|
||||
color: #2d5a27;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.palette-description {
|
||||
color: #68d391;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.colors-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.color-item {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
border: 1px solid rgba(104, 211, 145, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.color-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(45, 90, 39, 0.1);
|
||||
}
|
||||
|
||||
.color-swatch {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.color-swatch::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(45deg, transparent 45%, rgba(255,255,255,0.1) 50%, transparent 55%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.color-swatch:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.color-name {
|
||||
font-weight: 600;
|
||||
color: #2d5a27;
|
||||
margin-bottom: 5px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.color-hex {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #4a5568;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.color-role {
|
||||
font-size: 0.8rem;
|
||||
color: #68d391;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 底部 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 30px 0;
|
||||
margin-top: 40px;
|
||||
color: #68d391;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 平板端适配 */
|
||||
@media (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.colors-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 25px;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
#colorPicker {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.colors-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.color-details {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.palette {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.palette-name {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.header h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.palette {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.color-details {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
273
InfoGenie-frontend/public/60sapi/实用功能/配色方案/返回接口.json
Normal file
273
InfoGenie-frontend/public/60sapi/实用功能/配色方案/返回接口.json
Normal file
@@ -0,0 +1,273 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"input": {
|
||||
"hex": "#DE4F99",
|
||||
"rgb": {
|
||||
"r": 222,
|
||||
"g": 79,
|
||||
"b": 153
|
||||
},
|
||||
"hsl": {
|
||||
"h": 329,
|
||||
"s": 68,
|
||||
"l": 59
|
||||
},
|
||||
"name": "红色系"
|
||||
},
|
||||
"palettes": [
|
||||
{
|
||||
"name": "单色配色",
|
||||
"description": "基于同一色相,通过调整明度和饱和度创建的和谐配色方案,适合营造统一、专业的视觉效果",
|
||||
"colors": [
|
||||
{
|
||||
"hex": "#DE4F99",
|
||||
"name": "主色",
|
||||
"role": "primary",
|
||||
"theory": "基础色相"
|
||||
},
|
||||
{
|
||||
"hex": "#7C184C",
|
||||
"name": "深色变体",
|
||||
"role": "dark",
|
||||
"theory": "降低明度"
|
||||
},
|
||||
{
|
||||
"hex": "#EEA5CB",
|
||||
"name": "浅色变体",
|
||||
"role": "light",
|
||||
"theory": "提高明度"
|
||||
},
|
||||
{
|
||||
"hex": "#C96498",
|
||||
"name": "柔和变体",
|
||||
"role": "muted",
|
||||
"theory": "降低饱和度"
|
||||
},
|
||||
{
|
||||
"hex": "#ED4099",
|
||||
"name": "鲜艳变体",
|
||||
"role": "vibrant",
|
||||
"theory": "提高饱和度"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "互补配色",
|
||||
"description": "使用色轮上相对的颜色,创造强烈对比和视觉冲击力,适用于需要突出重点的设计",
|
||||
"colors": [
|
||||
{
|
||||
"hex": "#DE4F99",
|
||||
"name": "主色",
|
||||
"role": "primary",
|
||||
"theory": "基础色相"
|
||||
},
|
||||
{
|
||||
"hex": "#4FDE94",
|
||||
"name": "互补色",
|
||||
"role": "complementary",
|
||||
"theory": "色轮对面 +180°"
|
||||
},
|
||||
{
|
||||
"hex": "#F2BAD7",
|
||||
"name": "主色浅调",
|
||||
"role": "primary-light",
|
||||
"theory": "主色提高明度"
|
||||
},
|
||||
{
|
||||
"hex": "#BAF2D5",
|
||||
"name": "互补色浅调",
|
||||
"role": "complementary-light",
|
||||
"theory": "互补色提高明度"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "邻近配色",
|
||||
"description": "使用色轮上相邻的颜色,创造自然和谐的渐变效果,常见于自然景观中",
|
||||
"colors": [
|
||||
{
|
||||
"hex": "#DB4FDE",
|
||||
"name": "邻近色1",
|
||||
"role": "analogous-1",
|
||||
"theory": "色相 -30°"
|
||||
},
|
||||
{
|
||||
"hex": "#DE4F99",
|
||||
"name": "主色",
|
||||
"role": "primary",
|
||||
"theory": "基础色相"
|
||||
},
|
||||
{
|
||||
"hex": "#DE4F52",
|
||||
"name": "邻近色2",
|
||||
"role": "analogous-2",
|
||||
"theory": "色相 +30°"
|
||||
},
|
||||
{
|
||||
"hex": "#DE944F",
|
||||
"name": "邻近色3",
|
||||
"role": "analogous-3",
|
||||
"theory": "色相 +60°"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "三角配色",
|
||||
"description": "在色轮上形成等边三角形的三种颜色,提供丰富对比的同时保持和谐平衡",
|
||||
"colors": [
|
||||
{
|
||||
"hex": "#DE4F99",
|
||||
"name": "主色",
|
||||
"role": "primary",
|
||||
"theory": "基础色相"
|
||||
},
|
||||
{
|
||||
"hex": "#99DE4F",
|
||||
"name": "三角色1",
|
||||
"role": "triadic-1",
|
||||
"theory": "色相 +120°"
|
||||
},
|
||||
{
|
||||
"hex": "#4F99DE",
|
||||
"name": "三角色2",
|
||||
"role": "triadic-2",
|
||||
"theory": "色相 +240°"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "分裂互补配色",
|
||||
"description": "使用互补色两侧的颜色,比纯互补配色更柔和,同时保持强烈的视觉对比",
|
||||
"colors": [
|
||||
{
|
||||
"hex": "#DE4F99",
|
||||
"name": "主色",
|
||||
"role": "primary",
|
||||
"theory": "基础色相"
|
||||
},
|
||||
{
|
||||
"hex": "#52DE4F",
|
||||
"name": "分裂互补色1",
|
||||
"role": "split-comp-1",
|
||||
"theory": "互补色 -30°"
|
||||
},
|
||||
{
|
||||
"hex": "#4FDEDB",
|
||||
"name": "分裂互补色2",
|
||||
"role": "split-comp-2",
|
||||
"theory": "互补色 +30°"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "四边形配色",
|
||||
"description": "在色轮上形成正方形的四种颜色,提供最丰富的颜色变化,适合复杂的设计项目",
|
||||
"colors": [
|
||||
{
|
||||
"hex": "#DE4F99",
|
||||
"name": "主色",
|
||||
"role": "primary",
|
||||
"theory": "基础色相"
|
||||
},
|
||||
{
|
||||
"hex": "#DEDB4F",
|
||||
"name": "四边形色1",
|
||||
"role": "square-1",
|
||||
"theory": "色相 +90°"
|
||||
},
|
||||
{
|
||||
"hex": "#4FDE94",
|
||||
"name": "四边形色2",
|
||||
"role": "square-2",
|
||||
"theory": "色相 +180°"
|
||||
},
|
||||
{
|
||||
"hex": "#4F52DE",
|
||||
"name": "四边形色3",
|
||||
"role": "square-3",
|
||||
"theory": "色相 +270°"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Web 设计配色",
|
||||
"description": "专为 Web 界面设计优化的配色方案,考虑了可访问性和用户体验",
|
||||
"colors": [
|
||||
{
|
||||
"hex": "#DE4F99",
|
||||
"name": "品牌主色",
|
||||
"role": "brand-primary",
|
||||
"theory": "品牌识别色"
|
||||
},
|
||||
{
|
||||
"hex": "#982F65",
|
||||
"name": "按钮悬停",
|
||||
"role": "hover-state",
|
||||
"theory": "主色加深变体"
|
||||
},
|
||||
{
|
||||
"hex": "#F6E9F0",
|
||||
"name": "背景浅色",
|
||||
"role": "background",
|
||||
"theory": "高明度低饱和度"
|
||||
},
|
||||
{
|
||||
"hex": "#1BDE7A",
|
||||
"name": "强调色",
|
||||
"role": "accent",
|
||||
"theory": "互补色系强调"
|
||||
},
|
||||
{
|
||||
"hex": "#6B7280",
|
||||
"name": "文本辅助",
|
||||
"role": "text-secondary",
|
||||
"theory": "中性灰色文本"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "暖色调配色",
|
||||
"description": "基于暖色系的配色方案,营造温暖、活力和友好的氛围,适合餐饮、儿童产品等",
|
||||
"colors": [
|
||||
{
|
||||
"hex": "#DE4F99",
|
||||
"name": "主暖色",
|
||||
"role": "warm-primary",
|
||||
"theory": "暖色系基调"
|
||||
},
|
||||
{
|
||||
"hex": "#DE4FC8",
|
||||
"name": "暖色变体1",
|
||||
"role": "warm-variant-1",
|
||||
"theory": "暖色范围内调整"
|
||||
},
|
||||
{
|
||||
"hex": "#DE4F5E",
|
||||
"name": "暖色变体2",
|
||||
"role": "warm-variant-2",
|
||||
"theory": "暖色范围内调整"
|
||||
},
|
||||
{
|
||||
"hex": "#EEA5CB",
|
||||
"name": "暖色浅调",
|
||||
"role": "warm-tint",
|
||||
"theory": "提高明度的暖色"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"color_theory": "基于色彩理论生成的专业配色方案",
|
||||
"total_palettes": 8,
|
||||
"applications": [
|
||||
"Web 设计",
|
||||
"UI/UX",
|
||||
"品牌设计",
|
||||
"室内设计",
|
||||
"服装搭配"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
232
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/css/background.css
Normal file
232
InfoGenie-frontend/public/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
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/css/style.css
Normal file
1159
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/css/style.css
Normal file
File diff suppressed because it is too large
Load Diff
227
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/index.html
Normal file
227
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/index.html
Normal file
@@ -0,0 +1,227 @@
|
||||
<!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-alt" 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-updated-time" class="info-value">-</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>响应时间</label>
|
||||
<div id="response-time" 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>
|
||||
617
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/js/script.js
Normal file
617
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/js/script.js
Normal file
@@ -0,0 +1,617 @@
|
||||
// 链接OG信息查询 - JavaScript功能代码
|
||||
// 神秘高级风格的交互体验
|
||||
|
||||
class OGAnalyzer {
|
||||
constructor() {
|
||||
this.apiUrl = 'https://60s.api.shumengya.top/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.startTime = Date.now(); // 记录开始时间
|
||||
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');
|
||||
|
||||
// 检查是否有有效数据 - 放宽检查条件,只要有任何非空字段就显示
|
||||
const hasValidData = Object.values(data).some(value => {
|
||||
if (value === null || value === undefined) return false;
|
||||
if (typeof value === 'string') return value.trim() !== '';
|
||||
return true; // 其他类型的值都认为是有效的
|
||||
});
|
||||
|
||||
if (!hasValidData) {
|
||||
this.showError('该链接暂无可获取的OG信息,请检查链接是否正确或稍后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 基础信息 - 只显示有数据的字段
|
||||
this.updateElementWithVisibility('og-title', data.title, '标题');
|
||||
this.updateElementWithVisibility('og-description', data.description, '描述');
|
||||
this.updateElement('og-url', data.url || this.currentUrl); // URL始终显示
|
||||
this.updateElementWithVisibility('og-site-name', data.site_name, '网站名称');
|
||||
this.updateElement('og-type', data.type || 'website'); // 类型始终显示
|
||||
|
||||
// 媒体信息
|
||||
this.updateImageElementWithVisibility('og-image', data.image);
|
||||
this.updateElementWithVisibility('og-image-alt', data.image_alt, '图片描述');
|
||||
|
||||
// 技术信息
|
||||
this.updateElementWithVisibility('og-locale', data.locale, '语言');
|
||||
this.updateElementWithVisibility('og-updated-time', this.formatDate(data.updated_time), '更新时间');
|
||||
this.updateElement('response-time', `${Date.now() - this.startTime}ms`); // 响应时间始终显示
|
||||
|
||||
// 显示结果
|
||||
resultsElement.style.display = 'block';
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
updateElementWithVisibility(id, content, fieldName) {
|
||||
const element = document.getElementById(id);
|
||||
if (!element) return;
|
||||
|
||||
const parentItem = element.closest('.info-item');
|
||||
if (!parentItem) return;
|
||||
|
||||
if (content && content.trim() !== '') {
|
||||
element.textContent = content;
|
||||
parentItem.style.display = 'block';
|
||||
} else {
|
||||
parentItem.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateImageElementWithVisibility(id, imageSrc) {
|
||||
const element = document.getElementById(id);
|
||||
const mediaSection = document.querySelector('.media-info');
|
||||
const mediaPreview = document.getElementById('media-preview');
|
||||
|
||||
if (imageSrc && imageSrc.trim() !== '') {
|
||||
element.textContent = imageSrc;
|
||||
if (mediaSection) mediaSection.style.display = 'block';
|
||||
|
||||
if (mediaPreview) {
|
||||
mediaPreview.innerHTML = `
|
||||
<img src="${imageSrc}" alt="OG Image" class="og-preview-image"
|
||||
onerror="this.style.display='none'; this.nextElementSibling.style.display='block';">
|
||||
<div class="no-media" style="display: none;">
|
||||
<i class="fas fa-image-slash"></i>
|
||||
<span>图片加载失败</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
if (mediaSection) mediaSection.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
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.style.display = 'none';
|
||||
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.style.display = 'none';
|
||||
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 infoItems = document.querySelectorAll('.info-item');
|
||||
infoItems.forEach(item => item.style.display = 'block');
|
||||
|
||||
const mediaSection = document.querySelector('.media-info');
|
||||
if (mediaSection) mediaSection.style.display = 'block';
|
||||
|
||||
// 重置动画状态
|
||||
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
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/接口集合.json
Normal file
3
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/接口集合.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"https://60s.api.shumengya.top"
|
||||
]
|
||||
66
InfoGenie-frontend/public/60sapi/实用功能/链接OG信息/返回接口.json
Normal file
66
InfoGenie-frontend/public/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
|
||||
}
|
||||
252
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/css/background.css
Normal file
252
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/css/background.css
Normal file
@@ -0,0 +1,252 @@
|
||||
/* 背景样式文件 */
|
||||
|
||||
/* 主背景渐变 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f0f9f0 25%, #f8fdf8 50%, #e8f5e8 75%, #f0f9f0 100%);
|
||||
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(76, 175, 80, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(45, 90, 61, 0.08) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(76, 175, 80, 0.05) 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(76, 175, 80, 0.3), transparent),
|
||||
radial-gradient(2px 2px at 40px 70px, rgba(45, 90, 61, 0.2), transparent),
|
||||
radial-gradient(1px 1px at 90px 40px, rgba(76, 175, 80, 0.4), transparent),
|
||||
radial-gradient(1px 1px at 130px 80px, rgba(45, 90, 61, 0.3), transparent),
|
||||
radial-gradient(2px 2px at 160px 30px, rgba(76, 175, 80, 0.2), transparent);
|
||||
background-repeat: repeat;
|
||||
background-size: 200px 100px;
|
||||
animation: floatDots 20s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* 圆点浮动动画 */
|
||||
@keyframes floatDots {
|
||||
0% {
|
||||
transform: translateY(0px) translateX(0px);
|
||||
}
|
||||
33% {
|
||||
transform: translateY(-10px) translateX(5px);
|
||||
}
|
||||
66% {
|
||||
transform: translateY(5px) translateX(-5px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px) translateX(0px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 网格背景(可选,默认隐藏) */
|
||||
.grid-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
linear-gradient(rgba(76, 175, 80, 0.03) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(76, 175, 80, 0.03) 1px, transparent 1px);
|
||||
background-size: 50px 50px;
|
||||
pointer-events: none;
|
||||
z-index: -3;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.grid-background.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 响应式背景调整 */
|
||||
@media (max-width: 768px) {
|
||||
body::after {
|
||||
background-size: 150px 75px;
|
||||
animation-duration: 25s;
|
||||
}
|
||||
|
||||
body::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 30% 70%, rgba(76, 175, 80, 0.08) 0%, transparent 50%),
|
||||
radial-gradient(circle at 70% 30%, rgba(45, 90, 61, 0.06) 0%, transparent 50%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
animation-duration: 20s;
|
||||
}
|
||||
|
||||
body::after {
|
||||
background-size: 100px 50px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
/* 高对比度模式下的背景调整 */
|
||||
@media (prefers-contrast: high) {
|
||||
body {
|
||||
background: #f8fdf8;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
body::before,
|
||||
body::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画模式下的背景调整 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
body {
|
||||
animation: none;
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f0f9f0 50%, #f8fdf8 100%);
|
||||
}
|
||||
|
||||
body::after {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0%, 100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes floatDots {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) translateX(0px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 深色模式支持 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: linear-gradient(135deg, #1a2e1a 0%, #2d4a2d 25%, #1f3a1f 50%, #1a2e1a 75%, #2d4a2d 100%);
|
||||
}
|
||||
|
||||
body::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 80%, rgba(76, 175, 80, 0.15) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(144, 238, 144, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(76, 175, 80, 0.08) 0%, transparent 50%);
|
||||
}
|
||||
|
||||
body::after {
|
||||
background-image:
|
||||
radial-gradient(2px 2px at 20px 30px, rgba(144, 238, 144, 0.4), transparent),
|
||||
radial-gradient(2px 2px at 40px 70px, rgba(76, 175, 80, 0.3), transparent),
|
||||
radial-gradient(1px 1px at 90px 40px, rgba(144, 238, 144, 0.5), transparent),
|
||||
radial-gradient(1px 1px at 130px 80px, rgba(76, 175, 80, 0.4), transparent),
|
||||
radial-gradient(2px 2px at 160px 30px, rgba(144, 238, 144, 0.3), transparent);
|
||||
}
|
||||
}
|
||||
|
||||
/* 打印样式 */
|
||||
@media print {
|
||||
body {
|
||||
background: white !important;
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
body::before,
|
||||
body::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 特殊效果:鼠标悬停时的背景变化 */
|
||||
@media (hover: hover) {
|
||||
.container:hover {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.container:hover::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: -20px;
|
||||
right: -20px;
|
||||
bottom: -20px;
|
||||
background: radial-gradient(circle at var(--mouse-x, 50%) var(--mouse-y, 50%), rgba(76, 175, 80, 0.05) 0%, transparent 50%);
|
||||
border-radius: 30px;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
/* 季节性主题变化(可通过JavaScript控制) */
|
||||
.theme-spring body {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f0f9f0 25%, #e1f5e1 50%, #f8fdf8 75%, #e8f5e8 100%);
|
||||
}
|
||||
|
||||
.theme-summer body {
|
||||
background: linear-gradient(135deg, #f0f9f0 0%, #e8f5e8 25%, #f8fdf8 50%, #e1f5e1 75%, #f0f9f0 100%);
|
||||
}
|
||||
|
||||
.theme-autumn body {
|
||||
background: linear-gradient(135deg, #f5f0e8 0%, #f9f5f0 25%, #fdf8f0 50%, #f5f0e8 75%, #f9f5f0 100%);
|
||||
}
|
||||
|
||||
.theme-winter body {
|
||||
background: linear-gradient(135deg, #f0f5f8 0%, #f5f9fc 25%, #f8fbfd 50%, #f0f5f8 75%, #f5f9fc 100%);
|
||||
}
|
||||
|
||||
/* 性能优化:GPU加速 */
|
||||
body,
|
||||
body::before,
|
||||
body::after {
|
||||
will-change: transform;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
/* 无障碍支持:为屏幕阅读器隐藏装饰元素 */
|
||||
body::before,
|
||||
body::after {
|
||||
speak: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
647
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/css/style.css
Normal file
647
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/css/style.css
Normal file
@@ -0,0 +1,647 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
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: #2d5a3d;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 容器布局 */
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding: 30px 20px;
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f0f9f0 100%);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 4px 20px rgba(45, 90, 61, 0.1);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: #2d5a3d;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 0 2px 4px rgba(45, 90, 61, 0.1);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: #5a8a6b;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* 表单容器 */
|
||||
.form-container {
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 8px 32px rgba(45, 90, 61, 0.1);
|
||||
border: 1px solid #e8f5e8;
|
||||
}
|
||||
|
||||
.password-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
/* 表单组样式 */
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-weight: 600;
|
||||
color: #2d5a3d;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.1rem;
|
||||
color: #2d5a3d;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 长度控制 */
|
||||
.length-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
background: #f8fdf8;
|
||||
border-radius: 12px;
|
||||
border: 2px solid #e8f5e8;
|
||||
}
|
||||
|
||||
.length-slider {
|
||||
flex: 1;
|
||||
height: 6px;
|
||||
background: #e8f5e8;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.length-slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: linear-gradient(135deg, #4caf50, #45a049);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.length-slider::-moz-range-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: linear-gradient(135deg, #4caf50, #45a049);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.length-display {
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
font-size: 1.2rem;
|
||||
color: #2d5a3d;
|
||||
background: #ffffff;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #e8f5e8;
|
||||
}
|
||||
|
||||
/* 复选框组 */
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
background: #f8fdf8;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #e8f5e8;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-item:hover {
|
||||
background: #f0f9f0;
|
||||
border-color: #d4edda;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.checkbox-item input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
accent-color: #4caf50;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-item label {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
color: #2d5a3d;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 生成按钮 */
|
||||
.generate-btn {
|
||||
background: linear-gradient(135deg, #4caf50, #45a049);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 16px 32px;
|
||||
border-radius: 12px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 16px rgba(76, 175, 80, 0.3);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.generate-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(76, 175, 80, 0.4);
|
||||
}
|
||||
|
||||
.generate-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.generate-btn:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* 结果容器 */
|
||||
.result-container {
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 8px 32px rgba(45, 90, 61, 0.1);
|
||||
border: 1px solid #e8f5e8;
|
||||
animation: slideIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.result-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.result-header h3 {
|
||||
color: #2d5a3d;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: #45a049;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* 密码显示 */
|
||||
.password-display {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
width: 100%;
|
||||
padding: 16px 20px;
|
||||
border: 2px solid #e8f5e8;
|
||||
border-radius: 12px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #2d5a3d;
|
||||
background: #f8fdf8;
|
||||
text-align: center;
|
||||
letter-spacing: 1px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.password-input:focus {
|
||||
outline: none;
|
||||
border-color: #4caf50;
|
||||
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
/* 密码信息 */
|
||||
.password-info {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 12px 16px;
|
||||
background: #f8fdf8;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #e8f5e8;
|
||||
}
|
||||
|
||||
.info-item.full-width {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 0.9rem;
|
||||
color: #5a8a6b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 1rem;
|
||||
color: #2d5a3d;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.info-value.strength {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.strength.weak {
|
||||
background: #f44336;
|
||||
}
|
||||
|
||||
.strength.medium {
|
||||
background: #ff9800;
|
||||
}
|
||||
|
||||
.strength.strong {
|
||||
background: #4caf50;
|
||||
}
|
||||
|
||||
.strength.very-strong {
|
||||
background: #2e7d32;
|
||||
}
|
||||
|
||||
/* 字符集显示 */
|
||||
.character-sets {
|
||||
border-top: 1px solid #e8f5e8;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.character-sets h4 {
|
||||
color: #2d5a3d;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.sets-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.set-item {
|
||||
background: #e8f5e8;
|
||||
color: #2d5a3d;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 错误容器 */
|
||||
.error-container {
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
padding: 40px 30px;
|
||||
text-align: center;
|
||||
box-shadow: 0 8px 32px rgba(244, 67, 54, 0.1);
|
||||
border: 1px solid #ffebee;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.error-container h3 {
|
||||
color: #d32f2f;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.error-container p {
|
||||
color: #666;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
background: #f44336;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.retry-btn:hover {
|
||||
background: #d32f2f;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 30px 20px;
|
||||
color: #5a8a6b;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 提示框 */
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
|
||||
z-index: 1000;
|
||||
animation: toastSlide 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes toastSlide {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.form-container,
|
||||
.result-container {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.password-info {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端适配 (最大767px) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 20px 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-container,
|
||||
.result-container {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.password-form {
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.length-control {
|
||||
padding: 12px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.length-display {
|
||||
min-width: 35px;
|
||||
padding: 6px 10px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
padding: 10px 12px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.checkbox-item input[type="checkbox"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.generate-btn {
|
||||
padding: 14px 28px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
padding: 14px 16px;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.password-info {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
align-self: center;
|
||||
padding: 12px 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.toast {
|
||||
right: 15px;
|
||||
left: 15px;
|
||||
top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏手机适配 (最大480px) */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 15px 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.form-container,
|
||||
.result-container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.generate-btn {
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
padding: 12px 14px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 触摸设备优化 */
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
.checkbox-item,
|
||||
.generate-btn,
|
||||
.copy-btn,
|
||||
.retry-btn {
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.checkbox-item input[type="checkbox"] {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.length-slider::-webkit-slider-thumb {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 高对比度模式支持 */
|
||||
@media (prefers-contrast: high) {
|
||||
.form-container,
|
||||
.result-container {
|
||||
border: 2px solid #2d5a3d;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
border: 1px solid #2d5a3d;
|
||||
}
|
||||
|
||||
.password-input {
|
||||
border: 2px solid #2d5a3d;
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画模式支持 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
146
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/index.html
Normal file
146
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/index.html
Normal file
@@ -0,0 +1,146 @@
|
||||
<!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>🔐 随机密码生成器</h1>
|
||||
<p class="subtitle">生成安全可靠的随机密码</p>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="form-container">
|
||||
<form id="passwordForm" class="password-form">
|
||||
<div class="form-group">
|
||||
<label for="length">密码长度</label>
|
||||
<div class="length-control">
|
||||
<input type="range" id="length" name="length" min="4" max="128" value="16" class="length-slider">
|
||||
<span id="lengthDisplay" class="length-display">16</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="section-title">字符类型</label>
|
||||
<div class="checkbox-group">
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="numbers" name="numbers" checked>
|
||||
<label for="numbers">包含数字 (0-9)</label>
|
||||
</div>
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="uppercase" name="uppercase" checked>
|
||||
<label for="uppercase">包含大写字母 (A-Z)</label>
|
||||
</div>
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="lowercase" name="lowercase" checked>
|
||||
<label for="lowercase">包含小写字母 (a-z)</label>
|
||||
</div>
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="symbols" name="symbols">
|
||||
<label for="symbols">包含特殊字符 (!@#$%^&*)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="section-title">高级选项</label>
|
||||
<div class="checkbox-group">
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="excludeSimilar" name="excludeSimilar" checked>
|
||||
<label for="excludeSimilar">排除相似字符 (0,O,l,1,I)</label>
|
||||
</div>
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="excludeAmbiguous" name="excludeAmbiguous" checked>
|
||||
<label for="excludeAmbiguous">排除模糊字符</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="generate-btn" id="generateBtn">
|
||||
<span class="btn-text">生成密码</span>
|
||||
<span class="btn-loading" style="display: none;">生成中...</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="result-container" id="resultContainer" style="display: none;">
|
||||
<div class="result-header">
|
||||
<h3>生成的密码</h3>
|
||||
<button class="copy-btn" id="copyBtn" title="复制密码">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="password-display">
|
||||
<input type="text" id="passwordResult" class="password-input" readonly>
|
||||
</div>
|
||||
|
||||
<div class="password-info">
|
||||
<div class="info-item">
|
||||
<span class="info-label">长度:</span>
|
||||
<span id="infoLength" class="info-value">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">强度:</span>
|
||||
<span id="infoStrength" class="info-value strength">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">熵值:</span>
|
||||
<span id="infoEntropy" class="info-value">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">数字:</span>
|
||||
<span id="infoNumbers" class="info-value">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">大写:</span>
|
||||
<span id="infoUppercase" class="info-value">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">小写:</span>
|
||||
<span id="infoLowercase" class="info-value">-</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">符号:</span>
|
||||
<span id="infoSymbols" class="info-value">-</span>
|
||||
</div>
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">破解时间:</span>
|
||||
<span id="infoCrackTime" class="info-value">-</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="character-sets" id="characterSets">
|
||||
<h4>使用的字符集</h4>
|
||||
<div class="sets-list" id="setsList"></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>安全密码生成工具</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast" style="display: none;">
|
||||
<span id="toastMessage">密码已复制到剪贴板</span>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
412
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/js/script.js
Normal file
412
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/js/script.js
Normal file
@@ -0,0 +1,412 @@
|
||||
class PasswordGenerator {
|
||||
constructor() {
|
||||
this.apiUrl = 'https://60s.api.shumengya.top/v2/password';
|
||||
this.loadStartTime = 0;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.updateLengthDisplay();
|
||||
this.preloadResources();
|
||||
}
|
||||
|
||||
preloadResources() {
|
||||
// 预连接API服务器
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'preconnect';
|
||||
link.href = 'https://60s.api.shumengya.top';
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
// 长度滑块事件
|
||||
const lengthSlider = document.getElementById('length');
|
||||
lengthSlider.addEventListener('input', () => this.updateLengthDisplay());
|
||||
|
||||
// 生成按钮事件
|
||||
const generateBtn = document.getElementById('generateBtn');
|
||||
generateBtn.addEventListener('click', () => this.generatePassword());
|
||||
|
||||
// 复制按钮事件
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
copyBtn.addEventListener('click', () => this.copyPassword());
|
||||
|
||||
// 重试按钮事件
|
||||
const retryBtn = document.getElementById('retryBtn');
|
||||
retryBtn.addEventListener('click', () => this.generatePassword());
|
||||
|
||||
// 复选框变化事件
|
||||
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', () => this.validateForm());
|
||||
});
|
||||
|
||||
// 键盘快捷键
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.ctrlKey && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this.generatePassword();
|
||||
}
|
||||
if (e.ctrlKey && e.key === 'c' && document.activeElement.id === 'passwordResult') {
|
||||
this.copyPassword();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateLengthDisplay() {
|
||||
const lengthSlider = document.getElementById('length');
|
||||
const lengthDisplay = document.getElementById('lengthDisplay');
|
||||
lengthDisplay.textContent = lengthSlider.value;
|
||||
}
|
||||
|
||||
validateForm() {
|
||||
const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked');
|
||||
const generateBtn = document.getElementById('generateBtn');
|
||||
|
||||
// 至少需要选择一种字符类型
|
||||
const hasCharacterType = Array.from(checkboxes).some(cb =>
|
||||
['numbers', 'uppercase', 'lowercase', 'symbols'].includes(cb.id)
|
||||
);
|
||||
|
||||
generateBtn.disabled = !hasCharacterType;
|
||||
|
||||
if (!hasCharacterType) {
|
||||
this.showToast('请至少选择一种字符类型', 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
async generatePassword() {
|
||||
this.loadStartTime = Date.now();
|
||||
|
||||
try {
|
||||
this.showLoading(true);
|
||||
this.hideError();
|
||||
|
||||
const params = this.getFormParams();
|
||||
const password = await this.callAPI(params);
|
||||
|
||||
if (password) {
|
||||
this.displayPassword(password, params);
|
||||
this.showToast('密码生成成功!', 'success');
|
||||
|
||||
const loadTime = Date.now() - this.loadStartTime;
|
||||
console.log(`密码生成完成,耗时: ${loadTime}ms`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成密码失败:', error);
|
||||
this.showError(error.message || '生成密码时发生错误,请重试');
|
||||
} finally {
|
||||
this.showLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
getFormParams() {
|
||||
const length = document.getElementById('length').value;
|
||||
const numbers = document.getElementById('numbers').checked;
|
||||
const uppercase = document.getElementById('uppercase').checked;
|
||||
const lowercase = document.getElementById('lowercase').checked;
|
||||
const symbols = document.getElementById('symbols').checked;
|
||||
const excludeSimilar = document.getElementById('excludeSimilar').checked;
|
||||
const excludeAmbiguous = document.getElementById('excludeAmbiguous').checked;
|
||||
|
||||
return {
|
||||
length: parseInt(length),
|
||||
numbers: numbers ? 'true' : 'false',
|
||||
uppercase: uppercase ? 'true' : 'false',
|
||||
lowercase: lowercase ? 'true' : 'false',
|
||||
symbols: symbols ? 'true' : 'false',
|
||||
exclude_similar: excludeSimilar ? 'true' : 'false',
|
||||
exclude_ambiguous: excludeAmbiguous ? 'true' : 'false',
|
||||
encoding: 'json'
|
||||
};
|
||||
}
|
||||
|
||||
async callAPI(params) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
||||
|
||||
try {
|
||||
const url = new URL(this.apiUrl);
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] !== undefined && params[key] !== null) {
|
||||
url.searchParams.append(key, params[key]);
|
||||
}
|
||||
});
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: 'GET',
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'PasswordGenerator/1.0'
|
||||
}
|
||||
});
|
||||
|
||||
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.password) {
|
||||
return data.data.password;
|
||||
} else {
|
||||
throw new Error(data.message || '服务器返回了无效的密码数据');
|
||||
}
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (error.name === 'AbortError') {
|
||||
throw new Error('请求超时,请检查网络连接后重试');
|
||||
}
|
||||
|
||||
if (error.message.includes('Failed to fetch')) {
|
||||
throw new Error('网络连接失败,请检查网络后重试');
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
displayPassword(password, params) {
|
||||
// 显示结果容器
|
||||
const resultContainer = document.getElementById('resultContainer');
|
||||
const errorContainer = document.getElementById('errorContainer');
|
||||
|
||||
resultContainer.style.display = 'block';
|
||||
errorContainer.style.display = 'none';
|
||||
|
||||
// 设置密码
|
||||
const passwordInput = document.getElementById('passwordResult');
|
||||
passwordInput.value = password;
|
||||
|
||||
// 计算并显示密码信息
|
||||
this.updatePasswordInfo(password, params);
|
||||
|
||||
// 滚动到结果区域
|
||||
resultContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
updatePasswordInfo(password, params) {
|
||||
// 基本信息
|
||||
document.getElementById('infoLength').textContent = password.length;
|
||||
document.getElementById('infoEntropy').textContent = this.calculateEntropy(password).toFixed(1);
|
||||
|
||||
// 密码强度
|
||||
const strength = this.calculateStrength(password);
|
||||
const strengthElement = document.getElementById('infoStrength');
|
||||
strengthElement.textContent = strength.text;
|
||||
strengthElement.className = `info-value strength ${strength.class}`;
|
||||
|
||||
// 字符类型统计
|
||||
const stats = this.analyzeCharacters(password);
|
||||
document.getElementById('infoNumbers').textContent = stats.numbers;
|
||||
document.getElementById('infoUppercase').textContent = stats.uppercase;
|
||||
document.getElementById('infoLowercase').textContent = stats.lowercase;
|
||||
document.getElementById('infoSymbols').textContent = stats.symbols;
|
||||
|
||||
// 使用的字符集
|
||||
this.updateCharacterSets(params);
|
||||
|
||||
// 破解时间估算
|
||||
document.getElementById('infoCrackTime').textContent = this.estimateCrackTime(password);
|
||||
}
|
||||
|
||||
calculateEntropy(password) {
|
||||
const charset = this.getCharsetSize(password);
|
||||
return Math.log2(Math.pow(charset, password.length));
|
||||
}
|
||||
|
||||
getCharsetSize(password) {
|
||||
let size = 0;
|
||||
if (/[0-9]/.test(password)) size += 10;
|
||||
if (/[a-z]/.test(password)) size += 26;
|
||||
if (/[A-Z]/.test(password)) size += 26;
|
||||
if (/[^a-zA-Z0-9]/.test(password)) size += 32;
|
||||
return size;
|
||||
}
|
||||
|
||||
calculateStrength(password) {
|
||||
const entropy = this.calculateEntropy(password);
|
||||
|
||||
if (entropy < 30) {
|
||||
return { text: '弱', class: 'weak' };
|
||||
} else if (entropy < 50) {
|
||||
return { text: '中等', class: 'medium' };
|
||||
} else if (entropy < 70) {
|
||||
return { text: '强', class: 'strong' };
|
||||
} else {
|
||||
return { text: '非常强', class: 'very-strong' };
|
||||
}
|
||||
}
|
||||
|
||||
analyzeCharacters(password) {
|
||||
return {
|
||||
numbers: (password.match(/[0-9]/g) || []).length,
|
||||
uppercase: (password.match(/[A-Z]/g) || []).length,
|
||||
lowercase: (password.match(/[a-z]/g) || []).length,
|
||||
symbols: (password.match(/[^a-zA-Z0-9]/g) || []).length
|
||||
};
|
||||
}
|
||||
|
||||
updateCharacterSets(params) {
|
||||
const setsList = document.getElementById('setsList');
|
||||
const sets = [];
|
||||
|
||||
if (params.numbers === 'true') sets.push('数字 (0-9)');
|
||||
if (params.uppercase === 'true') sets.push('大写字母 (A-Z)');
|
||||
if (params.lowercase === 'true') sets.push('小写字母 (a-z)');
|
||||
if (params.symbols === 'true') sets.push('特殊字符 (!@#$...)');
|
||||
|
||||
setsList.innerHTML = sets.map(set => `<span class="set-item">${set}</span>`).join('');
|
||||
}
|
||||
|
||||
estimateCrackTime(password) {
|
||||
const charset = this.getCharsetSize(password);
|
||||
const combinations = Math.pow(charset, password.length);
|
||||
const guessesPerSecond = 1e9; // 假设每秒10亿次尝试
|
||||
const secondsToCrack = combinations / (2 * guessesPerSecond);
|
||||
|
||||
if (secondsToCrack < 60) {
|
||||
return '不到1分钟';
|
||||
} else if (secondsToCrack < 3600) {
|
||||
return `${Math.ceil(secondsToCrack / 60)}分钟`;
|
||||
} else if (secondsToCrack < 86400) {
|
||||
return `${Math.ceil(secondsToCrack / 3600)}小时`;
|
||||
} else if (secondsToCrack < 31536000) {
|
||||
return `${Math.ceil(secondsToCrack / 86400)}天`;
|
||||
} else if (secondsToCrack < 31536000000) {
|
||||
return `${Math.ceil(secondsToCrack / 31536000)}年`;
|
||||
} else {
|
||||
return '数千年以上';
|
||||
}
|
||||
}
|
||||
|
||||
async copyPassword() {
|
||||
const passwordInput = document.getElementById('passwordResult');
|
||||
|
||||
try {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(passwordInput.value);
|
||||
} else {
|
||||
// 降级方案
|
||||
passwordInput.select();
|
||||
passwordInput.setSelectionRange(0, 99999);
|
||||
document.execCommand('copy');
|
||||
}
|
||||
|
||||
this.showToast('密码已复制到剪贴板!', 'success');
|
||||
|
||||
// 复制按钮反馈
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
const originalText = copyBtn.innerHTML;
|
||||
copyBtn.innerHTML = '✓ 已复制';
|
||||
copyBtn.style.background = '#2e7d32';
|
||||
|
||||
setTimeout(() => {
|
||||
copyBtn.innerHTML = originalText;
|
||||
copyBtn.style.background = '';
|
||||
}, 2000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error);
|
||||
this.showToast('复制失败,请手动选择密码', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
showLoading(show) {
|
||||
const generateBtn = document.getElementById('generateBtn');
|
||||
|
||||
if (show) {
|
||||
generateBtn.disabled = true;
|
||||
generateBtn.innerHTML = '<span style="display: inline-block; animation: spin 1s linear infinite;">⟳</span> 生成中...';
|
||||
} else {
|
||||
generateBtn.disabled = false;
|
||||
generateBtn.innerHTML = '🔐 生成密码';
|
||||
}
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
const errorContainer = document.getElementById('errorContainer');
|
||||
const resultContainer = document.getElementById('resultContainer');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
|
||||
errorMessage.textContent = message;
|
||||
errorContainer.style.display = 'block';
|
||||
resultContainer.style.display = 'none';
|
||||
|
||||
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
hideError() {
|
||||
const errorContainer = document.getElementById('errorContainer');
|
||||
errorContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
showToast(message, type = 'info') {
|
||||
// 移除现有的toast
|
||||
const existingToast = document.querySelector('.toast');
|
||||
if (existingToast) {
|
||||
existingToast.remove();
|
||||
}
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast';
|
||||
toast.textContent = message;
|
||||
|
||||
// 根据类型设置颜色
|
||||
const colors = {
|
||||
success: '#4caf50',
|
||||
error: '#f44336',
|
||||
warning: '#ff9800',
|
||||
info: '#2196f3'
|
||||
};
|
||||
|
||||
toast.style.background = colors[type] || colors.info;
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
// 3秒后自动移除
|
||||
setTimeout(() => {
|
||||
if (toast.parentNode) {
|
||||
toast.remove();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加旋转动画样式
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new PasswordGenerator();
|
||||
});
|
||||
|
||||
// 页面可见性变化时的处理
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
// 页面重新可见时,可以进行一些刷新操作
|
||||
console.log('页面重新可见');
|
||||
}
|
||||
});
|
||||
|
||||
// 错误处理
|
||||
window.addEventListener('error', (event) => {
|
||||
console.error('全局错误:', event.error);
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
console.error('未处理的Promise拒绝:', event.reason);
|
||||
event.preventDefault();
|
||||
});
|
||||
32
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/返回接口.json
Normal file
32
InfoGenie-frontend/public/60sapi/实用功能/随机密码生成器/返回接口.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"password": "8mr2M7dZ6E3saj3F",
|
||||
"length": 16,
|
||||
"config": {
|
||||
"include_numbers": true,
|
||||
"include_symbols": false,
|
||||
"include_lowercase": true,
|
||||
"include_uppercase": true,
|
||||
"exclude_similar": true,
|
||||
"exclude_ambiguous": true
|
||||
},
|
||||
"character_sets": {
|
||||
"lowercase": "abcdefghjkmnpqrstuvwxyz",
|
||||
"uppercase": "ABCDEFGHIJKMNPQRSTUVWXYZ",
|
||||
"numbers": "23456789",
|
||||
"symbols": "",
|
||||
"used_sets": [
|
||||
"lowercase",
|
||||
"uppercase",
|
||||
"numbers"
|
||||
]
|
||||
},
|
||||
"generation_info": {
|
||||
"entropy": 92.5,
|
||||
"strength": "极强",
|
||||
"time_to_crack": "数百万年"
|
||||
}
|
||||
}
|
||||
}
|
||||
215
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/background.css
Normal file
215
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/background.css
Normal file
@@ -0,0 +1,215 @@
|
||||
/* 背景样式文件 - 独立管理背景相关样式 */
|
||||
|
||||
/* 主体背景 */
|
||||
body {
|
||||
background: linear-gradient(135deg, #e8f5e8 0%, #f0fff0 50%, #e8f5e8 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%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 容器背景装饰 */
|
||||
.container::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 80%, rgba(144, 205, 144, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(45, 90, 39, 0.05) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(144, 205, 144, 0.08) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* 输入区域背景 */
|
||||
.input-section {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
/* 结果区域背景 */
|
||||
.result-section {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
/* 格式组背景 */
|
||||
.format-group {
|
||||
background: rgba(248, 255, 248, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* 属性项背景 */
|
||||
.property-item {
|
||||
background: rgba(248, 255, 248, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* 调色板项背景 */
|
||||
.palette-item {
|
||||
background: rgba(248, 255, 248, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* 无障碍项背景 */
|
||||
.accessibility-item {
|
||||
background: rgba(248, 255, 248, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* 颜色预览背景 */
|
||||
.color-preview {
|
||||
background: rgba(248, 255, 248, 0.6);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
/* 输入框背景 */
|
||||
.input-group input,
|
||||
.input-group select {
|
||||
background: rgba(248, 255, 248, 0.9);
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.input-group input:focus,
|
||||
.input-group select:focus {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
/* 格式组内部元素背景 */
|
||||
.format-group p {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(3px);
|
||||
-webkit-backdrop-filter: blur(3px);
|
||||
}
|
||||
|
||||
/* 手机端背景优化 */
|
||||
@media (max-width: 767px) {
|
||||
body {
|
||||
background: linear-gradient(180deg, #e8f5e8 0%, #f0fff0 50%, #e8f5e8 100%);
|
||||
background-attachment: scroll; /* 手机端使用scroll避免性能问题 */
|
||||
}
|
||||
|
||||
.container::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 30% 70%, rgba(144, 205, 144, 0.08) 0%, transparent 40%),
|
||||
radial-gradient(circle at 70% 30%, rgba(45, 90, 39, 0.04) 0%, transparent 40%);
|
||||
}
|
||||
|
||||
/* 减少手机端的模糊效果以提升性能 */
|
||||
.input-section,
|
||||
.result-section {
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.format-group,
|
||||
.property-item,
|
||||
.palette-item,
|
||||
.accessibility-item {
|
||||
backdrop-filter: blur(3px);
|
||||
-webkit-backdrop-filter: blur(3px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 平板端背景优化 */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 25% 75%, rgba(144, 205, 144, 0.12) 0%, transparent 60%),
|
||||
radial-gradient(circle at 75% 25%, rgba(45, 90, 39, 0.06) 0%, transparent 60%),
|
||||
radial-gradient(circle at 50% 50%, rgba(144, 205, 144, 0.04) 0%, transparent 40%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 电脑端背景优化 */
|
||||
@media (min-width: 1025px) {
|
||||
body {
|
||||
background-size: 300% 300%;
|
||||
animation-duration: 20s;
|
||||
}
|
||||
|
||||
.container::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 15% 85%, rgba(144, 205, 144, 0.15) 0%, transparent 70%),
|
||||
radial-gradient(circle at 85% 15%, rgba(45, 90, 39, 0.08) 0%, transparent 70%),
|
||||
radial-gradient(circle at 35% 35%, rgba(144, 205, 144, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 65% 65%, rgba(45, 90, 39, 0.05) 0%, transparent 50%);
|
||||
}
|
||||
|
||||
/* 电脑端增强模糊效果 */
|
||||
.input-section,
|
||||
.result-section {
|
||||
backdrop-filter: blur(15px);
|
||||
-webkit-backdrop-filter: blur(15px);
|
||||
}
|
||||
|
||||
.format-group,
|
||||
.property-item,
|
||||
.palette-item,
|
||||
.accessibility-item {
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 深色模式支持(如果用户系统设置为深色模式) */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: linear-gradient(135deg, #1a2e1a 0%, #0f1f0f 50%, #1a2e1a 100%);
|
||||
}
|
||||
|
||||
.container::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 80%, rgba(144, 205, 144, 0.05) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(45, 90, 39, 0.03) 0%, transparent 50%);
|
||||
}
|
||||
|
||||
.input-section,
|
||||
.result-section {
|
||||
background: rgba(26, 46, 26, 0.9);
|
||||
}
|
||||
|
||||
.format-group,
|
||||
.property-item,
|
||||
.palette-item,
|
||||
.accessibility-item,
|
||||
.color-preview {
|
||||
background: rgba(26, 46, 26, 0.6);
|
||||
}
|
||||
|
||||
.input-group input,
|
||||
.input-group select {
|
||||
background: rgba(26, 46, 26, 0.8);
|
||||
color: #e8f5e8;
|
||||
border-color: rgba(144, 205, 144, 0.3);
|
||||
}
|
||||
|
||||
.format-group p {
|
||||
background: rgba(15, 31, 15, 0.8);
|
||||
color: #e8f5e8;
|
||||
}
|
||||
}
|
||||
187
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/index.html
Normal file
187
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/index.html
Normal file
@@ -0,0 +1,187 @@
|
||||
<!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="styles.css">
|
||||
<link rel="stylesheet" href="background.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>随机颜色/颜色转换工具</h1>
|
||||
<p class="subtitle">获取随机颜色或转换指定颜色格式</p>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="input-section">
|
||||
<div class="input-group">
|
||||
<label for="colorInput">输入颜色值(可选):</label>
|
||||
<input type="text" id="colorInput" placeholder="例如: #33AAFF">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="encodingSelect">输出格式:</label>
|
||||
<select id="encodingSelect">
|
||||
<option value="json">JSON</option>
|
||||
<option value="text">文本</option>
|
||||
<option value="html">HTML</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<button id="randomBtn" class="btn btn-primary">获取随机颜色</button>
|
||||
<button id="convertBtn" class="btn btn-secondary">转换颜色</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<div class="color-preview">
|
||||
<div id="colorDisplay" class="color-box"></div>
|
||||
<div class="color-info">
|
||||
<h3 id="colorName">颜色名称</h3>
|
||||
<p id="hexValue">#000000</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-formats">
|
||||
<div class="format-group">
|
||||
<h4>RGB</h4>
|
||||
<div class="format-values">
|
||||
<span id="rgbR">0</span>
|
||||
<span id="rgbG">0</span>
|
||||
<span id="rgbB">0</span>
|
||||
</div>
|
||||
<p id="rgbString">rgb(0, 0, 0)</p>
|
||||
</div>
|
||||
|
||||
<div class="format-group">
|
||||
<h4>HSL</h4>
|
||||
<div class="format-values">
|
||||
<span id="hslH">0°</span>
|
||||
<span id="hslS">0%</span>
|
||||
<span id="hslL">0%</span>
|
||||
</div>
|
||||
<p id="hslString">hsl(0, 0%, 0%)</p>
|
||||
</div>
|
||||
|
||||
<div class="format-group">
|
||||
<h4>HSV</h4>
|
||||
<div class="format-values">
|
||||
<span id="hsvH">0°</span>
|
||||
<span id="hsvS">0%</span>
|
||||
<span id="hsvV">0%</span>
|
||||
</div>
|
||||
<p id="hsvString">hsv(0, 0%, 0%)</p>
|
||||
</div>
|
||||
|
||||
<div class="format-group">
|
||||
<h4>CMYK</h4>
|
||||
<div class="format-values">
|
||||
<span id="cmykC">0%</span>
|
||||
<span id="cmykM">0%</span>
|
||||
<span id="cmykY">0%</span>
|
||||
<span id="cmykK">0%</span>
|
||||
</div>
|
||||
<p id="cmykString">cmyk(0%, 0%, 0%, 0%)</p>
|
||||
</div>
|
||||
|
||||
<div class="format-group">
|
||||
<h4>LAB</h4>
|
||||
<div class="format-values">
|
||||
<span id="labL">0</span>
|
||||
<span id="labA">0</span>
|
||||
<span id="labB">0</span>
|
||||
</div>
|
||||
<p id="labString">lab(0, 0, 0)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-properties">
|
||||
<div class="property-item">
|
||||
<label>亮度:</label>
|
||||
<span id="brightness">0</span>
|
||||
</div>
|
||||
<div class="property-item">
|
||||
<label>对比度 (白色):</label>
|
||||
<span id="contrastWhite">0</span>
|
||||
</div>
|
||||
<div class="property-item">
|
||||
<label>对比度 (黑色):</label>
|
||||
<span id="contrastBlack">0</span>
|
||||
</div>
|
||||
<div class="property-item">
|
||||
<label>最佳文字颜色:</label>
|
||||
<span id="bestTextColor">#000000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-palette">
|
||||
<h4>配色方案</h4>
|
||||
<div class="palette-group">
|
||||
<div class="palette-item">
|
||||
<label>互补色:</label>
|
||||
<div id="complementary" class="color-sample"></div>
|
||||
<span id="complementaryHex">#000000</span>
|
||||
</div>
|
||||
<div class="palette-item">
|
||||
<label>类似色:</label>
|
||||
<div class="analogous-colors">
|
||||
<div id="analogous1" class="color-sample"></div>
|
||||
<div id="analogous2" class="color-sample"></div>
|
||||
</div>
|
||||
<div class="analogous-hex">
|
||||
<span id="analogous1Hex">#000000</span>
|
||||
<span id="analogous2Hex">#000000</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="palette-item">
|
||||
<label>三角色:</label>
|
||||
<div class="triadic-colors">
|
||||
<div id="triadic1" class="color-sample"></div>
|
||||
<div id="triadic2" class="color-sample"></div>
|
||||
</div>
|
||||
<div class="triadic-hex">
|
||||
<span id="triadic1Hex">#000000</span>
|
||||
<span id="triadic2Hex">#000000</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accessibility-info">
|
||||
<h4>无障碍性</h4>
|
||||
<div class="accessibility-grid">
|
||||
<div class="accessibility-item">
|
||||
<span>AA 普通文本:</span>
|
||||
<span id="aaNormal" class="status">否</span>
|
||||
</div>
|
||||
<div class="accessibility-item">
|
||||
<span>AA 大文本:</span>
|
||||
<span id="aaLarge" class="status">否</span>
|
||||
</div>
|
||||
<div class="accessibility-item">
|
||||
<span>AAA 普通文本:</span>
|
||||
<span id="aaaNormal" class="status">否</span>
|
||||
</div>
|
||||
<div class="accessibility-item">
|
||||
<span>AAA 大文本:</span>
|
||||
<span id="aaaLarge" class="status">否</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="loading" id="loading" style="display: none;">
|
||||
<div class="spinner"></div>
|
||||
<p>正在获取颜色信息...</p>
|
||||
</div>
|
||||
|
||||
<div class="error" id="error" style="display: none;">
|
||||
<p id="errorMessage">获取颜色信息失败,请稍后重试</p>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
426
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/script.js
Normal file
426
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/script.js
Normal file
@@ -0,0 +1,426 @@
|
||||
// 随机颜色/颜色转换工具 JavaScript
|
||||
|
||||
class ColorTool {
|
||||
constructor() {
|
||||
this.apiUrl = 'https://60s.api.shumengya.top/v2/color';
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.hideResultSection();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
const randomBtn = document.getElementById('randomBtn');
|
||||
const convertBtn = document.getElementById('convertBtn');
|
||||
const colorInput = document.getElementById('colorInput');
|
||||
|
||||
randomBtn.addEventListener('click', () => this.getRandomColor());
|
||||
convertBtn.addEventListener('click', () => this.convertColor());
|
||||
|
||||
// 回车键支持
|
||||
colorInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.convertColor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hideResultSection() {
|
||||
const resultSection = document.querySelector('.result-section');
|
||||
resultSection.style.display = 'none';
|
||||
}
|
||||
|
||||
showResultSection() {
|
||||
const resultSection = document.querySelector('.result-section');
|
||||
resultSection.style.display = 'block';
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
const error = document.getElementById('error');
|
||||
loading.style.display = 'block';
|
||||
error.style.display = 'none';
|
||||
this.hideResultSection();
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
const loading = document.getElementById('loading');
|
||||
loading.style.display = 'none';
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
const error = document.getElementById('error');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
const loading = document.getElementById('loading');
|
||||
|
||||
loading.style.display = 'none';
|
||||
errorMessage.textContent = message;
|
||||
error.style.display = 'block';
|
||||
this.hideResultSection();
|
||||
}
|
||||
|
||||
hideError() {
|
||||
const error = document.getElementById('error');
|
||||
error.style.display = 'none';
|
||||
}
|
||||
|
||||
async getRandomColor() {
|
||||
try {
|
||||
this.showLoading();
|
||||
const encoding = document.getElementById('encodingSelect').value;
|
||||
const url = `${this.apiUrl}?encoding=${encoding}`;
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200) {
|
||||
this.displayColorData(data.data);
|
||||
this.hideLoading();
|
||||
this.hideError();
|
||||
this.showResultSection();
|
||||
} else {
|
||||
throw new Error(data.message || '获取颜色信息失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取随机颜色失败:', error);
|
||||
this.showError(`获取随机颜色失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async convertColor() {
|
||||
const colorInput = document.getElementById('colorInput');
|
||||
const colorValue = colorInput.value.trim();
|
||||
|
||||
if (!colorValue) {
|
||||
this.showError('请输入要转换的颜色值');
|
||||
return;
|
||||
}
|
||||
|
||||
// 简单的颜色格式验证
|
||||
if (!this.isValidColor(colorValue)) {
|
||||
this.showError('请输入有效的颜色值(如 #33AAFF)');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.showLoading();
|
||||
const encoding = document.getElementById('encodingSelect').value;
|
||||
const url = `${this.apiUrl}?color=${encodeURIComponent(colorValue)}&encoding=${encoding}`;
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code === 200) {
|
||||
this.displayColorData(data.data);
|
||||
this.hideLoading();
|
||||
this.hideError();
|
||||
this.showResultSection();
|
||||
} else {
|
||||
throw new Error(data.message || '转换颜色失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('转换颜色失败:', error);
|
||||
this.showError(`转换颜色失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
isValidColor(color) {
|
||||
// 支持十六进制颜色格式
|
||||
const hexPattern = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
||||
// 支持RGB格式
|
||||
const rgbPattern = /^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/;
|
||||
// 支持HSL格式
|
||||
const hslPattern = /^hsl\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%\s*\)$/;
|
||||
|
||||
return hexPattern.test(color) || rgbPattern.test(color) || hslPattern.test(color);
|
||||
}
|
||||
|
||||
displayColorData(data) {
|
||||
// 显示主要颜色信息
|
||||
this.updateColorDisplay(data);
|
||||
|
||||
// 显示各种格式
|
||||
this.updateColorFormats(data);
|
||||
|
||||
// 显示颜色属性
|
||||
this.updateColorProperties(data);
|
||||
|
||||
// 显示配色方案
|
||||
this.updateColorPalette(data);
|
||||
|
||||
// 显示无障碍性信息
|
||||
this.updateAccessibilityInfo(data);
|
||||
}
|
||||
|
||||
updateColorDisplay(data) {
|
||||
const colorDisplay = document.getElementById('colorDisplay');
|
||||
const colorName = document.getElementById('colorName');
|
||||
const hexValue = document.getElementById('hexValue');
|
||||
|
||||
colorDisplay.style.backgroundColor = data.hex;
|
||||
colorName.textContent = data.name || '未知颜色';
|
||||
hexValue.textContent = data.hex;
|
||||
}
|
||||
|
||||
updateColorFormats(data) {
|
||||
// RGB
|
||||
if (data.rgb) {
|
||||
document.getElementById('rgbR').textContent = data.rgb.r;
|
||||
document.getElementById('rgbG').textContent = data.rgb.g;
|
||||
document.getElementById('rgbB').textContent = data.rgb.b;
|
||||
document.getElementById('rgbString').textContent = data.rgb.string;
|
||||
}
|
||||
|
||||
// HSL
|
||||
if (data.hsl) {
|
||||
document.getElementById('hslH').textContent = data.hsl.h + '°';
|
||||
document.getElementById('hslS').textContent = data.hsl.s + '%';
|
||||
document.getElementById('hslL').textContent = data.hsl.l + '%';
|
||||
document.getElementById('hslString').textContent = data.hsl.string;
|
||||
}
|
||||
|
||||
// HSV
|
||||
if (data.hsv) {
|
||||
document.getElementById('hsvH').textContent = data.hsv.h + '°';
|
||||
document.getElementById('hsvS').textContent = data.hsv.s + '%';
|
||||
document.getElementById('hsvV').textContent = data.hsv.v + '%';
|
||||
document.getElementById('hsvString').textContent = data.hsv.string;
|
||||
}
|
||||
|
||||
// CMYK
|
||||
if (data.cmyk) {
|
||||
document.getElementById('cmykC').textContent = data.cmyk.c + '%';
|
||||
document.getElementById('cmykM').textContent = data.cmyk.m + '%';
|
||||
document.getElementById('cmykY').textContent = data.cmyk.y + '%';
|
||||
document.getElementById('cmykK').textContent = data.cmyk.k + '%';
|
||||
document.getElementById('cmykString').textContent = data.cmyk.string;
|
||||
}
|
||||
|
||||
// LAB
|
||||
if (data.lab) {
|
||||
document.getElementById('labL').textContent = data.lab.l;
|
||||
document.getElementById('labA').textContent = data.lab.a;
|
||||
document.getElementById('labB').textContent = data.lab.b;
|
||||
document.getElementById('labString').textContent = data.lab.string;
|
||||
}
|
||||
}
|
||||
|
||||
updateColorProperties(data) {
|
||||
// 亮度
|
||||
if (data.brightness !== undefined) {
|
||||
document.getElementById('brightness').textContent = data.brightness.toFixed(2);
|
||||
}
|
||||
|
||||
// 对比度
|
||||
if (data.contrast) {
|
||||
document.getElementById('contrastWhite').textContent = data.contrast.white.toFixed(2);
|
||||
document.getElementById('contrastBlack').textContent = data.contrast.black.toFixed(2);
|
||||
}
|
||||
|
||||
// 最佳文字颜色
|
||||
if (data.accessibility && data.accessibility.best_text_color) {
|
||||
const bestTextColor = document.getElementById('bestTextColor');
|
||||
bestTextColor.textContent = data.accessibility.best_text_color;
|
||||
bestTextColor.style.color = data.accessibility.best_text_color;
|
||||
}
|
||||
}
|
||||
|
||||
updateColorPalette(data) {
|
||||
// 互补色
|
||||
if (data.complementary) {
|
||||
const complementary = document.getElementById('complementary');
|
||||
const complementaryHex = document.getElementById('complementaryHex');
|
||||
complementary.style.backgroundColor = data.complementary;
|
||||
complementaryHex.textContent = data.complementary;
|
||||
}
|
||||
|
||||
// 类似色
|
||||
if (data.analogous && data.analogous.length >= 2) {
|
||||
const analogous1 = document.getElementById('analogous1');
|
||||
const analogous2 = document.getElementById('analogous2');
|
||||
const analogous1Hex = document.getElementById('analogous1Hex');
|
||||
const analogous2Hex = document.getElementById('analogous2Hex');
|
||||
|
||||
analogous1.style.backgroundColor = data.analogous[0];
|
||||
analogous2.style.backgroundColor = data.analogous[1];
|
||||
analogous1Hex.textContent = data.analogous[0];
|
||||
analogous2Hex.textContent = data.analogous[1];
|
||||
}
|
||||
|
||||
// 三角色
|
||||
if (data.triadic && data.triadic.length >= 2) {
|
||||
const triadic1 = document.getElementById('triadic1');
|
||||
const triadic2 = document.getElementById('triadic2');
|
||||
const triadic1Hex = document.getElementById('triadic1Hex');
|
||||
const triadic2Hex = document.getElementById('triadic2Hex');
|
||||
|
||||
triadic1.style.backgroundColor = data.triadic[0];
|
||||
triadic2.style.backgroundColor = data.triadic[1];
|
||||
triadic1Hex.textContent = data.triadic[0];
|
||||
triadic2Hex.textContent = data.triadic[1];
|
||||
}
|
||||
}
|
||||
|
||||
updateAccessibilityInfo(data) {
|
||||
if (data.accessibility) {
|
||||
const aaNormal = document.getElementById('aaNormal');
|
||||
const aaLarge = document.getElementById('aaLarge');
|
||||
const aaaNormal = document.getElementById('aaaNormal');
|
||||
const aaaLarge = document.getElementById('aaaLarge');
|
||||
|
||||
this.updateAccessibilityStatus(aaNormal, data.accessibility.aa_normal);
|
||||
this.updateAccessibilityStatus(aaLarge, data.accessibility.aa_large);
|
||||
this.updateAccessibilityStatus(aaaNormal, data.accessibility.aaa_normal);
|
||||
this.updateAccessibilityStatus(aaaLarge, data.accessibility.aaa_large);
|
||||
}
|
||||
}
|
||||
|
||||
updateAccessibilityStatus(element, status) {
|
||||
element.textContent = status ? '通过' : '未通过';
|
||||
element.className = 'status ' + (status ? 'pass' : 'fail');
|
||||
}
|
||||
|
||||
// 复制颜色值到剪贴板
|
||||
copyToClipboard(text) {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
this.showToast('已复制到剪贴板');
|
||||
}).catch(err => {
|
||||
console.error('复制失败:', err);
|
||||
this.fallbackCopyTextToClipboard(text);
|
||||
});
|
||||
} else {
|
||||
this.fallbackCopyTextToClipboard(text);
|
||||
}
|
||||
}
|
||||
|
||||
fallbackCopyTextToClipboard(text) {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.top = '0';
|
||||
textArea.style.left = '0';
|
||||
textArea.style.position = 'fixed';
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
const successful = document.execCommand('copy');
|
||||
if (successful) {
|
||||
this.showToast('已复制到剪贴板');
|
||||
} else {
|
||||
this.showToast('复制失败');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('复制失败:', err);
|
||||
this.showToast('复制失败');
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
showToast(message) {
|
||||
// 创建简单的提示框
|
||||
const toast = document.createElement('div');
|
||||
toast.textContent = message;
|
||||
toast.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #2d5a27;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
z-index: 1000;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
animation: slideIn 0.3s ease;
|
||||
`;
|
||||
|
||||
// 添加动画样式
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes slideIn {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'slideIn 0.3s ease reverse';
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(toast);
|
||||
document.head.removeChild(style);
|
||||
}, 300);
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加点击复制功能
|
||||
function addCopyListeners() {
|
||||
const colorTool = window.colorTool;
|
||||
|
||||
// 为所有颜色值添加点击复制功能
|
||||
document.addEventListener('click', (e) => {
|
||||
const target = e.target;
|
||||
|
||||
// 检查是否点击了颜色值相关元素
|
||||
if (target.id === 'hexValue' ||
|
||||
target.id === 'rgbString' ||
|
||||
target.id === 'hslString' ||
|
||||
target.id === 'hsvString' ||
|
||||
target.id === 'cmykString' ||
|
||||
target.id === 'labString' ||
|
||||
target.id === 'complementaryHex' ||
|
||||
target.id === 'analogous1Hex' ||
|
||||
target.id === 'analogous2Hex' ||
|
||||
target.id === 'triadic1Hex' ||
|
||||
target.id === 'triadic2Hex') {
|
||||
|
||||
const text = target.textContent;
|
||||
if (text && colorTool) {
|
||||
colorTool.copyToClipboard(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.colorTool = new ColorTool();
|
||||
addCopyListeners();
|
||||
|
||||
// 添加复制提示
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
#hexValue, #rgbString, #hslString, #hsvString, #cmykString, #labString,
|
||||
#complementaryHex, #analogous1Hex, #analogous2Hex, #triadic1Hex, #triadic2Hex {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
#hexValue:hover, #rgbString:hover, #hslString:hover, #hsvString:hover,
|
||||
#cmykString:hover, #labString:hover, #complementaryHex:hover,
|
||||
#analogous1Hex:hover, #analogous2Hex:hover, #triadic1Hex:hover, #triadic2Hex:hover {
|
||||
background: rgba(45, 90, 39, 0.1);
|
||||
border-radius: 4px;
|
||||
padding: 2px 4px;
|
||||
margin: -2px -4px;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
637
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/styles.css
Normal file
637
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/styles.css
Normal file
@@ -0,0 +1,637 @@
|
||||
/* 基础样式重置 */
|
||||
* {
|
||||
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: #2d3748;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
color: #2d5a27;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #4a5568;
|
||||
font-size: 1rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
/* 输入区域 */
|
||||
.input-section {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 20px rgba(45, 90, 39, 0.1);
|
||||
border: 1px solid rgba(144, 205, 144, 0.3);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: #2d5a27;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.input-group input,
|
||||
.input-group select {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border: 2px solid #90cd90;
|
||||
border-radius: 10px;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
background: #f8fff8;
|
||||
}
|
||||
|
||||
.input-group input:focus,
|
||||
.input-group select:focus {
|
||||
outline: none;
|
||||
border-color: #2d5a27;
|
||||
box-shadow: 0 0 0 3px rgba(45, 90, 39, 0.1);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
padding: 15px 20px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #2d5a27, #4a7c59);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, #1e3a1a, #2d5a27);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(45, 90, 39, 0.3);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: linear-gradient(135deg, #90cd90, #a8d8a8);
|
||||
color: #2d5a27;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: linear-gradient(135deg, #7bb87b, #90cd90);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(144, 205, 144, 0.4);
|
||||
}
|
||||
|
||||
/* 结果展示区域 */
|
||||
.result-section {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 20px rgba(45, 90, 39, 0.1);
|
||||
border: 1px solid rgba(144, 205, 144, 0.3);
|
||||
}
|
||||
|
||||
/* 颜色预览 */
|
||||
.color-preview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: #f8fff8;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(144, 205, 144, 0.2);
|
||||
}
|
||||
|
||||
.color-box {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 12px;
|
||||
border: 3px solid #ffffff;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.color-info h3 {
|
||||
color: #2d5a27;
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.color-info p {
|
||||
color: #4a5568;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* 颜色格式展示 */
|
||||
.color-formats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.format-group {
|
||||
background: #f8fff8;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(144, 205, 144, 0.2);
|
||||
}
|
||||
|
||||
.format-group h4 {
|
||||
color: #2d5a27;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.format-values {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.format-values span {
|
||||
background: #90cd90;
|
||||
color: #2d5a27;
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.format-group p {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #4a5568;
|
||||
font-size: 0.9rem;
|
||||
background: #ffffff;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(144, 205, 144, 0.2);
|
||||
}
|
||||
|
||||
/* 颜色属性 */
|
||||
.color-properties {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.property-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #f8fff8;
|
||||
padding: 12px 15px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(144, 205, 144, 0.2);
|
||||
}
|
||||
|
||||
.property-item label {
|
||||
color: #2d5a27;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.property-item span {
|
||||
color: #4a5568;
|
||||
font-weight: 600;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* 配色方案 */
|
||||
.color-palette {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.color-palette h4 {
|
||||
color: #2d5a27;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.palette-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.palette-item {
|
||||
background: #f8fff8;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(144, 205, 144, 0.2);
|
||||
}
|
||||
|
||||
.palette-item label {
|
||||
display: block;
|
||||
color: #2d5a27;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.color-sample {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.analogous-colors,
|
||||
.triadic-colors {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.analogous-hex,
|
||||
.triadic-hex {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.85rem;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
/* 无障碍性信息 */
|
||||
.accessibility-info h4 {
|
||||
color: #2d5a27;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.accessibility-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.accessibility-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #f8fff8;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(144, 205, 144, 0.2);
|
||||
}
|
||||
|
||||
.accessibility-item span:first-child {
|
||||
color: #2d5a27;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status.pass {
|
||||
background: #90cd90;
|
||||
color: #2d5a27;
|
||||
}
|
||||
|
||||
.status.fail {
|
||||
background: #ffcccb;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
/* 加载和错误状态 */
|
||||
.loading,
|
||||
.error {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
border-radius: 12px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
background: rgba(144, 205, 144, 0.1);
|
||||
border: 1px solid rgba(144, 205, 144, 0.3);
|
||||
}
|
||||
|
||||
.error {
|
||||
background: rgba(255, 204, 203, 0.3);
|
||||
border: 1px solid rgba(211, 47, 47, 0.3);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid rgba(144, 205, 144, 0.3);
|
||||
border-top: 4px solid #2d5a27;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 15px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading p {
|
||||
color: #2d5a27;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.error p {
|
||||
color: #d32f2f;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 平板端适配 (768px - 1024px) */
|
||||
@media (min-width: 768px) and (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
gap: 35px;
|
||||
}
|
||||
|
||||
.input-section,
|
||||
.result-section {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.color-box {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.color-formats {
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
}
|
||||
|
||||
.palette-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 电脑端适配 (1025px+) */
|
||||
@media (min-width: 1025px) {
|
||||
.container {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.input-section,
|
||||
.result-section {
|
||||
padding: 35px;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
gap: 30px;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.color-box {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.color-info h3 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.color-info p {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.color-formats {
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.format-group {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.palette-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
max-width: 500px;
|
||||
margin: 25px auto 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 18px 25px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端优化 (最高优先级) */
|
||||
@media (max-width: 767px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 25px;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.input-section,
|
||||
.result-section {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.input-group input,
|
||||
.input-group select {
|
||||
padding: 14px 12px;
|
||||
font-size: 16px; /* 防止iOS缩放 */
|
||||
}
|
||||
|
||||
.button-group {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 16px 20px;
|
||||
font-size: 1rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.color-box {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.color-formats {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.format-group {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.format-values {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.color-properties {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.property-item {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.palette-group {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.palette-item {
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.analogous-colors,
|
||||
.triadic-colors {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.analogous-hex,
|
||||
.triadic-hex {
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.accessibility-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.accessibility-item {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.error {
|
||||
padding: 30px 15px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
60
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/返回接口.json
Normal file
60
InfoGenie-frontend/public/60sapi/实用功能/随机颜色/返回接口.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
|
||||
"data": {
|
||||
"hex": "#A59619",
|
||||
"name": "红色系",
|
||||
"rgb": {
|
||||
"r": 165,
|
||||
"g": 150,
|
||||
"b": 25,
|
||||
"string": "rgb(165, 150, 25)"
|
||||
},
|
||||
"hsl": {
|
||||
"h": 54,
|
||||
"s": 74,
|
||||
"l": 37,
|
||||
"string": "hsl(54, 74%, 37%)"
|
||||
},
|
||||
"hsv": {
|
||||
"h": 54,
|
||||
"s": 85,
|
||||
"v": 65,
|
||||
"string": "hsv(54, 85%, 65%)"
|
||||
},
|
||||
"cmyk": {
|
||||
"c": 0,
|
||||
"m": 9,
|
||||
"y": 85,
|
||||
"k": 35,
|
||||
"string": "cmyk(0%, 9%, 85%, 35%)"
|
||||
},
|
||||
"lab": {
|
||||
"l": 62,
|
||||
"a": -7,
|
||||
"b": 61,
|
||||
"string": "lab(62, -7, 61)"
|
||||
},
|
||||
"brightness": 140.235,
|
||||
"contrast": {
|
||||
"white": 3.01,
|
||||
"black": 6.98
|
||||
},
|
||||
"accessibility": {
|
||||
"aa_normal": true,
|
||||
"aa_large": true,
|
||||
"aaa_normal": false,
|
||||
"aaa_large": true,
|
||||
"best_text_color": "#000000"
|
||||
},
|
||||
"complementary": "#1926A4",
|
||||
"analogous": [
|
||||
"#A45019",
|
||||
"#6CA419"
|
||||
],
|
||||
"triadic": [
|
||||
"#19A496",
|
||||
"#9619A4"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user