1064 lines
38 KiB
HTML
1064 lines
38 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>狐蒂云活动监控 - 性价比专区</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Microsoft YaHei', sans-serif;
|
||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||
min-height: 100vh;
|
||
color: #fff;
|
||
}
|
||
|
||
.header {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
backdrop-filter: blur(10px);
|
||
padding: 20px;
|
||
text-align: center;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 24px;
|
||
margin-bottom: 10px;
|
||
background: linear-gradient(90deg, #00d4ff, #7b2cbf);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
}
|
||
|
||
.status-bar {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.status-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 14px;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
}
|
||
|
||
.status-dot {
|
||
width: 10px !important;
|
||
height: 10px !important;
|
||
min-width: 10px !important;
|
||
min-height: 10px !important;
|
||
max-width: 10px !important;
|
||
max-height: 10px !important;
|
||
border-radius: 50% !important;
|
||
animation: pulse 2s infinite;
|
||
box-shadow: none !important;
|
||
display: inline-block !important;
|
||
flex-shrink: 0 !important;
|
||
}
|
||
|
||
.status-dot.online {
|
||
background: #00ff88 !important;
|
||
}
|
||
|
||
.status-dot.offline {
|
||
background: #ff4444 !important;
|
||
}
|
||
|
||
.status-dot.loading {
|
||
background: #ffaa00 !important;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.5; }
|
||
}
|
||
|
||
.container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 20px;
|
||
margin: 30px 0 20px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.section-title .badge {
|
||
background: #00d4ff;
|
||
color: #000;
|
||
padding: 2px 10px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.section-title.sold-out .badge {
|
||
background: #ff6b6b;
|
||
}
|
||
|
||
.products-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.product-card {
|
||
background: rgba(255, 255, 255, 0.05);
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
transition: transform 0.3s, box-shadow 0.3s;
|
||
}
|
||
|
||
.product-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 10px 40px rgba(0, 212, 255, 0.2);
|
||
}
|
||
|
||
.product-card.sold-out {
|
||
opacity: 0.6;
|
||
border-color: rgba(255, 107, 107, 0.3);
|
||
}
|
||
|
||
.product-card.sold-out:hover {
|
||
box-shadow: 0 10px 40px rgba(255, 107, 107, 0.1);
|
||
}
|
||
|
||
.product-header {
|
||
background: linear-gradient(135deg, rgba(0, 212, 255, 0.2), rgba(123, 44, 191, 0.2));
|
||
padding: 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.product-card.sold-out .product-header {
|
||
background: linear-gradient(135deg, rgba(255, 107, 107, 0.2), rgba(100, 50, 50, 0.2));
|
||
}
|
||
|
||
.product-title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.product-tag {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.product-desc {
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.7);
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.product-badge {
|
||
background: #00ff88;
|
||
color: #000;
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.product-card.sold-out .product-badge {
|
||
background: #ff6b6b;
|
||
color: #fff;
|
||
}
|
||
|
||
.product-specs {
|
||
padding: 20px;
|
||
}
|
||
|
||
.spec-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 8px 0;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.spec-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.spec-label {
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.spec-value {
|
||
color: #fff;
|
||
text-align: right;
|
||
max-width: 60%;
|
||
}
|
||
|
||
.product-footer {
|
||
padding: 20px;
|
||
background: rgba(0, 0, 0, 0.2);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.price-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.price-current {
|
||
font-size: 28px;
|
||
font-weight: bold;
|
||
color: #00ff88;
|
||
}
|
||
|
||
.product-card.sold-out .price-current {
|
||
color: #ff6b6b;
|
||
}
|
||
|
||
.price-current span {
|
||
font-size: 14px;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.price-old {
|
||
font-size: 14px;
|
||
color: rgba(255, 255, 255, 0.4);
|
||
text-decoration: line-through;
|
||
}
|
||
|
||
.discount-tag {
|
||
background: linear-gradient(90deg, #ff6b6b, #ff8e53);
|
||
color: #fff;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.buy-btn {
|
||
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
|
||
color: #fff;
|
||
border: none;
|
||
padding: 12px 24px;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
text-decoration: none;
|
||
transition: transform 0.2s, box-shadow 0.2s;
|
||
}
|
||
|
||
.buy-btn:hover {
|
||
transform: scale(1.05);
|
||
box-shadow: 0 5px 20px rgba(0, 212, 255, 0.4);
|
||
}
|
||
|
||
.buy-btn.sold-out {
|
||
background: rgba(255, 107, 107, 0.3);
|
||
cursor: not-allowed;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.remaining-tag {
|
||
background: rgba(255, 193, 7, 0.2);
|
||
color: #ffc107;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.error-message {
|
||
background: rgba(255, 68, 68, 0.2);
|
||
border: 1px solid rgba(255, 68, 68, 0.5);
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
text-align: center;
|
||
color: #ff6b6b;
|
||
}
|
||
|
||
.loading {
|
||
text-align: center;
|
||
padding: 40px;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.loading::after {
|
||
content: '';
|
||
display: inline-block;
|
||
width: 20px;
|
||
height: 20px;
|
||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||
border-top-color: #00d4ff;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin-left: 10px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.status-dot.loading::after {
|
||
display: none !important;
|
||
}
|
||
|
||
.status-item .loading {
|
||
padding: 0;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.countdown {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
padding: 5px 15px;
|
||
border-radius: 20px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.manual-refresh {
|
||
background: rgba(0, 212, 255, 0.2);
|
||
border: 1px solid rgba(0, 212, 255, 0.5);
|
||
color: #00d4ff;
|
||
padding: 5px 15px;
|
||
border-radius: 20px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.manual-refresh:hover {
|
||
background: rgba(0, 212, 255, 0.3);
|
||
}
|
||
|
||
.interval-select {
|
||
background: rgba(0, 212, 255, 0.2);
|
||
border: 1px solid rgba(0, 212, 255, 0.5);
|
||
color: #00d4ff;
|
||
padding: 5px 10px;
|
||
border-radius: 20px;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
outline: none;
|
||
}
|
||
|
||
.alert-config {
|
||
background: rgba(255, 255, 255, 0.05);
|
||
padding: 15px;
|
||
border-radius: 12px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.alert-config-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
margin-bottom: 12px;
|
||
color: #00d4ff;
|
||
}
|
||
|
||
.alert-config-section {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.alert-config-label {
|
||
font-size: 14px;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
margin-bottom: 8px;
|
||
display: block;
|
||
}
|
||
|
||
.alert-config-checkboxes {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.checkbox-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
background: rgba(0, 212, 255, 0.1);
|
||
padding: 5px 12px;
|
||
border-radius: 15px;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.checkbox-item input {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.toggle-config-btn {
|
||
background: linear-gradient(135deg, #00d4ff, #7b2cbf);
|
||
color: #fff;
|
||
border: none;
|
||
padding: 10px 20px;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
margin-bottom: 10px;
|
||
transition: transform 0.2s, box-shadow 0.2s;
|
||
}
|
||
|
||
.toggle-config-btn:hover {
|
||
transform: scale(1.05);
|
||
box-shadow: 0 5px 20px rgba(0, 212, 255, 0.4);
|
||
}
|
||
|
||
.alert-config-content {
|
||
overflow: hidden;
|
||
transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
|
||
}
|
||
|
||
.alert-config-content.collapsed {
|
||
max-height: 0;
|
||
opacity: 0;
|
||
}
|
||
|
||
.alert-config-content.expanded {
|
||
max-height: 500px;
|
||
opacity: 1;
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
color: rgba(255, 255, 255, 0.5);
|
||
}
|
||
|
||
.empty-state h3 {
|
||
margin-bottom: 10px;
|
||
color: rgba(255, 255, 255, 0.7);
|
||
}
|
||
|
||
.promo-code {
|
||
background: rgba(0, 255, 136, 0.1);
|
||
border: 1px dashed rgba(0, 255, 136, 0.3);
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
margin-top: 10px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.promo-code code {
|
||
color: #00ff88;
|
||
font-weight: bold;
|
||
background: rgba(0, 255, 136, 0.2);
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.product-monitor-checkbox {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-top: 10px;
|
||
padding: 8px;
|
||
background: rgba(0, 212, 255, 0.1);
|
||
border-radius: 8px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.product-monitor-checkbox input {
|
||
width: 18px;
|
||
height: 18px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.alert-banner {
|
||
background: linear-gradient(90deg, #ff6b6b, #ff8e53);
|
||
padding: 15px;
|
||
border-radius: 12px;
|
||
margin: 20px 0;
|
||
text-align: center;
|
||
animation: pulse 1.5s infinite;
|
||
}
|
||
|
||
.alert-banner h3 {
|
||
font-size: 18px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.alert-banner p {
|
||
font-size: 14px;
|
||
opacity: 0.9;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<h1>🦊 狐蒂云活动监控 - 性价比专区</h1>
|
||
<div class="status-bar">
|
||
<div class="status-item">
|
||
<span class="status-dot loading" id="statusDot" style="display:inline-block;width:10px;height:10px;min-width:10px;min-height:10px;max-width:10px;max-height:10px;border-radius:50%;"></span>
|
||
<span id="statusText">正在加载...</span>
|
||
</div>
|
||
<div class="status-item">
|
||
<span class="countdown">下次刷新: <span id="countdown">60</span>秒</span>
|
||
</div>
|
||
<div class="status-item">
|
||
<span>刷新间隔:</span>
|
||
<select class="interval-select" id="intervalSelect" onchange="changeInterval()">
|
||
<option value="300">5分钟</option>
|
||
<option value="180">3分钟</option>
|
||
<option value="60" selected>1分钟</option>
|
||
<option value="30">30秒</option>
|
||
<option value="20">20秒</option>
|
||
<option value="5">5秒</option>
|
||
<option value="3">3秒</option>
|
||
<option value="1">1秒</option>
|
||
</select>
|
||
</div>
|
||
<div class="status-item">
|
||
<button class="manual-refresh" onclick="manualRefresh()">🔄 立即刷新</button>
|
||
</div>
|
||
<div class="status-item">
|
||
<span>上次更新: <span id="lastUpdate">-</span></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<audio id="alertAudio" src="1671896015.mp3" preload="auto" style="display: none;"></audio>
|
||
|
||
<div class="container">
|
||
<div id="alertBanner"></div>
|
||
|
||
<div class="alert-config">
|
||
<button class="toggle-config-btn" onclick="toggleConfig()">
|
||
<span id="toggleBtnText">🔔 展开多条件提醒配置</span>
|
||
</button>
|
||
<div class="alert-config-content collapsed" id="configContent">
|
||
<div class="alert-config-section">
|
||
<label class="alert-config-label">选择产品地区(可多选):</label>
|
||
<div class="alert-config-checkboxes" id="regionCheckboxes">
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="香港" onchange="toggleRegion(this)"> 香港
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="美国" onchange="toggleRegion(this)"> 美国
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="日本" onchange="toggleRegion(this)"> 日本
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="韩国" onchange="toggleRegion(this)"> 韩国
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="台湾" onchange="toggleRegion(this)"> 台湾
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="上海" onchange="toggleRegion(this)"> 上海
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="alert-config-section">
|
||
<label class="alert-config-label">选择时长(可多选):</label>
|
||
<div class="alert-config-checkboxes" id="durationCheckboxes">
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="1年" onchange="toggleDuration(this)"> 1年
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="2年" onchange="toggleDuration(this)"> 2年
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="3年" onchange="toggleDuration(this)"> 3年
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="4年" onchange="toggleDuration(this)"> 4年
|
||
</label>
|
||
<label class="checkbox-item">
|
||
<input type="checkbox" value="5年" onchange="toggleDuration(this)"> 5年
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-title">
|
||
🟢 可购买商品
|
||
<span class="badge" id="availableCount">0</span>
|
||
</div>
|
||
<div class="products-grid" id="availableProducts">
|
||
<div class="loading">正在获取活动数据...</div>
|
||
</div>
|
||
|
||
<div class="section-title sold-out">
|
||
🔴 已售罄商品
|
||
<span class="badge" id="soldOutCount">0</span>
|
||
</div>
|
||
<div class="products-grid" id="soldOutProducts">
|
||
<div class="loading">正在获取活动数据...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const API_URL = '/api/activity';
|
||
const ACTIVITY_URL = 'https://www.szhdy.com/activities/default.html?method=activity&id=9';
|
||
let countdownTimer;
|
||
let countdown = 60;
|
||
let monitoredProducts = new Set();
|
||
let previousProductStates = new Map();
|
||
let audioPlayed = new Set();
|
||
let selectedRegions = new Set();
|
||
let selectedDurations = new Set();
|
||
let conditionalAlertPlayed = new Set();
|
||
|
||
function getInterval() {
|
||
return parseInt(document.getElementById('intervalSelect').value);
|
||
}
|
||
|
||
function toggleRegion(checkbox) {
|
||
if (checkbox.checked) {
|
||
selectedRegions.add(checkbox.value);
|
||
} else {
|
||
selectedRegions.delete(checkbox.value);
|
||
}
|
||
}
|
||
|
||
function toggleDuration(checkbox) {
|
||
if (checkbox.checked) {
|
||
selectedDurations.add(checkbox.value);
|
||
} else {
|
||
selectedDurations.delete(checkbox.value);
|
||
}
|
||
}
|
||
|
||
let isConfigExpanded = false;
|
||
|
||
function toggleConfig() {
|
||
const configContent = document.getElementById('configContent');
|
||
const toggleBtnText = document.getElementById('toggleBtnText');
|
||
|
||
isConfigExpanded = !isConfigExpanded;
|
||
|
||
if (isConfigExpanded) {
|
||
configContent.classList.remove('collapsed');
|
||
configContent.classList.add('expanded');
|
||
toggleBtnText.textContent = '🔔 收起多条件提醒配置';
|
||
} else {
|
||
configContent.classList.remove('expanded');
|
||
configContent.classList.add('collapsed');
|
||
toggleBtnText.textContent = '🔔 展开多条件提醒配置';
|
||
}
|
||
}
|
||
|
||
function toggleMonitor(productId) {
|
||
if (monitoredProducts.has(productId)) {
|
||
monitoredProducts.delete(productId);
|
||
audioPlayed.delete(productId);
|
||
} else {
|
||
monitoredProducts.add(productId);
|
||
}
|
||
}
|
||
|
||
function playAlert() {
|
||
const audio = document.getElementById('alertAudio');
|
||
audio.currentTime = 0;
|
||
audio.play().catch(e => console.log('音频播放需要用户交互'));
|
||
}
|
||
|
||
function showAlertBanner(products) {
|
||
const banner = document.getElementById('alertBanner');
|
||
if (products.length > 0) {
|
||
banner.innerHTML = `
|
||
<div class="alert-banner">
|
||
<h3>🎉 有产品可以购买啦!</h3>
|
||
<p>${products.map(p => escapeHtml(p.title)).join('、')}</p>
|
||
</div>
|
||
`;
|
||
} else {
|
||
banner.innerHTML = '';
|
||
}
|
||
}
|
||
|
||
function checkConditionalAlert(product) {
|
||
if (selectedRegions.size === 0 && selectedDurations.size === 0) {
|
||
return false;
|
||
}
|
||
|
||
const productId = product.id;
|
||
if (conditionalAlertPlayed.has(productId)) {
|
||
return false;
|
||
}
|
||
|
||
let regionMatch = selectedRegions.size === 0;
|
||
let durationMatch = selectedDurations.size === 0;
|
||
|
||
if (selectedRegions.size > 0) {
|
||
for (const region of selectedRegions) {
|
||
if (product.title.includes(region)) {
|
||
regionMatch = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (selectedDurations.size > 0 && product.duration) {
|
||
for (const duration of selectedDurations) {
|
||
if (product.duration.includes(duration)) {
|
||
durationMatch = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (regionMatch && durationMatch && !product.isSoldOut) {
|
||
conditionalAlertPlayed.add(productId);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
function changeInterval() {
|
||
countdown = getInterval();
|
||
updateCountdownDisplay();
|
||
}
|
||
|
||
async function fetchActivity() {
|
||
const statusDot = document.getElementById('statusDot');
|
||
const statusText = document.getElementById('statusText');
|
||
|
||
statusDot.className = 'status-dot loading';
|
||
statusText.textContent = '正在获取数据...';
|
||
|
||
try {
|
||
const response = await fetch(API_URL);
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
parseAndRender(data.html);
|
||
statusDot.className = 'status-dot online';
|
||
statusText.textContent = '数据获取成功';
|
||
document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString();
|
||
} else {
|
||
throw new Error(data.error);
|
||
}
|
||
} catch (error) {
|
||
console.error('获取数据失败:', error);
|
||
showError(error.message);
|
||
statusDot.className = 'status-dot offline';
|
||
statusText.textContent = '获取失败: ' + error.message;
|
||
}
|
||
}
|
||
|
||
function parseAndRender(html) {
|
||
const parser = new DOMParser();
|
||
const doc = parser.parseFromString(html, 'text/html');
|
||
|
||
const availableProducts = [];
|
||
const soldOutProducts = [];
|
||
const allProducts = [];
|
||
|
||
const section = doc.querySelector('#navigation_379');
|
||
if (!section) {
|
||
showError('未找到性价比专区');
|
||
return;
|
||
}
|
||
|
||
const cards = section.querySelectorAll('.activity-promotion-card, .cat-styleB-promotion-card, .cat-styleC-promotion-card');
|
||
|
||
cards.forEach(card => {
|
||
const product = parseProductCard(card);
|
||
allProducts.push(product);
|
||
if (product.isSoldOut) {
|
||
soldOutProducts.push(product);
|
||
} else {
|
||
availableProducts.push(product);
|
||
}
|
||
});
|
||
|
||
availableProducts.sort((a, b) => {
|
||
if (a.title.includes('韩国')) return -1;
|
||
if (b.title.includes('韩国')) return 1;
|
||
return 0;
|
||
});
|
||
|
||
checkProductStatusChanges(allProducts);
|
||
|
||
renderProducts(availableProducts, 'availableProducts', false);
|
||
renderProducts(soldOutProducts, 'soldOutProducts', true);
|
||
|
||
document.getElementById('availableCount').textContent = availableProducts.length;
|
||
document.getElementById('soldOutCount').textContent = soldOutProducts.length;
|
||
}
|
||
|
||
function checkProductStatusChanges(products) {
|
||
products.forEach(product => {
|
||
const productId = product.id;
|
||
const wasSoldOut = previousProductStates.get(productId);
|
||
const isNowSoldOut = product.isSoldOut;
|
||
|
||
if (wasSoldOut !== undefined) {
|
||
if (wasSoldOut && !isNowSoldOut) {
|
||
if (monitoredProducts.has(productId) && !audioPlayed.has(productId)) {
|
||
playAlert();
|
||
audioPlayed.add(productId);
|
||
}
|
||
|
||
if (checkConditionalAlert(product)) {
|
||
playAlert();
|
||
conditionalAlertPlayed.add(productId);
|
||
}
|
||
}
|
||
}
|
||
|
||
previousProductStates.set(productId, isNowSoldOut);
|
||
});
|
||
}
|
||
|
||
function parseProductCard(card) {
|
||
const product = {
|
||
id: card.getAttribute('data-id'),
|
||
title: '',
|
||
tag: '',
|
||
desc: '',
|
||
specs: [],
|
||
price: '',
|
||
oldPrice: '',
|
||
discount: '',
|
||
remaining: '',
|
||
promoCode: '',
|
||
duration: '',
|
||
buyUrl: '',
|
||
isSoldOut: false
|
||
};
|
||
|
||
let titleEl = card.querySelector('.card-title h1');
|
||
if (!titleEl) titleEl = card.querySelector('.cat-styleBtitle h1');
|
||
if (!titleEl) titleEl = card.querySelector('.cat-styleCtitle h1');
|
||
if (titleEl) product.title = titleEl.textContent.trim();
|
||
|
||
const tagEl = card.querySelector('.titleTags');
|
||
if (tagEl) product.tag = tagEl.textContent.trim();
|
||
|
||
let descEl = card.querySelector('.card-desc p');
|
||
if (!descEl) descEl = card.querySelector('.cat-styleBdesc p');
|
||
if (!descEl) descEl = card.querySelector('.cat-styleCdesc p');
|
||
if (descEl) product.desc = descEl.textContent.trim();
|
||
|
||
const formContainers = card.querySelectorAll('.form-container');
|
||
formContainers.forEach(container => {
|
||
const label = container.querySelector('.form-title h5');
|
||
const value = container.querySelector('.form-text');
|
||
if (label && value) {
|
||
const labelText = label.textContent.trim();
|
||
const valueText = value.textContent.trim();
|
||
|
||
if (labelText.includes('温馨提醒') && valueText.includes('优惠券')) {
|
||
const codeMatch = valueText.match(/(\s*(\S+)\s*)/);
|
||
if (codeMatch && codeMatch[1]) {
|
||
product.promoCode = codeMatch[1];
|
||
}
|
||
} else if (!labelText.includes('温馨提醒')) {
|
||
product.specs.push({ label: labelText, value: valueText });
|
||
|
||
if (!product.duration) {
|
||
if (labelText.includes('时长') || labelText.includes('周期') ||
|
||
labelText.includes('有效期') || labelText.includes('月') ||
|
||
labelText.includes('年') || labelText.includes('天')) {
|
||
product.duration = valueText;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
const currentPriceEl = card.querySelector('.main-price-current');
|
||
if (currentPriceEl) {
|
||
product.price = currentPriceEl.textContent.trim();
|
||
|
||
if (!product.duration) {
|
||
const parent = currentPriceEl.parentElement;
|
||
if (parent) {
|
||
const text = parent.textContent;
|
||
const durationMatch = text.match(/(?:\d+)\s*(?:月|年|天|个月|个年)/);
|
||
if (durationMatch) {
|
||
product.duration = durationMatch[0];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const oldPriceEl = card.querySelector('.main-price-old');
|
||
if (oldPriceEl) product.oldPrice = oldPriceEl.textContent.trim();
|
||
|
||
const discountEl = card.querySelector('.main-price-discount');
|
||
if (discountEl) product.discount = discountEl.textContent.trim();
|
||
|
||
const remainingEl = card.querySelector('.head-tag-item:not(.active)');
|
||
if (remainingEl) {
|
||
const text = remainingEl.textContent.trim();
|
||
if (text.includes('剩余')) {
|
||
product.remaining = text;
|
||
}
|
||
}
|
||
|
||
const allText = card.textContent;
|
||
if (!product.duration) {
|
||
const durationMatch = allText.match(/(?:\d+)\s*(?:月|年|天|个月|个年)/);
|
||
if (durationMatch) {
|
||
product.duration = durationMatch[0];
|
||
}
|
||
}
|
||
|
||
const durationEls = card.querySelectorAll('[class*="duration"], [class*="period"], [class*="cycle"], [class*="time"], [class*="month"], [class*="year"]');
|
||
durationEls.forEach(el => {
|
||
if (!product.duration) {
|
||
product.duration = el.textContent.trim();
|
||
}
|
||
});
|
||
|
||
let buyBtn = card.querySelector('.form-footer-butt');
|
||
if (!buyBtn) buyBtn = card.querySelector('.cat-styleBform-footer-butt');
|
||
if (!buyBtn) buyBtn = card.querySelector('.cat-styleCform-footer a');
|
||
if (buyBtn) {
|
||
if (buyBtn.classList.contains('disableButton')) {
|
||
product.isSoldOut = true;
|
||
}
|
||
const href = buyBtn.getAttribute('href');
|
||
if (href && href.includes('pid')) {
|
||
product.buyUrl = 'https://www.szhdy.com' + href;
|
||
}
|
||
}
|
||
|
||
return product;
|
||
}
|
||
|
||
function renderProducts(products, containerId, isSoldOut) {
|
||
const container = document.getElementById(containerId);
|
||
|
||
if (products.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="empty-state">
|
||
<h3>${isSoldOut ? '暂无售罄商品' : '暂无可购买商品'}</h3>
|
||
<p>${isSoldOut ? '所有商品都在售!' : '请等待下次刷新或手动刷新查看最新状态'}</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = products.map(product => `
|
||
<div class="product-card ${isSoldOut ? 'sold-out' : ''}">
|
||
<div class="product-header">
|
||
<div>
|
||
<div class="product-title">${escapeHtml(product.title)}</div>
|
||
${product.tag ? `<span class="product-tag">${escapeHtml(product.tag)}</span>` : ''}
|
||
${product.desc ? `<div class="product-desc">${escapeHtml(product.desc)}</div>` : ''}
|
||
</div>
|
||
<span class="product-badge">${isSoldOut ? '已售罄' : '可购买'}</span>
|
||
</div>
|
||
<div class="product-specs">
|
||
${product.specs.map(spec => `
|
||
<div class="spec-row">
|
||
<span class="spec-label">${escapeHtml(spec.label)}</span>
|
||
<span class="spec-value">${escapeHtml(spec.value)}</span>
|
||
</div>
|
||
`).join('')}
|
||
${product.duration ? `
|
||
<div class="spec-row">
|
||
<span class="spec-label">时长</span>
|
||
<span class="spec-value">${escapeHtml(product.duration)}</span>
|
||
</div>
|
||
` : ''}
|
||
${product.promoCode ? `
|
||
<div class="promo-code">
|
||
🎫 优惠券: <code>${escapeHtml(product.promoCode)}</code>
|
||
</div>
|
||
` : ''}
|
||
</div>
|
||
<div class="product-footer">
|
||
<div class="price-info">
|
||
<div class="price-current">
|
||
¥${escapeHtml(product.price)}
|
||
<span>元/续费同价</span>
|
||
${product.discount ? `<span class="discount-tag">${escapeHtml(product.discount)}</span>` : ''}
|
||
${product.remaining ? `<span class="remaining-tag">${escapeHtml(product.remaining)}</span>` : ''}
|
||
</div>
|
||
${product.oldPrice ? `<div class="price-old">原价: ¥${escapeHtml(product.oldPrice)}</div>` : ''}
|
||
</div>
|
||
${isSoldOut
|
||
? '<span class="buy-btn sold-out">已售罄</span>'
|
||
: `<a href="${product.buyUrl || ACTIVITY_URL}" target="_blank" class="buy-btn">立即购买</a>`
|
||
}
|
||
</div>
|
||
<div class="product-monitor-checkbox">
|
||
<input type="checkbox" id="monitor-${product.id}"
|
||
onchange="toggleMonitor('${product.id}')"
|
||
${monitoredProducts.has(product.id) ? 'checked' : ''}>
|
||
<label for="monitor-${product.id}">监控此产品,上架时提醒</label>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
function showError(message) {
|
||
const containers = ['availableProducts', 'soldOutProducts'];
|
||
containers.forEach(id => {
|
||
document.getElementById(id).innerHTML = `
|
||
<div class="error-message">
|
||
<h3>获取数据失败</h3>
|
||
<p>${escapeHtml(message)}</p>
|
||
<p style="margin-top: 10px; font-size: 12px; opacity: 0.7;">请检查网络连接或稍后重试</p>
|
||
</div>
|
||
`;
|
||
});
|
||
}
|
||
|
||
function escapeHtml(text) {
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
function startCountdown() {
|
||
countdown = getInterval();
|
||
updateCountdownDisplay();
|
||
|
||
if (countdownTimer) clearInterval(countdownTimer);
|
||
|
||
countdownTimer = setInterval(() => {
|
||
countdown--;
|
||
updateCountdownDisplay();
|
||
|
||
if (countdown <= 0) {
|
||
fetchActivity();
|
||
countdown = getInterval();
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
function updateCountdownDisplay() {
|
||
document.getElementById('countdown').textContent = countdown;
|
||
}
|
||
|
||
function manualRefresh() {
|
||
fetchActivity();
|
||
countdown = getInterval();
|
||
}
|
||
|
||
fetchActivity();
|
||
startCountdown();
|
||
</script>
|
||
</body>
|
||
</html>
|