添加更多的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,否则用户可能加载不出来
|
||||
5.返回接口.json储存了网页api返回的数据格式
|
||||
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
|
||||
7.接口集合.json保存了所有已知的后端API接口,一个访问不了尝试自动切换另一个
|
||||
8.在css中有关背景的css单独一个css文件,方便我直接迁移
|
||||
Reference in New Issue
Block a user