2.0.0 Alpha: Data Refinery
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
<div class="grid grid-cols-1 gap-8 xl:grid-cols-2">
|
||||
<section class="rounded-2xl border border-gray-100 bg-white p-6 shadow-lg dark:border-slate-700 dark:bg-slate-800"
|
||||
x-data="{ selected: 'last_20', periods: {{ period_stats|tojson }} }">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-gray-900 dark:text-white">阶段表现</h3>
|
||||
<p class="text-xs text-gray-500">窗口按该玩家最新一场比赛向前计算</p>
|
||||
</div>
|
||||
<select x-model="selected"
|
||||
class="rounded-lg border-gray-200 bg-gray-50 text-sm dark:border-slate-600 dark:bg-slate-700 dark:text-white">
|
||||
{% for period in period_stats %}
|
||||
<option value="{{ period.period_key }}">{{ period.period_label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{% for period in period_stats %}
|
||||
<div x-show="selected === '{{ period.period_key }}'"
|
||||
{% if period.period_key != 'last_20' %}style="display:none"{% endif %}
|
||||
class="mt-6">
|
||||
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
||||
<div class="rounded-xl bg-gray-50 p-3 dark:bg-slate-700/40">
|
||||
<div class="text-xs font-bold uppercase text-gray-400">Matches</div>
|
||||
<div class="mt-1 text-2xl font-black text-gray-900 dark:text-white">{{ period.matches }}</div>
|
||||
</div>
|
||||
<div class="rounded-xl bg-gray-50 p-3 dark:bg-slate-700/40">
|
||||
<div class="text-xs font-bold uppercase text-gray-400">Rating</div>
|
||||
<div class="mt-1 text-2xl font-black text-yrtv-600">{{ '%.2f'|format(period.avg_rating or 0) }}</div>
|
||||
</div>
|
||||
<div class="rounded-xl bg-gray-50 p-3 dark:bg-slate-700/40">
|
||||
<div class="text-xs font-bold uppercase text-gray-400">K/D</div>
|
||||
<div class="mt-1 text-2xl font-black text-gray-900 dark:text-white">{{ '%.2f'|format(period.avg_kd or 0) }}</div>
|
||||
</div>
|
||||
<div class="rounded-xl bg-gray-50 p-3 dark:bg-slate-700/40">
|
||||
<div class="text-xs font-bold uppercase text-gray-400">ADR</div>
|
||||
<div class="mt-1 text-2xl font-black text-gray-900 dark:text-white">{{ '%.1f'|format(period.avg_adr or 0) }}</div>
|
||||
</div>
|
||||
<div class="rounded-xl bg-gray-50 p-3 dark:bg-slate-700/40">
|
||||
<div class="text-xs font-bold uppercase text-gray-400">KAST</div>
|
||||
<div class="mt-1 text-2xl font-black text-gray-900 dark:text-white">{{ '%.1f%%'|format((period.avg_kast or 0) * 100) }}</div>
|
||||
</div>
|
||||
<div class="rounded-xl bg-gray-50 p-3 dark:bg-slate-700/40">
|
||||
<div class="text-xs font-bold uppercase text-gray-400">Win Rate</div>
|
||||
<div class="mt-1 text-2xl font-black {% if period.win_rate >= 0.5 %}text-green-600{% else %}text-red-500{% endif %}">
|
||||
{{ '%.0f%%'|format((period.win_rate or 0) * 100) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-xs text-gray-400">
|
||||
{% if period.sample_reliable %}
|
||||
样本充足
|
||||
{% else %}
|
||||
样本不足,仅供参考
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="mt-8 text-center text-sm text-gray-400">暂无阶段统计</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="rounded-2xl border border-gray-100 bg-white p-6 shadow-lg dark:border-slate-700 dark:bg-slate-800">
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-gray-900 dark:text-white">职业纪录</h3>
|
||||
<p class="text-xs text-gray-500">每项纪录都可追溯到具体比赛</p>
|
||||
</div>
|
||||
<div class="mt-6 grid grid-cols-2 gap-3 sm:grid-cols-3">
|
||||
{% for record in records %}
|
||||
<a href="{{ url_for('matches.detail', match_id=record.match_id) }}"
|
||||
class="rounded-xl border border-gray-100 bg-gray-50 p-3 transition hover:border-yrtv-300 hover:bg-yrtv-50 dark:border-slate-600 dark:bg-slate-700/40 dark:hover:bg-slate-700">
|
||||
<div class="truncate text-xs font-bold uppercase text-gray-400">{{ record.record_label }}</div>
|
||||
<div class="mt-1 text-2xl font-black text-gray-900 dark:text-white">
|
||||
{% if record.record_key in ['most_kills', 'most_headshots', 'longest_win_streak'] %}
|
||||
{{ record.record_value|int }}
|
||||
{% elif record.record_key == 'highest_adr' %}
|
||||
{{ '%.1f'|format(record.record_value) }}
|
||||
{% else %}
|
||||
{{ '%.2f'|format(record.record_value) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mt-2 truncate text-[10px] font-mono text-gray-400">{{ record.map_name }}</div>
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="col-span-full py-8 text-center text-sm text-gray-400">暂无职业纪录</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -213,6 +213,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include "players/_career_dashboard.html" %}
|
||||
|
||||
<!-- 2. Charts Section (Middle) -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<!-- Trend Chart -->
|
||||
@@ -221,8 +223,11 @@
|
||||
<h3 class="text-lg font-bold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<span>📈</span> 近期表现走势 (Performance Trend)
|
||||
</h3>
|
||||
<div class="flex bg-gray-100 dark:bg-slate-700 rounded-lg p-1">
|
||||
<button class="px-3 py-1 text-xs font-bold rounded-md bg-white dark:bg-slate-600 shadow-sm text-gray-800 dark:text-white">Recent 20</button>
|
||||
<div id="trend-period-buttons" class="flex flex-wrap bg-gray-100 dark:bg-slate-700 rounded-lg p-1">
|
||||
<button onclick="loadTrendPeriod('last_10', this)" class="px-3 py-1 text-xs font-bold rounded-md text-gray-500">10</button>
|
||||
<button onclick="loadTrendPeriod('last_20', this)" class="trend-active px-3 py-1 text-xs font-bold rounded-md bg-white dark:bg-slate-600 shadow-sm text-gray-800 dark:text-white">20</button>
|
||||
<button onclick="loadTrendPeriod('last_30', this)" class="px-3 py-1 text-xs font-bold rounded-md text-gray-500">30</button>
|
||||
<button onclick="loadTrendPeriod('career', this)" class="px-3 py-1 text-xs font-bold rounded-md text-gray-500">Career</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative h-80 w-full">
|
||||
@@ -256,7 +261,7 @@
|
||||
</div>
|
||||
|
||||
{% macro detail_item(label, value, key, format_str='{:.2f}', sublabel=None, count_label=None) %}
|
||||
{% set dist = distribution[key] if distribution else None %}
|
||||
{% set dist = distribution[key] if distribution and distribution[key] else None %}
|
||||
<div class="flex flex-col group relative h-full p-2 rounded hover:bg-gray-50 dark:hover:bg-slate-700/30 transition-colors">
|
||||
<div class="flex justify-between items-center mb-1">
|
||||
<span class="text-xs font-bold text-gray-400 uppercase tracking-wider truncate max-w-[150px]" title="{{ label }}">{{ label }}</span>
|
||||
@@ -273,7 +278,11 @@
|
||||
<div class="flex justify-between items-end mb-1">
|
||||
<div class="flex items-baseline gap-1">
|
||||
<span class="text-lg font-black text-gray-900 dark:text-white font-mono">
|
||||
{{ format_str.format(value if value is not none else 0) }}
|
||||
{% if value is none %}
|
||||
<span class="text-sm text-gray-400">N/A</span>
|
||||
{% else %}
|
||||
{{ format_str.format(value) }}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if sublabel %}
|
||||
<span class="text-[10px] text-gray-400">{{ sublabel }}</span>
|
||||
@@ -834,6 +843,7 @@
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let trendChartInstance = null;
|
||||
const profileSteamId = "{{ player.steam_id_64 }}";
|
||||
|
||||
function resetZoom() {
|
||||
if (trendChartInstance) {
|
||||
@@ -853,8 +863,29 @@ function likeComment(commentId, btn) {
|
||||
});
|
||||
}
|
||||
|
||||
function loadTrendPeriod(periodKey, button) {
|
||||
fetch(`/players/${profileSteamId}/charts_data?period=${periodKey}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!trendChartInstance) return;
|
||||
trendChartInstance.data.labels = data.trend.labels;
|
||||
trendChartInstance.data.datasets[0].data = data.trend.values;
|
||||
trendChartInstance.data.datasets[1].data = Array(data.trend.labels.length).fill(1.5);
|
||||
trendChartInstance.data.datasets[2].data = Array(data.trend.labels.length).fill(1.0);
|
||||
trendChartInstance.data.datasets[3].data = Array(data.trend.labels.length).fill(0.6);
|
||||
trendChartInstance.update();
|
||||
|
||||
document.querySelectorAll('#trend-period-buttons button').forEach(item => {
|
||||
item.classList.remove('bg-white', 'dark:bg-slate-600', 'shadow-sm', 'text-gray-800', 'dark:text-white');
|
||||
item.classList.add('text-gray-500');
|
||||
});
|
||||
button.classList.remove('text-gray-500');
|
||||
button.classList.add('bg-white', 'dark:bg-slate-600', 'shadow-sm', 'text-gray-800', 'dark:text-white');
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const steamId = "{{ player.steam_id_64 }}";
|
||||
const steamId = profileSteamId;
|
||||
|
||||
fetch(`/players/${steamId}/charts_data`)
|
||||
.then(response => response.json())
|
||||
|
||||
Reference in New Issue
Block a user