mirror of
https://github.com/vkaelin/LeagueStats.git
synced 2026-03-25 12:57:28 +00:00
feat: add stats for most played champions
This commit is contained in:
parent
763ba327ae
commit
d5394c64f8
7 changed files with 194 additions and 16 deletions
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="hidden">
|
||||
<symbol id="layers" class="stroke-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></symbol>
|
||||
<symbol id="people" class="stroke-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" /> <circle cx="9" cy="7" r="4" /> <path d="M23 21v-2a4 4 0 0 0-3-3.87" /> <path d="M16 3.13a4 4 0 0 1 0 7.75" /> </symbol>
|
||||
<symbol id="graph" class="stroke-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <line x1="18" y1="20" x2="18" y2="10" /> <line x1="12" y1="20" x2="12" y2="4" /> <line x1="6" y1="20" x2="6" y2="14" /> </symbol>
|
||||
<symbol id="info" class="stroke-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></symbol>
|
||||
|
|
|
|||
134
client/src/components/Summoner/SummonerChampions.vue
Normal file
134
client/src/components/Summoner/SummonerChampions.vue
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<div class="bg-blue-800 rounded-lg">
|
||||
<div class="relative heading flex items-center justify-center py-4 rounded-t-lg text-blue-200">
|
||||
<svg class="w-5 h-5" style="transform: rotate(-5deg);">
|
||||
<use xlink:href="#layers" />
|
||||
</svg>
|
||||
<span class="mx-4 text-lg font-bold uppercase">CHAMPIONS</span>
|
||||
<svg class="w-5 h-5" style="transform: rotate(5deg);">
|
||||
<use xlink:href="#layers" />
|
||||
</svg>
|
||||
<div class="absolute right-0 top-0 mt-3 mr-2">
|
||||
<Dropdown>
|
||||
<template v-slot:trigger>
|
||||
<svg class="w-4 h-4 cursor-pointer">
|
||||
<use xlink:href="#info" />
|
||||
</svg>
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<div class="px-2 text-white text-center text-sm select-none">
|
||||
<div>Stats based on</div>
|
||||
<div>
|
||||
<span class="text-teal-400 font-bold">{{ stats.global.count }}</span> matches
|
||||
</div>
|
||||
<div class="mt-2 leading-tight text-xs text-blue-100 font-normal italic">
|
||||
Load more matches
|
||||
<br />to have better results.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 px-4 flex items-baseline text-left text-sm text-blue-300 font-bold">
|
||||
<div class="ml-2 w-champion text-base text-blue-400">Champion</div>
|
||||
<div class="w-plays">plays</div>
|
||||
<div class="w-winrate">winrate</div>
|
||||
<div class="w-kda">kda</div>
|
||||
</div>
|
||||
<ul class="mt-1 text-sm text-gray-100 text-left">
|
||||
<li
|
||||
v-for="(champion, index) in stats.champion"
|
||||
:key="index"
|
||||
:class="[{'rounded-b-lg': index === stats.champion.length - 1}, {'bg-blue-760': index % 2 === 0}]"
|
||||
class="relative flex items-center px-4 py-2 leading-tight"
|
||||
>
|
||||
<div class="absolute text-xs" style="left: 6px;">{{ index + 1 }}.</div>
|
||||
<div class="ml-2 w-champion flex items-center">
|
||||
<div
|
||||
:style="{backgroundImage: `url('https://ddragon.leagueoflegends.com/cdn/${version}/img/champion/${champion._id}.png')`}"
|
||||
class="w-8 h-8 bg-center bg-cover rounded-full flex-shrink-0"
|
||||
></div>
|
||||
<div class="mx-1 truncate">{{ champion.champion }}</div>
|
||||
</div>
|
||||
<div class="w-plays">
|
||||
<div class="text-blue-400 text-xs">{{ champion.count }}</div>
|
||||
<div
|
||||
:style="{width: widthBar(champion.count, mostPlayed)}"
|
||||
class="mt-2px bg-blue-400 rounded-full h-1"
|
||||
></div>
|
||||
</div>
|
||||
<div class="w-winrate">
|
||||
<div class="text-green-400 text-xs">{{ champion.wins * 100 / champion.count|percent }}</div>
|
||||
<div
|
||||
:style="{width: widthBar(champion.wins, champion.count)}"
|
||||
class="mt-2px bg-green-400 rounded-full h-1"
|
||||
></div>
|
||||
</div>
|
||||
<div class="w-kda">
|
||||
<div
|
||||
class="text-purple-400 text-xs"
|
||||
>{{ kda(champion.kills, champion.deaths, champion.assists) }}</div>
|
||||
<div
|
||||
:style="{width: widthBar(kda(champion.kills, champion.deaths, champion.assists), bestKda)}"
|
||||
class="mt-2px bg-purple-400 rounded-full h-1"
|
||||
></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import Dropdown from '@/components/Dropdown.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dropdown,
|
||||
},
|
||||
|
||||
computed: {
|
||||
bestKda() {
|
||||
const bestChamp = this.stats.champion.reduce((a, b) => {
|
||||
return this.kda(a.kills, a.deaths, a.assists) > this.kda(b.kills, b.deaths, b.assists) ? a : b
|
||||
})
|
||||
return this.kda(bestChamp.kills, bestChamp.deaths, bestChamp.assists)
|
||||
},
|
||||
mostPlayed() {
|
||||
return this.stats.champion.reduce((a, b) => a.count > b.count ? a : b).count
|
||||
},
|
||||
...mapGetters('ddragon', ['version']),
|
||||
...mapState({
|
||||
stats: state => state.summoner.infos.stats
|
||||
}),
|
||||
},
|
||||
|
||||
methods: {
|
||||
kda(kills, deaths, assists) {
|
||||
return this.$options.filters.round((kills + assists) / deaths)
|
||||
},
|
||||
widthBar(value, total) {
|
||||
return `${value * 36 / total}px`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.w-champion {
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.w-plays {
|
||||
width: 55px;
|
||||
}
|
||||
|
||||
.w-winrate {
|
||||
width: 65px;
|
||||
}
|
||||
|
||||
.w-kda {
|
||||
width: 36px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="bg-blue-800 rounded-lg">
|
||||
<div class="mt-4 bg-blue-800 rounded-lg">
|
||||
<div class="relative heading flex justify-center py-4 rounded-t-lg text-blue-200">
|
||||
<svg class="w-6 h-6">
|
||||
<use xlink:href="#graph" />
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Vue.filter('secToTime', (sec) => {
|
|||
})
|
||||
|
||||
Vue.filter('percent', (value) => {
|
||||
return `${+value.toFixed(2)}%`
|
||||
return `${+value.toFixed(1)}%`
|
||||
})
|
||||
|
||||
Vue.filter('round', (value) => {
|
||||
|
|
|
|||
|
|
@ -62,18 +62,18 @@
|
|||
|
||||
<div class="mt-3 text-center flex">
|
||||
<div class="mt-4 w-3/12">
|
||||
<SummonerChampions />
|
||||
<SummonerStats />
|
||||
<SummonerMates />
|
||||
</div>
|
||||
<ul class="w-9/12 text-gray-900">
|
||||
<div class="w-9/12">
|
||||
<ul>
|
||||
<Match
|
||||
v-for="(match, index) in summonerInfos.matches"
|
||||
:key="index"
|
||||
:data="summonerInfos.matches[index]"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<LoadingButton
|
||||
v-if="moreMatchesToFetch"
|
||||
@clicked="moreMatches"
|
||||
|
|
@ -81,6 +81,8 @@
|
|||
btn-class="mt-4 block mx-auto bg-blue-800 px-4 py-2 rounded-md font-semibold hover:bg-blue-1000 shadow-lg"
|
||||
>More matches</LoadingButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="summonerLoading">
|
||||
|
|
@ -109,6 +111,7 @@ import MainFooter from '@/components/MainFooter.vue'
|
|||
import Match from '@/components/Match/Match.vue'
|
||||
import RecentActivity from '@/components/Summoner/RecentActivity.vue'
|
||||
import SearchForm from '@/components/SearchForm.vue'
|
||||
import SummonerChampions from '@/components/Summoner/SummonerChampions.vue'
|
||||
import SummonerLoader from '@/components/Summoner/SummonerLoader.vue'
|
||||
import SummonerMates from '@/components/Summoner/SummonerMates.vue'
|
||||
import SummonerRanked from '@/components/Summoner/SummonerRanked.vue'
|
||||
|
|
@ -122,6 +125,7 @@ export default {
|
|||
Match,
|
||||
RecentActivity,
|
||||
SearchForm,
|
||||
SummonerChampions,
|
||||
SummonerLoader,
|
||||
SummonerMates,
|
||||
SummonerRanked,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class StatsHelper {
|
|||
})
|
||||
}
|
||||
}
|
||||
const championStats = await this.matchRepository.championStats(account.puuid)
|
||||
const championClassStats = await this.matchRepository.championClassStats(account.puuid)
|
||||
const mates = await this.matchRepository.mates(account.puuid, account.name)
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ class StatsHelper {
|
|||
role: roleStats.sort(this.sortTeamByRole),
|
||||
class: championClassStats,
|
||||
mates,
|
||||
champion: championStats,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,43 @@ class MatchRepository {
|
|||
this.Match = Match
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's statistics for the 5 most played champions
|
||||
* @param puuid of the summoner
|
||||
*/
|
||||
championStats(puuid) {
|
||||
return this.Match.query().aggregate([
|
||||
{
|
||||
$match: {
|
||||
summoner_puuid: puuid,
|
||||
result: { $not: { $eq: 'Remake' } }
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$champion.id",
|
||||
champion: { $first: "$champion.name" },
|
||||
count: { $sum: 1 },
|
||||
wins: {
|
||||
$sum: {
|
||||
$cond: [{ $eq: ["$result", "Win"] }, 1, 0]
|
||||
}
|
||||
},
|
||||
losses: {
|
||||
$sum: {
|
||||
$cond: [{ $eq: ["$result", "Fail"] }, 1, 0]
|
||||
}
|
||||
},
|
||||
kills: { $sum: "$stats.kills" },
|
||||
deaths: { $sum: "$stats.deaths" },
|
||||
assists: { $sum: "$stats.assists" },
|
||||
}
|
||||
},
|
||||
{ $sort: { 'count': -1 } },
|
||||
{ $limit: 5 },
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Summoner's statistics for all played champion classes
|
||||
* @param puuid of the summoner
|
||||
|
|
|
|||
Loading…
Reference in a new issue