1.2.3-hotfix: Fixed data center not showing graphs.
This commit is contained in:
@@ -1,22 +1,355 @@
|
||||
{% extends "tactics/layout.html" %}
|
||||
|
||||
{% block title %}Data Center - Tactics{% endblock %}
|
||||
|
||||
{% block tactics_content %}
|
||||
<div class="bg-white dark:bg-slate-800 shadow rounded-lg p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">Data Center: Comparison</h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Controls -->
|
||||
<div class="flex space-x-4">
|
||||
<input type="text" placeholder="Search players to compare..." class="flex-1 rounded-md border-gray-300 dark:bg-slate-700 dark:border-slate-600 dark:text-white">
|
||||
<button class="px-4 py-2 bg-yrtv-600 text-white rounded-md">Add to Compare</button>
|
||||
<!-- Data Center Tab Content -->
|
||||
<div x-show="activeTab === 'data'" class="space-y-6 h-full flex flex-col">
|
||||
<!-- Header / Controls -->
|
||||
<div class="flex justify-between items-center bg-white dark:bg-slate-800 p-4 rounded-xl shadow-sm border border-gray-200 dark:border-slate-700">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<span>📊</span> 数据对比中心 (Data Comparison)
|
||||
</h3>
|
||||
<p class="text-xs text-gray-500 mt-1">拖拽左侧队员至下方区域,或点击搜索添加</p>
|
||||
</div>
|
||||
|
||||
<!-- Chart Placeholder -->
|
||||
<div class="h-96 bg-gray-50 dark:bg-slate-700 rounded flex items-center justify-center">
|
||||
<p class="text-gray-500 dark:text-gray-400">Multi-player Radar Chart / Bar Chart Area</p>
|
||||
<div class="flex gap-3">
|
||||
<div class="relative">
|
||||
<input type="text" x-model="searchQuery" @keydown.enter="searchPlayer()" placeholder="Search Player..." class="pl-3 pr-8 py-2 border border-gray-300 dark:border-slate-600 rounded-lg text-sm bg-gray-50 dark:bg-slate-900 dark:text-white focus:ring-2 focus:ring-yrtv-500">
|
||||
<button @click="searchPlayer()" class="absolute right-2 top-2 text-gray-400 hover:text-yrtv-600">🔍</button>
|
||||
</div>
|
||||
<button @click="clearDataLineup()" class="px-4 py-2 bg-red-50 text-red-600 rounded-lg hover:bg-red-100 text-sm font-bold transition">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<!-- Main Content Grid -->
|
||||
<div class="flex-1 grid grid-cols-1 lg:grid-cols-4 gap-6 min-h-0">
|
||||
|
||||
<!-- Left: Selected Players (Drop Zone) -->
|
||||
<div class="lg:col-span-1 bg-white dark:bg-slate-800 rounded-xl shadow-lg border border-gray-100 dark:border-slate-700 flex flex-col overflow-hidden transition-colors duration-200"
|
||||
:class="{'border-yrtv-400 bg-yrtv-50 dark:bg-slate-700 ring-2 ring-yrtv-200': isDraggingOverData}"
|
||||
@dragover.prevent="isDraggingOverData = true"
|
||||
@dragleave="isDraggingOverData = false"
|
||||
@drop="dropData($event)">
|
||||
|
||||
<div class="p-4 border-b border-gray-100 dark:border-slate-700 bg-gray-50 dark:bg-slate-700/50">
|
||||
<h4 class="font-bold text-gray-700 dark:text-gray-200 flex justify-between">
|
||||
<span>对比列表</span>
|
||||
<span class="text-xs bg-yrtv-100 text-yrtv-700 px-2 py-0.5 rounded-full" x-text="dataLineup.length + '/5'">0/5</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 p-4 space-y-3 overflow-y-auto custom-scroll min-h-[100px]">
|
||||
|
||||
<template x-for="(p, idx) in dataLineup" :key="p.steam_id_64">
|
||||
<div class="flex items-center p-3 bg-white dark:bg-slate-700 border border-gray-200 dark:border-slate-600 rounded-xl shadow-sm group hover:border-yrtv-300 transition relative">
|
||||
<!-- Color Indicator -->
|
||||
<div class="w-1.5 h-full absolute left-0 top-0 rounded-l-xl" :style="'background-color: ' + getPlayerColor(idx)"></div>
|
||||
|
||||
<div class="ml-3 flex-shrink-0">
|
||||
<template x-if="p.avatar_url">
|
||||
<img :src="p.avatar_url" class="w-10 h-10 rounded-full object-cover border border-gray-200 dark:border-slate-500">
|
||||
</template>
|
||||
<template x-if="!p.avatar_url">
|
||||
<div class="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-500 font-bold text-xs">
|
||||
<span x-text="(p.username || p.name).substring(0,2).toUpperCase()"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="ml-3 flex-1 min-w-0">
|
||||
<div class="text-sm font-bold text-gray-900 dark:text-white truncate" x-text="p.username || p.name"></div>
|
||||
<div class="text-xs text-gray-500 font-mono truncate" x-text="p.steam_id_64"></div>
|
||||
</div>
|
||||
<button @click="removeFromDataLineup(idx)" class="text-gray-400 hover:text-red-500 p-1 opacity-0 group-hover:opacity-100 transition">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-if="dataLineup.length < 5">
|
||||
<div class="h-24 border-2 border-dashed border-gray-200 dark:border-slate-600 rounded-xl flex flex-col items-center justify-center text-gray-400 text-sm hover:bg-gray-50 dark:hover:bg-slate-800 transition cursor-default"
|
||||
:class="{'border-yrtv-400 text-yrtv-600 bg-white': isDraggingOverData}">
|
||||
<span>+ 拖拽或搜索添加</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Visualization (Scrollable) -->
|
||||
<div class="lg:col-span-3 space-y-6 overflow-y-auto custom-scroll pr-2">
|
||||
|
||||
<!-- 1. Radar & Key Stats -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<!-- Radar Chart -->
|
||||
<div class="bg-white dark:bg-slate-800 p-6 rounded-xl shadow-lg border border-gray-100 dark:border-slate-700 min-h-[400px] flex flex-col">
|
||||
<h4 class="font-bold text-gray-800 dark:text-gray-200 mb-4">能力模型对比 (Capability Radar)</h4>
|
||||
<div class="flex-1 relative">
|
||||
<canvas id="dataRadarChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Basic Stats Table -->
|
||||
<div class="bg-white dark:bg-slate-800 p-6 rounded-xl shadow-lg border border-gray-100 dark:border-slate-700 flex flex-col">
|
||||
<h4 class="font-bold text-gray-800 dark:text-gray-200 mb-4">基础数据 (Basic Stats)</h4>
|
||||
<div class="flex-1 overflow-x-auto">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr class="text-gray-500 border-b border-gray-100 dark:border-slate-700">
|
||||
<th class="py-2 text-left">Player</th>
|
||||
<th class="py-2 text-right">Rating</th>
|
||||
<th class="py-2 text-right">K/D</th>
|
||||
<th class="py-2 text-right">ADR</th>
|
||||
<th class="py-2 text-right">KAST</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 dark:divide-slate-700">
|
||||
<template x-for="(stat, idx) in dataResult" :key="stat.steam_id">
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/50">
|
||||
<td class="py-3 flex items-center gap-2">
|
||||
<div class="w-3 h-3 rounded-full" :style="'background-color: ' + getPlayerColor(idx)"></div>
|
||||
<span class="font-bold dark:text-white truncate max-w-[100px]" x-text="stat.username"></span>
|
||||
</td>
|
||||
<td class="py-3 text-right font-mono font-bold" :class="getRatingColor(stat.basic.rating)" x-text="stat.basic.rating.toFixed(2)"></td>
|
||||
<td class="py-3 text-right font-mono" x-text="stat.basic.kd.toFixed(2)"></td>
|
||||
<td class="py-3 text-right font-mono" x-text="stat.basic.adr.toFixed(1)"></td>
|
||||
<td class="py-3 text-right font-mono" x-text="(stat.basic.kast * 100).toFixed(1) + '%'"></td>
|
||||
</tr>
|
||||
</template>
|
||||
<template x-if="!dataResult || dataResult.length === 0">
|
||||
<tr><td colspan="5" class="py-8 text-center text-gray-400">请选择选手进行对比</td></tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Detailed Breakdown (New) -->
|
||||
<div class="bg-white dark:bg-slate-800 p-6 rounded-xl shadow-lg border border-gray-100 dark:border-slate-700">
|
||||
<h4 class="font-bold text-gray-800 dark:text-gray-200 mb-6">详细数据对比 (Detailed Stats)</h4>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr class="bg-gray-50 dark:bg-slate-700/50 text-gray-500">
|
||||
<th class="px-4 py-3 text-left rounded-l-lg">Metric</th>
|
||||
<template x-for="(stat, idx) in dataResult" :key="'dh-'+stat.steam_id">
|
||||
<th class="px-4 py-3 text-center" :class="{'rounded-r-lg': idx === dataResult.length-1}">
|
||||
<span class="border-b-2 px-1 font-bold dark:text-gray-300" :style="'border-color: ' + getPlayerColor(idx)" x-text="stat.username"></span>
|
||||
</th>
|
||||
</template>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 dark:divide-slate-700">
|
||||
<!-- Row 1 -->
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">Rating (Rating/KD)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400 font-bold" x-text="stat.detailed.rating_t.toFixed(2)"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400 font-bold" x-text="stat.detailed.rating_ct.toFixed(2)"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">KD Ratio</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="stat.detailed.kd_t.toFixed(2)"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="stat.detailed.kd_ct.toFixed(2)"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">Win Rate (胜率)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="(stat.detailed.win_rate_t * 100).toFixed(1) + '%'"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="(stat.detailed.win_rate_ct * 100).toFixed(1) + '%'"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">First Kill Rate (首杀率)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="(stat.detailed.first_kill_t * 100).toFixed(1) + '%'"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="(stat.detailed.first_kill_ct * 100).toFixed(1) + '%'"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">First Death Rate (首死率)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="(stat.detailed.first_death_t * 100).toFixed(1) + '%'"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="(stat.detailed.first_death_ct * 100).toFixed(1) + '%'"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">KAST (贡献率)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="(stat.detailed.kast_t * 100).toFixed(1) + '%'"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="(stat.detailed.kast_ct * 100).toFixed(1) + '%'"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
|
||||
<!-- Row 4 -->
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">RWS (Round Win Share)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="stat.detailed.rws_t.toFixed(2)"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="stat.detailed.rws_ct.toFixed(2)"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">Multi-Kill Rate (多杀率)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="(stat.detailed.multikill_t * 100).toFixed(1) + '%'"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="(stat.detailed.multikill_ct * 100).toFixed(1) + '%'"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
|
||||
<!-- Row 5 -->
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">Headshot Rate (爆头率)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="(stat.detailed.hs_t * 100).toFixed(1) + '%'"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="(stat.detailed.hs_ct * 100).toFixed(1) + '%'"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400">Obj (下包 vs 拆包)</td>
|
||||
<template x-for="stat in dataResult">
|
||||
<td class="px-4 py-2 text-center font-mono text-xs">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto">
|
||||
<span class="text-amber-600 dark:text-amber-400" x-text="stat.detailed.obj_t.toFixed(2)"></span>
|
||||
<span class="text-blue-600 dark:text-blue-400" x-text="stat.detailed.obj_ct.toFixed(2)"></span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full max-w-[120px] mx-auto text-[10px] text-gray-400">
|
||||
<span>T-Side</span><span>CT-Side</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Map Performance -->
|
||||
<div class="bg-white dark:bg-slate-800 p-6 rounded-xl shadow-lg border border-gray-100 dark:border-slate-700">
|
||||
<h4 class="font-bold text-gray-800 dark:text-gray-200 mb-6">地图表现 (Map Performance)</h4>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead class="bg-gray-50 dark:bg-slate-700/50">
|
||||
<tr>
|
||||
<th class="px-4 py-2 text-left rounded-l-lg">Map</th>
|
||||
<template x-for="(stat, idx) in dataResult" :key="'h-'+stat.steam_id">
|
||||
<th class="px-4 py-2 text-center" :class="{'rounded-r-lg': idx === dataResult.length-1}">
|
||||
<span class="border-b-2 px-1" :style="'border-color: ' + getPlayerColor(idx)" x-text="stat.username"></span>
|
||||
</th>
|
||||
</template>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 dark:divide-slate-700">
|
||||
<!-- We need to iterate maps. Assuming mapMap is computed in JS -->
|
||||
<template x-for="mapName in allMaps" :key="mapName">
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/30">
|
||||
<td class="px-4 py-3 font-bold text-gray-600 dark:text-gray-300" x-text="mapName"></td>
|
||||
<template x-for="stat in dataResult" :key="'d-'+stat.steam_id+mapName">
|
||||
<td class="px-4 py-3 text-center">
|
||||
<template x-if="getMapStat(stat.steam_id, mapName)">
|
||||
<div>
|
||||
<div class="font-bold font-mono" :class="getRatingColor(getMapStat(stat.steam_id, mapName).rating)" x-text="getMapStat(stat.steam_id, mapName).rating.toFixed(2)"></div>
|
||||
<div class="text-[10px] text-gray-400" x-text="(getMapStat(stat.steam_id, mapName).win_rate * 100).toFixed(0) + '% (' + getMapStat(stat.steam_id, mapName).matches + ')'"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="!getMapStat(stat.steam_id, mapName)">
|
||||
<span class="text-gray-300">-</span>
|
||||
</template>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user