1.0.0 : Web Implemented.
This commit is contained in:
195
web/templates/home/index.html
Normal file
195
web/templates/home/index.html
Normal file
@@ -0,0 +1,195 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="space-y-8">
|
||||
<!-- Hero Section -->
|
||||
<div class="bg-gradient-to-r from-yrtv-900 to-yrtv-600 rounded-2xl shadow-xl overflow-hidden">
|
||||
<div class="px-6 py-12 sm:px-12 sm:py-16 lg:py-20 text-center">
|
||||
<h1 class="text-4xl font-extrabold tracking-tight text-white sm:text-5xl lg:text-6xl">
|
||||
JKTV CS2 队伍数据洞察平台
|
||||
</h1>
|
||||
<p class="mt-6 max-w-lg mx-auto text-xl text-yrtv-100 sm:max-w-3xl">
|
||||
深度挖掘比赛数据,提供战术研判、阵容模拟与多维能力分析。
|
||||
</p>
|
||||
<div class="mt-10 max-w-sm mx-auto sm:max-w-none sm:flex sm:justify-center">
|
||||
<a href="{{ url_for('matches.index') }}" class="flex items-center justify-center px-4 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-yrtv-700 bg-white hover:bg-yrtv-50 dark:bg-slate-800 dark:text-white dark:hover:bg-slate-700 sm:px-8">
|
||||
近期比赛
|
||||
</a>
|
||||
<a href="{{ url_for('players.index') }}" class="mt-3 sm:mt-0 sm:ml-3 flex items-center justify-center px-4 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-yrtv-500 bg-opacity-60 hover:bg-opacity-70 dark:bg-yrtv-600 dark:hover:bg-yrtv-700 sm:px-8">
|
||||
数据中心
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Match Parser Input -->
|
||||
<div class="mt-10 max-w-lg mx-auto">
|
||||
<form id="parserForm" class="sm:flex">
|
||||
<label for="match-url" class="sr-only">Match URL</label>
|
||||
<input id="match-url" name="url" type="text" placeholder="Paste 5E Match URL here..." required class="block w-full px-5 py-3 text-base text-gray-900 placeholder-gray-500 border border-transparent rounded-md shadow-sm focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-yrtv-600">
|
||||
<button type="submit" class="mt-3 w-full px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yrtv-500 shadow-sm hover:bg-yrtv-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yrtv-600 sm:mt-0 sm:ml-3 sm:flex-shrink-0 sm:inline-flex sm:items-center sm:w-auto">
|
||||
Parse
|
||||
</button>
|
||||
</form>
|
||||
<p id="parserMsg" class="mt-3 text-sm text-yrtv-100"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Live & Recent Status -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<!-- Activity Heatmap -->
|
||||
<div class="lg:col-span-3 bg-white dark:bg-slate-800 shadow rounded-lg p-6">
|
||||
<h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white mb-4">活跃度 (Activity)</h3>
|
||||
<div class="overflow-x-auto">
|
||||
<div id="calendar-heatmap" class="flex space-x-1 min-w-max pb-2">
|
||||
<!-- JS will populate this -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center justify-end text-xs text-gray-500 space-x-1">
|
||||
<span>Less</span>
|
||||
<span class="w-3 h-3 bg-gray-100 dark:bg-slate-700 rounded-sm"></span>
|
||||
<span class="w-3 h-3 bg-green-200 rounded-sm"></span>
|
||||
<span class="w-3 h-3 bg-green-400 rounded-sm"></span>
|
||||
<span class="w-3 h-3 bg-green-600 rounded-sm"></span>
|
||||
<span class="w-3 h-3 bg-green-800 rounded-sm"></span>
|
||||
<span>More</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Live Status -->
|
||||
<div class="bg-white dark:bg-slate-800 shadow rounded-lg p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white">正在进行 (Live)</h3>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
||||
Online
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-center py-8 text-gray-500">
|
||||
{% if live_matches %}
|
||||
<ul class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{% for m in live_matches %}
|
||||
<li class="py-2">
|
||||
<span class="font-bold">{{ m.map_name }}</span>: {{ m.score_team1 }} - {{ m.score_team2 }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>暂无正在进行的比赛</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Matches -->
|
||||
<div class="lg:col-span-2 bg-white dark:bg-slate-800 shadow rounded-lg p-6">
|
||||
<h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white mb-4">近期战况</h3>
|
||||
<div class="flow-root">
|
||||
<ul class="-my-5 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{% for match in recent_matches %}
|
||||
<li class="py-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white truncate">
|
||||
{{ match.map_name }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-500 truncate">
|
||||
{{ match.start_time | default('Unknown Date') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="inline-flex items-center text-base font-semibold text-gray-900 dark:text-white">
|
||||
{{ match.score_team1 }} : {{ match.score_team2 }}
|
||||
</div>
|
||||
<div>
|
||||
<a href="{{ url_for('matches.detail', match_id=match.match_id) }}" class="text-sm text-yrtv-600 hover:text-yrtv-900">详情</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="py-4 text-center text-gray-500">暂无比赛数据</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// --- Match Parser ---
|
||||
const parserForm = document.getElementById('parserForm');
|
||||
const parserMsg = document.getElementById('parserMsg');
|
||||
|
||||
if (parserForm) {
|
||||
parserForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const url = document.getElementById('match-url').value;
|
||||
parserMsg.innerText = "Parsing...";
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('url', url);
|
||||
|
||||
fetch("{{ url_for('main.parse_match') }}", {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
parserMsg.innerText = data.message;
|
||||
if(data.success) {
|
||||
document.getElementById('match-url').value = '';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
parserMsg.innerText = "Error: " + err;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- Heatmap ---
|
||||
const heatmapData = {{ heatmap_data|tojson }};
|
||||
const heatmapContainer = document.getElementById('calendar-heatmap');
|
||||
|
||||
if (heatmapContainer) {
|
||||
// Generate last 365 days
|
||||
const today = new Date();
|
||||
const oneDay = 24 * 60 * 60 * 1000;
|
||||
|
||||
let weeks = [];
|
||||
let currentWeek = [];
|
||||
const startDate = new Date(today.getTime() - (52 * 7 * oneDay));
|
||||
|
||||
for (let i = 0; i < 365; i++) {
|
||||
const d = new Date(startDate.getTime() + (i * oneDay));
|
||||
const dateStr = d.toISOString().split('T')[0];
|
||||
const count = heatmapData[dateStr] || 0;
|
||||
|
||||
let colorClass = 'bg-gray-100 dark:bg-slate-700';
|
||||
if (count > 0) colorClass = 'bg-green-200';
|
||||
if (count > 2) colorClass = 'bg-green-400';
|
||||
if (count > 5) colorClass = 'bg-green-600';
|
||||
if (count > 8) colorClass = 'bg-green-800';
|
||||
|
||||
currentWeek.push({date: dateStr, count: count, color: colorClass});
|
||||
|
||||
if (currentWeek.length === 7) {
|
||||
weeks.push(currentWeek);
|
||||
currentWeek = [];
|
||||
}
|
||||
}
|
||||
if (currentWeek.length > 0) weeks.push(currentWeek);
|
||||
|
||||
weeks.forEach(week => {
|
||||
const weekDiv = document.createElement('div');
|
||||
weekDiv.className = 'flex flex-col space-y-1';
|
||||
week.forEach(day => {
|
||||
const dayDiv = document.createElement('div');
|
||||
dayDiv.className = `w-3 h-3 rounded-sm ${day.color}`;
|
||||
dayDiv.title = `${day.date}: ${day.count} matches`;
|
||||
weekDiv.appendChild(dayDiv);
|
||||
});
|
||||
heatmapContainer.appendChild(weekDiv);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user