添加更多的API接口功能

This commit is contained in:
2025-09-08 22:08:09 +08:00
parent 227ccf9c29
commit 6889ca37e5
31 changed files with 8796 additions and 9 deletions

View 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);
}
}

View 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>

View 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');
});

View 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;
}
}

View 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": "有降水,推荐您在室内进行休闲运动。"
}
}
}
}
}

View 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;
}
}

View 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>

View 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');
}
});

View 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增强安全性",
"避免在公共场合输入密码",
"不要将密码保存在浏览器中(除非使用可信的密码管理器)",
"避免使用个人信息作为密码",
"长密码比复杂密码更安全"
]
}
}

View File

@@ -1,8 +0,0 @@
1.生成为静态网页jscsshtml分离出来不要混合在一起放入html里难以阅读
2.网页要适配手机端电脑端和平板端三个设备分别做不同的css格式优先优化手机端用户体验
3.网页默认风格以淡绿色清新风格为主,除非用户要求
4.尽量不要引用外部cssjs实在要引用就使用中国国内的cdn否则用户可能加载不出来
5.返回接口.json储存了网页api返回的数据格式
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
7.接口集合.json保存了所有已知的后端API接口一个访问不了尝试自动切换另一个
8.在css中有关背景的css单独一个css文件方便我直接迁移

View 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);
}
}

View 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>

View 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';
}
});

View 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;
}
}

View 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": "结果基于通用公式和统计数据,仅供参考,不能替代专业医疗建议。如有健康问题,请咨询医生。"
}
}

View 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;
}
}

View 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>

View 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);
}

View 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;
}
}

View 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",
"品牌设计",
"室内设计",
"服装搭配"
]
}
}
}

View 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;
}

View 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;
}
}

View 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>

View 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();
});

View 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": "数百万年"
}
}
}

View 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;
}
}

View 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"></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"></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>

View 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);
});

View 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;
}
}

View 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"
]
}
}

View File

@@ -4,5 +4,4 @@
4.尽量不要引用外部cssjs实在要引用就使用中国国内的cdn否则用户可能加载不出来
5.返回接口.json储存了网页api返回的数据格式
6.严格按照用户要求执行,不得随意添加什么注解,如“以下数据来自...”
7.接口集合.json保存了所有已知的后端API接口一个访问不了尝试自动切换另一个
8.在css中有关背景的css单独一个css文件方便我直接迁移