add some design to the app
This commit is contained in:
Generated
+9
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chart.js": "^4.2.1",
|
"chart.js": "^4.2.1",
|
||||||
"chartjs-plugin-annotation": "^2.2.1",
|
"chartjs-plugin-annotation": "^2.2.1",
|
||||||
|
"chartjs-plugin-datalabels": "^2.2.0",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
"vue-chartjs": "^5.2.0",
|
"vue-chartjs": "^5.2.0",
|
||||||
@@ -4385,6 +4386,14 @@
|
|||||||
"chart.js": ">=3.7.0"
|
"chart.js": ">=3.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chartjs-plugin-datalabels": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": ">=3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chart.js": "^4.2.1",
|
"chart.js": "^4.2.1",
|
||||||
"chartjs-plugin-annotation": "^2.2.1",
|
"chartjs-plugin-annotation": "^2.2.1",
|
||||||
|
"chartjs-plugin-datalabels": "^2.2.0",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
"vue-chartjs": "^5.2.0",
|
"vue-chartjs": "^5.2.0",
|
||||||
|
|||||||
+60
-10
@@ -1,31 +1,81 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav>
|
<header>
|
||||||
<router-link to="/">Home</router-link> |
|
<nav>
|
||||||
<router-link to="/about">About</router-link> |
|
<!-- <div class="navbar-element">hamburger</div> -->
|
||||||
<router-link to="/players">Players</router-link>
|
<div class="navbar-element"><span>logo </span>TONE API</div>
|
||||||
</nav>
|
<div class="spacer"></div>
|
||||||
<router-view/>
|
<router-link to="/" class="navbar-element">Home</router-link>
|
||||||
|
<router-link to="/about" class="navbar-element">About</router-link>
|
||||||
|
<router-link to="/players" class="navbar-element">Players</router-link>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<router-view />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-color: #282a36;
|
||||||
|
--accent: #353744;
|
||||||
|
--current-line: #44475a;
|
||||||
|
--comment: #6272a4;
|
||||||
|
--foreground: #f8f8f2;
|
||||||
|
--orange: #ffb86c;
|
||||||
|
--cyan: #8be9fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--bg-color);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #2c3e50;
|
color: var(--foreground);
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
'header header'
|
||||||
|
'filters body '
|
||||||
|
'stats body '
|
||||||
|
'footer body ';
|
||||||
|
grid-template-columns: 1fr 5fr;
|
||||||
|
grid-template-rows: auto 1fr auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
grid-area: header;
|
||||||
|
height: 64px;
|
||||||
|
background: var(--bg-color);
|
||||||
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
padding: 30px;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a {
|
nav a {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #2c3e50;
|
color: var(--foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a.router-link-exact-active {
|
nav a.router-link-exact-active {
|
||||||
color: #42b983;
|
color: var(--orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-element {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
padding: 24px 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -14,19 +14,22 @@ import {
|
|||||||
Legend
|
Legend
|
||||||
} from 'chart.js'
|
} from 'chart.js'
|
||||||
import annotationPlugin from 'chartjs-plugin-annotation'
|
import annotationPlugin from 'chartjs-plugin-annotation'
|
||||||
|
import dataLabel from 'chartjs-plugin-datalabels'
|
||||||
import { Store, useStore } from 'vuex'
|
import { Store, useStore } from 'vuex'
|
||||||
import { Player, Weapon } from '@/store/index'
|
import { Player, Weapon } from '@/store/index'
|
||||||
|
|
||||||
import { Scatter } from 'vue-chartjs'
|
import { Scatter } from 'vue-chartjs'
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annotationPlugin)
|
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annotationPlugin, dataLabel)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'PlayerChart',
|
name: 'PlayerChart',
|
||||||
props: {
|
props: {
|
||||||
filters: Object
|
filters: Object,
|
||||||
|
playerHighlighted: String
|
||||||
},
|
},
|
||||||
|
emits: ['highlightPlayer'],
|
||||||
components: {
|
components: {
|
||||||
Scatter
|
Scatter
|
||||||
},
|
},
|
||||||
@@ -37,10 +40,11 @@ export default defineComponent({
|
|||||||
{
|
{
|
||||||
label: 'Players',
|
label: 'Players',
|
||||||
labels: [] as string[],
|
labels: [] as string[],
|
||||||
borderColor: '#f87979',
|
borderColor: ['#f87979'],
|
||||||
backgroundColor: '#f87979',
|
backgroundColor: '#f87979',
|
||||||
pointRadius: [1],
|
pointRadius: [1],
|
||||||
pointStyle: ['dot'],
|
pointStyle: ['dot'],
|
||||||
|
hoverRadius: [4],
|
||||||
data: [] as {x:number, y:number}[]
|
data: [] as {x:number, y:number}[]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -55,13 +59,24 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
return { x: this.players[e].deaths, y: this.players[e].kills }
|
return { x: this.players[e].deaths, y: this.players[e].kills }
|
||||||
})
|
})
|
||||||
chartData.datasets[0].pointRadius = [1].fill(1, 0, chartData.datasets[0].labels.length)
|
chartData.datasets[0].pointRadius = Array(chartData.datasets[0].labels.length).fill(1)
|
||||||
chartData.datasets[0].pointStyle = ['dot'].fill('dot', 0, chartData.datasets[0].labels.length)
|
chartData.datasets[0].pointStyle = Array(chartData.datasets[0].labels.length).fill('dot')
|
||||||
|
chartData.datasets[0].borderColor = Array(chartData.datasets[0].labels.length).fill('darkblue')
|
||||||
|
chartData.datasets[0].hoverRadius = Array(chartData.datasets[0].labels.length).fill(4)
|
||||||
|
if (this.$props.playerHighlighted) {
|
||||||
|
const index = this.playerIdList.indexOf(this.$props.playerHighlighted)
|
||||||
|
chartData.datasets[0].pointRadius[index] = 20
|
||||||
|
chartData.datasets[0].pointStyle[index] = 'crossRot'
|
||||||
|
chartData.datasets[0].borderColor[index] = 'red'
|
||||||
|
chartData.datasets[0].hoverRadius[index] = 30
|
||||||
|
}
|
||||||
return chartData
|
return chartData
|
||||||
},
|
},
|
||||||
chartOptions () {
|
chartOptions () {
|
||||||
const options = {
|
const options = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
maintainAspectRatio: true,
|
||||||
|
animation: { duration: 500 },
|
||||||
plugins: {
|
plugins: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
@@ -77,7 +92,27 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
annotation: {}
|
annotation: {},
|
||||||
|
datalabels: {
|
||||||
|
formatter: (value:any, context:any) => {
|
||||||
|
return this.players[this.playerIdList[context.dataIndex]].username
|
||||||
|
},
|
||||||
|
backgroundColor: Array(this.playerIdList.length).fill(null),
|
||||||
|
borderColor: 'black',
|
||||||
|
borderWidth: Array(this.playerIdList.length).fill(0),
|
||||||
|
borderRadius: 5,
|
||||||
|
display: Array(this.playerIdList.length).fill('auto'),
|
||||||
|
align: '-45',
|
||||||
|
anchor: 'end',
|
||||||
|
clamp: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick: (e:Event, element:any) => {
|
||||||
|
this.$emit('highlightPlayer', element.length > 0 ? this.playerIdList[element[0].index] : undefined)
|
||||||
|
},
|
||||||
|
onHover: (e:any, element:any) => {
|
||||||
|
if (!e.native.target) return
|
||||||
|
(e.native.target as HTMLElement).style.cursor = element[0] ? 'pointer' : 'default'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.players) {
|
if (!this.players) {
|
||||||
@@ -107,9 +142,16 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.$props.playerHighlighted) {
|
||||||
|
const index = this.playerIdList.indexOf(this.$props.playerHighlighted)
|
||||||
|
options.plugins.datalabels.display[index] = true
|
||||||
|
options.plugins.datalabels.backgroundColor[index] = 'red'
|
||||||
|
options.plugins.datalabels.borderWidth[index] = 1
|
||||||
|
}
|
||||||
return options
|
return options
|
||||||
},
|
},
|
||||||
players ():{[key:string]:Player} { return this.store.getters.getPlayerList(this.filters) },
|
players ():{[key:string]:Player} { return this.store.getters.getPlayerList(this.filters) || {} },
|
||||||
playerIdList ():string[] { return Object.keys(this.players) }
|
playerIdList ():string[] { return Object.keys(this.players) }
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -123,8 +165,5 @@ export default defineComponent({
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.playerChart {
|
.playerChart {
|
||||||
position:sticky;
|
|
||||||
height:fit-content;
|
|
||||||
top:0
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+108
-16
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="playerList">
|
<div class="playerTable" v-on:keydown="selectNextPlayer" tabindex="0">
|
||||||
<div class="player">
|
<div class="playerHeaders">
|
||||||
|
<span>Index</span>
|
||||||
<span v-on:click="sortPlayerList('username')">username</span>
|
<span v-on:click="sortPlayerList('username')">username</span>
|
||||||
<span v-on:click="sortPlayerList('kills')">kills</span>
|
<span v-on:click="sortPlayerList('kills')">kills</span>
|
||||||
<span v-on:click="sortPlayerList('deaths')">deaths</span>
|
<span v-on:click="sortPlayerList('deaths')">deaths</span>
|
||||||
@@ -8,7 +9,9 @@
|
|||||||
<span v-on:click="sortPlayerList('max_kill_distance')">max kill distance</span>
|
<span v-on:click="sortPlayerList('max_kill_distance')">max kill distance</span>
|
||||||
<span v-on:click="sortPlayerList('avg_kill_distance')">average kill distance</span>
|
<span v-on:click="sortPlayerList('avg_kill_distance')">average kill distance</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="player" v-for="playerId in playerIdList" v-bind:key="playerId">
|
<div class="playerRow" v-for="(playerId, index) in playerIdList" v-bind:key="playerId"
|
||||||
|
v-on:click="$emit('highlightPlayer', playerId)" :ref="`player:` + playerId">
|
||||||
|
<span>{{ index }}</span>
|
||||||
<span>{{ players[playerId].username }}</span>
|
<span>{{ players[playerId].username }}</span>
|
||||||
<span>{{ players[playerId].kills }}</span>
|
<span>{{ players[playerId].kills }}</span>
|
||||||
<span>{{ players[playerId].deaths }}</span>
|
<span>{{ players[playerId].deaths }}</span>
|
||||||
@@ -23,33 +26,58 @@
|
|||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { Player } from '@/store/index'
|
import { Player } from '@/store/index'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
|
import { Vue } from 'vue-class-component'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
filters: Object
|
filters: Object,
|
||||||
|
playerHighlighted: String
|
||||||
},
|
},
|
||||||
|
emits: ['highlightPlayer'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
sortingData: { direction: -1, argument: '' as keyof Player | 'k/d' },
|
sortingData: { direction: -1, argument: '' as keyof Player | 'k/d' },
|
||||||
playerIdList: [] as string[],
|
playerIdList: [] as string[],
|
||||||
store: useStore()
|
store: useStore(),
|
||||||
|
selected: undefined as string | undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
players ():{[key:string]:Player} { return this.store.getters.getPlayerList(this.filters) }
|
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) }
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
players: {
|
players: {
|
||||||
handler (newval:{[key:string]:Player}): void {
|
handler (newval: { [key: string]: Player }): void {
|
||||||
if (!newval) return
|
if (!newval) return
|
||||||
this.playerIdList = []
|
this.playerIdList = []
|
||||||
this.sortingData.direction *= -1
|
this.sortingData.direction *= -1
|
||||||
this.playerIdList = Object.keys(newval)
|
this.playerIdList = Object.keys(newval)
|
||||||
this.sortPlayerList(this.sortingData.argument)
|
this.sortPlayerList(this.sortingData.argument)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const element = (this.$refs['player:' + this.selected] as HTMLElement[])
|
||||||
|
if (element && element.length > 0) element[0].classList.add('selected')
|
||||||
|
})
|
||||||
},
|
},
|
||||||
immediate: true
|
immediate: true
|
||||||
|
},
|
||||||
|
playerHighlighted (newval, oldval) {
|
||||||
|
const oldElement = this.$refs['player:' + oldval] as HTMLElement[]
|
||||||
|
if (oldElement) {
|
||||||
|
oldElement[0].classList.remove('selected')
|
||||||
|
delete this.selected
|
||||||
|
}
|
||||||
|
const newElement = this.$refs['player:' + newval] as HTMLElement[]
|
||||||
|
if (!newElement) return
|
||||||
|
newElement[0].classList.add('selected')
|
||||||
|
newElement[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||||
|
this.selected = newval
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updated () {
|
||||||
|
const element = this.$refs['player:' + this.$props.playerHighlighted] as HTMLElement[]
|
||||||
|
if (!element || !element[0]) return
|
||||||
|
element[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
sortPlayerList (arg: ((keyof Player) | 'k/d')) {
|
sortPlayerList (arg: ((keyof Player) | 'k/d')) {
|
||||||
if (arg === this.sortingData.argument) {
|
if (arg === this.sortingData.argument) {
|
||||||
@@ -61,7 +89,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
this.sortingData.argument = arg
|
this.sortingData.argument = arg
|
||||||
|
|
||||||
this.playerIdList.sort((a:string, b:string) => {
|
this.playerIdList.sort((a: string, b: string) => {
|
||||||
if (!this.players) {
|
if (!this.players) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -82,6 +110,26 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
selectNextPlayer (e: KeyboardEvent) {
|
||||||
|
const values = {
|
||||||
|
Home: -Infinity,
|
||||||
|
End: Infinity,
|
||||||
|
PageDown: 100,
|
||||||
|
PageUp: -100,
|
||||||
|
ArrowUp: -1,
|
||||||
|
ArrowDown: 1,
|
||||||
|
ArrowRight: 10,
|
||||||
|
ArrowLeft: -10
|
||||||
|
} as { [key: string]: number }
|
||||||
|
if (!this.selected) return
|
||||||
|
if (!Object.keys(values).includes(e.key)) return
|
||||||
|
e.preventDefault()
|
||||||
|
const index = this.playerIdList.indexOf(this.selected)
|
||||||
|
let nextIndex = (index + values[e.key])
|
||||||
|
if (nextIndex >= this.playerIdList.length) nextIndex = this.playerIdList.length - 1
|
||||||
|
if (nextIndex < 0) nextIndex = 0
|
||||||
|
this.$emit('highlightPlayer', this.playerIdList[nextIndex])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,17 +137,61 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.player {
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerTable {
|
||||||
|
overflow: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerHeaders {
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerRow,
|
||||||
|
.playerHeaders {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 200px 75px 75px 75px 100px 100px;
|
grid-template-columns: 50px 20ch 6ch 6ch 6ch 10ch 10ch;
|
||||||
background: lightgray;
|
background: var(--bg-color);
|
||||||
margin: 10px;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
|
/* border: 2px solid transparent; */
|
||||||
|
transition: border .25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerHeaders,
|
||||||
|
.playerRow:nth-child(2n) {
|
||||||
|
background: var(--current-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerRow:nth-child(2n+1) {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerHeaders span:hover,
|
||||||
|
.playerRow:hover {
|
||||||
|
background: var(--bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerRow>span:not(:last-child),
|
||||||
|
.playerHeaders>span:not(:last-child) {
|
||||||
|
border-right: solid var(--bg-color) 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player span {
|
.player span {
|
||||||
text-overflow: ellipsis;
|
pointer-events: none;
|
||||||
overflow: hidden;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
.selected {
|
||||||
|
background: var(--comment) !important;
|
||||||
|
}</style>
|
||||||
|
|||||||
+10
-1
@@ -43,7 +43,15 @@ export default createStore<State>({
|
|||||||
weapons: {}
|
weapons: {}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getPlayerList: (state) => ({ server, weapon }: { server?: number, weapon?: string }) => {
|
getPlayerList: (state) => ({ server, weapon, minKills, minDeaths }: { server?: number, weapon?: string, minKills:number, minDeaths:number }) => {
|
||||||
|
let playerFilter: string[] = []
|
||||||
|
if (state.players['']) {
|
||||||
|
playerFilter = Object.entries(state.players['']).filter(([, value]:any) => { return (value.kills >= minKills) || (value.deaths >= minDeaths) }).map(e => e[0])
|
||||||
|
}
|
||||||
|
if (playerFilter.length > 0 && state.players[(server || '') + (weapon || '')]) {
|
||||||
|
return Object.fromEntries(Object.entries(state.players[(server || '') + (weapon || '')]).filter(([key]) => playerFilter.includes(key)))
|
||||||
|
}
|
||||||
|
|
||||||
return state.players[(server || '') + (weapon || '')]
|
return state.players[(server || '') + (weapon || '')]
|
||||||
},
|
},
|
||||||
getWeaponList: (state) => ({ server, player }: { server?: number, player?: string }) => {
|
getWeaponList: (state) => ({ server, player }: { server?: number, player?: string }) => {
|
||||||
@@ -76,6 +84,7 @@ export default createStore<State>({
|
|||||||
data[e].max_kill_distance = Number(data[e].max_kill_distance)
|
data[e].max_kill_distance = Number(data[e].max_kill_distance)
|
||||||
data[e].avg_kill_distance = Number(data[e].avg_kill_distance)
|
data[e].avg_kill_distance = Number(data[e].avg_kill_distance)
|
||||||
})
|
})
|
||||||
|
|
||||||
context.commit('setPlayers', { data, filters: { weapon, server } })
|
context.commit('setPlayers', { data, filters: { weapon, server } })
|
||||||
},
|
},
|
||||||
async fetchWeapons (context, { player, server }) {
|
async fetchWeapons (context, { player, server }) {
|
||||||
|
|||||||
+30
-17
@@ -1,17 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<select v-on:change="changeFilter({ server: ($event.target as HTMLInputElement).value })">
|
<div id="filters">
|
||||||
<option></option>
|
<input type="number" min="0" v-model="filters.minKills">
|
||||||
<option v-for="(serverData) in servers" v-bind:key="serverData.id" :value="serverData.id">{{ serverData.name }}
|
<input type="number" min="0" v-model="filters.minDeaths">
|
||||||
</option>
|
<select v-on:change="changeFilter({ server: ($event.target as HTMLInputElement).value })">
|
||||||
</select>
|
<option></option>
|
||||||
<select v-on:change="changeFilter({ weapon: ($event.target as HTMLInputElement).value })">
|
<option v-for="(serverData) in servers" v-bind:key="serverData.id" :value="serverData.id">{{ serverData.name }}
|
||||||
<option></option>
|
</option>
|
||||||
<option v-for="(weaponData, weaponId) in weapons" v-bind:key="weaponId" :value="weaponId">{{ weaponId }}</option>
|
</select>
|
||||||
</select>
|
<select v-on:change="changeFilter({ weapon: ($event.target as HTMLInputElement).value })">
|
||||||
<div class="playersData">
|
<option></option>
|
||||||
|
<option v-for="(weaponData, weaponId) in weapons" v-bind:key="weaponId" :value="weaponId">{{ weaponId }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<PlayerList :filters="filters"></PlayerList>
|
<div id="playerView">
|
||||||
<PlayerChart :filters="filters"></PlayerChart>
|
<PlayerList :filters="filters" v-on:highlight-player="playerHighlighted = $event"
|
||||||
|
:playerHighlighted="playerHighlighted"></PlayerList>
|
||||||
|
<PlayerChart :filters="filters" v-on:highlight-player="playerHighlighted = $event"
|
||||||
|
:playerHighlighted="playerHighlighted"></PlayerChart>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -30,13 +36,13 @@ export default defineComponent({
|
|||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
filters: {},
|
filters: { minKills: 100, minDeaths: 100 },
|
||||||
store: useStore()
|
store: useStore(),
|
||||||
} as { filters: { weapon?: string, server?: string }, store: Store<any> }
|
playerHighlighted: undefined
|
||||||
|
} as { filters: { weapon?: string, server?: string }, store: Store<any>, playerHighlighted?: string }
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
servers (): Server[] { return this.store.state.servers },
|
servers (): Server[] { return this.store.state.servers },
|
||||||
players (): { [key: string]: Player } { return this.store.getters.getPlayerList(this.filters) },
|
|
||||||
weapons (): { [key: string]: Weapon } { return this.store.getters.getWeaponList(this.filters) }
|
weapons (): { [key: string]: Weapon } { return this.store.getters.getWeaponList(this.filters) }
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -58,8 +64,15 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.playersData {
|
#playerView {
|
||||||
|
margin: 0 1rem 1rem 1rem;
|
||||||
|
/* height:100%; */
|
||||||
display: grid;
|
display: grid;
|
||||||
|
overflow: auto;
|
||||||
grid-template-columns: 50% 50%;
|
grid-template-columns: 50% 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#filters {
|
||||||
|
grid-area: filters;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user