添加更多的API接口功能
This commit is contained in:
202
frontend/60sapi/实用功能/实时天气[目前有问题]/background.css
Normal file
202
frontend/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
frontend/60sapi/实用功能/实时天气[目前有问题]/index.html
Normal file
143
frontend/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
frontend/60sapi/实用功能/实时天气[目前有问题]/script.js
Normal file
354
frontend/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
frontend/60sapi/实用功能/实时天气[目前有问题]/styles.css
Normal file
442
frontend/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
frontend/60sapi/实用功能/实时天气[目前有问题]/返回接口.json
Normal file
68
frontend/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
frontend/60sapi/实用功能/密码强度检测/css/style.css
Normal file
878
frontend/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
frontend/60sapi/实用功能/密码强度检测/index.html
Normal file
218
frontend/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
frontend/60sapi/实用功能/密码强度检测/js/script.js
Normal file
516
frontend/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
frontend/60sapi/实用功能/密码强度检测/返回接口.json
Normal file
37
frontend/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)增强安全性",
|
||||||
|
"避免在公共场合输入密码",
|
||||||
|
"不要将密码保存在浏览器中(除非使用可信的密码管理器)",
|
||||||
|
"避免使用个人信息作为密码",
|
||||||
|
"长密码比复杂密码更安全"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
1.生成为静态网页,js,css,html分离出来,不要混合在一起放入html里,难以阅读
|
|
||||||
2.网页要适配手机端,电脑端和平板端三个设备分别做不同的css格式,优先优化手机端用户体验
|
|
||||||
3.网页默认风格以淡绿色清新风格为主,除非用户要求
|
|
||||||
4.尽量不要引用外部css,js,实在要引用就使用中国国内的cdn,否则用户可能加载不出来
|
|
||||||
5.返回接口.json储存了网页api返回的数据格式
|
|
||||||
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
|
|
||||||
7.接口集合.json保存了所有已知的后端API接口,一个访问不了尝试自动切换另一个
|
|
||||||
8.在css中有关背景的css单独一个css文件,方便我直接迁移
|
|
||||||
243
frontend/60sapi/实用功能/身体健康分析/background.css
Normal file
243
frontend/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
frontend/60sapi/实用功能/身体健康分析/index.html
Normal file
115
frontend/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
frontend/60sapi/实用功能/身体健康分析/script.js
Normal file
515
frontend/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
frontend/60sapi/实用功能/身体健康分析/styles.css
Normal file
697
frontend/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
frontend/60sapi/实用功能/身体健康分析/返回接口.json
Normal file
93
frontend/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
frontend/60sapi/实用功能/配色方案/background.css
Normal file
187
frontend/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
frontend/60sapi/实用功能/配色方案/index.html
Normal file
64
frontend/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
frontend/60sapi/实用功能/配色方案/script.js
Normal file
315
frontend/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
frontend/60sapi/实用功能/配色方案/styles.css
Normal file
422
frontend/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
frontend/60sapi/实用功能/配色方案/返回接口.json
Normal file
273
frontend/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",
|
||||||
|
"品牌设计",
|
||||||
|
"室内设计",
|
||||||
|
"服装搭配"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
252
frontend/60sapi/实用功能/随机密码生成器/css/background.css
Normal file
252
frontend/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
frontend/60sapi/实用功能/随机密码生成器/css/style.css
Normal file
647
frontend/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
frontend/60sapi/实用功能/随机密码生成器/index.html
Normal file
146
frontend/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
frontend/60sapi/实用功能/随机密码生成器/js/script.js
Normal file
412
frontend/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
frontend/60sapi/实用功能/随机密码生成器/返回接口.json
Normal file
32
frontend/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
frontend/60sapi/实用功能/随机颜色/background.css
Normal file
215
frontend/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
frontend/60sapi/实用功能/随机颜色/index.html
Normal file
187
frontend/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
frontend/60sapi/实用功能/随机颜色/script.js
Normal file
426
frontend/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
frontend/60sapi/实用功能/随机颜色/styles.css
Normal file
637
frontend/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
frontend/60sapi/实用功能/随机颜色/返回接口.json
Normal file
60
frontend/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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,5 +4,4 @@
|
|||||||
4.尽量不要引用外部css,js,实在要引用就使用中国国内的cdn,否则用户可能加载不出来
|
4.尽量不要引用外部css,js,实在要引用就使用中国国内的cdn,否则用户可能加载不出来
|
||||||
5.返回接口.json储存了网页api返回的数据格式
|
5.返回接口.json储存了网页api返回的数据格式
|
||||||
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
|
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
|
||||||
7.接口集合.json保存了所有已知的后端API接口,一个访问不了尝试自动切换另一个
|
|
||||||
8.在css中有关背景的css单独一个css文件,方便我直接迁移
|
8.在css中有关背景的css单独一个css文件,方便我直接迁移
|
||||||
Reference in New Issue
Block a user