add gamemode filter and multiselection

This commit is contained in:
2023-06-21 13:10:37 +02:00
parent 99272676cb
commit ac31b7fb7a
13 changed files with 333 additions and 13528 deletions
+6
View File
@@ -3,3 +3,9 @@ indent_style = space
indent_size = 2 indent_size = 2
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
+10 -1
View File
@@ -1,21 +1,30 @@
module.exports = { module.exports = {
root: true, root: true,
env: { env: {
node: true node: true
}, },
extends: [ extends: [
'plugin:vue/vue3-essential', 'plugin:vue/vue3-essential',
'@vue/standard', '@vue/standard',
'@vue/typescript/recommended' '@vue/typescript/recommended'
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2020 ecmaVersion: 2020
}, },
rules: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'@typescript-eslint/indent': ['error', 2], '@typescript-eslint/indent': ['error', 2],
camelcase: 0 camelcase: 0
} },
extends: [
'plugin:vue/vue3-essential',
'@vue/standard',
'@vue/typescript/recommended'
]
} }
-13463
View File
File diff suppressed because it is too large Load Diff
+186
View File
@@ -0,0 +1,186 @@
<template>
<div class="gamemodeChart" ref="container">
<LoadingBar v-if="progress !== 1" :value="progress"></LoadingBar>
<Doughnut :data="chart" :options="chartOptions" v-if="progress === 1" />
</div>
</template>
<script lang="ts">
import { Chart as ChartJS, ArcElement, Tooltip, Legend, ChartData, ChartOptions } from 'chart.js'
import dataLabel from 'chartjs-plugin-datalabels'
import { Weapon, Filter, useKillStore } from '@/stores/kill'
import { Doughnut } from 'vue-chartjs'
import { defineComponent, PropType, Ref, unref } from 'vue'
import gamemodes from '../stores/gamemodes.json'
import LoadingBar from './LoadingBar.vue'
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
export default defineComponent({
name: 'GamemodeChart',
props: {
filters: Object as PropType<Filter>,
playerHighlighted: String
},
emits: ['highlightPlayer'],
components: {
Doughnut, LoadingBar
},
mounted () {
this.refreshColors++
},
data: () => {
return {
store: useKillStore(),
refreshColors: 0
}
},
computed: {
progress () {
const filter = new Filter(this.filters)
delete filter.gamemode
const data = this.store.getList('gamemodes', filter)?.value
if (!data) return this.store.fetch('gamemodes', filter).value.progress.value
return data.progress.value
},
colors () {
// eslint-disable-next-line no-unused-expressions
this.refreshColors
const colors = {} as { [key: string]: string }
if (this.$refs.container) {
const style = getComputedStyle(this.$refs.container as Element)
colors.fg = style.getPropertyValue('--foreground')
colors.bg = style.getPropertyValue('--bg-color')
colors.orange = style.getPropertyValue('--orange')
colors.cyan = style.getPropertyValue('--cyan')
colors.yellow = style.getPropertyValue('--yellow')
colors.green = style.getPropertyValue('--green')
colors.purple = style.getPropertyValue('--purple')
colors.red = style.getPropertyValue('--red')
colors.pink = style.getPropertyValue('--pink')
colors.currentLine = style.getPropertyValue('--current-line')
} else {
colors.fg = '#ffffff'
}
return colors
},
chart (): ChartData<'doughnut', number[]> {
const colors = [
'cyan',
'green',
'orange',
'pink',
'purple',
'red',
'yellow'
]
const chartData: ChartData<'doughnut', number[]> = {
datasets: [
{
label: 'Weapons',
borderColor: () => this.colors.fg,
backgroundColor: (context) => {
return this.colors[
colors[(context.dataIndex * 3) % colors.length]
]
},
data: [] as number[]
}
]
}
if (!this.sortedList) {
return chartData
}
chartData.datasets[0].data = this.sortedList.map((e) => {
if (!this.values) {
return 0
}
return this.values[e].value.kills
})
return chartData
},
chartOptions (): ChartOptions<'doughnut'> {
// eslint-disable-next-line no-unused-expressions
this.$props.playerHighlighted
const options: ChartOptions<'doughnut'> = {
responsive: true,
maintainAspectRatio: true,
animation: { duration: 500 },
layout: { autoPadding: false },
plugins: {
datalabels: {
formatter: (value, context) => {
const weaponId = this.sortedList[context.dataIndex]
// if (!(weapons as {[key:string]:string})[weaponId]) console.log(weaponId)
return (gamemodes as { [key: string]: string })[weaponId] || weaponId
},
backgroundColor: (context) => {
return context.dataset.backgroundColor(context, options)
},
borderColor: this.colors.fg,
borderRadius: 25,
borderWidth: 2,
color: this.colors.bg,
display: (context) => {
return context.dataIndex === context.dataset.data.length - 1
? true
: 'auto'
},
padding: 6
},
tooltip: {
callbacks: {
label: (ctx: any) => {
let label: string
if (!this.sortedList) {
label = ctx.dataset.labels[ctx.dataIndex]
} else {
label = this.sortedList[ctx.dataIndex]
}
label += ' (' + this.values[label].value.kills + ' kills)'
return label
}
}
}
}
}
return options
},
values (): { [key: string]: Ref<Weapon> } {
const filter = new Filter({ ...this.filters, player: this.playerHighlighted })
delete filter.gamemode
const data = this.store.getList('gamemodes', filter)?.value.data
if (!data) return this.store.fetch('gamemodes', filter).value.data
return data
},
sortedList (): string[] {
if (!this.values) return []
const weapons = Object.keys(this.values).filter(e => unref(this.values[e]).kills > 0)
weapons.sort((a, b) => {
if (Number(this.values[a].value.kills) < Number(unref(this.values[b]).kills)) {
return -1
}
if (Number(this.values[a].value.kills) > Number(unref(this.values[b]).kills)) {
return 1
}
return 0
})
return weapons
}
}
})
</script>
<style scoped>
.weaponChart {
width: calc(min(50vw, 50vh) - 3em);
height: calc(min(50vw, 50vh) - 3em);
}
@media only screen and (max-width: 922px) {
canvas {
display: none !important;
}
}
</style>
+3 -3
View File
@@ -66,11 +66,11 @@ export default defineComponent({
} else if (mustScrollDown) { } else if (mustScrollDown) {
this.vIndex = Math.min(scrollIndex, this.list.length - this.toLoad) this.vIndex = Math.min(scrollIndex, this.list.length - this.toLoad)
} }
if (mustScrollDown || mustScrollUp) { // if (mustScrollDown || mustScrollUp) {
const msg = (mustScrollDown ? 'down ' : '') + (mustScrollUp ? 'up ' : '') // const msg = (mustScrollDown ? 'down ' : '') + (mustScrollUp ? 'up ' : '')
// console.log(this.toLoad) // console.log(this.toLoad)
// console.log((this.list.length * this.$props.rowHeight) - (this.vIndex * this.$props.rowHeight) - (this.visibleCount * this.$props.rowHeight)) // console.log((this.list.length * this.$props.rowHeight) - (this.vIndex * this.$props.rowHeight) - (this.visibleCount * this.$props.rowHeight))
} // }
} }
} }
+2 -2
View File
@@ -22,7 +22,7 @@ import dataLabel from 'chartjs-plugin-datalabels'
import { useKillStore, Player, Filter } from '@/stores/kill' import { useKillStore, Player, Filter } from '@/stores/kill'
import { Scatter } from 'vue-chartjs' import { Scatter } from 'vue-chartjs'
import { defineComponent, PropType, Ref, toRaw, triggerRef, unref } from 'vue' import { defineComponent, PropType, toRaw, unref } from 'vue'
import { ChartEvent } from 'chart.js/dist/core/core.plugins' import { ChartEvent } from 'chart.js/dist/core/core.plugins'
import { _DeepPartialObject } from 'chart.js/dist/types/utils' import { _DeepPartialObject } from 'chart.js/dist/types/utils'
@@ -55,7 +55,7 @@ export default defineComponent({
if (this.filters) { if (this.filters) {
const filter = new Filter(this.filters) const filter = new Filter(this.filters)
delete filter.player delete filter.player
return this.store.getList('players', filter)?.value.progress return this.store.getList('players', filter)?.value.progress.value
} }
return 0 return 0
}, },
+1 -4
View File
@@ -32,7 +32,6 @@ import { defineComponent, PropType, Ref, isRef } from 'vue'
import { useKillStore, Player, Filter } from '@/stores/kill' import { useKillStore, Player, Filter } from '@/stores/kill'
import VirtualList from './List/VirtualList.vue' import VirtualList from './List/VirtualList.vue'
import LoadingBar from './LoadingBar.vue' import LoadingBar from './LoadingBar.vue'
import { cloneProxy } from 'vue-chartjs/dist/utils'
export default defineComponent({ export default defineComponent({
components: { VirtualList, LoadingBar }, components: { VirtualList, LoadingBar },
@@ -51,14 +50,13 @@ export default defineComponent({
progress () { progress () {
const filter = new Filter(this.filters) const filter = new Filter(this.filters)
delete filter.player delete filter.player
return this.store.getList('players', filter)?.value.progress return this.store.getList('players', filter)?.value.progress.value
}, },
players (): { [key: string]: Ref<Player> } { players (): { [key: string]: Ref<Player> } {
const filter = new Filter(this.filters) const filter = new Filter(this.filters)
delete filter.player delete filter.player
const data = this.store.getList('players', filter)?.value.data const data = this.store.getList('players', filter)?.value.data
if (!data) return this.store.fetch('players', filter).value.data if (!data) return this.store.fetch('players', filter).value.data
console.log(isRef(data), isRef(Object.values(data)[0]))
return data return data
}, },
playerList (): (Ref<Player> & { id: string })[] { playerList (): (Ref<Player> & { id: string })[] {
@@ -149,7 +147,6 @@ export default defineComponent({
} }
.playerTable { .playerTable {
grid-area: list;
margin-right: 1rem; margin-right: 1rem;
height: 100%; height: 100%;
user-select: none; user-select: none;
+15 -17
View File
@@ -1,12 +1,12 @@
<template> <template>
<div class="weaponChart" ref="container"> <div class="weaponChart" ref="container">
<LoadingBar v-if="progress !== 1" :value="progress"></LoadingBar> <LoadingBar v-if="progress !== 1" :value="progress"></LoadingBar>
<Doughnut :data="chart" :options="chartOptions" v-if="progress === 1"/> <Doughnut :data="chart" :options="chartOptions" v-if="progress === 1" />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js' import { Chart as ChartJS, ArcElement, Tooltip, Legend, ChartData, ChartOptions } from 'chart.js'
import dataLabel from 'chartjs-plugin-datalabels' import dataLabel from 'chartjs-plugin-datalabels'
import { Weapon, Filter, useKillStore } from '@/stores/kill' import { Weapon, Filter, useKillStore } from '@/stores/kill'
import { Doughnut } from 'vue-chartjs' import { Doughnut } from 'vue-chartjs'
@@ -41,8 +41,8 @@ export default defineComponent({
const filter = new Filter(this.filters) const filter = new Filter(this.filters)
delete filter.weapon delete filter.weapon
const data = this.store.getList('weapons', filter)?.value const data = this.store.getList('weapons', filter)?.value
if (!data) return this.store.fetch('weapons', filter).value.progress if (!data) return this.store.fetch('weapons', filter).value.progress.value
return data.progress return data.progress.value
}, },
colors () { colors () {
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions
@@ -65,7 +65,7 @@ export default defineComponent({
} }
return colors return colors
}, },
chart () { chart (): ChartData<'doughnut', number[]> {
const colors = [ const colors = [
'cyan', 'cyan',
'green', 'green',
@@ -75,13 +75,12 @@ export default defineComponent({
'red', 'red',
'yellow' 'yellow'
] ]
const chartData = { const chartData: ChartData<'doughnut', number[]> = {
datasets: [ datasets: [
{ {
label: 'Weapons', label: 'Weapons',
labels: this.sortedWeaponList || ([] as string[]),
borderColor: () => this.colors.fg, borderColor: () => this.colors.fg,
backgroundColor: (context: any) => { backgroundColor: (context) => {
return this.colors[ return this.colors[
colors[(context.dataIndex * 3) % colors.length] colors[(context.dataIndex * 3) % colors.length]
] ]
@@ -101,30 +100,30 @@ export default defineComponent({
}) })
return chartData return chartData
}, },
chartOptions () { chartOptions (): ChartOptions<'doughnut'> {
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions
this.$props.playerHighlighted this.$props.playerHighlighted
const options = { const options: ChartOptions<'doughnut'> = {
responsive: true, responsive: true,
maintainAspectRatio: true, maintainAspectRatio: true,
animation: { duration: 500 }, animation: { duration: 500 },
layout: { autoPadding: false }, layout: { autoPadding: false },
plugins: { plugins: {
datalabels: { datalabels: {
formatter: (value: any, context: any) => { formatter: (value, context) => {
const weaponId = this.sortedWeaponList[context.dataIndex] const weaponId = this.sortedWeaponList[context.dataIndex]
// if (!(weapons as {[key:string]:string})[weaponId]) console.log(weaponId) // if (!(weapons as {[key:string]:string})[weaponId]) console.log(weaponId)
return (weapons as {[key:string]:string})[weaponId] || weaponId return (weapons as { [key: string]: string })[weaponId] || weaponId
}, },
backgroundColor: (context: any) => { backgroundColor: (context) => {
return context.dataset.backgroundColor(context) return context.dataset.backgroundColor(context, options)
}, },
borderColor: this.colors.fg, borderColor: this.colors.fg,
borderRadius: 25, borderRadius: 25,
borderWidth: 2, borderWidth: 2,
color: this.colors.bg, color: this.colors.bg,
display: (context: any) => { display: (context) => {
return context.dataIndex === context.dataset.labels.length - 1 return context.dataIndex === context.dataset.data.length - 1
? true ? true
: 'auto' : 'auto'
}, },
@@ -153,7 +152,6 @@ export default defineComponent({
delete filter.weapon delete filter.weapon
const data = this.store.getList('weapons', filter)?.value.data const data = this.store.getList('weapons', filter)?.value.data
if (!data) return this.store.fetch('weapons', filter).value.data if (!data) return this.store.fetch('weapons', filter).value.data
console.log(filter.toURLSearchParams().toString(), filter)
return data return data
}, },
sortedWeaponList (): string[] { sortedWeaponList (): string[] {
+2 -2
View File
@@ -52,8 +52,8 @@ socket.onmessage = function (e) {
} }
function registerWebSocketKill (data : websocketData) { function registerWebSocketKill (data : websocketData) {
const { player: _, ...filterWithoutPlayer } = store.$state.currentFilter const filter = new Filter(store.$state.currentFilter)
const filter = new Filter(filterWithoutPlayer) delete filter.player
const list = unref(store.getList('players', filter)) const list = unref(store.getList('players', filter))
if (!list) return if (!list) return
if (!(!filter.server || filter.server.includes(data.servername))) return if (!(!filter.server || filter.server.includes(data.servername))) return
+19
View File
@@ -0,0 +1,19 @@
{
"private_match": "Private Match",
"aitdm": "Attrition",
"at": "Bounty Hunt",
"coliseum": "Coliseum",
"cp": "Amped Hardpoint",
"ctf": "Capture the Flag",
"fw": "Frontier War",
"lts": "Last Titan Standing",
"mfd": "Marked For Death",
"ps": "Pilots vs. Pilots",
"solo": "Campaign",
"tdm": "Skirmish",
"ttdm": "Titan Brawl",
"sns": "Stick and Stones",
"gg": "GunGame",
"fd": "Frontier Defense",
"ffa": "Free For All"
}
+11 -8
View File
@@ -1,5 +1,5 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { Ref, ref, shallowRef, triggerRef, unref, isRef } from 'vue' import { Ref, ref, shallowRef, triggerRef, unref } from 'vue'
export class Filter { export class Filter {
constructor (filt?:{server?:string[], player?: string, weapon?: string[], host?: string[], map?: string[], gamemode?: string[]}) { constructor (filt?:{server?:string[], player?: string, weapon?: string[], host?: string[], map?: string[], gamemode?: string[]}) {
@@ -53,7 +53,10 @@ function fetchWithLoading (url: string, progress: (percentage: number) => void)
return new Response( return new Response(
new ReadableStream({ new ReadableStream({
start (controller) { start (controller) {
const reader = response.body!.getReader() if (response.body === null) {
throw new Error('response.body is null')
}
const reader = response.body.getReader()
read() read()
function read () { function read () {
@@ -109,7 +112,7 @@ export interface Server extends Kill {
export interface KillData<T extends Kill> { export interface KillData<T extends Kill> {
data: { [key: string]: Ref<T> }; data: { [key: string]: Ref<T> };
progress?: number; progress: Ref<number>;
} }
export interface NSServer { export interface NSServer {
@@ -173,7 +176,7 @@ export const useKillStore = defineStore('kill', {
fetch <T extends StateProperty> (type:T, filter?:Filter): Ref<KillData<StateType<T>>> { fetch <T extends StateProperty> (type:T, filter?:Filter): Ref<KillData<StateType<T>>> {
let entry = this.getList(type, filter) let entry = this.getList(type, filter)
if (entry === undefined) { if (entry === undefined) {
entry = shallowRef<KillData<StateType<T>>>({ data: {} }) entry = shallowRef<KillData<StateType<T>>>({ data: {}, progress: ref(0) })
unref(this.$state.killData[type as keyof typeof this.$state.killData] as Map<string, Ref<KillData<StateType<T>>>>).set(filter?.toURLSearchParams().toString() ?? '', entry) unref(this.$state.killData[type as keyof typeof this.$state.killData] as Map<string, Ref<KillData<StateType<T>>>>).set(filter?.toURLSearchParams().toString() ?? '', entry)
// console.log(isRef(entry), this.$state.killData) // console.log(isRef(entry), this.$state.killData)
} }
@@ -181,9 +184,9 @@ export const useKillStore = defineStore('kill', {
`https://tone.sleepycat.date/v2/client/${type}?` + `https://tone.sleepycat.date/v2/client/${type}?` +
filter?.toURLSearchParams() ?? '', filter?.toURLSearchParams() ?? '',
(progress) => { (progress) => {
if (entry && progress !== 1) { if (entry && progress !== 1 && unref(entry).progress.value !== 1) {
unref(entry).progress = progress unref(entry).progress.value = progress
triggerRef(entry) triggerRef(unref(entry).progress)
} }
} }
).then(async (response) => { ).then(async (response) => {
@@ -194,7 +197,7 @@ export const useKillStore = defineStore('kill', {
ref(e[1]) as Ref<StateType<T>> ref(e[1]) as Ref<StateType<T>>
]) ])
) )
unref(entry).progress = 1 unref(entry).progress.value = 1
triggerRef(entry) triggerRef(entry)
} }
+75 -26
View File
@@ -1,6 +1,12 @@
<template> <template>
<div id="filters"> <div id="filters">
<span class="multiselect-wrapper"> <span class="multiselect-wrapper">
<!-- group-values="servers"
group-label="id"
label="name"
:group-select="true"
:options="groupedServers" -->
<VueMultiselect <VueMultiselect
selectLabel="" selectLabel=""
deselectLabel="remove" deselectLabel="remove"
@@ -21,6 +27,7 @@
deselectLabel="remove" deselectLabel="remove"
placeholder="Select gamemode" placeholder="Select gamemode"
v-model="model.gamemode" v-model="model.gamemode"
:custom-label="((e: any) => gamemodeLocale[e] || e)"
:options="sortedGamemodeList" :options="sortedGamemodeList"
:allow-empty="true" :allow-empty="true"
:close-on-select="false" :close-on-select="false"
@@ -43,7 +50,7 @@
</VueMultiselect> </VueMultiselect>
<button @click="model.weapon = undefined" :disabled="!model.weapon">X</button> <button @click="model.weapon = undefined" :disabled="!model.weapon">X</button>
</span> </span>
<button @click="applyFilters" :disabled="filters.server == model.server && filters.gamemode == model.gamemode && filters.weapon == model.weapon">Apply Filters</button>
<span class="multiselect-wrapper"> <span class="multiselect-wrapper">
<VueMultiselect <VueMultiselect
:options-limit="20" :options-limit="20"
@@ -57,7 +64,6 @@
></VueMultiselect> ></VueMultiselect>
<button @click="playerHighlighted = undefined" :disabled="!playerHighlighted">X</button> <button @click="playerHighlighted = undefined" :disabled="!playerHighlighted">X</button>
</span> </span>
<button @click="applyFilters" :disabled="filters.server == model.server && filters.gamemode == model.gamemode && filters.weapon == model.weapon">Apply Filters</button>
</div> </div>
<div id="playerView"> <div id="playerView">
@@ -79,6 +85,10 @@
:playerHighlighted="playerHighlighted?.id" :playerHighlighted="playerHighlighted?.id"
> >
</WeaponChart> </WeaponChart>
<GamemodeChart
:filters="{ player: playerHighlighted?.id, ...filters }"
:playerHighlighted="playerHighlighted?.id"
></GamemodeChart>
</div> </div>
</template> </template>
@@ -87,24 +97,31 @@ import { Player, Weapon, Server, Kill, useKillStore, Filter } from '@/stores/kil
import PlayerList from '@/components/PlayerList.vue' import PlayerList from '@/components/PlayerList.vue'
import PlayerChart from '@/components/PlayerChart.vue' import PlayerChart from '@/components/PlayerChart.vue'
import WeaponChart from '@/components/WeaponChart.vue' import WeaponChart from '@/components/WeaponChart.vue'
import GamemodeChart from '@/components/GamemodeChart.vue'
import { Ref, defineComponent, unref } from 'vue' import { Ref, defineComponent, unref } from 'vue'
import VueMultiselect from 'vue-multiselect' import VueMultiselect from 'vue-multiselect'
import 'vue-multiselect/dist/vue-multiselect.css' import 'vue-multiselect/dist/vue-multiselect.css'
import weapons from '../stores/weapons.json' import weapons from '../stores/weapons.json'
import gamemodes from '../stores/gamemodes.json'
import router from '@/router' import router from '@/router'
function filterOutNull<T> (value:T | null):value is T {
return value !== null
}
export default defineComponent({ export default defineComponent({
name: 'PlayerView', name: 'PlayerView',
components: { components: {
VueMultiselect, VueMultiselect,
PlayerList, PlayerList,
PlayerChart, PlayerChart,
WeaponChart WeaponChart,
GamemodeChart
}, },
data () { data () {
return { return {
model: { server: undefined, weapon: undefined } as unknown as { model: { } as unknown as {
weapon: string[] | undefined; weapon: string[] | undefined;
server: string[] | undefined; server: string[] | undefined;
gamemode: string[] | undefined; gamemode: string[] | undefined;
@@ -112,7 +129,8 @@ export default defineComponent({
filters: new Filter(), filters: new Filter(),
store: useKillStore(), store: useKillStore(),
playerHighlighted: undefined as (Player & { id: string }) | undefined, playerHighlighted: undefined as (Player & { id: string }) | undefined,
weaponLocale: weapons as { [key: string]: string } weaponLocale: weapons as { [key: string]: string },
gamemodeLocale: gamemodes as { [key: string]: string }
} }
}, },
beforeCreate () { beforeCreate () {
@@ -128,10 +146,8 @@ export default defineComponent({
}, },
computed: { computed: {
servers (): { [key: string]: Ref<Server> } { servers (): { [key: string]: Ref<Server> } {
const filter = new Filter(this.filters) const data = this.store.getList('servers')?.value.data
delete filter.server if (!data) return unref(this.store.fetch('servers')).data
const data = this.store.getList('servers', filter)?.value.data
if (!data) return unref(this.store.fetch('servers', filter)).data
return data return data
}, },
groupedServers () { groupedServers () {
@@ -149,11 +165,11 @@ export default defineComponent({
return hosts return hosts
}, },
weapons (): { [key: string]: Ref<Weapon> } { weapons (): { [key: string]: Ref<Weapon> } {
const filter = new Filter(this.filters) // const filter = new Filter(this.filters)
delete filter.weapon // delete filter.weapon
delete filter.player // delete filter.player
const data = this.store.getList('weapons', filter)?.value.data const data = this.store.getList('weapons')?.value.data
if (!data) return this.store.fetch('weapons', filter).value.data if (!data) return this.store.fetch('weapons').value.data
return data return data
}, },
players (): { [key: string]: Ref<Player> } { players (): { [key: string]: Ref<Player> } {
@@ -164,11 +180,11 @@ export default defineComponent({
return data return data
}, },
gamemodes (): { [key: string]: Ref<Kill> } { gamemodes (): { [key: string]: Ref<Kill> } {
const filter = new Filter(this.filters) // const filter = new Filter(this.filters)
delete filter.gamemode // delete filter.gamemode
delete filter.player // delete filter.player
const data = this.store.getList('gamemodes', filter)?.value.data const data = this.store.getList('gamemodes')?.value.data
if (!data) return this.store.fetch('gamemodes', filter).value.data if (!data) return this.store.fetch('gamemodes').value.data
return data return data
}, },
sortedWeaponList (): string[] { sortedWeaponList (): string[] {
@@ -204,6 +220,14 @@ export default defineComponent({
} }
}, },
watch: { watch: {
model: {
handler () {
if (this.model.gamemode?.length === 0) this.model.gamemode = undefined
if (this.model.server?.length === 0) this.model.server = undefined
if (this.model.weapon?.length === 0) this.model.weapon = undefined
},
deep: true
},
players () { players () {
const player = this.$route.query.player?.toString() const player = this.$route.query.player?.toString()
if (player && this.playerHighlighted?.id !== player) { if (player && this.playerHighlighted?.id !== player) {
@@ -255,22 +279,24 @@ export default defineComponent({
} }
} }
if (this.filters.weapon !== this.$route.query.weapon) { if (this.filters.weapon !== this.$route.query.weapon) {
this.model.weapon = [this.$route.query.weapon].flat().filter(e => e ?? false).map(e => e!.toString()) if (this.$route.query.weapon) this.model.weapon = [this.$route.query.weapon].flat().filter(filterOutNull).map(e => e.toString())
else this.model.weapon = undefined
} }
if (this.filters.server !== this.$route.query.server) { if (this.filters.server !== this.$route.query.server) {
if (this.$route.query.server) this.model.server = [this.$route.query.server].flat().filter(e => e ?? false).map(e => e!.toString()) if (this.$route.query.server) this.model.server = [this.$route.query.server].flat().filter(filterOutNull).map(e => e.toString())
else this.model.server = undefined else this.model.server = undefined
} }
if (this.filters.gamemode !== this.$route.query.gamemode) { if (this.filters.gamemode !== this.$route.query.gamemode) {
if (this.$route.query.gamemode) this.model.gamemode = [this.$route.query.gamemode].flat().filter(e => e ?? false).map(e => e!.toString()) if (this.$route.query.gamemode) this.model.gamemode = [this.$route.query.gamemode].flat().filter(filterOutNull).map(e => e.toString())
else this.model.gamemode = undefined else this.model.gamemode = undefined
} }
this.applyFilters()
} }
} }
}) })
</script> </script>
<style scoped> <style>
.multiselect { .multiselect {
margin: 0 0.5em 1em 0.5em; margin: 0 0.5em 1em 0.5em;
} }
@@ -290,9 +316,9 @@ export default defineComponent({
display: grid; display: grid;
overflow: auto; overflow: auto;
grid-template-areas: grid-template-areas:
"list chart" "list chart chart"
"list info"; "list info1 info2";
grid-template-columns: 50% 50%; grid-template-columns: 50% 25% 25%;
grid-template-rows: 50% 50%; grid-template-rows: 50% 50%;
} }
@@ -319,13 +345,36 @@ export default defineComponent({
.playerChart { .playerChart {
display: none; display: none;
grid-area: "chart";
} }
.weaponChart { .weaponChart {
display: none; display: none;
grid-area: 'info1';
}
.gamemodeChart{
display:none;
grid-area: 'info2';
}
.playerTable{
grid-area: 'list';
} }
} }
.playerChart {
grid-area: chart;
}
.weaponChart {
grid-area: info1;
}
.gamemodeChart{
grid-area: info2;
}
.playerTable{
grid-area: list;
}
#filters { #filters {
grid-area: filters; grid-area: filters;
} }
+1
View File
@@ -5,6 +5,7 @@
"module": "esnext", "module": "esnext",
"strict": true, "strict": true,
"jsx": "preserve", "jsx": "preserve",
"noImplicitAny": false,
"moduleResolution": "node", "moduleResolution": "node",
"experimentalDecorators": true, "experimentalDecorators": true,
"skipLibCheck": true, "skipLibCheck": true,