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>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="hidden">
|
<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="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="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>
|
<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>
|
<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">
|
<div class="relative heading flex justify-center py-4 rounded-t-lg text-blue-200">
|
||||||
<svg class="w-6 h-6">
|
<svg class="w-6 h-6">
|
||||||
<use xlink:href="#graph" />
|
<use xlink:href="#graph" />
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ Vue.filter('secToTime', (sec) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
Vue.filter('percent', (value) => {
|
Vue.filter('percent', (value) => {
|
||||||
return `${+value.toFixed(2)}%`
|
return `${+value.toFixed(1)}%`
|
||||||
})
|
})
|
||||||
|
|
||||||
Vue.filter('round', (value) => {
|
Vue.filter('round', (value) => {
|
||||||
|
|
|
||||||
|
|
@ -62,24 +62,26 @@
|
||||||
|
|
||||||
<div class="mt-3 text-center flex">
|
<div class="mt-3 text-center flex">
|
||||||
<div class="mt-4 w-3/12">
|
<div class="mt-4 w-3/12">
|
||||||
|
<SummonerChampions />
|
||||||
<SummonerStats />
|
<SummonerStats />
|
||||||
<SummonerMates />
|
<SummonerMates />
|
||||||
</div>
|
</div>
|
||||||
<ul class="w-9/12 text-gray-900">
|
<div class="w-9/12">
|
||||||
<Match
|
<ul>
|
||||||
v-for="(match, index) in summonerInfos.matches"
|
<Match
|
||||||
:key="index"
|
v-for="(match, index) in summonerInfos.matches"
|
||||||
:data="summonerInfos.matches[index]"
|
:key="index"
|
||||||
/>
|
:data="summonerInfos.matches[index]"
|
||||||
</ul>
|
/>
|
||||||
|
</ul>
|
||||||
|
<LoadingButton
|
||||||
|
v-if="moreMatchesToFetch"
|
||||||
|
@clicked="moreMatches"
|
||||||
|
:loading="matchesLoading"
|
||||||
|
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>
|
||||||
|
|
||||||
<LoadingButton
|
|
||||||
v-if="moreMatchesToFetch"
|
|
||||||
@clicked="moreMatches"
|
|
||||||
:loading="matchesLoading"
|
|
||||||
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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -109,6 +111,7 @@ import MainFooter from '@/components/MainFooter.vue'
|
||||||
import Match from '@/components/Match/Match.vue'
|
import Match from '@/components/Match/Match.vue'
|
||||||
import RecentActivity from '@/components/Summoner/RecentActivity.vue'
|
import RecentActivity from '@/components/Summoner/RecentActivity.vue'
|
||||||
import SearchForm from '@/components/SearchForm.vue'
|
import SearchForm from '@/components/SearchForm.vue'
|
||||||
|
import SummonerChampions from '@/components/Summoner/SummonerChampions.vue'
|
||||||
import SummonerLoader from '@/components/Summoner/SummonerLoader.vue'
|
import SummonerLoader from '@/components/Summoner/SummonerLoader.vue'
|
||||||
import SummonerMates from '@/components/Summoner/SummonerMates.vue'
|
import SummonerMates from '@/components/Summoner/SummonerMates.vue'
|
||||||
import SummonerRanked from '@/components/Summoner/SummonerRanked.vue'
|
import SummonerRanked from '@/components/Summoner/SummonerRanked.vue'
|
||||||
|
|
@ -122,6 +125,7 @@ export default {
|
||||||
Match,
|
Match,
|
||||||
RecentActivity,
|
RecentActivity,
|
||||||
SearchForm,
|
SearchForm,
|
||||||
|
SummonerChampions,
|
||||||
SummonerLoader,
|
SummonerLoader,
|
||||||
SummonerMates,
|
SummonerMates,
|
||||||
SummonerRanked,
|
SummonerRanked,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ class StatsHelper {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const championStats = await this.matchRepository.championStats(account.puuid)
|
||||||
const championClassStats = await this.matchRepository.championClassStats(account.puuid)
|
const championClassStats = await this.matchRepository.championClassStats(account.puuid)
|
||||||
const mates = await this.matchRepository.mates(account.puuid, account.name)
|
const mates = await this.matchRepository.mates(account.puuid, account.name)
|
||||||
|
|
||||||
|
|
@ -33,6 +34,7 @@ class StatsHelper {
|
||||||
role: roleStats.sort(this.sortTeamByRole),
|
role: roleStats.sort(this.sortTeamByRole),
|
||||||
class: championClassStats,
|
class: championClassStats,
|
||||||
mates,
|
mates,
|
||||||
|
champion: championStats,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,43 @@ class MatchRepository {
|
||||||
this.Match = Match
|
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
|
* Get Summoner's statistics for all played champion classes
|
||||||
* @param puuid of the summoner
|
* @param puuid of the summoner
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue