add chart
This commit is contained in:
Generated
+27
@@ -8,8 +8,10 @@
|
|||||||
"name": "toneapi_webclient",
|
"name": "toneapi_webclient",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chart.js": "^4.2.1",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
|
"vue-chartjs": "^5.2.0",
|
||||||
"vue-class-component": "^8.0.0-0",
|
"vue-class-component": "^8.0.0-0",
|
||||||
"vue-router": "^4.0.3",
|
"vue-router": "^4.0.3",
|
||||||
"vuex": "^4.0.0"
|
"vuex": "^4.0.0"
|
||||||
@@ -1917,6 +1919,11 @@
|
|||||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
|
||||||
|
},
|
||||||
"node_modules/@leichtgewicht/ip-codec": {
|
"node_modules/@leichtgewicht/ip-codec": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||||
@@ -4358,6 +4365,17 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": "^7.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",
|
||||||
@@ -12237,6 +12255,15 @@
|
|||||||
"@vue/shared": "3.2.47"
|
"@vue/shared": "3.2.47"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-chartjs": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": "^4.1.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-class-component": {
|
"node_modules/vue-class-component": {
|
||||||
"version": "8.0.0-rc.1",
|
"version": "8.0.0-rc.1",
|
||||||
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-8.0.0-rc.1.tgz",
|
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-8.0.0-rc.1.tgz",
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chart.js": "^4.2.1",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
|
"vue-chartjs": "^5.2.0",
|
||||||
"vue-class-component": "^8.0.0-0",
|
"vue-class-component": "^8.0.0-0",
|
||||||
"vue-router": "^4.0.3",
|
"vue-router": "^4.0.3",
|
||||||
"vuex": "^4.0.0"
|
"vuex": "^4.0.0"
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<div class="playerChart">
|
||||||
|
<Scatter :data="chart" :options="options" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Tooltip,
|
||||||
|
Legend
|
||||||
|
} from 'chart.js'
|
||||||
|
|
||||||
|
import { Scatter } from 'vue-chartjs'
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend)
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'PlayerChart',
|
||||||
|
props: {
|
||||||
|
players: Object
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Scatter
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
chart () {
|
||||||
|
const chartData = {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Players',
|
||||||
|
labels: [] as string[],
|
||||||
|
borderColor: '#f87979',
|
||||||
|
backgroundColor: '#f87979',
|
||||||
|
data: [] as {x:number, y:number}[]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (!this.$props.players) {
|
||||||
|
return chartData
|
||||||
|
}
|
||||||
|
chartData.datasets[0].labels = Object.keys(this.$props.players)
|
||||||
|
chartData.datasets[0].data = Object.values(this.$props.players).map((e:any) => ({ x: e.deaths, y: e.kills }))
|
||||||
|
return chartData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* watch: {
|
||||||
|
players: function (newval: any, oldval: any): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
}, */
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: (ctx:any) => {
|
||||||
|
let label:string
|
||||||
|
if (!this.$props.players) {
|
||||||
|
label = ctx.dataset.labels[ctx.dataIndex]
|
||||||
|
} else {
|
||||||
|
label = this.$props.players[ctx.dataset.labels[ctx.dataIndex]].username
|
||||||
|
}
|
||||||
|
label += ' (' + ctx.parsed.y + ' kills ' + ctx.parsed.x + ' deaths)'
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.playerChart {
|
||||||
|
position:sticky;
|
||||||
|
height:fit-content;
|
||||||
|
top:0
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,86 +1,91 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="playerList">
|
<div class="playerList">
|
||||||
<select v-on:change="weaponFilter">
|
|
||||||
<option>global</option>
|
|
||||||
<option v-for="(weaponData, weaponId) in weapons" v-bind:key="weaponId">{{weaponId}}</option>
|
|
||||||
</select>
|
|
||||||
<div class="player">
|
<div class="player">
|
||||||
<span>username</span>
|
<span v-on:click="sortPlayerList('username')">username</span>
|
||||||
<span>kills</span>
|
<span v-on:click="sortPlayerList('kills')">kills</span>
|
||||||
<span>deaths</span>
|
<span v-on:click="sortPlayerList('deaths')">deaths</span>
|
||||||
<span>k/d</span>
|
<span v-on:click="sortPlayerList('k/d')">k/d</span>
|
||||||
<span>max kill distance</span>
|
<span v-on:click="sortPlayerList('max_kill_distance')">max kill distance</span>
|
||||||
<span>average kill distance</span>
|
<span v-on:click="sortPlayerList('avg_kill_distance')">average kill distance</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="player" v-for="(playerData, playerId) in players" v-bind:key="playerId">
|
<div class="player" v-for="playerId in playerIdList" v-bind:key="playerId">
|
||||||
<span>{{ playerData.username }}</span>
|
<span>{{ $props.players[playerId].username }}</span>
|
||||||
<span>{{ playerData.kills }}</span>
|
<span>{{ players[playerId].kills }}</span>
|
||||||
<span>{{ playerData.deaths }}</span>
|
<span>{{ players[playerId].deaths }}</span>
|
||||||
<span>{{ Math.round(playerData.kills/playerData.deaths*100)/100 }}</span>
|
<span>{{ Math.round(players[playerId].kills / players[playerId].deaths * 100) / 100 }}</span>
|
||||||
<span>{{ playerData.max_kill_distance }}</span>
|
<span>{{ players[playerId].max_kill_distance }}</span>
|
||||||
<span>{{ Math.round(playerData.avg_kill_distance * 100) / 100}}</span>
|
<span>{{ Math.round(players[playerId].avg_kill_distance * 100) / 100 }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Options, Vue } from 'vue-class-component'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
@Options({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
msg: String
|
players: Object
|
||||||
}
|
},
|
||||||
})
|
data () {
|
||||||
export default class HelloWorld extends Vue {
|
return {
|
||||||
msg!: string;
|
sortingData: { direction: -1, argument: '' },
|
||||||
players: any = {}
|
playerIdList: [] as string[]
|
||||||
weapons: any = {}
|
}
|
||||||
created () {
|
},
|
||||||
this.fetchPlayers({})
|
watch: {
|
||||||
this.fetchWeapons({})
|
players: function (newval: any, oldval: any): void {
|
||||||
}
|
this.playerIdList = []
|
||||||
|
this.sortingData.direction *= -1
|
||||||
|
this.playerIdList = Object.keys(newval)
|
||||||
|
this.sortPlayerList(this.sortingData.argument)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sortPlayerList (arg: string) {
|
||||||
|
if (arg === this.sortingData.argument) {
|
||||||
|
this.sortingData.direction *= -1
|
||||||
|
} else {
|
||||||
|
if (arg === 'username') {
|
||||||
|
this.sortingData.direction = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.sortingData.argument = arg
|
||||||
|
|
||||||
async fetchPlayers ({ weapon, server }:{weapon?:string, server?:string}) {
|
this.playerIdList.sort((a:any, b:any) => {
|
||||||
const searchParams:Record<string, string> = {}
|
if (!this.$props.players) {
|
||||||
if (weapon) { searchParams.weapon = weapon }
|
return 0
|
||||||
if (server) { searchParams.server = server }
|
}
|
||||||
|
let varA = this.$props.players[a][arg]
|
||||||
|
let varB = this.$props.players[b][arg]
|
||||||
|
if (arg === 'k/d') {
|
||||||
|
varA = this.$props.players[a].kills / this.$props.players[a].deaths
|
||||||
|
varB = this.$props.players[b].kills / this.$props.players[b].deaths
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(
|
if (varA < varB) {
|
||||||
'https://tone.sleepycat.date/v1/client/players?' + new URLSearchParams(searchParams)
|
return -1 * this.sortingData.direction
|
||||||
)
|
}
|
||||||
const data = await response.json()
|
if (varA > varB) {
|
||||||
this.players = data
|
return 1 * this.sortingData.direction
|
||||||
}
|
}
|
||||||
|
// names must be equal
|
||||||
async fetchWeapons ({ server }:{server?:string}) {
|
return 0
|
||||||
const searchParams:Record<string, string> = {}
|
})
|
||||||
if (server) { searchParams.server = server }
|
|
||||||
const response = await fetch(
|
|
||||||
'https://tone.sleepycat.date/v1/client/weapons?' + new URLSearchParams(searchParams)
|
|
||||||
)
|
|
||||||
const data = await response.json()
|
|
||||||
this.weapons = data
|
|
||||||
console.log(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
weaponFilter (e:Event) {
|
|
||||||
const weapon = (e.target as HTMLInputElement).value
|
|
||||||
if (weapon === 'global') {
|
|
||||||
return this.fetchPlayers({ })
|
|
||||||
}
|
}
|
||||||
this.fetchPlayers({ weapon })
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.player {
|
.player {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 200px 100px 100px 100px 100px 100px;
|
grid-template-columns: 200px 75px 75px 75px 100px 100px;
|
||||||
background: lightgray;
|
background: lightgray;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player span {
|
.player span {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -1,23 +1,78 @@
|
|||||||
<template>
|
<template>
|
||||||
<PlayerList></PlayerList>
|
<select v-on:change="weaponFilter">
|
||||||
|
<option>global</option>
|
||||||
|
<option v-for="(weaponData, weaponId) in weapons" v-bind:key="weaponId">{{ weaponId }}</option>
|
||||||
|
</select>
|
||||||
|
<div class="playersData">
|
||||||
|
|
||||||
|
<PlayerList :players="players"></PlayerList>
|
||||||
|
<PlayerChart :players="players"></PlayerChart>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Options, Vue } from 'vue-class-component'
|
import { Options, Vue } from 'vue-class-component'
|
||||||
import PlayerList from '@/components/PlayerList.vue'
|
import PlayerList from '@/components/PlayerList.vue'
|
||||||
|
import PlayerChart from '@/components/PlayerChart.vue'
|
||||||
|
|
||||||
@Options({
|
@Options({
|
||||||
components: {
|
components: {
|
||||||
PlayerList
|
PlayerList, PlayerChart
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export default class PlayerView extends Vue {
|
export default class PlayerView extends Vue {
|
||||||
mounted = function () {
|
players: any = {}
|
||||||
console.log('q')
|
weapons: any = {}
|
||||||
};
|
created () {
|
||||||
|
this.fetchPlayers({})
|
||||||
|
this.fetchWeapons({})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchWeapons ({ server }: { server?: string }) {
|
||||||
|
const searchParams: Record<string, string> = {}
|
||||||
|
if (server) { searchParams.server = server }
|
||||||
|
const response = await fetch(
|
||||||
|
'https://tone.sleepycat.date/v1/client/weapons?' + new URLSearchParams(searchParams)
|
||||||
|
)
|
||||||
|
const data = await response.json()
|
||||||
|
this.weapons = data
|
||||||
|
}
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return { a: 1 }
|
return { a: 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weaponFilter (e: Event) {
|
||||||
|
const weapon = (e.target as HTMLInputElement).value
|
||||||
|
if (weapon === 'global') {
|
||||||
|
return this.fetchPlayers({})
|
||||||
|
}
|
||||||
|
this.fetchPlayers({ weapon })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchPlayers ({ weapon, server }: { weapon?: string, server?: string }) {
|
||||||
|
const searchParams: Record<string, string> = {}
|
||||||
|
if (weapon) { searchParams.weapon = weapon }
|
||||||
|
if (server) { searchParams.server = server }
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
'https://tone.sleepycat.date/v1/client/players?' + new URLSearchParams(searchParams)
|
||||||
|
)
|
||||||
|
const data = await response.json()
|
||||||
|
Object.keys(data).forEach(e => {
|
||||||
|
data[e].kills = Number(data[e].kills)
|
||||||
|
data[e].deaths = Number(data[e].deaths)
|
||||||
|
data[e].max_kill_distance = Number(data[e].max_kill_distance)
|
||||||
|
data[e].avg_kill_distance = Number(data[e].avg_kill_distance)
|
||||||
|
})
|
||||||
|
this.players = data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.playersData {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user