upgrade from webpack to vite + typescript
Some checks failed
CSGOWTF/csgowtf/pipeline/head There was a failure building this commit
Some checks failed
CSGOWTF/csgowtf/pipeline/head There was a failure building this commit
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
@@ -1,10 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{js,json,yml}]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
15
.eslintrc.cjs
Normal file
15
.eslintrc.cjs
Normal file
@@ -0,0 +1,15 @@
|
||||
/* eslint-env node */
|
||||
require("@rushstack/eslint-patch/modern-module-resolution");
|
||||
|
||||
module.exports = {
|
||||
"root": true,
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-typescript/recommended",
|
||||
"@vue/eslint-config-prettier"
|
||||
],
|
||||
"env": {
|
||||
"vue/setup-compiler-macros": true
|
||||
}
|
||||
}
|
17
.eslintrc.js
17
.eslintrc.js
@@ -1,17 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
}
|
||||
}
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -89,7 +89,7 @@ web_modules/
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.local
|
||||
.env.test
|
||||
.env.production
|
||||
|
||||
@@ -220,7 +220,7 @@ fabric.properties
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
# Android studio 3.1+ serialized cche file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### WebStorm+all Patch ###
|
||||
@@ -283,4 +283,4 @@ $RECYCLE.BIN/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/webstorm+all,yarn,windows,linux,node,vuejs
|
||||
|
||||
|
||||
a
|
||||
|
341
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
341
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
File diff suppressed because one or more lines are too long
631
.yarn/releases/yarn-3.0.2.cjs
vendored
631
.yarn/releases/yarn-3.0.2.cjs
vendored
File diff suppressed because one or more lines are too long
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Normal file
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -4,4 +4,4 @@ plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.0.2.cjs
|
||||
yarnPath: .yarn/releases/yarn-3.2.0.cjs
|
||||
|
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible">
|
||||
<meta content="width=device-width,initial-scale=1.0" name="viewport">
|
||||
@@ -40,11 +40,11 @@
|
||||
<meta content="https://csgow.tf/images/logo.png"
|
||||
property="og:image:secure_url">
|
||||
|
||||
<link href="<%= BASE_URL %>images/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180">
|
||||
<link href="<%= BASE_URL %>images/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">
|
||||
<link href="<%= BASE_URL %>images/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
|
||||
<link href="/images/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180">
|
||||
<link href="/images/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">
|
||||
<link href="/images/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
|
||||
|
||||
<link href="<%= BASE_URL %>site.webmanifest" rel="manifest">
|
||||
<link href="/site.webmanifest" rel="manifest">
|
||||
|
||||
<link rel="preconnect" href="https://steamcdn-a.akamaihd.net" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://steamcdn-a.akamaihd.net">
|
||||
@@ -53,14 +53,10 @@
|
||||
<link rel="preconnect" href="https://piwik.harting.hosting" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://piwik.harting.hosting">
|
||||
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
|
||||
Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app" class="d-flex flex-column min-vh-100"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
<title>csgoWTF</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
51
package.json
51
package.json
@@ -1,43 +1,46 @@
|
||||
{
|
||||
"name": "csgowtf",
|
||||
"version": "1.0.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build --mode production",
|
||||
"lint": "vue-cli-service lint"
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview --port 5050",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.2",
|
||||
"axios": "^0.25.0",
|
||||
"@popperjs/core": "^2.11.4",
|
||||
"axios": "^0.26.1",
|
||||
"bootstrap": "^5.1.3",
|
||||
"core-js": "^3.21.0",
|
||||
"dotenv-webpack": "^7.1.0",
|
||||
"echarts": "^5.3.0",
|
||||
"bootstrap-icons": "^1.8.1",
|
||||
"echarts": "^5.3.1",
|
||||
"fork-awesome": "^1.2.0",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"iso-639-1": "^2.1.13",
|
||||
"jquery": "^3.6.0",
|
||||
"luxon": "^2.3.0",
|
||||
"luxon": "^2.3.1",
|
||||
"pinia": "^2.0.12",
|
||||
"string-sanitizer": "^2.0.2",
|
||||
"vue": "^3.2.30",
|
||||
"vue": "^3.2.31",
|
||||
"vue-matomo": "^4.1.0",
|
||||
"vue-router": "^4.0.12",
|
||||
"vue-router": "^4.0.14",
|
||||
"vue3-cookies": "^1.0.6",
|
||||
"vuex": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.15",
|
||||
"@vue/cli-plugin-eslint": "~4.5.15",
|
||||
"@vue/cli-plugin-router": "~4.5.15",
|
||||
"@vue/cli-plugin-vuex": "~4.5.15",
|
||||
"@vue/cli-service": "~4.5.15",
|
||||
"@vue/compiler-sfc": "^3.2.30",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-vue": "^7.20.0",
|
||||
"sass": "^1.49.7",
|
||||
"sass-loader": "^10.2.1"
|
||||
"@rushstack/eslint-patch": "^1.1.1",
|
||||
"@types/node": "^16.11.26",
|
||||
"@vitejs/plugin-vue": "^2.2.4",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^10.0.0",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-plugin-vue": "^8.5.0",
|
||||
"prettier": "^2.6.0",
|
||||
"sass": "^1.49.9",
|
||||
"typescript": "~4.6.2",
|
||||
"vite": "^2.8.6",
|
||||
"vue-tsc": "^0.33.2-patch.1"
|
||||
},
|
||||
"packageManager": "yarn@3.0.2"
|
||||
"packageManager": "yarn@3.2.0"
|
||||
}
|
||||
|
58
src/App.vue
58
src/App.vue
@@ -1,58 +1,60 @@
|
||||
<template>
|
||||
<img alt="" class="bg-img" src="">
|
||||
<img alt="" class="bg-img" src="" />
|
||||
<header>
|
||||
<Nav/>
|
||||
<Nav />
|
||||
</header>
|
||||
<main>
|
||||
<div :style="{height: offset + 'px'}"/>
|
||||
<InfoModal/>
|
||||
<router-view name="main"/>
|
||||
<div :style="{ height: offset + 'px' }" />
|
||||
<InfoModal />
|
||||
<router-view name="main" />
|
||||
</main>
|
||||
<footer class="mt-auto">
|
||||
<Footer/>
|
||||
<Footer />
|
||||
</footer>
|
||||
<CookieConsentBtn id="cookie-btn"/>
|
||||
<CookieConsentBtn id="cookie-btn" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Nav from "@/components/Nav";
|
||||
import Footer from "@/components/Footer";
|
||||
import CookieConsentBtn from "@/components/CookieConsentBtn";
|
||||
import {onMounted, ref} from "vue";
|
||||
import InfoModal from "@/components/InfoModal";
|
||||
import Nav from "/src/components/NavComponent.vue";
|
||||
import Footer from "/src/components/FooterComponent.vue";
|
||||
import CookieConsentBtn from "/src/components/CookieConsentBtn.vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import InfoModal from "/src/components/InfoModal.vue";
|
||||
|
||||
export default {
|
||||
components: {InfoModal, Footer, Nav, CookieConsentBtn},
|
||||
components: { InfoModal, Footer, Nav, CookieConsentBtn },
|
||||
setup() {
|
||||
const offset = ref(0)
|
||||
const offset = ref(0);
|
||||
|
||||
const setOffset = () => {
|
||||
return document.getElementsByTagName('nav')[0].clientHeight
|
||||
}
|
||||
return document.getElementsByTagName("nav")[0].clientHeight;
|
||||
};
|
||||
|
||||
const setBgHeight = () => {
|
||||
document.querySelector('.bg-img').style.height = document.documentElement.clientHeight + 'px'
|
||||
}
|
||||
document.querySelector(".bg-img").style.height =
|
||||
document.documentElement.clientHeight + "px";
|
||||
};
|
||||
|
||||
window.onresize = () => {
|
||||
offset.value = setOffset()
|
||||
setBgHeight()
|
||||
}
|
||||
offset.value = setOffset();
|
||||
setBgHeight();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
offset.value = setOffset()
|
||||
setBgHeight()
|
||||
})
|
||||
offset.value = setOffset();
|
||||
setBgHeight();
|
||||
});
|
||||
|
||||
return {offset}
|
||||
}
|
||||
}
|
||||
return { offset };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@font-face {
|
||||
font-family: "Obitron";
|
||||
src: local("Obitron"), url("../public/fonts/Orbitron-VariableFont_wght.ttf") format("truetype");
|
||||
src: local("Obitron"),
|
||||
url("../public/fonts/Orbitron-VariableFont_wght.ttf") format("truetype");
|
||||
}
|
||||
|
||||
.bg-img {
|
||||
|
@@ -1,66 +1,86 @@
|
||||
<template>
|
||||
<div v-if="!consent" class="card text-end bg-secondary text-white border border-1">
|
||||
<div
|
||||
v-if="!consent"
|
||||
class="card text-end bg-secondary text-white border border-1"
|
||||
>
|
||||
<div class="card-body">
|
||||
<form class="mb-1">
|
||||
<div class="form-check">
|
||||
<input id="essential-cookies" checked class="form-check-input" disabled type="checkbox" value="">
|
||||
<input
|
||||
id="essential-cookies"
|
||||
checked
|
||||
class="form-check-input"
|
||||
disabled
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
<label class="form-check-label" for="essential-cookies">
|
||||
Essential
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input id="tracking" v-model="tracking" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="tracking">
|
||||
Matomo
|
||||
</label>
|
||||
<input
|
||||
id="tracking"
|
||||
v-model="tracking"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label class="form-check-label" for="tracking"> Matomo </label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<a href="/privacy-policy" class="text-muted">Privacy Policy</a>
|
||||
<a class="text-muted" href="/privacy-policy">Privacy Policy</a>
|
||||
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<button class="btn btn-outline-primary" type="button" @click="handleConsentForget">Decline</button>
|
||||
<button class="btn btn-info" type="button" @click="handleConsent">Accept</button>
|
||||
<button
|
||||
class="btn btn-outline-primary"
|
||||
type="button"
|
||||
@click="handleConsentForget"
|
||||
>
|
||||
Decline
|
||||
</button>
|
||||
<button class="btn btn-info" type="button" @click="handleConsent">
|
||||
Accept
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {onMounted, ref} from "vue";
|
||||
import {useCookies} from 'vue3-cookies'
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useCookies } from "vue3-cookies";
|
||||
|
||||
export default {
|
||||
name: "CookieConsentBtn",
|
||||
setup() {
|
||||
const tracking = ref(true)
|
||||
const {cookies} = useCookies()
|
||||
const consent = ref(false)
|
||||
const tracking = ref(true);
|
||||
const { cookies } = useCookies();
|
||||
const consent = ref(false);
|
||||
|
||||
const handleConsent = () => {
|
||||
window._paq.push(['rememberCookieConsentGiven'])
|
||||
cookies.set('consent', 'given', Infinity)
|
||||
window._paq.push(["rememberCookieConsentGiven"]);
|
||||
cookies.set("consent", "given", Infinity);
|
||||
|
||||
if (tracking.value){
|
||||
window._paq.push(['rememberConsentGiven'])
|
||||
if (tracking.value) {
|
||||
window._paq.push(["rememberConsentGiven"]);
|
||||
}
|
||||
consent.value = true
|
||||
}
|
||||
consent.value = true;
|
||||
};
|
||||
const handleConsentForget = () => {
|
||||
consent.value = true
|
||||
}
|
||||
consent.value = true;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window._paq.push(['requireCookieConsent']);
|
||||
window._paq.push(['trackPageView']);
|
||||
window._paq.push(["requireCookieConsent"]);
|
||||
window._paq.push(["trackPageView"]);
|
||||
|
||||
if (cookies.get('consent') === 'given')
|
||||
consent.value = true
|
||||
})
|
||||
if (cookies.get("consent") === "given") consent.value = true;
|
||||
});
|
||||
|
||||
return {handleConsent, handleConsentForget, tracking, consent}
|
||||
}
|
||||
}
|
||||
return { handleConsent, handleConsentForget, tracking, consent };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@@ -2,48 +2,51 @@
|
||||
<div class="damage-site">
|
||||
<div class="total-damage">
|
||||
<h3 class="text-center mt-2">Total Damage</h3>
|
||||
<TotalDamage/>
|
||||
<TotalDamage />
|
||||
</div>
|
||||
<div class="hitgroup">
|
||||
<!-- <h3 class="text-center">Damage by Hitgroup</h3>-->
|
||||
<!-- <h3 class="text-center">Damage by Hitgroup</h3>-->
|
||||
<HitgroupPuppet :equipment_map="data.equipment_map" :stats="data.stats" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HitgroupPuppet from '@/components/HitgroupPuppet'
|
||||
import TotalDamage from "@/components/TotalDamage"
|
||||
import {onMounted, reactive} from "vue";
|
||||
import {useStore} from "vuex";
|
||||
import {GetWeaponDmg} from "@/utils";
|
||||
import HitgroupPuppet from "/src/components/HitgroupPuppet";
|
||||
import TotalDamage from "/src/components/TotalDamage";
|
||||
import { onMounted, reactive } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import { GetWeaponDmg } from "/src/utils";
|
||||
|
||||
export default {
|
||||
name: "DamageSite.vue",
|
||||
components: {HitgroupPuppet, TotalDamage},
|
||||
components: { HitgroupPuppet, TotalDamage },
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
const data = reactive({
|
||||
equipment_map: {},
|
||||
stats: [],
|
||||
})
|
||||
});
|
||||
|
||||
const getWeaponDamage = async () => {
|
||||
const resData = await GetWeaponDmg(store, store.state.matchDetails.match_id)
|
||||
const resData = await GetWeaponDmg(
|
||||
store,
|
||||
store.state.matchDetails.match_id
|
||||
);
|
||||
if (resData !== null) {
|
||||
data.equipment_map = resData.equipment_map
|
||||
data.stats = resData.stats
|
||||
data.equipment_map = resData.equipment_map;
|
||||
data.stats = resData.stats;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getWeaponDamage()
|
||||
})
|
||||
getWeaponDamage();
|
||||
});
|
||||
|
||||
return {data}
|
||||
}
|
||||
}
|
||||
return { data };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<div class="details-site">
|
||||
<div class="multi-kills">
|
||||
<h3 class="text-center mt-2">Multi-Kills</h3>
|
||||
<MultiKillsChart/>
|
||||
</div>
|
||||
<!-- <hr>-->
|
||||
<!-- <div class="spray">-->
|
||||
<!-- <h3 class="text-center">Spray</h3>-->
|
||||
<!-- <SprayGraph :spray="data.spray"/>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MultiKillsChart from "@/components/MultiKillsChart";
|
||||
import {useStore} from "vuex";
|
||||
import {onMounted, reactive} from "vue";
|
||||
import {GetWeaponDmg} from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "Details",
|
||||
components: {MultiKillsChart},
|
||||
setup() {
|
||||
const store = useStore()
|
||||
|
||||
const data = reactive({
|
||||
spray: [],
|
||||
})
|
||||
|
||||
const getWeaponDamage = async () => {
|
||||
const resData = await GetWeaponDmg(store, store.state.matchDetails.match_id)
|
||||
if (resData !== null) {
|
||||
data.spray = resData.spray
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getWeaponDamage()
|
||||
})
|
||||
|
||||
return {data}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.details-site {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
border: 1px solid white;
|
||||
}
|
||||
}
|
||||
</style>
|
66
src/components/DetailsComponent.vue
Normal file
66
src/components/DetailsComponent.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="details-site">
|
||||
<div class="multi-kills">
|
||||
<h3 class="text-center mt-2">Multi-Kills</h3>
|
||||
<MultiKillsChart />
|
||||
</div>
|
||||
<!-- <hr>-->
|
||||
<!-- <div class="spray">-->
|
||||
<!-- <h3 class="text-center">Spray</h3>-->
|
||||
<!-- <SprayGraph :spray="data.spray"/>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MultiKillsChart from "/src/components/MultiKillsChart";
|
||||
import { useStore } from "vuex";
|
||||
import { onMounted, reactive } from "vue";
|
||||
import { GetWeaponDmg } from "/src/utils";
|
||||
|
||||
export default {
|
||||
name: "DetailsComponent",
|
||||
components: { MultiKillsChart },
|
||||
setup() {
|
||||
const store = useStore();
|
||||
|
||||
const data = reactive({
|
||||
spray: [],
|
||||
});
|
||||
|
||||
const getWeaponDamage = async () => {
|
||||
const resData = await GetWeaponDmg(
|
||||
store,
|
||||
store.state.matchDetails.match_id
|
||||
);
|
||||
if (resData !== null) {
|
||||
data.spray = resData.spray;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getWeaponDamage();
|
||||
});
|
||||
|
||||
return { data };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.details-site {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
border: 1px solid white;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -7,33 +7,45 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GetPlayerValue } from "/src/utils";
|
||||
import { useStore } from "vuex";
|
||||
import {
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
reactive,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
|
||||
import {GetPlayerValue} from "@/utils";
|
||||
import {useStore} from "vuex";
|
||||
import {onBeforeMount, onMounted, onUnmounted, reactive, ref, watch} from "vue";
|
||||
|
||||
import * as echarts from 'echarts/core';
|
||||
import * as echarts from "echarts/core";
|
||||
import {
|
||||
GridComponent,
|
||||
MarkAreaComponent,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
VisualMapComponent
|
||||
} from 'echarts/components';
|
||||
import {LineChart} from 'echarts/charts';
|
||||
import {UniversalTransition} from 'echarts/features';
|
||||
import {CanvasRenderer} from 'echarts/renderers';
|
||||
VisualMapComponent,
|
||||
} from "echarts/components";
|
||||
import { LineChart } from "echarts/charts";
|
||||
import { UniversalTransition } from "echarts/features";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
|
||||
export default {
|
||||
name: "EqValueGraph",
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
let myChart1, max_rounds
|
||||
let valueList = []
|
||||
let dataList = []
|
||||
const width = ref(window.innerWidth >= 800 && window.innerWidth <= 1200 ? window.innerWidth : window.innerWidth < 800 ? 800 : 1200)
|
||||
const height = ref(width.value * 1 / 3)
|
||||
let myChart1, max_rounds;
|
||||
let valueList = [];
|
||||
let dataList = [];
|
||||
const width = ref(
|
||||
window.innerWidth >= 800 && window.innerWidth <= 1200
|
||||
? window.innerWidth
|
||||
: window.innerWidth < 800
|
||||
? 800
|
||||
: 1200
|
||||
);
|
||||
const height = ref((width.value * 1) / 3);
|
||||
|
||||
const data = reactive({
|
||||
rounds: {},
|
||||
@@ -42,21 +54,23 @@ export default {
|
||||
eq_team_2: [],
|
||||
eq_team_player_1: [],
|
||||
eq_team_player_2: [],
|
||||
})
|
||||
});
|
||||
|
||||
const getTeamPlayer = (stats, team) => {
|
||||
let arr = []
|
||||
let arr = [];
|
||||
for (let i = (team - 1) * 5; i < team * 5; i++) {
|
||||
arr.push(stats[i].player.steamid64)
|
||||
arr.push(stats[i].player.steamid64);
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
const parseObject = async () => {
|
||||
data.rounds = await GetPlayerValue(store, store.state.matchDetails.match_id)
|
||||
if (data.rounds === null)
|
||||
data.rounds = {}
|
||||
data.rounds = await GetPlayerValue(
|
||||
store,
|
||||
store.state.matchDetails.match_id
|
||||
);
|
||||
if (data.rounds === null) data.rounds = {};
|
||||
|
||||
for (const round in data.rounds) {
|
||||
for (const player in data.rounds[round]) {
|
||||
@@ -65,8 +79,9 @@ export default {
|
||||
data.eq_team_player_1.push({
|
||||
round: round,
|
||||
player: player,
|
||||
eq: (data.rounds[round][player][0] + data.rounds[round][player][2])
|
||||
})
|
||||
eq:
|
||||
data.rounds[round][player][0] + data.rounds[round][player][2],
|
||||
});
|
||||
}
|
||||
}
|
||||
for (let p in data.team[1]) {
|
||||
@@ -74,32 +89,35 @@ export default {
|
||||
data.eq_team_player_2.push({
|
||||
round: round,
|
||||
player: player,
|
||||
eq: (data.rounds[round][player][0] + data.rounds[round][player][2])
|
||||
})
|
||||
eq:
|
||||
data.rounds[round][player][0] + data.rounds[round][player][2],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sumArr = (arr) => {
|
||||
return arr.reduce((acc, current) => ({
|
||||
...acc,
|
||||
[current.round]: (acc[current.round] || 0) + current.eq
|
||||
}), {})
|
||||
}
|
||||
return arr.reduce(
|
||||
(acc, current) => ({
|
||||
...acc,
|
||||
[current.round]: (acc[current.round] || 0) + current.eq,
|
||||
}),
|
||||
{}
|
||||
);
|
||||
};
|
||||
|
||||
const BuildGraphData = (team_1, team_2, max_rounds) => {
|
||||
let newArr = []
|
||||
const half_point = max_rounds / 2 - 1
|
||||
let newArr = [];
|
||||
const half_point = max_rounds / 2 - 1;
|
||||
for (let round in team_1) {
|
||||
if (round <= half_point) {
|
||||
newArr.push(team_1[round] - team_2[round])
|
||||
} else
|
||||
newArr.push(team_2[round] - team_1[round])
|
||||
newArr.push(team_1[round] - team_2[round]);
|
||||
} else newArr.push(team_2[round] - team_1[round]);
|
||||
}
|
||||
return newArr
|
||||
}
|
||||
return newArr;
|
||||
};
|
||||
|
||||
const optionGen = (dataList, valueList) => {
|
||||
return {
|
||||
@@ -107,44 +125,42 @@ export default {
|
||||
visualMap: [
|
||||
{
|
||||
show: false,
|
||||
type: 'continuous',
|
||||
type: "continuous",
|
||||
seriesIndex: 0,
|
||||
color: ['#3a6e99', '#c3a235'],
|
||||
color: ["#3a6e99", "#c3a235"],
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: 'Round <b>{b0}</b><br />{a0} <b>{c0}</b>',
|
||||
trigger: "axis",
|
||||
formatter: "Round <b>{b0}</b><br />{a0} <b>{c0}</b>",
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
type: "category",
|
||||
data: dataList,
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{},
|
||||
},
|
||||
],
|
||||
yAxis: [{}],
|
||||
grid: [
|
||||
{
|
||||
bottom: '10%'
|
||||
bottom: "10%",
|
||||
},
|
||||
{
|
||||
top: '0%'
|
||||
top: "0%",
|
||||
},
|
||||
{
|
||||
right: '0%'
|
||||
right: "0%",
|
||||
},
|
||||
{
|
||||
left: '0%'
|
||||
}
|
||||
left: "0%",
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'Net-Worth',
|
||||
type: 'line',
|
||||
name: "Net-Worth",
|
||||
type: "line",
|
||||
lineStyle: {
|
||||
width: 4
|
||||
width: 4,
|
||||
},
|
||||
showSymbol: false,
|
||||
data: valueList,
|
||||
@@ -152,45 +168,51 @@ export default {
|
||||
data: [
|
||||
[
|
||||
{
|
||||
name: 'Half-Point',
|
||||
name: "Half-Point",
|
||||
xAxis: max_rounds / 2 - 1,
|
||||
label: {
|
||||
color: 'white'
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
{
|
||||
xAxis: max_rounds / 2
|
||||
}
|
||||
]
|
||||
xAxis: max_rounds / 2,
|
||||
},
|
||||
],
|
||||
],
|
||||
itemStyle: {
|
||||
color: 'rgba(200,200,200, 0.3)'
|
||||
}
|
||||
}
|
||||
color: "rgba(200,200,200, 0.3)",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const disposeCharts = () => {
|
||||
if (myChart1 != null && myChart1 !== '' && myChart1 !== undefined) {
|
||||
myChart1.dispose()
|
||||
if (myChart1 != null && myChart1 !== "" && myChart1 !== undefined) {
|
||||
myChart1.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildCharts = () => {
|
||||
disposeCharts()
|
||||
disposeCharts();
|
||||
|
||||
myChart1 = echarts.init(document.getElementById('economy-graph'), {}, {
|
||||
width: width.value,
|
||||
height: height.value
|
||||
})
|
||||
myChart1.setOption(optionGen(dataList, valueList))
|
||||
}
|
||||
myChart1 = echarts.init(
|
||||
document.getElementById("economy-graph"),
|
||||
{},
|
||||
{
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
}
|
||||
);
|
||||
myChart1.setOption(optionGen(dataList, valueList));
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
max_rounds = store.state.matchDetails.max_rounds ? store.state.matchDetails.max_rounds : 30
|
||||
})
|
||||
max_rounds = store.state.matchDetails.max_rounds
|
||||
? store.state.matchDetails.max_rounds
|
||||
: 30;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (store.state.matchDetails.stats) {
|
||||
@@ -202,48 +224,51 @@ export default {
|
||||
LineChart,
|
||||
CanvasRenderer,
|
||||
UniversalTransition,
|
||||
MarkAreaComponent
|
||||
MarkAreaComponent,
|
||||
]);
|
||||
|
||||
data.team.push(getTeamPlayer(store.state.matchDetails.stats, 1))
|
||||
data.team.push(getTeamPlayer(store.state.matchDetails.stats, 2))
|
||||
data.team.push(getTeamPlayer(store.state.matchDetails.stats, 1));
|
||||
data.team.push(getTeamPlayer(store.state.matchDetails.stats, 2));
|
||||
|
||||
parseObject()
|
||||
parseObject();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
disposeCharts()
|
||||
})
|
||||
disposeCharts();
|
||||
});
|
||||
|
||||
watch(() => data.rounds, () => {
|
||||
data.eq_team_1 = sumArr(data.eq_team_player_1)
|
||||
data.eq_team_2 = sumArr(data.eq_team_player_2)
|
||||
watch(
|
||||
() => data.rounds,
|
||||
() => {
|
||||
data.eq_team_1 = sumArr(data.eq_team_player_1);
|
||||
data.eq_team_2 = sumArr(data.eq_team_player_2);
|
||||
|
||||
valueList = BuildGraphData(data.eq_team_1, data.eq_team_2, max_rounds)
|
||||
valueList = BuildGraphData(data.eq_team_1, data.eq_team_2, max_rounds);
|
||||
|
||||
dataList = Array.from(Array(valueList.length + 1).keys())
|
||||
dataList.shift()
|
||||
dataList = Array.from(Array(valueList.length + 1).keys());
|
||||
dataList.shift();
|
||||
|
||||
buildCharts()
|
||||
})
|
||||
buildCharts();
|
||||
}
|
||||
);
|
||||
|
||||
window.onresize = () => {
|
||||
if (window.innerWidth > 1200) {
|
||||
width.value = 1200
|
||||
width.value = 1200;
|
||||
}
|
||||
if (window.innerWidth <= 1200 && window.innerWidth >= 800) {
|
||||
width.value = window.innerWidth - 20
|
||||
width.value = window.innerWidth - 20;
|
||||
}
|
||||
if (window.innerWidth < 800) {
|
||||
width.value = 800
|
||||
width.value = 800;
|
||||
}
|
||||
|
||||
height.value = width.value * 1 / 3
|
||||
buildCharts()
|
||||
}
|
||||
}
|
||||
}
|
||||
height.value = (width.value * 1) / 3;
|
||||
buildCharts();
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -7,14 +7,22 @@
|
||||
<table class="table table-borderless text-muted">
|
||||
<tr>
|
||||
<td>
|
||||
<span class="text-uppercase float-end" :class="toggle === 'duration' ? 'text-warning' : ''">Duration</span>
|
||||
<span
|
||||
:class="toggle === 'duration' ? 'text-warning' : ''"
|
||||
class="text-uppercase float-end"
|
||||
>Duration</span
|
||||
>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<i id="toggle-off" class="fa fa-toggle-off show"></i>
|
||||
<i id="toggle-on" class="fa fa-toggle-on"></i>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-uppercase float-start" :class="toggle === 'total' ? 'text-warning' : ''">Count</span>
|
||||
<span
|
||||
:class="toggle === 'total' ? 'text-warning' : ''"
|
||||
class="text-uppercase float-start"
|
||||
>Count</span
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -27,123 +35,145 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts/core';
|
||||
import {GridComponent, LegendComponent, TooltipComponent} from 'echarts/components';
|
||||
import {BarChart} from 'echarts/charts';
|
||||
import {CanvasRenderer} from 'echarts/renderers';
|
||||
import {onMounted, onUnmounted, ref, watch} from "vue";
|
||||
import {checkStatEmpty, getPlayerArr} from "@/utils";
|
||||
import {useStore} from "vuex";
|
||||
import * as echarts from "echarts/core";
|
||||
import {
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent,
|
||||
} from "echarts/components";
|
||||
import { BarChart } from "echarts/charts";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import { checkStatEmpty, getPlayerArr } from "/src/utils";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "FlashChart",
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
const toggle = ref('duration')
|
||||
let myChart1, myChart2
|
||||
const color = ['#bb792c', '#9bd270', '#eac42a']
|
||||
const width = ref(window.innerWidth <= 600 ? window.innerWidth : 600)
|
||||
const height = ref(width.value * 2 / 3)
|
||||
const toggle = ref("duration");
|
||||
let myChart1, myChart2;
|
||||
const color = ["#bb792c", "#9bd270", "#eac42a"];
|
||||
const width = ref(window.innerWidth <= 600 ? window.innerWidth : 600);
|
||||
const height = ref((width.value * 2) / 3);
|
||||
|
||||
const toggleShow = () => {
|
||||
const offBtn = document.getElementById('toggle-off')
|
||||
const onBtn = document.getElementById('toggle-on')
|
||||
const offBtn = document.getElementById("toggle-off");
|
||||
const onBtn = document.getElementById("toggle-on");
|
||||
|
||||
if (offBtn.classList.contains('show')) {
|
||||
offBtn.classList.remove('show')
|
||||
onBtn.classList.add('show')
|
||||
toggle.value = 'total'
|
||||
} else if (onBtn.classList.contains('show')) {
|
||||
onBtn.classList.remove('show')
|
||||
offBtn.classList.add('show')
|
||||
toggle.value = 'duration'
|
||||
if (offBtn.classList.contains("show")) {
|
||||
offBtn.classList.remove("show");
|
||||
onBtn.classList.add("show");
|
||||
toggle.value = "total";
|
||||
} else if (onBtn.classList.contains("show")) {
|
||||
onBtn.classList.remove("show");
|
||||
offBtn.classList.add("show");
|
||||
toggle.value = "duration";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const valueArr = (stats, team, toggle, prop) => {
|
||||
if (['team', 'enemy', 'self'].indexOf(prop) > -1) {
|
||||
let arr = []
|
||||
if (["team", "enemy", "self"].indexOf(prop) > -1) {
|
||||
let arr = [];
|
||||
for (let i = (team - 1) * 5; i < team * 5; i++) {
|
||||
arr.push(checkStatEmpty(Function('return(function(stats, i){ return stats[i].flash.' + toggle.value + '.' + prop + '})')()(stats, i)).toFixed(2))
|
||||
arr.push(
|
||||
checkStatEmpty(
|
||||
Function(
|
||||
"return(function(stats, i){ return stats[i].flash." +
|
||||
toggle.value +
|
||||
"." +
|
||||
prop +
|
||||
"})"
|
||||
)()(stats, i)
|
||||
).toFixed(2)
|
||||
);
|
||||
}
|
||||
arr.reverse()
|
||||
arr.reverse();
|
||||
|
||||
return arr
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setOptions = (id, color) => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
type: "shadow",
|
||||
shadowStyle: {
|
||||
shadowBlur: 2,
|
||||
shadowColor: 'rgba(255, 255, 255, .3)'
|
||||
}
|
||||
}
|
||||
shadowColor: "rgba(255, 255, 255, .3)",
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
left: "3%",
|
||||
right: "4%",
|
||||
bottom: "3%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, 0.01]
|
||||
type: "value",
|
||||
boundaryGap: [0, 0.01],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: getPlayerArr(store.state.matchDetails.stats, id, true)
|
||||
type: "category",
|
||||
data: getPlayerArr(store.state.matchDetails.stats, id, true),
|
||||
},
|
||||
color: color,
|
||||
series: [
|
||||
{
|
||||
name: 'Enemy',
|
||||
type: 'bar',
|
||||
data: valueArr(store.state.matchDetails.stats, id, toggle, 'enemy'),
|
||||
name: "Enemy",
|
||||
type: "bar",
|
||||
data: valueArr(store.state.matchDetails.stats, id, toggle, "enemy"),
|
||||
},
|
||||
{
|
||||
name: 'Team',
|
||||
type: 'bar',
|
||||
data: valueArr(store.state.matchDetails.stats, id, toggle, 'team'),
|
||||
name: "Team",
|
||||
type: "bar",
|
||||
data: valueArr(store.state.matchDetails.stats, id, toggle, "team"),
|
||||
},
|
||||
{
|
||||
name: 'Self',
|
||||
type: 'bar',
|
||||
data: valueArr(store.state.matchDetails.stats, id, toggle, 'self'),
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
name: "Self",
|
||||
type: "bar",
|
||||
data: valueArr(store.state.matchDetails.stats, id, toggle, "self"),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const disposeCharts = () => {
|
||||
if (myChart1 != null && myChart1 !== '' && myChart1 !== undefined) {
|
||||
myChart1.dispose()
|
||||
if (myChart1 != null && myChart1 !== "" && myChart1 !== undefined) {
|
||||
myChart1.dispose();
|
||||
}
|
||||
if (myChart2 != null && myChart2 !== '' && myChart2 !== undefined) {
|
||||
myChart2.dispose()
|
||||
if (myChart2 != null && myChart2 !== "" && myChart2 !== undefined) {
|
||||
myChart2.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildCharts = () => {
|
||||
disposeCharts()
|
||||
disposeCharts();
|
||||
|
||||
myChart1 = echarts.init(document.getElementById('flash-chart-1'), {}, {
|
||||
width: width.value,
|
||||
height: height.value
|
||||
});
|
||||
myChart1 = echarts.init(
|
||||
document.getElementById("flash-chart-1"),
|
||||
{},
|
||||
{
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
}
|
||||
);
|
||||
myChart1.setOption(setOptions(1, color));
|
||||
|
||||
myChart2 = echarts.init(document.getElementById('flash-chart-2'), {}, {
|
||||
width: width.value,
|
||||
height: height.value
|
||||
});
|
||||
myChart2 = echarts.init(
|
||||
document.getElementById("flash-chart-2"),
|
||||
{},
|
||||
{
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
}
|
||||
);
|
||||
myChart2.setOption(setOptions(2, color));
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (store.state.matchDetails.stats) {
|
||||
@@ -152,33 +182,36 @@ export default {
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
BarChart,
|
||||
CanvasRenderer
|
||||
CanvasRenderer,
|
||||
]);
|
||||
|
||||
buildCharts()
|
||||
buildCharts();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
disposeCharts()
|
||||
})
|
||||
disposeCharts();
|
||||
});
|
||||
|
||||
watch(() => toggle.value, () => {
|
||||
buildCharts()
|
||||
})
|
||||
watch(
|
||||
() => toggle.value,
|
||||
() => {
|
||||
buildCharts();
|
||||
}
|
||||
);
|
||||
|
||||
window.onresize = () => {
|
||||
if (window.innerWidth <= 600) {
|
||||
width.value = window.innerWidth - 20
|
||||
height.value = width.value * 2 / 3
|
||||
width.value = window.innerWidth - 20;
|
||||
height.value = (width.value * 2) / 3;
|
||||
|
||||
buildCharts()
|
||||
buildCharts();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {toggleShow, toggle}
|
||||
}
|
||||
}
|
||||
return { toggleShow, toggle };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -204,7 +237,7 @@ export default {
|
||||
margin-top: 1rem;
|
||||
|
||||
td {
|
||||
font-size: .8rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
td:first-child,
|
||||
|
@@ -1,48 +0,0 @@
|
||||
<template>
|
||||
<div class="footer bg-secondary text-center pt-4 pb-2">
|
||||
<div class="text">
|
||||
<p class="fs-6">Made with <i class="fa fa-heart text-warning" aria-hidden="true"></i>, <span
|
||||
style="color: #41b883">Vue.js</span> and<a aria-label="Gitea" class="text-warning ms-2"
|
||||
href="https://git.harting.dev/CSGOWTF"
|
||||
target="_blank">
|
||||
<i aria-hidden="true" class="fa fa-gitea"></i>
|
||||
</a></p>
|
||||
|
||||
<div class="d-flex justify-content-center align-items-center gap-4">
|
||||
<p><a class="text-decoration-none text-warning"
|
||||
href="https://git.harting.dev/CSGOWTF/csgowtf/issues"
|
||||
target="_blank">Issue Tracker</a></p>
|
||||
<p class="text-muted">Version {{ version }}</p>
|
||||
<p>
|
||||
<a class="text-decoration-none text-warning" href="/privacy-policy">Privacy Policy</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Footer",
|
||||
setup() {
|
||||
const version = process.env.VUE_APP_VERSION
|
||||
return {version}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
.fa-gitea:hover {
|
||||
color: #609926 !important;
|
||||
}
|
||||
|
||||
.fa-heart:hover {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: .85rem;
|
||||
}
|
||||
}
|
||||
</style>
|
60
src/components/FooterComponent.vue
Normal file
60
src/components/FooterComponent.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="footer bg-secondary text-center pt-4 pb-2">
|
||||
<div class="text">
|
||||
<p class="fs-6">
|
||||
Made with <i aria-hidden="true" class="fa fa-heart text-warning"></i>,
|
||||
<span style="color: #41b883">Vue.js</span> and<a
|
||||
aria-label="Gitea"
|
||||
class="text-warning ms-2"
|
||||
href="https://git.harting.dev/CSGOWTF"
|
||||
target="_blank"
|
||||
>
|
||||
<i aria-hidden="true" class="fa fa-gitea"></i>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div class="d-flex justify-content-center align-items-center gap-4">
|
||||
<p>
|
||||
<a
|
||||
class="text-decoration-none text-warning"
|
||||
href="https://git.harting.dev/CSGOWTF/csgowtf/issues"
|
||||
target="_blank"
|
||||
>Issue Tracker</a
|
||||
>
|
||||
</p>
|
||||
<p class="text-muted">Version {{ version }}</p>
|
||||
<p>
|
||||
<a class="text-decoration-none text-warning" href="/privacy-policy"
|
||||
>Privacy Policy</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "FooterComponent",
|
||||
setup() {
|
||||
const version = import.meta.env.VITE_VERSION;
|
||||
return { version };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
.fa-gitea:hover {
|
||||
color: #609926 !important;
|
||||
}
|
||||
|
||||
.fa-heart:hover {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,26 +1,51 @@
|
||||
<template>
|
||||
<div class="hitgroup pt-2">
|
||||
<div class="d-flex flex-lg-nowrap flex-wrap justify-content-center gap-4">
|
||||
<div class="d-flex flex-column justify-content-center align-items-center w-auto">
|
||||
<div
|
||||
class="d-flex flex-column justify-content-center align-items-center w-auto"
|
||||
>
|
||||
<div class="select-group mb-4">
|
||||
<select v-if="store.state.playersArr" v-model="data.selectPlayer" class="form-select">
|
||||
<select
|
||||
v-if="store.state.playersArr"
|
||||
v-model="data.selectPlayer"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="All">All</option>
|
||||
<option value="Team 1">Team 1</option>
|
||||
<option value="Team 2">Team 2</option>
|
||||
<option disabled>───────────────────────────</option>
|
||||
<option v-for="(value, index) in props.stats" :key="index"
|
||||
:value="Object.keys(value).toString() === store.state.playersArr[index].player.steamid64 ? store.state.playersArr[index].player : ''">
|
||||
<option
|
||||
v-for="(value, index) in props.stats"
|
||||
:key="index"
|
||||
:value="
|
||||
Object.keys(value).toString() ===
|
||||
store.state.playersArr[index].player.steamid64
|
||||
? store.state.playersArr[index].player
|
||||
: ''
|
||||
"
|
||||
>
|
||||
{{
|
||||
Object.keys(value).toString() === store.state.playersArr[index].player.steamid64 ? store.state.playersArr[index].player.name : ''
|
||||
Object.keys(value).toString() ===
|
||||
store.state.playersArr[index].player.steamid64
|
||||
? store.state.playersArr[index].player.name
|
||||
: ""
|
||||
}}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<select v-if="data.selectPlayer !== ''" :key="data.selectPlayer" v-model="data.selectWeapon"
|
||||
class="form-select">
|
||||
<select
|
||||
v-if="data.selectPlayer !== ''"
|
||||
:key="data.selectPlayer"
|
||||
v-model="data.selectWeapon"
|
||||
class="form-select"
|
||||
>
|
||||
<option class="select-hr" value="All">All</option>
|
||||
<option disabled>───────────────────────────</option>
|
||||
<option v-for="(value, index) in processPlayerWeapon()" :key="index" :value="value">
|
||||
<option
|
||||
v-for="(value, index) in processPlayerWeapon()"
|
||||
:key="index"
|
||||
:value="value"
|
||||
>
|
||||
<!-- This is here, because weapons are not always named correctly -->
|
||||
<!-- {{ Object.values(value).toString().charAt(0).toUpperCase() + Object.values(value).toString().slice(1) }}-->
|
||||
{{ Object.values(value).toString() }}
|
||||
@@ -28,31 +53,59 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="hitgroup-puppet"/>
|
||||
<div id="hitgroup-puppet" />
|
||||
</div>
|
||||
|
||||
<div v-if="data.weaponDmg"
|
||||
id="bar-graph"
|
||||
class="w-auto"
|
||||
:style="{
|
||||
minWidth: dmgWidth + 'px'
|
||||
}">
|
||||
<div
|
||||
v-if="data.weaponDmg"
|
||||
id="bar-graph"
|
||||
:style="{
|
||||
minWidth: dmgWidth + 'px',
|
||||
}"
|
||||
class="w-auto"
|
||||
>
|
||||
<table class="table table-borderless">
|
||||
<tr v-for="(value, index) in data.weaponDmg" :key="index">
|
||||
<td v-if="index < 10 && (data.selectWeapon === 'All' || Object.keys(data.selectWeapon).toString() === Object.keys(value).toString())"
|
||||
style="width: 100px">
|
||||
<img :alt="Object.values(value).toString()"
|
||||
:src="DisplayWeapon(parseInt(Object.keys(value)[0]))"/>
|
||||
<td
|
||||
v-if="
|
||||
index < 10 &&
|
||||
(data.selectWeapon === 'All' ||
|
||||
Object.keys(data.selectWeapon).toString() ===
|
||||
Object.keys(value).toString())
|
||||
"
|
||||
style="width: 100px"
|
||||
>
|
||||
<img
|
||||
:alt="Object.values(value).toString()"
|
||||
:src="DisplayWeapon(parseInt(Object.keys(value)[0]))"
|
||||
/>
|
||||
</td>
|
||||
<td v-if="index < 10 && (data.selectWeapon === 'All' || Object.keys(data.selectWeapon).toString() === Object.keys(value).toString())">
|
||||
<span :style="{
|
||||
width: (processWeaponDmg(Object.keys(value).toString()) / processWeaponDmg(Object.keys(data.weaponDmg[0]).toString()) * 100).toFixed(0) + '%',
|
||||
backgroundColor: 'orangered',
|
||||
display: 'block',
|
||||
}"
|
||||
class="rounded"
|
||||
<td
|
||||
v-if="
|
||||
index < 10 &&
|
||||
(data.selectWeapon === 'All' ||
|
||||
Object.keys(data.selectWeapon).toString() ===
|
||||
Object.keys(value).toString())
|
||||
"
|
||||
>
|
||||
<span
|
||||
:style="{
|
||||
width:
|
||||
(
|
||||
(processWeaponDmg(Object.keys(value).toString()) /
|
||||
processWeaponDmg(
|
||||
Object.keys(data.weaponDmg[0]).toString()
|
||||
)) *
|
||||
100
|
||||
).toFixed(0) + '%',
|
||||
backgroundColor: 'orangered',
|
||||
display: 'block',
|
||||
}"
|
||||
class="rounded"
|
||||
>
|
||||
<span>{{ processWeaponDmg(Object.keys(value).toString()) }}</span>
|
||||
<span>{{
|
||||
processWeaponDmg(Object.keys(value).toString())
|
||||
}}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -63,15 +116,19 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts/core';
|
||||
import {GeoComponent, TooltipComponent, VisualMapComponent} from 'echarts/components';
|
||||
import {MapChart} from 'echarts/charts';
|
||||
import {CanvasRenderer} from 'echarts/renderers';
|
||||
import {onMounted, onUnmounted, reactive, ref, watch} from "vue";
|
||||
import {useStore} from "vuex";
|
||||
import {DisplayWeapon} from '@/utils'
|
||||
import * as echarts from "echarts/core";
|
||||
import {
|
||||
GeoComponent,
|
||||
TooltipComponent,
|
||||
VisualMapComponent,
|
||||
} from "echarts/components";
|
||||
import { MapChart } from "echarts/charts";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { onMounted, onUnmounted, reactive, ref, watch } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import { DisplayWeapon } from "/src/utils";
|
||||
|
||||
import $ from 'jquery'
|
||||
import $ from "jquery";
|
||||
|
||||
export default {
|
||||
name: "HitgroupPuppet.vue",
|
||||
@@ -82,413 +139,460 @@ export default {
|
||||
},
|
||||
stats: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
const data = reactive({
|
||||
selectPlayer: 'All',
|
||||
selectWeapon: 'All',
|
||||
selectPlayer: "All",
|
||||
selectWeapon: "All",
|
||||
eq_map: [],
|
||||
weaponDmg: []
|
||||
})
|
||||
weaponDmg: [],
|
||||
});
|
||||
|
||||
let myChart1
|
||||
let myChart1;
|
||||
|
||||
const getWindowWidth = () => {
|
||||
const windowWidth = window.innerWidth
|
||||
if (windowWidth <= 750)
|
||||
return windowWidth
|
||||
else
|
||||
return 650
|
||||
}
|
||||
const windowWidth = window.innerWidth;
|
||||
if (windowWidth <= 750) return windowWidth;
|
||||
else return 650;
|
||||
};
|
||||
|
||||
const setDmgWidth = () => {
|
||||
const windowWidth = getWindowWidth()
|
||||
if (windowWidth >= 500)
|
||||
return 500
|
||||
else
|
||||
return windowWidth - 10
|
||||
}
|
||||
const windowWidth = getWindowWidth();
|
||||
if (windowWidth >= 500) return 500;
|
||||
else return windowWidth - 10;
|
||||
};
|
||||
|
||||
const dmgWidth = ref(setDmgWidth())
|
||||
const dmgWidth = ref(setDmgWidth());
|
||||
|
||||
const setHeight = () => {
|
||||
const windowWidth = getWindowWidth()
|
||||
if (windowWidth >= 751)
|
||||
return windowWidth * 3 / 7.5
|
||||
const windowWidth = getWindowWidth();
|
||||
if (windowWidth >= 751) return (windowWidth * 3) / 7.5;
|
||||
else if (windowWidth >= 501 && windowWidth <= 750)
|
||||
return windowWidth * 3 / 6.5
|
||||
else
|
||||
return windowWidth * 3 / 5.5
|
||||
}
|
||||
return (windowWidth * 3) / 6.5;
|
||||
else return (windowWidth * 3) / 5.5;
|
||||
};
|
||||
|
||||
const width = ref(getWindowWidth())
|
||||
const height = ref(setHeight())
|
||||
const width = ref(getWindowWidth());
|
||||
const height = ref(setHeight());
|
||||
|
||||
const processWeaponDmg = (id) => {
|
||||
let value = ''
|
||||
data.weaponDmg.forEach(w => {
|
||||
let value = "";
|
||||
data.weaponDmg.forEach((w) => {
|
||||
if (Object.keys(w).toString() === id) {
|
||||
value = Object.values(w).toString()
|
||||
value = Object.values(w).toString();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return value
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const processPlayerWeapon = () => {
|
||||
let arr = []
|
||||
if (data.selectPlayer === 'All') {
|
||||
props.stats.forEach(player => {
|
||||
Object.values(player).forEach(enemies => {
|
||||
Object.values(enemies).forEach(weapons => {
|
||||
Object.values(weapons).forEach(weapon => {
|
||||
arr.push(weapon[0])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
} else if (data.selectPlayer === 'Team 1') {
|
||||
props.stats.forEach(player => {
|
||||
store.state.playersArr.forEach(p => {
|
||||
if (p.player.steamid64 === Object.keys(player).toString() && p.team_id === 1)
|
||||
Object.values(player).forEach(enemies => {
|
||||
Object.values(enemies).forEach(weapons => {
|
||||
Object.values(weapons).forEach(weapon => {
|
||||
arr.push(weapon[0])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
} else if (data.selectPlayer === 'Team 2') {
|
||||
props.stats.forEach(player => {
|
||||
store.state.playersArr.forEach(p => {
|
||||
if (p.player.steamid64 === Object.keys(player).toString() && p.team_id === 2)
|
||||
Object.values(player).forEach(enemies => {
|
||||
Object.values(enemies).forEach(weapons => {
|
||||
Object.values(weapons).forEach(weapon => {
|
||||
arr.push(weapon[0])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
let arr = [];
|
||||
if (data.selectPlayer === "All") {
|
||||
props.stats.forEach((player) => {
|
||||
Object.values(player).forEach((enemies) => {
|
||||
Object.values(enemies).forEach((weapons) => {
|
||||
Object.values(weapons).forEach((weapon) => {
|
||||
arr.push(weapon[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if (data.selectPlayer === "Team 1") {
|
||||
props.stats.forEach((player) => {
|
||||
store.state.playersArr.forEach((p) => {
|
||||
if (
|
||||
p.player.steamid64 === Object.keys(player).toString() &&
|
||||
p.team_id === 1
|
||||
)
|
||||
Object.values(player).forEach((enemies) => {
|
||||
Object.values(enemies).forEach((weapons) => {
|
||||
Object.values(weapons).forEach((weapon) => {
|
||||
arr.push(weapon[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if (data.selectPlayer === "Team 2") {
|
||||
props.stats.forEach((player) => {
|
||||
store.state.playersArr.forEach((p) => {
|
||||
if (
|
||||
p.player.steamid64 === Object.keys(player).toString() &&
|
||||
p.team_id === 2
|
||||
)
|
||||
Object.values(player).forEach((enemies) => {
|
||||
Object.values(enemies).forEach((weapons) => {
|
||||
Object.values(weapons).forEach((weapon) => {
|
||||
arr.push(weapon[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
props.stats.forEach(player => {
|
||||
props.stats.forEach((player) => {
|
||||
if (Object.keys(player).toString() === data.selectPlayer.steamid64) {
|
||||
Object.values(player).forEach(enemies => {
|
||||
Object.values(enemies).forEach(weapons => {
|
||||
Object.values(weapons).forEach(weapon => {
|
||||
arr.push(weapon[0])
|
||||
})
|
||||
})
|
||||
})
|
||||
Object.values(player).forEach((enemies) => {
|
||||
Object.values(enemies).forEach((weapons) => {
|
||||
Object.values(weapons).forEach((weapon) => {
|
||||
arr.push(weapon[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const unique = arr.filter((a, b) => arr.indexOf(a) === b && a < 400)
|
||||
const unique = arr.filter((a, b) => arr.indexOf(a) === b && a < 400);
|
||||
|
||||
let arr2 = []
|
||||
let arr2 = [];
|
||||
|
||||
unique.forEach(w => {
|
||||
unique.forEach((w) => {
|
||||
for (let weapon in props.equipment_map) {
|
||||
if (parseInt(w) === parseInt(weapon)) {
|
||||
let obj = {}
|
||||
obj[w] = props.equipment_map[weapon]
|
||||
arr2.push(obj)
|
||||
let obj = {};
|
||||
obj[w] = props.equipment_map[weapon];
|
||||
arr2.push(obj);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return arr2
|
||||
}
|
||||
return arr2;
|
||||
};
|
||||
|
||||
const processDmg = (by = 'hitgroup') => {
|
||||
let arr = []
|
||||
const processDmg = (by = "hitgroup") => {
|
||||
let arr = [];
|
||||
if (data.selectPlayer && data.selectWeapon) {
|
||||
switch (data.selectPlayer) {
|
||||
case "All":
|
||||
props.stats.forEach(player => {
|
||||
Object.values(player).forEach(enemies => {
|
||||
Object.values(enemies).forEach(weapons => {
|
||||
Object.values(weapons).forEach(weapon => {
|
||||
props.stats.forEach((player) => {
|
||||
Object.values(player).forEach((enemies) => {
|
||||
Object.values(enemies).forEach((weapons) => {
|
||||
Object.values(weapons).forEach((weapon) => {
|
||||
// 0: weapon
|
||||
// 1: hitgroup
|
||||
// 2: dmg
|
||||
if (weapon) {
|
||||
if (by === 'hitgroup') {
|
||||
if (Object.values(weapon)[0] === parseInt(Object.keys(data.selectWeapon).toString())) {
|
||||
let obj = {}
|
||||
obj[weapon[1]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (data.selectWeapon === 'All') {
|
||||
let obj = {}
|
||||
obj[weapon[1]] = weapon[2]
|
||||
arr.push(obj)
|
||||
if (by === "hitgroup") {
|
||||
if (
|
||||
Object.values(weapon)[0] ===
|
||||
parseInt(Object.keys(data.selectWeapon).toString())
|
||||
) {
|
||||
let obj = {};
|
||||
obj[weapon[1]] = weapon[2];
|
||||
arr.push(obj);
|
||||
} else if (data.selectWeapon === "All") {
|
||||
let obj = {};
|
||||
obj[weapon[1]] = weapon[2];
|
||||
arr.push(obj);
|
||||
}
|
||||
} else if (by === 'weapon') {
|
||||
if (Object.values(weapon)[0] === parseInt(Object.keys(data.selectWeapon).toString())) {
|
||||
let obj = {}
|
||||
obj[weapon[0]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (data.selectWeapon === 'All') {
|
||||
let obj = {}
|
||||
obj[weapon[0]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (by === "weapon") {
|
||||
if (
|
||||
Object.values(weapon)[0] ===
|
||||
parseInt(Object.keys(data.selectWeapon).toString())
|
||||
) {
|
||||
let obj = {};
|
||||
obj[weapon[0]] = weapon[2];
|
||||
arr.push(obj);
|
||||
} else if (data.selectWeapon === "All") {
|
||||
let obj = {};
|
||||
obj[weapon[0]] = weapon[2];
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case "Team 1":
|
||||
props.stats.forEach(player => {
|
||||
store.state.playersArr.forEach(p => {
|
||||
if (p.player.steamid64 === Object.keys(player).toString() && p.team_id === 1)
|
||||
Object.values(player).forEach(enemies => {
|
||||
Object.values(enemies).forEach(weapons => {
|
||||
Object.values(weapons).forEach(weapon => {
|
||||
props.stats.forEach((player) => {
|
||||
store.state.playersArr.forEach((p) => {
|
||||
if (
|
||||
p.player.steamid64 === Object.keys(player).toString() &&
|
||||
p.team_id === 1
|
||||
)
|
||||
Object.values(player).forEach((enemies) => {
|
||||
Object.values(enemies).forEach((weapons) => {
|
||||
Object.values(weapons).forEach((weapon) => {
|
||||
// 0: weapon
|
||||
// 1: hitgroup
|
||||
// 2: dmg
|
||||
if (weapon) {
|
||||
if (by === 'hitgroup') {
|
||||
if (Object.values(weapon)[0] === parseInt(Object.keys(data.selectWeapon).toString())) {
|
||||
let obj = {}
|
||||
obj[weapon[1]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (data.selectWeapon === 'All') {
|
||||
let obj = {}
|
||||
obj[weapon[1]] = weapon[2]
|
||||
arr.push(obj)
|
||||
if (by === "hitgroup") {
|
||||
if (
|
||||
Object.values(weapon)[0] ===
|
||||
parseInt(
|
||||
Object.keys(data.selectWeapon).toString()
|
||||
)
|
||||
) {
|
||||
let obj = {};
|
||||
obj[weapon[1]] = weapon[2];
|
||||
arr.push(obj);
|
||||
} else if (data.selectWeapon === "All") {
|
||||
let obj = {};
|
||||
obj[weapon[1]] = weapon[2];
|
||||
arr.push(obj);
|
||||
}
|
||||
} else if (by === 'weapon') {
|
||||
if (Object.values(weapon)[0] === parseInt(Object.keys(data.selectWeapon).toString())) {
|
||||
let obj = {}
|
||||
obj[weapon[0]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (data.selectWeapon === 'All') {
|
||||
let obj = {}
|
||||
obj[weapon[0]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (by === "weapon") {
|
||||
if (
|
||||
Object.values(weapon)[0] ===
|
||||
parseInt(
|
||||
Object.keys(data.selectWeapon).toString()
|
||||
)
|
||||
) {
|
||||
let obj = {};
|
||||
obj[weapon[0]] = weapon[2];
|
||||
arr.push(obj);
|
||||
} else if (data.selectWeapon === "All") {
|
||||
let obj = {};
|
||||
obj[weapon[0]] = weapon[2];
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case "Team 2":
|
||||
props.stats.forEach(player => {
|
||||
store.state.playersArr.forEach(p => {
|
||||
if (p.player.steamid64 === Object.keys(player).toString() && p.team_id === 2)
|
||||
Object.values(player).forEach(enemies => {
|
||||
Object.values(enemies).forEach(weapons => {
|
||||
Object.values(weapons).forEach(weapon => {
|
||||
props.stats.forEach((player) => {
|
||||
store.state.playersArr.forEach((p) => {
|
||||
if (
|
||||
p.player.steamid64 === Object.keys(player).toString() &&
|
||||
p.team_id === 2
|
||||
)
|
||||
Object.values(player).forEach((enemies) => {
|
||||
Object.values(enemies).forEach((weapons) => {
|
||||
Object.values(weapons).forEach((weapon) => {
|
||||
// 0: weapon
|
||||
// 1: hitgroup
|
||||
// 2: dmg
|
||||
if (weapon) {
|
||||
if (by === 'hitgroup') {
|
||||
if (Object.values(weapon)[0] === parseInt(Object.keys(data.selectWeapon).toString())) {
|
||||
let obj = {}
|
||||
obj[weapon[1]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (data.selectWeapon === 'All') {
|
||||
let obj = {}
|
||||
obj[weapon[1]] = weapon[2]
|
||||
arr.push(obj)
|
||||
if (by === "hitgroup") {
|
||||
if (
|
||||
Object.values(weapon)[0] ===
|
||||
parseInt(
|
||||
Object.keys(data.selectWeapon).toString()
|
||||
)
|
||||
) {
|
||||
let obj = {};
|
||||
obj[weapon[1]] = weapon[2];
|
||||
arr.push(obj);
|
||||
} else if (data.selectWeapon === "All") {
|
||||
let obj = {};
|
||||
obj[weapon[1]] = weapon[2];
|
||||
arr.push(obj);
|
||||
}
|
||||
} else if (by === 'weapon') {
|
||||
if (Object.values(weapon)[0] === parseInt(Object.keys(data.selectWeapon).toString())) {
|
||||
let obj = {}
|
||||
obj[weapon[0]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (data.selectWeapon === 'All') {
|
||||
let obj = {}
|
||||
obj[weapon[0]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (by === "weapon") {
|
||||
if (
|
||||
Object.values(weapon)[0] ===
|
||||
parseInt(
|
||||
Object.keys(data.selectWeapon).toString()
|
||||
)
|
||||
) {
|
||||
let obj = {};
|
||||
obj[weapon[0]] = weapon[2];
|
||||
arr.push(obj);
|
||||
} else if (data.selectWeapon === "All") {
|
||||
let obj = {};
|
||||
obj[weapon[0]] = weapon[2];
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
props.stats.forEach(player => {
|
||||
if (Object.keys(player).toString() === data.selectPlayer.steamid64) {
|
||||
Object.values(player).forEach(enemies => {
|
||||
Object.values(enemies).forEach(weapons => {
|
||||
Object.values(weapons).forEach(weapon => {
|
||||
props.stats.forEach((player) => {
|
||||
if (
|
||||
Object.keys(player).toString() === data.selectPlayer.steamid64
|
||||
) {
|
||||
Object.values(player).forEach((enemies) => {
|
||||
Object.values(enemies).forEach((weapons) => {
|
||||
Object.values(weapons).forEach((weapon) => {
|
||||
// 0: weapon
|
||||
// 1: hitgroup
|
||||
// 2: dmg
|
||||
if (weapon) {
|
||||
if (by === 'hitgroup') {
|
||||
if (Object.values(weapon)[0] === parseInt(Object.keys(data.selectWeapon).toString())) {
|
||||
let obj = {}
|
||||
obj[weapon[1]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (data.selectWeapon === 'All') {
|
||||
let obj = {}
|
||||
obj[weapon[1]] = weapon[2]
|
||||
arr.push(obj)
|
||||
if (by === "hitgroup") {
|
||||
if (
|
||||
Object.values(weapon)[0] ===
|
||||
parseInt(Object.keys(data.selectWeapon).toString())
|
||||
) {
|
||||
let obj = {};
|
||||
obj[weapon[1]] = weapon[2];
|
||||
arr.push(obj);
|
||||
} else if (data.selectWeapon === "All") {
|
||||
let obj = {};
|
||||
obj[weapon[1]] = weapon[2];
|
||||
arr.push(obj);
|
||||
}
|
||||
} else if (by === 'weapon') {
|
||||
if (Object.values(weapon)[0] === parseInt(Object.keys(data.selectWeapon).toString())) {
|
||||
let obj = {}
|
||||
obj[weapon[0]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (data.selectWeapon === 'All') {
|
||||
let obj = {}
|
||||
obj[weapon[0]] = weapon[2]
|
||||
arr.push(obj)
|
||||
} else if (by === "weapon") {
|
||||
if (
|
||||
Object.values(weapon)[0] ===
|
||||
parseInt(Object.keys(data.selectWeapon).toString())
|
||||
) {
|
||||
let obj = {};
|
||||
obj[weapon[0]] = weapon[2];
|
||||
arr.push(obj);
|
||||
} else if (data.selectWeapon === "All") {
|
||||
let obj = {};
|
||||
obj[weapon[0]] = weapon[2];
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
arr = []
|
||||
arr = [];
|
||||
}
|
||||
|
||||
if (by === 'hitgroup') {
|
||||
buildCharts(sumDmgArr(arr))
|
||||
} else if (by === 'weapon') {
|
||||
data.weaponDmg = sumDmgArr(arr, 'weapon')
|
||||
if (by === "hitgroup") {
|
||||
buildCharts(sumDmgArr(arr));
|
||||
} else if (by === "weapon") {
|
||||
data.weaponDmg = sumDmgArr(arr, "weapon");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sumDmgArr = (arr, by = 'hitgroup') => {
|
||||
const sumDmgArr = (arr, by = "hitgroup") => {
|
||||
let holder = {};
|
||||
|
||||
arr.forEach(function (d) {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (holder.hasOwnProperty(parseInt(Object.keys(d).toString()))) {
|
||||
holder[parseInt(Object.keys(d).toString())] = holder[parseInt(Object.keys(d).toString())] + parseInt(Object.values(d).toString());
|
||||
holder[parseInt(Object.keys(d).toString())] =
|
||||
holder[parseInt(Object.keys(d).toString())] +
|
||||
parseInt(Object.values(d).toString());
|
||||
} else {
|
||||
holder[parseInt(Object.keys(d).toString())] = parseInt(Object.values(d).toString());
|
||||
holder[parseInt(Object.keys(d).toString())] = parseInt(
|
||||
Object.values(d).toString()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let arr2 = [];
|
||||
|
||||
if (by === 'hitgroup') {
|
||||
if (by === "hitgroup") {
|
||||
for (let i = 1; i < 8; i++) {
|
||||
if (holder[i] !== undefined) {
|
||||
arr2.push(holder[i])
|
||||
arr2.push(holder[i]);
|
||||
} else {
|
||||
arr2.push(0)
|
||||
arr2.push(0);
|
||||
}
|
||||
}
|
||||
} else if (by === 'weapon') {
|
||||
} else if (by === "weapon") {
|
||||
for (let i = 1; i < 312; i++) {
|
||||
if (holder[i] !== undefined) {
|
||||
let obj = {}
|
||||
obj[i] = holder[i]
|
||||
arr2.push(obj)
|
||||
let obj = {};
|
||||
obj[i] = holder[i];
|
||||
arr2.push(obj);
|
||||
}
|
||||
}
|
||||
|
||||
arr2.sort((a, b) => {
|
||||
return Object.values(b).toString() - Object.values(a).toString()
|
||||
})
|
||||
return Object.values(b).toString() - Object.values(a).toString();
|
||||
});
|
||||
}
|
||||
|
||||
return arr2
|
||||
}
|
||||
return arr2;
|
||||
};
|
||||
|
||||
const getMax = (arr) => {
|
||||
let max = 0
|
||||
let max = 0;
|
||||
for (let i = 0; i < 7; i++) {
|
||||
if (arr[i] > max)
|
||||
max = arr[i]
|
||||
if (arr[i] > max) max = arr[i];
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
||||
return max;
|
||||
};
|
||||
|
||||
const optionGen = (arr = []) => {
|
||||
return {
|
||||
tooltip: {},
|
||||
visualMap: {
|
||||
left: 'center',
|
||||
bottom: '5%',
|
||||
left: "center",
|
||||
bottom: "5%",
|
||||
textStyle: {
|
||||
color: 'white',
|
||||
color: "white",
|
||||
},
|
||||
min: 0,
|
||||
max: getMax(arr) || 100,
|
||||
orient: 'horizontal',
|
||||
orient: "horizontal",
|
||||
realtime: true,
|
||||
calculable: true,
|
||||
inRange: {
|
||||
color: ['#00ff00', '#db6e00', '#cf0000']
|
||||
}
|
||||
color: ["#00ff00", "#db6e00", "#cf0000"],
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Hitgroup',
|
||||
type: 'map',
|
||||
map: 'hitgroup-puppet',
|
||||
top: '0%',
|
||||
name: "Hitgroup",
|
||||
type: "map",
|
||||
map: "hitgroup-puppet",
|
||||
top: "0%",
|
||||
emphasis: {
|
||||
label: {
|
||||
show: false
|
||||
}
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
selectedMode: false,
|
||||
data: [
|
||||
{name: 'Head', value: arr[0] || 0},
|
||||
{name: 'Chest', value: arr[1] || 0},
|
||||
{name: 'Stomach', value: arr[2] || 0},
|
||||
{name: 'Left Arm', value: arr[3] || 0},
|
||||
{name: 'Right Arm', value: arr[4] || 0},
|
||||
{name: 'Left Foot', value: arr[5] || 0},
|
||||
{name: 'Right Foot', value: arr[6] || 0}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
{ name: "Head", value: arr[0] || 0 },
|
||||
{ name: "Chest", value: arr[1] || 0 },
|
||||
{ name: "Stomach", value: arr[2] || 0 },
|
||||
{ name: "Left Arm", value: arr[3] || 0 },
|
||||
{ name: "Right Arm", value: arr[4] || 0 },
|
||||
{ name: "Left Foot", value: arr[5] || 0 },
|
||||
{ name: "Right Foot", value: arr[6] || 0 },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const disposeCharts = () => {
|
||||
if (myChart1 != null && myChart1 !== '' && myChart1 !== undefined) {
|
||||
myChart1.dispose()
|
||||
if (myChart1 != null && myChart1 !== "" && myChart1 !== undefined) {
|
||||
myChart1.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildCharts = (arr) => {
|
||||
disposeCharts()
|
||||
disposeCharts();
|
||||
|
||||
myChart1 = echarts.init(document.getElementById('hitgroup-puppet'), {}, {width: 300, height: 500})
|
||||
myChart1 = echarts.init(
|
||||
document.getElementById("hitgroup-puppet"),
|
||||
{},
|
||||
{ width: 300, height: 500 }
|
||||
);
|
||||
|
||||
const url = '/images/icons/hitgroup-puppet.svg'
|
||||
const url = "/images/icons/hitgroup-puppet.svg";
|
||||
$.get(url, function (svg) {
|
||||
echarts.registerMap('hitgroup-puppet', {svg: svg})
|
||||
echarts.registerMap("hitgroup-puppet", { svg: svg });
|
||||
myChart1.setOption(optionGen(arr));
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (store.state.matchDetails.stats) {
|
||||
@@ -497,48 +601,65 @@ export default {
|
||||
VisualMapComponent,
|
||||
GeoComponent,
|
||||
MapChart,
|
||||
CanvasRenderer
|
||||
CanvasRenderer,
|
||||
]);
|
||||
|
||||
buildCharts()
|
||||
buildCharts();
|
||||
|
||||
watch(() => props.stats, () => {
|
||||
processDmg()
|
||||
processDmg('weapon')
|
||||
processPlayerWeapon()
|
||||
})
|
||||
watch(
|
||||
() => props.stats,
|
||||
() => {
|
||||
processDmg();
|
||||
processDmg("weapon");
|
||||
processPlayerWeapon();
|
||||
}
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
disposeCharts()
|
||||
})
|
||||
disposeCharts();
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
if (window.innerWidth <= 750) {
|
||||
width.value = getWindowWidth() - 20
|
||||
height.value = setHeight()
|
||||
dmgWidth.value = setDmgWidth()
|
||||
width.value = getWindowWidth() - 20;
|
||||
height.value = setHeight();
|
||||
dmgWidth.value = setDmgWidth();
|
||||
}
|
||||
|
||||
buildCharts()
|
||||
}
|
||||
buildCharts();
|
||||
};
|
||||
|
||||
watch(() => data.selectPlayer, () => {
|
||||
data.selectWeapon = 'All'
|
||||
processPlayerWeapon()
|
||||
processDmg()
|
||||
processDmg('weapon')
|
||||
})
|
||||
watch(
|
||||
() => data.selectPlayer,
|
||||
() => {
|
||||
data.selectWeapon = "All";
|
||||
processPlayerWeapon();
|
||||
processDmg();
|
||||
processDmg("weapon");
|
||||
}
|
||||
);
|
||||
|
||||
watch(() => data.selectWeapon, () => {
|
||||
processDmg()
|
||||
processDmg('weapon')
|
||||
})
|
||||
watch(
|
||||
() => data.selectWeapon,
|
||||
() => {
|
||||
processDmg();
|
||||
processDmg("weapon");
|
||||
}
|
||||
);
|
||||
|
||||
return {props, data, store, dmgWidth, processPlayerWeapon, processWeaponDmg, DisplayWeapon}
|
||||
}
|
||||
}
|
||||
return {
|
||||
props,
|
||||
data,
|
||||
store,
|
||||
dmgWidth,
|
||||
processPlayerWeapon,
|
||||
processWeaponDmg,
|
||||
DisplayWeapon,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -1,17 +1,26 @@
|
||||
<template>
|
||||
<div v-if="infos.data" id="modal">
|
||||
<div v-for="(info, id) in infos.data" :key="id" class="custom-modal">
|
||||
<div :class="info.type === 'error'
|
||||
? 'bg-danger text-white'
|
||||
: info.type === 'warning'
|
||||
? 'bg-warning text-secondary'
|
||||
: info.type === 'success'
|
||||
? 'bg-success text-white'
|
||||
: 'bg-secondary text-white'"
|
||||
class="card">
|
||||
<div
|
||||
:class="
|
||||
info.type === 'error'
|
||||
? 'bg-danger text-white'
|
||||
: info.type === 'warning'
|
||||
? 'bg-warning text-secondary'
|
||||
: info.type === 'success'
|
||||
? 'bg-success text-white'
|
||||
: 'bg-secondary text-white'
|
||||
"
|
||||
class="card"
|
||||
>
|
||||
<div class="card-body d-flex justify-content-between">
|
||||
<span class="info-text">{{ info.message }}</span>
|
||||
<button aria-label="Close" class="btn-close" type="button" @click="closeModal(id)"/>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="btn-close"
|
||||
type="button"
|
||||
@click="closeModal(id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -19,36 +28,36 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {useStore} from "vuex";
|
||||
import {onMounted, reactive} from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import { onMounted, reactive } from "vue";
|
||||
|
||||
export default {
|
||||
name: "InfoModal",
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
const infos = reactive({
|
||||
data: []
|
||||
})
|
||||
data: [],
|
||||
});
|
||||
|
||||
const closeModal = (id) => {
|
||||
store.commit('removeInfoState', id)
|
||||
}
|
||||
store.commit("removeInfoState", id);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
store.subscribe(((mutation, state) => {
|
||||
if (mutation.type === 'changeInfoState') {
|
||||
infos.data = state.info
|
||||
store.subscribe((mutation, state) => {
|
||||
if (mutation.type === "changeInfoState") {
|
||||
infos.data = state.info;
|
||||
|
||||
setTimeout(() => {
|
||||
closeModal(store.state.info.length - 1)
|
||||
}, 5000)
|
||||
closeModal(store.state.info.length - 1);
|
||||
}, 5000);
|
||||
}
|
||||
}))
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
return {infos, closeModal}
|
||||
}
|
||||
}
|
||||
return { infos, closeModal };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -59,17 +68,17 @@ export default {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
opacity: .8;
|
||||
opacity: 0.8;
|
||||
width: min(100vw - 2rem, 50ch);
|
||||
height: var(--height);
|
||||
|
||||
.btn-close {
|
||||
background-color: white;
|
||||
opacity: .5;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
font-size: .8rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,59 +1,95 @@
|
||||
<template>
|
||||
<div class="container w-50">
|
||||
<TranslateChatButton
|
||||
v-if="data.chat.length > 0"
|
||||
:translated="data.translatedText.length > 0"
|
||||
class="translate-btn"
|
||||
@translated="handleTranslatedText"
|
||||
v-if="data.chat.length > 0"
|
||||
:translated="data.translatedText.length > 0"
|
||||
class="translate-btn"
|
||||
@translated="handleTranslatedText"
|
||||
/>
|
||||
<div v-if="data.chat.length > 0" class="chat-history mt-2">
|
||||
<table id="chat" :style="`max-width: ${data.clientWidth}px; width: ${data.clientWidth}px`" class="table table-borderless">
|
||||
<table
|
||||
id="chat"
|
||||
:style="`max-width: ${data.clientWidth}px; width: ${data.clientWidth}px`"
|
||||
class="table table-borderless"
|
||||
>
|
||||
<tbody>
|
||||
<tr v-for="(m, id) in data.chat" :key="id">
|
||||
<td class="td-time">
|
||||
{{ ConvertTickToTime(m.tick, m.tick_rate) }}
|
||||
</td>
|
||||
<td class="td-avatar">
|
||||
<img :class="'team-color-' + m.color"
|
||||
:src="constructAvatarUrl(m.avatar)"
|
||||
alt="Player avatar"
|
||||
class="avatar">
|
||||
</td>
|
||||
<td :class="m.startSide === 1 ? 'text-info' : 'text-warning'"
|
||||
<tr v-for="(m, id) in data.chat" :key="id">
|
||||
<td class="td-time">
|
||||
{{ ConvertTickToTime(m.tick, m.tick_rate) }}
|
||||
</td>
|
||||
<td class="td-avatar">
|
||||
<img
|
||||
:class="'team-color-' + m.color"
|
||||
:src="constructAvatarUrl(m.avatar)"
|
||||
alt="Player avatar"
|
||||
class="avatar"
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
:class="m.startSide === 1 ? 'text-info' : 'text-warning'"
|
||||
class="td-name d-flex"
|
||||
@click="GoToPlayer(m.steamid64)">
|
||||
<span>
|
||||
<i v-if="m.tracked" class="fa fa-dot-circle-o text-success tracked" title="Tracked user"/>
|
||||
<span :class="(m.vac && FormatVacDate(m.vac_date, store.state.matchDetails.date) !== '')
|
||||
|| (!m.vac && m.game_ban && FormatVacDate(m.game_ban_date, store.state.matchDetails.date) !== '')
|
||||
? 'ban-shadow'
|
||||
: ''"
|
||||
:title="!m.vac && m.game_ban
|
||||
? 'Game-banned: ' + FormatVacDate(m.game_ban_date, store.state.matchDetails.date)
|
||||
: m.vac && !m.game_ban
|
||||
? 'Vac-banned: ' + FormatVacDate(m.vac_date, store.state.matchDetails.date)
|
||||
: ''">
|
||||
{{ m.player }}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="td-icon">
|
||||
<i class="fa fa-caret-right"/>
|
||||
<span v-if="!m.all_chat" class="ms-1">
|
||||
(team)
|
||||
</span>
|
||||
</td>
|
||||
<td class="td-message">
|
||||
{{ data.translatedText.length === 0 ? m.message : data.originalChat[id].message }}
|
||||
<span v-if="m.translated_from"
|
||||
:class="m.translated_from ? 'text-success' : ''"
|
||||
:title="`Translated from ${ISO6391.getName(m.translated_from)}`"
|
||||
class="ms-2 helpicon">
|
||||
<br/>
|
||||
{{ m.message }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@click="GoToPlayer(m.steamid64)"
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
v-if="m.tracked"
|
||||
class="fa fa-dot-circle-o text-success tracked"
|
||||
title="Tracked user"
|
||||
/>
|
||||
<span
|
||||
:class="
|
||||
(m.vac &&
|
||||
FormatVacDate(
|
||||
m.vac_date,
|
||||
store.state.matchDetails.date
|
||||
) !== '') ||
|
||||
(!m.vac &&
|
||||
m.game_ban &&
|
||||
FormatVacDate(
|
||||
m.game_ban_date,
|
||||
store.state.matchDetails.date
|
||||
) !== '')
|
||||
? 'ban-shadow'
|
||||
: ''
|
||||
"
|
||||
:title="
|
||||
!m.vac && m.game_ban
|
||||
? 'Game-banned: ' +
|
||||
FormatVacDate(
|
||||
m.game_ban_date,
|
||||
store.state.matchDetails.date
|
||||
)
|
||||
: m.vac && !m.game_ban
|
||||
? 'Vac-banned: ' +
|
||||
FormatVacDate(m.vac_date, store.state.matchDetails.date)
|
||||
: ''
|
||||
"
|
||||
>
|
||||
{{ m.player }}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="td-icon">
|
||||
<i class="fa fa-caret-right" />
|
||||
<span v-if="!m.all_chat" class="ms-1"> (team) </span>
|
||||
</td>
|
||||
<td class="td-message">
|
||||
{{
|
||||
data.translatedText.length === 0
|
||||
? m.message
|
||||
: data.originalChat[id].message
|
||||
}}
|
||||
<span
|
||||
v-if="m.translated_from"
|
||||
:class="m.translated_from ? 'text-success' : ''"
|
||||
:title="`Translated from ${ISO6391.getName(m.translated_from)}`"
|
||||
class="ms-2 helpicon"
|
||||
>
|
||||
<br />
|
||||
{{ m.message }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -64,69 +100,79 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {useStore} from "vuex";
|
||||
import {onMounted, reactive} from "vue";
|
||||
import {constructAvatarUrl, ConvertTickToTime, FormatVacDate, GetChatHistory, GoToPlayer, truncate} from "@/utils";
|
||||
import TranslateChatButton from "@/components/TranslateChatButton";
|
||||
import ISO6391 from 'iso-639-1'
|
||||
import { useStore } from "vuex";
|
||||
import { onMounted, reactive } from "vue";
|
||||
import {
|
||||
constructAvatarUrl,
|
||||
ConvertTickToTime,
|
||||
FormatVacDate,
|
||||
GetChatHistory,
|
||||
GoToPlayer,
|
||||
truncate,
|
||||
} from "/src/utils";
|
||||
import TranslateChatButton from "/src/components/TranslateChatButton";
|
||||
import ISO6391 from "iso-639-1";
|
||||
|
||||
export default {
|
||||
name: "MatchChatHistory",
|
||||
components: {TranslateChatButton},
|
||||
components: { TranslateChatButton },
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
const data = reactive({
|
||||
chat: [],
|
||||
translatedText: [],
|
||||
originalChat: [],
|
||||
clientWidth: 0
|
||||
})
|
||||
clientWidth: 0,
|
||||
});
|
||||
|
||||
const handleTranslatedText = async (e) => {
|
||||
const [res, toggle] = await e
|
||||
const [res, toggle] = await e;
|
||||
|
||||
if (res !== null) {
|
||||
if (toggle === 'translated') {
|
||||
data.translatedText = await setPlayer(sortChatHistory(res, true))
|
||||
data.chat = data.translatedText
|
||||
} else if (toggle === 'original') {
|
||||
data.chat = data.originalChat
|
||||
if (toggle === "translated") {
|
||||
data.translatedText = await setPlayer(sortChatHistory(res, true));
|
||||
data.chat = data.translatedText;
|
||||
} else if (toggle === "original") {
|
||||
data.chat = data.originalChat;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getChatHistory = async () => {
|
||||
const resData = await GetChatHistory(store, store.state.matchDetails.match_id)
|
||||
const resData = await GetChatHistory(
|
||||
store,
|
||||
store.state.matchDetails.match_id
|
||||
);
|
||||
if (resData !== null) {
|
||||
data.chat = await setPlayer(sortChatHistory(resData))
|
||||
data.originalChat = data.chat
|
||||
data.chat = await setPlayer(sortChatHistory(resData));
|
||||
data.originalChat = data.chat;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sortChatHistory = (res = {}, translated = false) => {
|
||||
let arr = []
|
||||
let arr = [];
|
||||
if (res !== {}) {
|
||||
Object.keys(res).forEach(i => {
|
||||
res[i].forEach(o => {
|
||||
Object.keys(res).forEach((i) => {
|
||||
res[i].forEach((o) => {
|
||||
let obj = Object.assign({
|
||||
player: i,
|
||||
tick: o.tick,
|
||||
all_chat: o.all_chat,
|
||||
message: o.message,
|
||||
translated_from: translated ? o.translated_from : null,
|
||||
translated_to: translated ? o.translated_to : null
|
||||
})
|
||||
arr.push(obj)
|
||||
})
|
||||
})
|
||||
translated_to: translated ? o.translated_to : null,
|
||||
});
|
||||
arr.push(obj);
|
||||
});
|
||||
});
|
||||
}
|
||||
arr.sort((a, b) => a.tick - b.tick)
|
||||
return arr
|
||||
}
|
||||
arr.sort((a, b) => a.tick - b.tick);
|
||||
return arr;
|
||||
};
|
||||
|
||||
const setPlayer = async (chat) => {
|
||||
let arr = []
|
||||
let arr = [];
|
||||
for (const o of chat) {
|
||||
for (const p of store.state.matchDetails.stats) {
|
||||
if (o.player === p.player.steamid64) {
|
||||
@@ -142,35 +188,39 @@ export default {
|
||||
game_ban: p.player.game_ban,
|
||||
game_ban_date: p.player.game_ban_date,
|
||||
tick: o.tick,
|
||||
tick_rate: store.state.matchDetails.tick_rate && store.state.matchDetails.tick_rate !== -1 ? store.state.matchDetails.tick_rate : 64,
|
||||
tick_rate:
|
||||
store.state.matchDetails.tick_rate &&
|
||||
store.state.matchDetails.tick_rate !== -1
|
||||
? store.state.matchDetails.tick_rate
|
||||
: 64,
|
||||
all_chat: o.all_chat,
|
||||
message: o.message,
|
||||
translated_from: o.translated_from,
|
||||
translated_to: o.translated_to
|
||||
})
|
||||
arr.push(obj)
|
||||
translated_to: o.translated_to,
|
||||
});
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
const sizeTable = () => {
|
||||
if (document.documentElement.clientWidth <= 768) {
|
||||
data.clientWidth = document.documentElement.clientWidth - 32
|
||||
data.clientWidth = document.documentElement.clientWidth - 32;
|
||||
} else {
|
||||
data.clientWidth = 700
|
||||
data.clientWidth = 700;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onresize = () => {
|
||||
sizeTable()
|
||||
}
|
||||
sizeTable();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getChatHistory()
|
||||
sizeTable()
|
||||
})
|
||||
getChatHistory();
|
||||
sizeTable();
|
||||
});
|
||||
|
||||
return {
|
||||
data,
|
||||
@@ -180,10 +230,10 @@ export default {
|
||||
GoToPlayer,
|
||||
ConvertTickToTime,
|
||||
FormatVacDate,
|
||||
handleTranslatedText
|
||||
}
|
||||
}
|
||||
}
|
||||
handleTranslatedText,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -195,11 +245,11 @@ export default {
|
||||
}
|
||||
|
||||
.translate-btn {
|
||||
margin-top: .5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: .5rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.td-time {
|
||||
@@ -226,8 +276,8 @@ td {
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.tracked {
|
||||
font-size: .8rem;
|
||||
margin-right: .2rem;
|
||||
font-size: 0.8rem;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.ban-shadow {
|
||||
|
@@ -1,125 +1,226 @@
|
||||
<template>
|
||||
<div v-if="props.matches.length === 0" id="matches-placeholder">
|
||||
<span v-for="i in 20" :key="i" :class="i % 2 === 1 ? 'placeholder-wave' : 'placeholder-wave-alt'"
|
||||
class="placeholder col-12"></span>
|
||||
<span
|
||||
v-for="i in 20"
|
||||
:key="i"
|
||||
:class="i % 2 === 1 ? 'placeholder-wave' : 'placeholder-wave-alt'"
|
||||
class="placeholder col-12"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div v-else id="matches">
|
||||
<table class="table table-borderless">
|
||||
<thead class="border-bottom">
|
||||
<tr>
|
||||
<th class="text-center map" scope="col">Map</th>
|
||||
<th class="text-center rank" scope="col">Rank</th>
|
||||
<th class="text-center length" scope="col" title="Match Length">
|
||||
<img alt="Match length" class="match-len helpicon" src="/images/icons/timer_both.svg">
|
||||
</th>
|
||||
<th class="text-center score" scope="col">Score</th>
|
||||
<th v-if="!props.explore" class="text-center kills" scope="col">K</th>
|
||||
<th v-if="!props.explore" class="text-center assists" scope="col">A</th>
|
||||
<th v-if="!props.explore" class="text-center deaths" scope="col">D</th>
|
||||
<th v-if="!props.explore" class="text-center kdiff helptext" scope="col" title="Kill-to-death difference">+/-</th>
|
||||
<th v-if="!props.explore" class="text-center hltv helptext" scope="col" title="HLTV 1.0 Rating">Rating</th>
|
||||
<th class="text-center duration" scope="col">Duration</th>
|
||||
<th class="date" scope="col">Date</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-center map" scope="col">Map</th>
|
||||
<th class="text-center rank" scope="col">Rank</th>
|
||||
<th class="text-center length" scope="col" title="Match Length">
|
||||
<img
|
||||
alt="Match length"
|
||||
class="match-len helpicon"
|
||||
src="/images/icons/timer_both.svg"
|
||||
/>
|
||||
</th>
|
||||
<th class="text-center score" scope="col">Score</th>
|
||||
<th v-if="!props.explore" class="text-center kills" scope="col">K</th>
|
||||
<th v-if="!props.explore" class="text-center assists" scope="col">
|
||||
A
|
||||
</th>
|
||||
<th v-if="!props.explore" class="text-center deaths" scope="col">
|
||||
D
|
||||
</th>
|
||||
<th
|
||||
v-if="!props.explore"
|
||||
class="text-center kdiff helptext"
|
||||
scope="col"
|
||||
title="Kill-to-death difference"
|
||||
>
|
||||
+/-
|
||||
</th>
|
||||
<th
|
||||
v-if="!props.explore"
|
||||
class="text-center hltv helptext"
|
||||
scope="col"
|
||||
title="HLTV 1.0 Rating"
|
||||
>
|
||||
Rating
|
||||
</th>
|
||||
<th class="text-center duration" scope="col">Duration</th>
|
||||
<th class="date" scope="col">Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="match in props.matches"
|
||||
<tr
|
||||
v-for="match in props.matches"
|
||||
:key="match.match_id"
|
||||
:class="props.colorFront ? (GetWinLoss(match.match_result, match.stats.team_id) + (match.vac || match.game_ban ? ' ban' : '')) : (match.vac || match.game_ban ? ' matches_ban' : '')"
|
||||
:title="match.vac ? 'VAC-banned player in this game' : match.game_ban ? 'Game-banned player in this game' : ''"
|
||||
:class="
|
||||
props.colorFront
|
||||
? GetWinLoss(match.match_result, match.stats.team_id) +
|
||||
(match.vac || match.game_ban ? ' ban' : '')
|
||||
: match.vac || match.game_ban
|
||||
? ' matches_ban'
|
||||
: ''
|
||||
"
|
||||
:title="
|
||||
match.vac
|
||||
? 'VAC-banned player in this game'
|
||||
: match.game_ban
|
||||
? 'Game-banned player in this game'
|
||||
: ''
|
||||
"
|
||||
class="match default"
|
||||
@click="GoToMatch(match.match_id)"
|
||||
>
|
||||
<td class="td-map text-center">
|
||||
<i v-if="match.parsed" class="fa fa-bar-chart parsed helpicon"
|
||||
title="Demo has been parsed for additional data"></i>
|
||||
<i v-if="!match.parsed && MatchNotParsedTime(match.date)" class="fa fa-hourglass-half not-yet-parsed helpicon"
|
||||
title="Match has not been parsed yet"></i>
|
||||
<img v-if="match.map !== ''"
|
||||
:alt="match.map"
|
||||
:src="'/images/map_icons/map_icon_' + match.map + '.svg'"
|
||||
:title="FixMapName(match.map)"
|
||||
class="map-icon">
|
||||
<i v-else class="fa fa-question-circle-o map-not-found" title="Match not parsed"></i>
|
||||
</td>
|
||||
<td class="td-rank text-center">
|
||||
<img v-if="props.explore"
|
||||
:alt="DisplayRank(Math.floor(match.avg_rank || 0))[1]"
|
||||
:src="DisplayRank(Math.floor(match.avg_rank || 0))[0]"
|
||||
:title="DisplayRank(Math.floor(match.avg_rank || 0))[1]" class="rank-icon">
|
||||
<img v-else
|
||||
:alt="DisplayRank(match.stats.rank?.new)[1]"
|
||||
:class="match.stats.rank?.new > match.stats.rank?.old ? 'uprank' : match.stats.rank?.new < match.stats.rank?.old ? 'downrank' : ''"
|
||||
:src="DisplayRank(match.stats.rank?.new)[0]"
|
||||
:title="DisplayRank(match.stats.rank?.new)[1]" class="rank-icon">
|
||||
</td>
|
||||
<td class="td-length text-center">
|
||||
<img v-if="match.max_rounds === 30 || !match.max_rounds"
|
||||
alt="Match long"
|
||||
class="match-len"
|
||||
src="/images/icons/timer_long.svg"
|
||||
title="Long Match">
|
||||
<img v-if="match.max_rounds === 16"
|
||||
alt="Match short"
|
||||
class="match-len"
|
||||
src="/images/icons/timer_short.svg"
|
||||
title="Short Match">
|
||||
</td>
|
||||
<td class="td-score text-center fw-bold">
|
||||
<span
|
||||
:class="match.match_result === 1 ? 'text-success' : match.match_result === 0 ? 'text-warning' : 'text-danger'">{{
|
||||
match.score[0]
|
||||
}}</span> - <span
|
||||
:class="match.match_result === 2 ? 'text-success' : match.match_result === 0 ? 'text-warning' : 'text-danger'">{{
|
||||
match.score[1]
|
||||
}}</span>
|
||||
</td>
|
||||
<td v-if="match.stats" class="td-kills text-center">
|
||||
{{ match.stats.kills ? match.stats.kills : "0" }}
|
||||
</td>
|
||||
<td v-if="match.stats" class="td-assists text-center">
|
||||
{{ match.stats.assists ? match.stats.assists : "0" }}
|
||||
</td>
|
||||
<td v-if="match.stats" class="td-deaths text-center">
|
||||
{{ match.stats.deaths ? match.stats.deaths : "0" }}
|
||||
</td>
|
||||
<td v-if="match.stats"
|
||||
:class="(match.stats.kills ? match.stats.kills : 0) - (match.stats.deaths ? match.stats.deaths : 0) >= 0 ? 'text-success' : 'text-danger'"
|
||||
class="td-plus text-center">
|
||||
{{
|
||||
(match.stats.kills ? match.stats.kills : 0) - (match.stats.deaths ? match.stats.deaths : 0)
|
||||
}}
|
||||
</td>
|
||||
<td v-if="match.stats"
|
||||
:class="GetHLTV_1(
|
||||
match.stats.kills,
|
||||
match.score[0] + match.score[1],
|
||||
match.stats.deaths,
|
||||
match.stats.multi_kills?.duo,
|
||||
match.stats.multi_kills?.triple,
|
||||
match.stats.multi_kills?.quad,
|
||||
match.stats.multi_kills?.pent) >= 1 ? 'text-success' : 'text-warning'"
|
||||
class="td-hltv text-center fw-bold">
|
||||
{{
|
||||
GetHLTV_1(
|
||||
>
|
||||
<td class="td-map text-center">
|
||||
<i
|
||||
v-if="match.parsed"
|
||||
class="fa fa-bar-chart parsed helpicon"
|
||||
title="Demo has been parsed for additional data"
|
||||
></i>
|
||||
<i
|
||||
v-if="!match.parsed && MatchNotParsedTime(match.date)"
|
||||
class="fa fa-hourglass-half not-yet-parsed helpicon"
|
||||
title="Match has not been parsed yet"
|
||||
></i>
|
||||
<img
|
||||
v-if="match.map !== ''"
|
||||
:alt="match.map"
|
||||
:src="'/images/map_icons/map_icon_' + match.map + '.svg'"
|
||||
:title="FixMapName(match.map)"
|
||||
class="map-icon"
|
||||
/>
|
||||
<i
|
||||
v-else
|
||||
class="fa fa-question-circle-o map-not-found"
|
||||
title="Match not parsed"
|
||||
></i>
|
||||
</td>
|
||||
<td class="td-rank text-center">
|
||||
<img
|
||||
v-if="props.explore"
|
||||
:alt="DisplayRank(Math.floor(match.avg_rank || 0))[1]"
|
||||
:src="DisplayRank(Math.floor(match.avg_rank || 0))[0]"
|
||||
:title="DisplayRank(Math.floor(match.avg_rank || 0))[1]"
|
||||
class="rank-icon"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
:alt="DisplayRank(match.stats.rank?.new)[1]"
|
||||
:class="
|
||||
match.stats.rank?.new > match.stats.rank?.old
|
||||
? 'uprank'
|
||||
: match.stats.rank?.new < match.stats.rank?.old
|
||||
? 'downrank'
|
||||
: ''
|
||||
"
|
||||
:src="DisplayRank(match.stats.rank?.new)[0]"
|
||||
:title="DisplayRank(match.stats.rank?.new)[1]"
|
||||
class="rank-icon"
|
||||
/>
|
||||
</td>
|
||||
<td class="td-length text-center">
|
||||
<img
|
||||
v-if="match.max_rounds === 30 || !match.max_rounds"
|
||||
alt="Match long"
|
||||
class="match-len"
|
||||
src="/images/icons/timer_long.svg"
|
||||
title="Long Match"
|
||||
/>
|
||||
<img
|
||||
v-if="match.max_rounds === 16"
|
||||
alt="Match short"
|
||||
class="match-len"
|
||||
src="/images/icons/timer_short.svg"
|
||||
title="Short Match"
|
||||
/>
|
||||
</td>
|
||||
<td class="td-score text-center fw-bold">
|
||||
<span
|
||||
:class="
|
||||
match.match_result === 1
|
||||
? 'text-success'
|
||||
: match.match_result === 0
|
||||
? 'text-warning'
|
||||
: 'text-danger'
|
||||
"
|
||||
>{{ match.score[0] }}</span
|
||||
>
|
||||
-
|
||||
<span
|
||||
:class="
|
||||
match.match_result === 2
|
||||
? 'text-success'
|
||||
: match.match_result === 0
|
||||
? 'text-warning'
|
||||
: 'text-danger'
|
||||
"
|
||||
>{{ match.score[1] }}</span
|
||||
>
|
||||
</td>
|
||||
<td v-if="match.stats" class="td-kills text-center">
|
||||
{{ match.stats.kills ? match.stats.kills : "0" }}
|
||||
</td>
|
||||
<td v-if="match.stats" class="td-assists text-center">
|
||||
{{ match.stats.assists ? match.stats.assists : "0" }}
|
||||
</td>
|
||||
<td v-if="match.stats" class="td-deaths text-center">
|
||||
{{ match.stats.deaths ? match.stats.deaths : "0" }}
|
||||
</td>
|
||||
<td
|
||||
v-if="match.stats"
|
||||
:class="
|
||||
(match.stats.kills ? match.stats.kills : 0) -
|
||||
(match.stats.deaths ? match.stats.deaths : 0) >=
|
||||
0
|
||||
? 'text-success'
|
||||
: 'text-danger'
|
||||
"
|
||||
class="td-plus text-center"
|
||||
>
|
||||
{{
|
||||
(match.stats.kills ? match.stats.kills : 0) -
|
||||
(match.stats.deaths ? match.stats.deaths : 0)
|
||||
}}
|
||||
</td>
|
||||
<td
|
||||
v-if="match.stats"
|
||||
:class="
|
||||
GetHLTV_1(
|
||||
match.stats.kills,
|
||||
match.score[0] + match.score[1],
|
||||
match.stats.deaths,
|
||||
match.stats.multi_kills?.duo,
|
||||
match.stats.multi_kills?.triple,
|
||||
match.stats.multi_kills?.quad,
|
||||
match.stats.multi_kills?.pent)
|
||||
}}
|
||||
</td>
|
||||
<td :title="FormatFullDuration(match.duration)" class="td-duration text-center">
|
||||
{{ FormatDuration(match.duration) }}
|
||||
|
||||
</td>
|
||||
<td :title="FormatFullDate(match.date)" class="td-date">
|
||||
{{ FormatDate(match.date) }}
|
||||
</td>
|
||||
</tr>
|
||||
match.stats.multi_kills?.pent
|
||||
) >= 1
|
||||
? 'text-success'
|
||||
: 'text-warning'
|
||||
"
|
||||
class="td-hltv text-center fw-bold"
|
||||
>
|
||||
{{
|
||||
GetHLTV_1(
|
||||
match.stats.kills,
|
||||
match.score[0] + match.score[1],
|
||||
match.stats.deaths,
|
||||
match.stats.multi_kills?.duo,
|
||||
match.stats.multi_kills?.triple,
|
||||
match.stats.multi_kills?.quad,
|
||||
match.stats.multi_kills?.pent
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td
|
||||
:title="FormatFullDuration(match.duration)"
|
||||
class="td-duration text-center"
|
||||
>
|
||||
{{ FormatDuration(match.duration) }}
|
||||
</td>
|
||||
<td :title="FormatFullDate(match.date)" class="td-date">
|
||||
{{ FormatDate(match.date) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -136,8 +237,8 @@ import {
|
||||
GetHLTV_1,
|
||||
GetWinLoss,
|
||||
GoToMatch,
|
||||
MatchNotParsedTime
|
||||
} from "@/utils";
|
||||
MatchNotParsedTime,
|
||||
} from "/src/utils";
|
||||
|
||||
export default {
|
||||
name: "MatchesTable",
|
||||
@@ -145,17 +246,17 @@ export default {
|
||||
colorFront: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
matches: {
|
||||
type: Array,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
explore: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
return {
|
||||
@@ -169,14 +270,13 @@ export default {
|
||||
GoToMatch,
|
||||
MatchNotParsedTime,
|
||||
DisplayRank,
|
||||
FixMapName
|
||||
}
|
||||
}
|
||||
}
|
||||
FixMapName,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
#matches-placeholder {
|
||||
.placeholder {
|
||||
height: 78px;
|
||||
@@ -197,7 +297,8 @@ table {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
th:last-child, td:last-child {
|
||||
th:last-child,
|
||||
td:last-child {
|
||||
text-align: right;
|
||||
width: 150px;
|
||||
}
|
||||
@@ -242,7 +343,7 @@ table {
|
||||
top: 4px;
|
||||
left: 48px;
|
||||
font-size: 4.35rem;
|
||||
color: rgba(255, 193, 7, .86);
|
||||
color: rgba(255, 193, 7, 0.86);
|
||||
}
|
||||
|
||||
img {
|
||||
@@ -266,7 +367,8 @@ table {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.td-date, .date {
|
||||
.td-date,
|
||||
.date {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -282,115 +384,125 @@ table {
|
||||
$ban: false;
|
||||
|
||||
&.default {
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&.win {
|
||||
$first: rgb(0, 255, 0);
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&.draw {
|
||||
$first: rgb(255, 255, 0);
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&.loss {
|
||||
$first: rgb(255, 0, 0);
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&.ban {
|
||||
$last: rgb(93, 3, 3);
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -398,23 +510,25 @@ table {
|
||||
&.matches_ban {
|
||||
$first: rgb(0, 0, 0);
|
||||
$last: rgb(93, 3, 3);
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.2) 0%,
|
||||
rgba($first, 0.1) 15%,
|
||||
rgba(0, 0, 0, 0.4) 30%,
|
||||
rgba(0, 0, 0, 0.4) 70%,
|
||||
rgba($last, 0.6) 80%,
|
||||
rgba($last, 0.6) 100%
|
||||
);
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($first, 0.3) 0%,
|
||||
rgba($first, 0.2) 15%,
|
||||
rgba(0, 0, 0, 0.5) 30%,
|
||||
rgba(0, 0, 0, 0.5) 70%,
|
||||
rgba($last, 0.7) 80%,
|
||||
rgba($last, 0.7) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -433,23 +547,25 @@ table {
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
table tr {
|
||||
.map-icon {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
.map {
|
||||
padding: 0.5rem !important;
|
||||
}
|
||||
.td-map {
|
||||
padding: 0 1rem !important;
|
||||
.map-icon {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.parsed {
|
||||
display: none;
|
||||
.map {
|
||||
padding: 0.5rem !important;
|
||||
}
|
||||
.not-yet-parsed {
|
||||
display: none;
|
||||
|
||||
.td-map {
|
||||
padding: 0 1rem !important;
|
||||
|
||||
.parsed {
|
||||
display: none;
|
||||
}
|
||||
.not-yet-parsed {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
@@ -463,12 +579,12 @@ table {
|
||||
|
||||
.parsed {
|
||||
position: absolute;
|
||||
left: .3rem !important;
|
||||
left: 0.3rem !important;
|
||||
}
|
||||
|
||||
.not-yet-parsed {
|
||||
position: absolute;
|
||||
left: .3rem !important;
|
||||
left: 0.3rem !important;
|
||||
}
|
||||
|
||||
img {
|
||||
@@ -484,16 +600,28 @@ table {
|
||||
}
|
||||
|
||||
.td-score {
|
||||
font-size: .7rem !important;
|
||||
font-size: 0.7rem !important;
|
||||
//width: 110px !important;
|
||||
}
|
||||
|
||||
.td-date {
|
||||
font-size: .8rem !important;
|
||||
font-size: 0.8rem !important;
|
||||
}
|
||||
|
||||
.kills, .deaths, .assists, .kdiff, .duration, .hltv, .length,
|
||||
.td-kills, .td-deaths, .td-assists, .td-plus, .td-duration, .td-hltv, .td-length {
|
||||
.kills,
|
||||
.deaths,
|
||||
.assists,
|
||||
.kdiff,
|
||||
.duration,
|
||||
.hltv,
|
||||
.length,
|
||||
.td-kills,
|
||||
.td-deaths,
|
||||
.td-assists,
|
||||
.td-plus,
|
||||
.td-duration,
|
||||
.td-hltv,
|
||||
.td-length {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -506,13 +634,15 @@ table {
|
||||
.trackme-btn {
|
||||
top: 25px;
|
||||
}
|
||||
.map, .td-map {
|
||||
.map,
|
||||
.td-map {
|
||||
padding-left: 4rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.td-plus, .kdiff {
|
||||
.td-plus,
|
||||
.kdiff {
|
||||
display: none;
|
||||
}
|
||||
.td-rank img {
|
||||
|
@@ -6,140 +6,177 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts/core';
|
||||
import {GridComponent, TooltipComponent, VisualMapComponent} from 'echarts/components';
|
||||
import {HeatmapChart} from 'echarts/charts';
|
||||
import {CanvasRenderer} from 'echarts/renderers';
|
||||
import {onMounted, onUnmounted, ref} from "vue";
|
||||
import {checkStatEmpty, getPlayerArr} from "../utils";
|
||||
import {useStore} from "vuex";
|
||||
import * as echarts from "echarts/core";
|
||||
import {
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
VisualMapComponent,
|
||||
} from "echarts/components";
|
||||
import { HeatmapChart } from "echarts/charts";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import { checkStatEmpty, getPlayerArr } from "../utils";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "MultiKillsChart",
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
const multiKills = ['2k', '3k', '4k', '5k']
|
||||
let myChart1, myChart2
|
||||
const width = ref(window.innerWidth <= 500 ? window.innerWidth : 500)
|
||||
const height = ref(width.value)
|
||||
const multiKills = ["2k", "3k", "4k", "5k"];
|
||||
let myChart1, myChart2;
|
||||
const width = ref(window.innerWidth <= 500 ? window.innerWidth : 500);
|
||||
const height = ref(width.value);
|
||||
|
||||
const multiKillArr = (stats, team) => {
|
||||
let arr = []
|
||||
let arr = [];
|
||||
for (let i = (team - 1) * 5; i < team * 5; i++) {
|
||||
for (let j = 0; j < multiKills.length; j++) {
|
||||
if (j === 0)
|
||||
arr.push([i % 5, j, checkStatEmpty(stats[i].multi_kills.duo) === 0 ? null : stats[i].multi_kills.duo])
|
||||
arr.push([
|
||||
i % 5,
|
||||
j,
|
||||
checkStatEmpty(stats[i].multi_kills.duo) === 0
|
||||
? null
|
||||
: stats[i].multi_kills.duo,
|
||||
]);
|
||||
if (j === 1)
|
||||
arr.push([i % 5, j, checkStatEmpty(stats[i].multi_kills.triple) === 0 ? null : stats[i].multi_kills.triple])
|
||||
arr.push([
|
||||
i % 5,
|
||||
j,
|
||||
checkStatEmpty(stats[i].multi_kills.triple) === 0
|
||||
? null
|
||||
: stats[i].multi_kills.triple,
|
||||
]);
|
||||
if (j === 2)
|
||||
arr.push([i % 5, j, checkStatEmpty(stats[i].multi_kills.quad) === 0 ? null : stats[i].multi_kills.quad])
|
||||
arr.push([
|
||||
i % 5,
|
||||
j,
|
||||
checkStatEmpty(stats[i].multi_kills.quad) === 0
|
||||
? null
|
||||
: stats[i].multi_kills.quad,
|
||||
]);
|
||||
if (j === 3)
|
||||
arr.push([i % 5, j, checkStatEmpty(stats[i].multi_kills.pent) === 0 ? null : stats[i].multi_kills.pent])
|
||||
arr.push([
|
||||
i % 5,
|
||||
j,
|
||||
checkStatEmpty(stats[i].multi_kills.pent) === 0
|
||||
? null
|
||||
: stats[i].multi_kills.pent,
|
||||
]);
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
const getMax = (stats, team) => {
|
||||
let max = 0
|
||||
let max = 0;
|
||||
for (let i = (team - 1) * 5; i < team * 5; i++) {
|
||||
if (stats[i].multi_kills.duo > max)
|
||||
max = stats[i].multi_kills.duo
|
||||
if (stats[i].multi_kills.duo > max) max = stats[i].multi_kills.duo;
|
||||
if (stats[i].multi_kills.triple > max)
|
||||
max = stats[i].multi_kills.triple
|
||||
if (stats[i].multi_kills.quad > max)
|
||||
max = stats[i].multi_kills.quad
|
||||
if (stats[i].multi_kills.pent > max)
|
||||
max = stats[i].multi_kills.pent
|
||||
max = stats[i].multi_kills.triple;
|
||||
if (stats[i].multi_kills.quad > max) max = stats[i].multi_kills.quad;
|
||||
if (stats[i].multi_kills.pent > max) max = stats[i].multi_kills.pent;
|
||||
}
|
||||
return max
|
||||
}
|
||||
return max;
|
||||
};
|
||||
|
||||
const optionGen = (team) => {
|
||||
return {
|
||||
tooltip: {},
|
||||
grid: {
|
||||
height: '65%',
|
||||
top: '0%',
|
||||
bottom: '10%'
|
||||
height: "65%",
|
||||
top: "0%",
|
||||
bottom: "10%",
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: getPlayerArr(store.state.matchDetails.stats, team, true).reverse(),
|
||||
type: "category",
|
||||
data: getPlayerArr(
|
||||
store.state.matchDetails.stats,
|
||||
team,
|
||||
true
|
||||
).reverse(),
|
||||
splitArea: {
|
||||
show: true
|
||||
show: true,
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: 14,
|
||||
color: 'white',
|
||||
rotate: 50
|
||||
}
|
||||
color: "white",
|
||||
rotate: 50,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
type: "category",
|
||||
data: multiKills,
|
||||
splitArea: {
|
||||
show: true
|
||||
show: true,
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'white'
|
||||
}
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
visualMap: {
|
||||
min: 0,
|
||||
max: getMax(store.state.matchDetails.stats, team),
|
||||
calculable: true,
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
bottom: '5%',
|
||||
orient: "horizontal",
|
||||
left: "center",
|
||||
bottom: "5%",
|
||||
textStyle: {
|
||||
color: 'white'
|
||||
}
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'heatmap',
|
||||
type: "heatmap",
|
||||
data: multiKillArr(store.state.matchDetails.stats, team),
|
||||
label: {
|
||||
fontSize: 14,
|
||||
show: true
|
||||
show: true,
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
shadowColor: "rgba(0, 0, 0, 0.5)",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const disposeCharts = () => {
|
||||
if (myChart1 != null && myChart1 !== '' && myChart1 !== undefined) {
|
||||
myChart1.dispose()
|
||||
if (myChart1 != null && myChart1 !== "" && myChart1 !== undefined) {
|
||||
myChart1.dispose();
|
||||
}
|
||||
if (myChart2 != null && myChart2 !== '' && myChart2 !== undefined) {
|
||||
myChart2.dispose()
|
||||
if (myChart2 != null && myChart2 !== "" && myChart2 !== undefined) {
|
||||
myChart2.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildCharts = () => {
|
||||
disposeCharts()
|
||||
disposeCharts();
|
||||
|
||||
myChart1 = echarts.init(document.getElementById('multi-kills-chart-1'), {}, {
|
||||
width: width.value,
|
||||
height: height.value
|
||||
});
|
||||
myChart1 = echarts.init(
|
||||
document.getElementById("multi-kills-chart-1"),
|
||||
{},
|
||||
{
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
}
|
||||
);
|
||||
myChart1.setOption(optionGen(1));
|
||||
|
||||
myChart2 = echarts.init(document.getElementById('multi-kills-chart-2'), {}, {
|
||||
width: width.value,
|
||||
height: height.value
|
||||
});
|
||||
myChart2 = echarts.init(
|
||||
document.getElementById("multi-kills-chart-2"),
|
||||
{},
|
||||
{
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
}
|
||||
);
|
||||
myChart2.setOption(optionGen(2));
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (store.state.matchDetails.stats) {
|
||||
@@ -148,27 +185,27 @@ export default {
|
||||
GridComponent,
|
||||
VisualMapComponent,
|
||||
HeatmapChart,
|
||||
CanvasRenderer
|
||||
CanvasRenderer,
|
||||
]);
|
||||
|
||||
buildCharts()
|
||||
buildCharts();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
disposeCharts()
|
||||
})
|
||||
disposeCharts();
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
if (window.innerWidth <= 500) {
|
||||
width.value = window.innerWidth - 20
|
||||
height.value = width.value
|
||||
width.value = window.innerWidth - 20;
|
||||
height.value = width.value;
|
||||
|
||||
buildCharts()
|
||||
buildCharts();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -2,155 +2,183 @@
|
||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top">
|
||||
<div class="container">
|
||||
<router-link class="navbar-brand" to="/" @click="closeNav('mainNav')">
|
||||
<img alt="logo-nav"
|
||||
class="logo-nav"
|
||||
src="/images/logo.svg">
|
||||
<img alt="logo-nav" class="logo-nav" src="/images/logo.svg" />
|
||||
</router-link>
|
||||
<button aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler"
|
||||
data-bs-target="#mainNav" data-bs-toggle="collapse" type="button">
|
||||
<button
|
||||
aria-controls="mainNav"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
class="navbar-toggler"
|
||||
data-bs-target="#mainNav"
|
||||
data-bs-toggle="collapse"
|
||||
type="button"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div id="mainNav" class="collapse navbar-collapse navbar-nav justify-content-between">
|
||||
<div
|
||||
id="mainNav"
|
||||
class="collapse navbar-collapse navbar-nav justify-content-between"
|
||||
>
|
||||
<ul class="list-unstyled">
|
||||
<li class="nav-item">
|
||||
<router-link class="nav-link" to="/matches" @click="closeNav('mainNav')">
|
||||
<router-link
|
||||
class="nav-link"
|
||||
to="/matches"
|
||||
@click="closeNav('mainNav')"
|
||||
>
|
||||
Matches
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<form id="searchform" class="d-flex" @keydown.enter.prevent="parseSearch" @submit.prevent="parseSearch">
|
||||
<form
|
||||
id="searchform"
|
||||
class="d-flex"
|
||||
@keydown.enter.prevent="parseSearch"
|
||||
@submit.prevent="parseSearch"
|
||||
>
|
||||
<label for="search">
|
||||
<i class="fa fa-search"></i>
|
||||
</label>
|
||||
|
||||
<input id="search" v-model="data.searchInput" aria-label="Search"
|
||||
autocomplete="off"
|
||||
class="form-control bg-transparent border-0"
|
||||
placeholder="SteamID64, Profile Link or Custom URL"
|
||||
title="SteamID64, Profile Link or Custom URL"
|
||||
type="search">
|
||||
<input
|
||||
id="search"
|
||||
v-model="data.searchInput"
|
||||
aria-label="Search"
|
||||
autocomplete="off"
|
||||
class="form-control bg-transparent border-0"
|
||||
placeholder="SteamID64, Profile Link or Custom URL"
|
||||
title="SteamID64, Profile Link or Custom URL"
|
||||
type="search"
|
||||
/>
|
||||
<button
|
||||
id="search-button"
|
||||
class="btn border-2 btn-outline-info"
|
||||
type="button"
|
||||
@click="parseSearch"
|
||||
id="search-button"
|
||||
class="btn border-2 btn-outline-info"
|
||||
type="button"
|
||||
@click="parseSearch"
|
||||
>
|
||||
Search!
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {reactive} from "vue";
|
||||
import {useStore} from 'vuex'
|
||||
import {closeNav, GetUser, GoToPlayer} from '@/utils'
|
||||
import {StatusCodes as STATUS} from "http-status-codes";
|
||||
import { reactive } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import { closeNav, GetUser, GoToPlayer } from "/src/utils";
|
||||
import { StatusCodes as STATUS } from "http-status-codes";
|
||||
|
||||
export default {
|
||||
name: 'Nav',
|
||||
name: "NavComponent",
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
const data = reactive({
|
||||
searchInput: '',
|
||||
})
|
||||
searchInput: "",
|
||||
});
|
||||
|
||||
const parseSearch = async () => {
|
||||
const input = data.searchInput
|
||||
const customUrlPattern = 'https://steamcommunity.com/id/'
|
||||
const profileUrlPattern = 'https://steamcommunity.com/profiles/'
|
||||
const id64Pattern = /^\d{17}$/
|
||||
const vanityPattern = /^[A-Za-z0-9-_]{3,32}$/
|
||||
const input = data.searchInput;
|
||||
const customUrlPattern = "https://steamcommunity.com/id/";
|
||||
const profileUrlPattern = "https://steamcommunity.com/profiles/";
|
||||
const id64Pattern = /^\d{17}$/;
|
||||
const vanityPattern = /^[A-Za-z0-9-_]{3,32}$/;
|
||||
|
||||
store.commit({
|
||||
type: 'changeVanityUrl',
|
||||
id: ''
|
||||
})
|
||||
type: "changeVanityUrl",
|
||||
id: "",
|
||||
});
|
||||
store.commit({
|
||||
type: 'changeId64',
|
||||
id: ''
|
||||
})
|
||||
type: "changeId64",
|
||||
id: "",
|
||||
});
|
||||
|
||||
if (data.searchInput !== '') {
|
||||
if (data.searchInput !== "") {
|
||||
if (id64Pattern.test(input)) {
|
||||
store.commit({
|
||||
type: 'changeId64',
|
||||
id: input
|
||||
})
|
||||
type: "changeId64",
|
||||
id: input,
|
||||
});
|
||||
} else if (input.match(customUrlPattern)) {
|
||||
store.commit({
|
||||
type: 'changeVanityUrl',
|
||||
id: input.split('/')[4].split('?')[0]
|
||||
})
|
||||
type: "changeVanityUrl",
|
||||
id: input.split("/")[4].split("?")[0],
|
||||
});
|
||||
} else if (input.match(profileUrlPattern)) {
|
||||
const tmp = input.split('/')[4].split('?')[0]
|
||||
const tmp = input.split("/")[4].split("?")[0];
|
||||
if (id64Pattern.test(tmp)) {
|
||||
store.commit({
|
||||
type: 'changeId64',
|
||||
id: tmp
|
||||
})
|
||||
type: "changeId64",
|
||||
id: tmp,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
store.commit({
|
||||
type: 'changeVanityUrl',
|
||||
id: input
|
||||
})
|
||||
type: "changeVanityUrl",
|
||||
id: input,
|
||||
});
|
||||
}
|
||||
|
||||
if (store.state.vanityUrl && !vanityPattern.test(store.state.vanityUrl)) {
|
||||
if (
|
||||
store.state.vanityUrl &&
|
||||
!vanityPattern.test(store.state.vanityUrl)
|
||||
) {
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: STATUS.NOT_ACCEPTABLE,
|
||||
message: 'Only alphanumeric symbols, "_", and "-", between 3-32 characters',
|
||||
type: 'warning'
|
||||
}
|
||||
})
|
||||
message:
|
||||
'Only alphanumeric symbols, "_", and "-", between 3-32 characters',
|
||||
type: "warning",
|
||||
},
|
||||
});
|
||||
store.commit({
|
||||
type: 'changeVanityUrl',
|
||||
id: ''
|
||||
})
|
||||
data.searchInput = ''
|
||||
type: "changeVanityUrl",
|
||||
id: "",
|
||||
});
|
||||
data.searchInput = "";
|
||||
}
|
||||
|
||||
if (store.state.id64 !== '' || store.state.vanityUrl !== '') {
|
||||
const resData = await GetUser(store, store.state.vanityUrl || store.state.id64)
|
||||
if (store.state.id64 !== "" || store.state.vanityUrl !== "") {
|
||||
const resData = await GetUser(
|
||||
store,
|
||||
store.state.vanityUrl || store.state.id64
|
||||
);
|
||||
|
||||
if (resData !== null) {
|
||||
data.searchInput = ''
|
||||
document.activeElement.blur()
|
||||
data.searchInput = "";
|
||||
document.activeElement.blur();
|
||||
|
||||
store.commit({
|
||||
type: 'changePlayerDetails',
|
||||
data: resData
|
||||
})
|
||||
type: "changePlayerDetails",
|
||||
data: resData,
|
||||
});
|
||||
|
||||
if (store.state.vanityUrl) {
|
||||
closeNav('mainNav')
|
||||
GoToPlayer(store.state.vanityUrl)
|
||||
closeNav("mainNav");
|
||||
GoToPlayer(store.state.vanityUrl);
|
||||
} else if (store.state.id64) {
|
||||
closeNav('mainNav')
|
||||
GoToPlayer(store.state.id64)
|
||||
closeNav("mainNav");
|
||||
GoToPlayer(store.state.id64);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.attributes.id)
|
||||
closeNav('mainNav')
|
||||
})
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!e.target.attributes.id) closeNav("mainNav");
|
||||
});
|
||||
|
||||
return {
|
||||
data, parseSearch, closeNav
|
||||
}
|
||||
}
|
||||
}
|
||||
data,
|
||||
parseSearch,
|
||||
closeNav,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -163,7 +191,7 @@ nav {
|
||||
max-width: 100vw;
|
||||
width: 100vw;
|
||||
height: 70px;
|
||||
background: rgba(16, 18, 26, .9);
|
||||
background: rgba(16, 18, 26, 0.9);
|
||||
box-shadow: 0 1px 10px 0 #111;
|
||||
z-index: 2;
|
||||
vertical-align: center !important;
|
||||
@@ -233,13 +261,13 @@ nav {
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 4px 2px -2px rgba(95, 120, 146, 0.59);
|
||||
transition: .2s ease-in-out;
|
||||
transform: scale(.975);
|
||||
transition: 0.2s ease-in-out;
|
||||
transform: scale(0.975);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #aaa;
|
||||
font-size: .9rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +334,7 @@ nav {
|
||||
.navbar-collapse {
|
||||
background: var(--bs-secondary);
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--bs-primary)
|
||||
border: 1px solid var(--bs-primary);
|
||||
}
|
||||
|
||||
#mainNav {
|
||||
@@ -319,7 +347,7 @@ nav {
|
||||
li {
|
||||
line-height: 1;
|
||||
padding: 0 0 20px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .1);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +1,28 @@
|
||||
<template>
|
||||
<div class="side-info">
|
||||
|
||||
<div v-if="props.player_meta.most_mates" class="side-info-box most-played-with">
|
||||
<div
|
||||
v-if="props.player_meta.most_mates"
|
||||
class="side-info-box most-played-with"
|
||||
>
|
||||
<div class="heading">
|
||||
<h5>Most played with</h5>
|
||||
</div>
|
||||
<hr>
|
||||
<ul v-for="mate in props.player_meta.most_mates" :key="mate.player.steamid64" class="list-unstyled">
|
||||
<li @click="GoToPlayer(mate.player.vanity_url || mate.player.steamid64)">
|
||||
<hr />
|
||||
<ul
|
||||
v-for="mate in props.player_meta.most_mates"
|
||||
:key="mate.player.steamid64"
|
||||
class="list-unstyled"
|
||||
>
|
||||
<li
|
||||
@click="GoToPlayer(mate.player.vanity_url || mate.player.steamid64)"
|
||||
>
|
||||
<span class="start">
|
||||
<img :class="mate.player.tracked ? 'tracked' : ''" :src="constructAvatarUrl(mate.player.avatar)"
|
||||
:title="mate.player.tracked ? 'Tracked' : ''" alt="Player avatar">
|
||||
<img
|
||||
:class="mate.player.tracked ? 'tracked' : ''"
|
||||
:src="constructAvatarUrl(mate.player.avatar)"
|
||||
:title="mate.player.tracked ? 'Tracked' : ''"
|
||||
alt="Player avatar"
|
||||
/>
|
||||
<span class="text">{{ mate.player.name }}</span>
|
||||
</span>
|
||||
<span class="end">
|
||||
@@ -24,7 +36,7 @@
|
||||
<div class="heading">
|
||||
<h5>Most played with</h5>
|
||||
</div>
|
||||
<hr>
|
||||
<hr />
|
||||
<ul class="list-unstyled placeholder-glow">
|
||||
<li class="placeholder col-11"></li>
|
||||
</ul>
|
||||
@@ -34,17 +46,29 @@
|
||||
<div class="heading">
|
||||
<h5>Best Mate <span class="text-muted">(by winrate)</span></h5>
|
||||
</div>
|
||||
<hr>
|
||||
<ul v-for="mate in props.player_meta.best_mates" :key="mate.player.steamid64" class="list-unstyled">
|
||||
<li @click="GoToPlayer(mate.player.vanity_url || mate.player.steamid64)">
|
||||
<hr />
|
||||
<ul
|
||||
v-for="mate in props.player_meta.best_mates"
|
||||
:key="mate.player.steamid64"
|
||||
class="list-unstyled"
|
||||
>
|
||||
<li
|
||||
@click="GoToPlayer(mate.player.vanity_url || mate.player.steamid64)"
|
||||
>
|
||||
<span class="start">
|
||||
<img :class="mate.player.tracked ? 'tracked' : ''" :src="constructAvatarUrl(mate.player.avatar)"
|
||||
:title="mate.player.tracked ? 'Tracked' : ''" alt="Player avatar">
|
||||
<img
|
||||
:class="mate.player.tracked ? 'tracked' : ''"
|
||||
:src="constructAvatarUrl(mate.player.avatar)"
|
||||
:title="mate.player.tracked ? 'Tracked' : ''"
|
||||
alt="Player avatar"
|
||||
/>
|
||||
<span class="text">{{ mate.player.name }}</span>
|
||||
</span>
|
||||
<span class="end">
|
||||
{{ mate.win_rate ? (mate.win_rate * 100).toFixed(0) : 0 }} %
|
||||
<span v-if="mate.total" class="total text-muted">({{ mate.total }})</span>
|
||||
<span v-if="mate.total" class="total text-muted"
|
||||
>({{ mate.total }})</span
|
||||
>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -54,18 +78,25 @@
|
||||
<div class="heading">
|
||||
<h5>Best Mate <span class="text-muted">(by winrate)</span></h5>
|
||||
</div>
|
||||
<hr>
|
||||
<hr />
|
||||
<ul class="list-unstyled placeholder-glow">
|
||||
<li class="placeholder col-11"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="props.player_meta.eq_map && props.player_meta.weapon_dmg" class="side-info-box preferred-weapons">
|
||||
<div
|
||||
v-if="props.player_meta.eq_map && props.player_meta.weapon_dmg"
|
||||
class="side-info-box preferred-weapons"
|
||||
>
|
||||
<div class="heading">
|
||||
<h5>Weapons <span class="text-muted">(by dmg)</span></h5>
|
||||
</div>
|
||||
<hr>
|
||||
<ul v-for="(id, key) in data.best_weapons" :key="id[0]" class="list-unstyled">
|
||||
<hr />
|
||||
<ul
|
||||
v-for="(id, key) in data.best_weapons"
|
||||
:key="id[0]"
|
||||
class="list-unstyled"
|
||||
>
|
||||
<li>
|
||||
<span class="start">
|
||||
<span class="text">{{ id[0] }}</span>
|
||||
@@ -84,7 +115,7 @@
|
||||
<div class="heading">
|
||||
<h5>Weapons <span class="text-muted">(by dmg)</span></h5>
|
||||
</div>
|
||||
<hr>
|
||||
<hr />
|
||||
<ul class="list-unstyled placeholder-glow">
|
||||
<li class="placeholder col-11"></li>
|
||||
</ul>
|
||||
@@ -94,17 +125,23 @@
|
||||
<div class="heading">
|
||||
<h5>Best Map <span class="text-muted">(by winrate)</span></h5>
|
||||
</div>
|
||||
<hr>
|
||||
<hr />
|
||||
<ul v-for="map in data.best_maps" :key="map[0]" class="list-unstyled">
|
||||
<li>
|
||||
<span class="start">
|
||||
<img :src="'/images/map_icons/map_icon_' + map[0] + '.svg'" alt="Player avatar">
|
||||
<img
|
||||
:src="'/images/map_icons/map_icon_' + map[0] + '.svg'"
|
||||
alt="Player avatar"
|
||||
/>
|
||||
<span class="text">{{ FixMapName(map[0]) }}</span>
|
||||
</span>
|
||||
<span class="end">
|
||||
{{ (map[1] * 100).toFixed(0) }} %
|
||||
<span v-if="props.player_meta.total_maps[map[0]]"
|
||||
class="total text-muted">({{ props.player_meta.total_maps[map[0]] }})</span>
|
||||
<span
|
||||
v-if="props.player_meta.total_maps[map[0]]"
|
||||
class="total text-muted"
|
||||
>({{ props.player_meta.total_maps[map[0]] }})</span
|
||||
>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -114,7 +151,7 @@
|
||||
<div class="heading">
|
||||
<h5>Best Map <span class="text-muted">(by winrate)</span></h5>
|
||||
</div>
|
||||
<hr>
|
||||
<hr />
|
||||
<ul class="list-unstyled placeholder-glow">
|
||||
<li class="placeholder col-11"></li>
|
||||
</ul>
|
||||
@@ -123,97 +160,111 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {constructAvatarUrl, FixMapName, GoToPlayer, sortObjectValue} from "@/utils";
|
||||
import {reactive, ref, watch} from "vue";
|
||||
import {
|
||||
constructAvatarUrl,
|
||||
FixMapName,
|
||||
GoToPlayer,
|
||||
sortObjectValue,
|
||||
} from "/src/utils";
|
||||
import { reactive, ref, watch } from "vue";
|
||||
|
||||
export default {
|
||||
name: "PlayerSideInfo",
|
||||
props: {
|
||||
player_meta: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const displayCounter = 3
|
||||
const displayCounter = 3;
|
||||
|
||||
const mostMatesLoading = ref(true)
|
||||
const bestMatesLoading = ref(true)
|
||||
const weaponsLoading = ref(true)
|
||||
const mapsLoading = ref(true)
|
||||
const mostMatesLoading = ref(true);
|
||||
const bestMatesLoading = ref(true);
|
||||
const weaponsLoading = ref(true);
|
||||
const mapsLoading = ref(true);
|
||||
|
||||
const data = reactive({
|
||||
best_maps: [],
|
||||
best_weapons_tmp: [],
|
||||
best_weapons: []
|
||||
})
|
||||
best_weapons: [],
|
||||
});
|
||||
|
||||
const mapWeaponDamage = () => {
|
||||
if (props.player_meta.eq_map && props.player_meta.weapon_dmg) {
|
||||
Object.keys(props.player_meta.eq_map).forEach((key) => {
|
||||
for (const id in props.player_meta.weapon_dmg) {
|
||||
Object.keys(props.player_meta.weapon_dmg[id]).forEach((k) => {
|
||||
if (k === 'eq') {
|
||||
if (k === "eq") {
|
||||
if (props.player_meta.weapon_dmg[id][k] === key * 1) {
|
||||
data.best_weapons_tmp.push([props.player_meta.eq_map[key], props.player_meta.weapon_dmg[id]['dmg']])
|
||||
data.best_weapons_tmp.push([
|
||||
props.player_meta.eq_map[key],
|
||||
props.player_meta.weapon_dmg[id]["dmg"],
|
||||
]);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
data.best_weapons_tmp.sort((a, b) => {
|
||||
return b[1] - a[1]
|
||||
})
|
||||
return b[1] - a[1];
|
||||
});
|
||||
|
||||
data.best_weapons = data.best_weapons_tmp
|
||||
data.best_weapons_tmp = []
|
||||
data.best_weapons = data.best_weapons_tmp;
|
||||
data.best_weapons_tmp = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setDmgGraphWidth = () => {
|
||||
setTimeout(() => {
|
||||
let weaponsContainer
|
||||
const dmg100 = ref(0)
|
||||
const dmg = ref(0)
|
||||
let weaponsContainer;
|
||||
const dmg100 = ref(0);
|
||||
const dmg = ref(0);
|
||||
|
||||
for (let i = 0; i <= 4; i++) {
|
||||
weaponsContainer = document.querySelector('.dmg-chart-' + i)
|
||||
weaponsContainer = document.querySelector(".dmg-chart-" + i);
|
||||
if (weaponsContainer !== null) {
|
||||
if (i === 0) {
|
||||
dmg100.value = weaponsContainer.innerHTML * 1
|
||||
weaponsContainer.style.width = '100%'
|
||||
dmg100.value = weaponsContainer.innerHTML * 1;
|
||||
weaponsContainer.style.width = "100%";
|
||||
}
|
||||
|
||||
dmg.value = weaponsContainer.innerHTML * 1
|
||||
weaponsContainer.style.width = dmg.value * 100 / dmg100.value + '%'
|
||||
dmg.value = weaponsContainer.innerHTML * 1;
|
||||
weaponsContainer.style.width =
|
||||
(dmg.value * 100) / dmg100.value + "%";
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
watch(() => props.player_meta, () => {
|
||||
mapWeaponDamage()
|
||||
watch(
|
||||
() => props.player_meta,
|
||||
() => {
|
||||
mapWeaponDamage();
|
||||
|
||||
data.best_maps = sortObjectValue(props.player_meta.win_maps, 'desc')
|
||||
data.best_maps = sortObjectValue(props.player_meta.win_maps, "desc");
|
||||
|
||||
if (data.best_maps.length > displayCounter)
|
||||
data.best_maps.splice(displayCounter, data.best_maps.length - displayCounter)
|
||||
if (data.best_maps.length > displayCounter)
|
||||
data.best_maps.splice(
|
||||
displayCounter,
|
||||
data.best_maps.length - displayCounter
|
||||
);
|
||||
|
||||
if (!props.player_meta.most_mates) {
|
||||
mostMatesLoading.value = false
|
||||
if (!props.player_meta.most_mates) {
|
||||
mostMatesLoading.value = false;
|
||||
}
|
||||
if (!props.player_meta.best_mates) {
|
||||
bestMatesLoading.value = false;
|
||||
}
|
||||
if (!props.player_meta.win_maps) {
|
||||
mapsLoading.value = false;
|
||||
}
|
||||
if (!props.player_meta.eq_map || !props.player_meta.weapon_dmg) {
|
||||
weaponsLoading.value = false;
|
||||
}
|
||||
}
|
||||
if (!props.player_meta.best_mates) {
|
||||
bestMatesLoading.value = false
|
||||
}
|
||||
if (!props.player_meta.win_maps) {
|
||||
mapsLoading.value = false
|
||||
}
|
||||
if (!props.player_meta.eq_map || !props.player_meta.weapon_dmg) {
|
||||
weaponsLoading.value = false
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
props,
|
||||
@@ -225,10 +276,10 @@ export default {
|
||||
setDmgGraphWidth,
|
||||
GoToPlayer,
|
||||
constructAvatarUrl,
|
||||
FixMapName
|
||||
}
|
||||
}
|
||||
}
|
||||
FixMapName,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -251,12 +302,14 @@ export default {
|
||||
.side-info-box {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: rgba(20, 20, 20, .8);
|
||||
border: 1px solid rgba(white, .3);
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
border: 1px solid rgba(white, 0.3);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
ol, ul, dl {
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -280,12 +333,12 @@ export default {
|
||||
|
||||
hr {
|
||||
margin: 0 0 5px 0;
|
||||
border-color: rgba(white, .3);
|
||||
border-color: rgba(white, 0.3);
|
||||
}
|
||||
|
||||
ul li {
|
||||
line-height: 25px;
|
||||
font-size: .9rem;
|
||||
font-size: 0.9rem;
|
||||
padding: 0 10px;
|
||||
margin: 10px 0;
|
||||
cursor: pointer;
|
||||
@@ -302,7 +355,7 @@ export default {
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.tracked {
|
||||
font-size: .8rem;
|
||||
font-size: 0.8rem;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@@ -328,7 +381,8 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.best-map, .best-mate {
|
||||
.best-map,
|
||||
.best-mate {
|
||||
ul li {
|
||||
.start {
|
||||
width: 75%;
|
||||
|
@@ -5,187 +5,268 @@
|
||||
<div v-if="store.state.matchDetails.max_rounds === 16" id="short-match">
|
||||
<div class="team-1">
|
||||
<div class="score-text">
|
||||
<span v-if="store.state.matchDetails.score[0] < 10"
|
||||
:style="store.state.matchDetails.score[0] < 10 ? 'margin-left: -10px;' : ''"
|
||||
class="hidden">0</span><span
|
||||
:class="store.state.matchDetails.score[0] === 9 ? 'text-success' : store.state.matchDetails.score[0] === 8 ? 'text-warning' : 'text-danger'">{{
|
||||
store.state.matchDetails.score[0]
|
||||
}}</span>
|
||||
<span
|
||||
v-if="store.state.matchDetails.score[0] < 10"
|
||||
:style="
|
||||
store.state.matchDetails.score[0] < 10
|
||||
? 'margin-left: -10px;'
|
||||
: ''
|
||||
"
|
||||
class="hidden"
|
||||
>0</span
|
||||
><span
|
||||
:class="
|
||||
store.state.matchDetails.score[0] === 9
|
||||
? 'text-success'
|
||||
: store.state.matchDetails.score[0] === 8
|
||||
? 'text-warning'
|
||||
: 'text-danger'
|
||||
"
|
||||
>{{ store.state.matchDetails.score[0] }}</span
|
||||
>
|
||||
</div>
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg">
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg">
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg" />
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg" />
|
||||
</div>
|
||||
<div class="team-2">
|
||||
<div class="score-text">
|
||||
<span v-if="store.state.matchDetails.score[1] < 10"
|
||||
:style="store.state.matchDetails.score[1] < 10 ? 'margin-left: -10px;' : ''"
|
||||
class="hidden">0</span><span
|
||||
:class="store.state.matchDetails.score[1] === 9 ? 'text-success' : store.state.matchDetails.score[1] === 8 ? 'text-warning' : 'text-danger'">{{
|
||||
store.state.matchDetails.score[1]
|
||||
}}</span>
|
||||
<span
|
||||
v-if="store.state.matchDetails.score[1] < 10"
|
||||
:style="
|
||||
store.state.matchDetails.score[1] < 10
|
||||
? 'margin-left: -10px;'
|
||||
: ''
|
||||
"
|
||||
class="hidden"
|
||||
>0</span
|
||||
><span
|
||||
:class="
|
||||
store.state.matchDetails.score[1] === 9
|
||||
? 'text-success'
|
||||
: store.state.matchDetails.score[1] === 8
|
||||
? 'text-warning'
|
||||
: 'text-danger'
|
||||
"
|
||||
>{{ store.state.matchDetails.score[1] }}</span
|
||||
>
|
||||
</div>
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg">
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg">
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg" />
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="store.state.matchDetails.max_rounds === 30 || !store.state.matchDetails.max_rounds" id="long-match">
|
||||
<div
|
||||
v-if="
|
||||
store.state.matchDetails.max_rounds === 30 ||
|
||||
!store.state.matchDetails.max_rounds
|
||||
"
|
||||
id="long-match"
|
||||
>
|
||||
<div class="team-1">
|
||||
<div class="score-text">
|
||||
<span v-if="store.state.matchDetails.score[0] < 10"
|
||||
:style="store.state.matchDetails.score[0] < 10 ? 'margin-left: -10px;' : ''"
|
||||
class="hidden">0</span><span
|
||||
:class="store.state.matchDetails.match_result === 1 ? 'text-success' : store.state.matchDetails.match_result === 0 ? 'text-warning' : 'text-danger'">{{
|
||||
store.state.matchDetails.score[0]
|
||||
}}</span>
|
||||
<span
|
||||
v-if="store.state.matchDetails.score[0] < 10"
|
||||
:style="
|
||||
store.state.matchDetails.score[0] < 10
|
||||
? 'margin-left: -10px;'
|
||||
: ''
|
||||
"
|
||||
class="hidden"
|
||||
>0</span
|
||||
><span
|
||||
:class="
|
||||
store.state.matchDetails.match_result === 1
|
||||
? 'text-success'
|
||||
: store.state.matchDetails.match_result === 0
|
||||
? 'text-warning'
|
||||
: 'text-danger'
|
||||
"
|
||||
>{{ store.state.matchDetails.score[0] }}</span
|
||||
>
|
||||
</div>
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg">
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg">
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg" />
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg" />
|
||||
</div>
|
||||
<div class="team-2">
|
||||
<div class="score-text">
|
||||
<span v-if="store.state.matchDetails.score[1] < 10"
|
||||
:style="store.state.matchDetails.score[1] < 10 ? 'margin-left: -10px;' : ''"
|
||||
class="hidden">0</span><span
|
||||
:class="store.state.matchDetails.match_result === 2 ? 'text-success' : store.state.matchDetails.match_result === 0 ? 'text-warning' : 'text-danger'">{{
|
||||
store.state.matchDetails.score[1]
|
||||
}}</span>
|
||||
<span
|
||||
v-if="store.state.matchDetails.score[1] < 10"
|
||||
:style="
|
||||
store.state.matchDetails.score[1] < 10
|
||||
? 'margin-left: -10px;'
|
||||
: ''
|
||||
"
|
||||
class="hidden"
|
||||
>0</span
|
||||
><span
|
||||
:class="
|
||||
store.state.matchDetails.match_result === 2
|
||||
? 'text-success'
|
||||
: store.state.matchDetails.match_result === 0
|
||||
? 'text-warning'
|
||||
: 'text-danger'
|
||||
"
|
||||
>{{ store.state.matchDetails.score[1] }}</span
|
||||
>
|
||||
</div>
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg">
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg">
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg" />
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg" />
|
||||
</div>
|
||||
</div>
|
||||
</caption>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="player__vac"></th>
|
||||
<th class="player__avatar"></th>
|
||||
<th class="player__name"></th>
|
||||
<th class="player__rank"></th>
|
||||
<th class="player__kills">K</th>
|
||||
<th class="player__assist">A</th>
|
||||
<th class="player__deaths">D</th>
|
||||
<th class="player__diff helptext" title="Kill death difference">+/-</th>
|
||||
<th class="player__kd">K/D</th>
|
||||
<th v-if="store.state.matchDetails.parsed" class="player__adr helptext" title="Average damage per round">
|
||||
ADR
|
||||
</th>
|
||||
<th class="player__hs helptext" title="Percentage of kills with a headshot">HS%</th>
|
||||
<th class="player__rating helptext" title="Estimated HLTV Rating 1.0">Rating</th>
|
||||
<th class="player__mvp helptext" title="Most valuable player">MVP</th>
|
||||
<th class="player__score">Score</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="player__vac"></th>
|
||||
<th class="player__avatar"></th>
|
||||
<th class="player__name"></th>
|
||||
<th class="player__rank"></th>
|
||||
<th class="player__kills">K</th>
|
||||
<th class="player__assist">A</th>
|
||||
<th class="player__deaths">D</th>
|
||||
<th class="player__diff helptext" title="Kill death difference">
|
||||
+/-
|
||||
</th>
|
||||
<th class="player__kd">K/D</th>
|
||||
<th
|
||||
v-if="store.state.matchDetails.parsed"
|
||||
class="player__adr helptext"
|
||||
title="Average damage per round"
|
||||
>
|
||||
ADR
|
||||
</th>
|
||||
<th
|
||||
class="player__hs helptext"
|
||||
title="Percentage of kills with a headshot"
|
||||
>
|
||||
HS%
|
||||
</th>
|
||||
<th class="player__rating helptext" title="Estimated HLTV Rating 1.0">
|
||||
Rating
|
||||
</th>
|
||||
<th class="player__mvp helptext" title="Most valuable player">MVP</th>
|
||||
<th class="player__score">Score</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr v-for="player in teamStats(1)"
|
||||
<tr
|
||||
v-for="player in teamStats(1)"
|
||||
:key="player.player.steamid64"
|
||||
class="team-1">
|
||||
<ScoreTeamPlayer :assists="player.assists"
|
||||
:avatar="player.player.avatar"
|
||||
:color="player.color"
|
||||
:deaths="player.deaths"
|
||||
:dmg="player.dmg?.enemy"
|
||||
:game_ban="player.player.game_ban"
|
||||
:game_ban_date="player.player.game_ban_date"
|
||||
:hs="player.headshot"
|
||||
:kdiff="player.kills - player.deaths"
|
||||
:kills="player.kills"
|
||||
:mk_duo="player.multi_kills?.duo"
|
||||
:mk_pent="player.multi_kills?.pent"
|
||||
:mk_quad="player.multi_kills?.quad"
|
||||
:mk_triple="player.multi_kills?.triple"
|
||||
:mvp="player.mvp"
|
||||
:name="player.player.name"
|
||||
:parsed="store.state.matchDetails.parsed"
|
||||
:player_score="player.score"
|
||||
:rank_new="player.rank?.new"
|
||||
:rank_old="player.rank?.old"
|
||||
:rounds_played="store.state.matchDetails.score.reduce((a, b) => a + b)"
|
||||
:steamid64="player.player.steamid64"
|
||||
:tracked="player.player.tracked"
|
||||
:vac="player.player.vac"
|
||||
:vac_date="player.player.vac_date"
|
||||
/>
|
||||
</tr>
|
||||
class="team-1"
|
||||
>
|
||||
<ScoreTeamPlayer
|
||||
:assists="player.assists"
|
||||
:avatar="player.player.avatar"
|
||||
:color="player.color"
|
||||
:deaths="player.deaths"
|
||||
:dmg="player.dmg?.enemy"
|
||||
:game_ban="player.player.game_ban"
|
||||
:game_ban_date="player.player.game_ban_date"
|
||||
:hs="player.headshot"
|
||||
:kdiff="player.kills - player.deaths"
|
||||
:kills="player.kills"
|
||||
:mk_duo="player.multi_kills?.duo"
|
||||
:mk_pent="player.multi_kills?.pent"
|
||||
:mk_quad="player.multi_kills?.quad"
|
||||
:mk_triple="player.multi_kills?.triple"
|
||||
:mvp="player.mvp"
|
||||
:name="player.player.name"
|
||||
:parsed="store.state.matchDetails.parsed"
|
||||
:player_score="player.score"
|
||||
:rank_new="player.rank?.new"
|
||||
:rank_old="player.rank?.old"
|
||||
:rounds_played="
|
||||
store.state.matchDetails.score.reduce((a, b) => a + b)
|
||||
"
|
||||
:steamid64="player.player.steamid64"
|
||||
:tracked="player.player.tracked"
|
||||
:vac="player.player.vac"
|
||||
:vac_date="player.player.vac_date"
|
||||
/>
|
||||
</tr>
|
||||
|
||||
<tr class="hr_outer">
|
||||
<td colspan="14"></td>
|
||||
</tr>
|
||||
<tr class="hr">
|
||||
<td colspan="14"></td>
|
||||
</tr>
|
||||
<tr class="hr_outer">
|
||||
<td colspan="14"></td>
|
||||
</tr>
|
||||
<tr class="hr_outer">
|
||||
<td colspan="14"></td>
|
||||
</tr>
|
||||
<tr class="hr">
|
||||
<td colspan="14"></td>
|
||||
</tr>
|
||||
<tr class="hr_outer">
|
||||
<td colspan="14"></td>
|
||||
</tr>
|
||||
|
||||
<tr v-for="player in teamStats(2)"
|
||||
<tr
|
||||
v-for="player in teamStats(2)"
|
||||
:key="player.player.steamid64"
|
||||
class="team-2">
|
||||
<ScoreTeamPlayer :assists="player.assists"
|
||||
:avatar="player.player.avatar"
|
||||
:color="player.color"
|
||||
:deaths="player.deaths"
|
||||
:dmg="player.dmg?.enemy"
|
||||
:game_ban="player.player.game_ban"
|
||||
:game_ban_date="player.player.game_ban_date"
|
||||
:hs="player.headshot"
|
||||
:kdiff="player.kills - player.deaths"
|
||||
:kills="player.kills"
|
||||
:mk_duo="player.multi_kills?.duo"
|
||||
:mk_pent="player.multi_kills?.pent"
|
||||
:mk_quad="player.multi_kills?.quad"
|
||||
:mk_triple="player.multi_kills?.triple"
|
||||
:mvp="player.mvp"
|
||||
:name="player.player.name"
|
||||
:parsed="store.state.matchDetails.parsed"
|
||||
:player_score="player.score"
|
||||
:rank_new="player.rank?.new"
|
||||
:rank_old="player.rank?.old"
|
||||
:rounds_played="store.state.matchDetails.score.reduce((a, b) => a + b)"
|
||||
:steamid64="player.player.steamid64"
|
||||
:tracked="player.player.tracked"
|
||||
:vac="player.player.vac"
|
||||
:vac_date="player.player.vac_date"
|
||||
/>
|
||||
</tr>
|
||||
class="team-2"
|
||||
>
|
||||
<ScoreTeamPlayer
|
||||
:assists="player.assists"
|
||||
:avatar="player.player.avatar"
|
||||
:color="player.color"
|
||||
:deaths="player.deaths"
|
||||
:dmg="player.dmg?.enemy"
|
||||
:game_ban="player.player.game_ban"
|
||||
:game_ban_date="player.player.game_ban_date"
|
||||
:hs="player.headshot"
|
||||
:kdiff="player.kills - player.deaths"
|
||||
:kills="player.kills"
|
||||
:mk_duo="player.multi_kills?.duo"
|
||||
:mk_pent="player.multi_kills?.pent"
|
||||
:mk_quad="player.multi_kills?.quad"
|
||||
:mk_triple="player.multi_kills?.triple"
|
||||
:mvp="player.mvp"
|
||||
:name="player.player.name"
|
||||
:parsed="store.state.matchDetails.parsed"
|
||||
:player_score="player.score"
|
||||
:rank_new="player.rank?.new"
|
||||
:rank_old="player.rank?.old"
|
||||
:rounds_played="
|
||||
store.state.matchDetails.score.reduce((a, b) => a + b)
|
||||
"
|
||||
:steamid64="player.player.steamid64"
|
||||
:tracked="player.player.tracked"
|
||||
:vac="player.player.vac"
|
||||
:vac_date="player.player.vac_date"
|
||||
/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScoreTeamPlayer from '@/components/ScoreTeamPlayer.vue'
|
||||
import {useStore} from "vuex";
|
||||
import ScoreTeamPlayer from "/src/components/ScoreTeamPlayer.vue";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'ScoreTeam',
|
||||
components: {ScoreTeamPlayer},
|
||||
name: "ScoreTeam",
|
||||
components: { ScoreTeamPlayer },
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
const teamStats = (team) => {
|
||||
let arr = []
|
||||
let arr = [];
|
||||
|
||||
if (team === 1) {
|
||||
arr = []
|
||||
arr = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
arr.push(store.state.matchDetails.stats[i])
|
||||
arr.push(store.state.matchDetails.stats[i]);
|
||||
}
|
||||
} else if (team === 2) {
|
||||
arr = []
|
||||
arr = [];
|
||||
for (let i = 5; i < store.state.matchDetails.stats.length; i++) {
|
||||
arr.push(store.state.matchDetails.stats[i])
|
||||
arr.push(store.state.matchDetails.stats[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
return {store, teamStats}
|
||||
}
|
||||
}
|
||||
return { store, teamStats };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -222,7 +303,7 @@ table {
|
||||
.team-2 {
|
||||
position: absolute;
|
||||
font-size: 3rem;
|
||||
opacity: .8;
|
||||
opacity: 0.8;
|
||||
|
||||
margin-left: -100px;
|
||||
|
||||
@@ -267,7 +348,8 @@ table {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
tr.team-1, tr.team-2 {
|
||||
tr.team-1,
|
||||
tr.team-2 {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
@@ -290,7 +372,6 @@ table {
|
||||
.player__vac {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
|
@@ -1,29 +1,68 @@
|
||||
<template>
|
||||
<td class="player__vac">
|
||||
<div v-if="!props.vac && !props.game_ban" class="vac-placeholder"></div>
|
||||
<img v-if="props.vac && FormatVacDate(props.vac_date, store.state.matchDetails.date) !== ''"
|
||||
:title="'Vac-banned: ' + FormatVacDate(props.vac_date, store.state.matchDetails.date)"
|
||||
alt="VAC-Ban"
|
||||
src="/images/icons/vac_banned.svg">
|
||||
<img v-if="!props.vac && props.game_ban && FormatVacDate(props.game_ban_date, store.state.matchDetails.date) !== ''"
|
||||
:title="'Game-banned: ' + FormatVacDate(props.game_ban_date, store.state.matchDetails.date)"
|
||||
alt="Game-Ban"
|
||||
src="/images/icons/game_banned.svg">
|
||||
<img
|
||||
v-if="
|
||||
props.vac &&
|
||||
FormatVacDate(props.vac_date, store.state.matchDetails.date) !== ''
|
||||
"
|
||||
:title="
|
||||
'Vac-banned: ' +
|
||||
FormatVacDate(props.vac_date, store.state.matchDetails.date)
|
||||
"
|
||||
alt="VAC-Ban"
|
||||
src="/images/icons/vac_banned.svg"
|
||||
/>
|
||||
<img
|
||||
v-if="
|
||||
!props.vac &&
|
||||
props.game_ban &&
|
||||
FormatVacDate(props.game_ban_date, store.state.matchDetails.date) !== ''
|
||||
"
|
||||
:title="
|
||||
'Game-banned: ' +
|
||||
FormatVacDate(props.game_ban_date, store.state.matchDetails.date)
|
||||
"
|
||||
alt="Game-Ban"
|
||||
src="/images/icons/game_banned.svg"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<img :class="'team-color-' + props.color" :src="constructAvatarUrl(props.avatar)" alt="Player avatar"
|
||||
class="player__avatar">
|
||||
<img
|
||||
:class="'team-color-' + props.color"
|
||||
:src="constructAvatarUrl(props.avatar)"
|
||||
alt="Player avatar"
|
||||
class="player__avatar"
|
||||
/>
|
||||
</td>
|
||||
<td class="player__name" @click="GoToPlayer(props.steamid64)">
|
||||
<i v-if="props.tracked" class="fa fa-dot-circle-o text-success tracked" title="Tracked user"></i>
|
||||
<i
|
||||
v-if="props.tracked"
|
||||
class="fa fa-dot-circle-o text-success tracked"
|
||||
title="Tracked user"
|
||||
></i>
|
||||
{{ props.name }}
|
||||
<i class="fa fa-external-link"></i>
|
||||
</td>
|
||||
<td v-if="props.parsed" class="player__rank">
|
||||
<img :alt="DisplayRank(props.rank_old)[1]"
|
||||
:class="props.rank_new > props.rank_old ? 'uprank' : props.rank_new < props.rank_old ? 'downrank' : ''"
|
||||
:src="DisplayRank(props.rank_old)[0]"
|
||||
:title="props.rank_new > props.rank_old ? 'Uprank to ' + DisplayRank(props.rank_new)[1] : props.rank_new < props.rank_old ? 'Downrank to ' + DisplayRank(props.rank_new)[1] : DisplayRank(props.rank_old)[1]">
|
||||
<img
|
||||
:alt="DisplayRank(props.rank_old)[1]"
|
||||
:class="
|
||||
props.rank_new > props.rank_old
|
||||
? 'uprank'
|
||||
: props.rank_new < props.rank_old
|
||||
? 'downrank'
|
||||
: ''
|
||||
"
|
||||
:src="DisplayRank(props.rank_old)[0]"
|
||||
:title="
|
||||
props.rank_new > props.rank_old
|
||||
? 'Uprank to ' + DisplayRank(props.rank_new)[1]
|
||||
: props.rank_new < props.rank_old
|
||||
? 'Downrank to ' + DisplayRank(props.rank_new)[1]
|
||||
: DisplayRank(props.rank_old)[1]
|
||||
"
|
||||
/>
|
||||
</td>
|
||||
<td v-if="!props.parsed" class="rank-placeholder"></td>
|
||||
<td class="player__kills">
|
||||
@@ -35,23 +74,42 @@
|
||||
<td class="player__deaths">
|
||||
{{ props.deaths }}
|
||||
</td>
|
||||
<td :class="props.kdiff >= 0 ? 'text-success' : 'text-danger'" class="player__kdiff">
|
||||
<td
|
||||
:class="props.kdiff >= 0 ? 'text-success' : 'text-danger'"
|
||||
class="player__kdiff"
|
||||
>
|
||||
{{ props.kdiff }}
|
||||
</td>
|
||||
<td class="player__kd">
|
||||
{{
|
||||
(props.kills > 0 && props.deaths > 0) ? (props.kills / props.deaths).toFixed(2) : (props.kills > 0 && props.deaths === 0) ? props.kills : 0.00
|
||||
props.kills > 0 && props.deaths > 0
|
||||
? (props.kills / props.deaths).toFixed(2)
|
||||
: props.kills > 0 && props.deaths === 0
|
||||
? props.kills
|
||||
: 0.0
|
||||
}}
|
||||
</td>
|
||||
<td v-if="props.parsed" class="player__adr">
|
||||
{{ (props.dmg / props.rounds_played).toFixed(2) }}
|
||||
</td>
|
||||
<td class="player__hs">
|
||||
{{ (props.hs > 0 && props.kills > 0) ? (props.hs * 100 / props.kills).toFixed(0) + "%" : "0%" }}
|
||||
{{
|
||||
props.hs > 0 && props.kills > 0
|
||||
? ((props.hs * 100) / props.kills).toFixed(0) + "%"
|
||||
: "0%"
|
||||
}}
|
||||
</td>
|
||||
<td class="player__rating">
|
||||
{{
|
||||
GetHLTV_1(props.kills, props.rounds_played, props.deaths, props.mk_duo, props.mk_triple, props.mk_quad, props.mk_pent)
|
||||
GetHLTV_1(
|
||||
props.kills,
|
||||
props.rounds_played,
|
||||
props.deaths,
|
||||
props.mk_duo,
|
||||
props.mk_triple,
|
||||
props.mk_quad,
|
||||
props.mk_pent
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="player__mvp">
|
||||
@@ -63,143 +121,157 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {constructAvatarUrl, DisplayRank, FormatVacDate, GetHLTV_1, GoToPlayer} from "@/utils";
|
||||
import {useStore} from "vuex";
|
||||
import {
|
||||
constructAvatarUrl,
|
||||
DisplayRank,
|
||||
FormatVacDate,
|
||||
GetHLTV_1,
|
||||
GoToPlayer,
|
||||
} from "/src/utils";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'ScoreTeamPlayer',
|
||||
name: "ScoreTeamPlayer",
|
||||
props: {
|
||||
steamid64: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
default: "",
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'Avatar'
|
||||
default: "Avatar",
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'Name'
|
||||
default: "Name",
|
||||
},
|
||||
rank_old: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
rank_new: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
kills: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
assists: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
deaths: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
kdiff: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
hs: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
rounds_played: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
mk_duo: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
mk_triple: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
mk_quad: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
mk_pent: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
dmg: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
mvp: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
player_score: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
default: "",
|
||||
},
|
||||
tracked: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
parsed: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
vac: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
vac_date: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
game_ban: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
game_ban_date: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0
|
||||
}
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore()
|
||||
return {props, GetHLTV_1, GoToPlayer, DisplayRank, constructAvatarUrl, FormatVacDate, store}
|
||||
}
|
||||
}
|
||||
const store = useStore();
|
||||
return {
|
||||
props,
|
||||
GetHLTV_1,
|
||||
GoToPlayer,
|
||||
DisplayRank,
|
||||
constructAvatarUrl,
|
||||
FormatVacDate,
|
||||
store,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -231,11 +303,11 @@ export default {
|
||||
cursor: pointer;
|
||||
|
||||
.tracked {
|
||||
font-size: .8rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.fa-external-link {
|
||||
font-size: .8rem;
|
||||
font-size: 0.8rem;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
@@ -250,11 +322,18 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.player__kills, .player__assist, .player__deaths, .player__kdiff, .player__mvp {
|
||||
.player__kills,
|
||||
.player__assist,
|
||||
.player__deaths,
|
||||
.player__kdiff,
|
||||
.player__mvp {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.player__kd, .player__hs, .player__rating, .player__score {
|
||||
.player__kd,
|
||||
.player__hs,
|
||||
.player__rating,
|
||||
.player__score {
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
|
@@ -3,24 +3,25 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {watch} from "vue";
|
||||
import { watch } from "vue";
|
||||
|
||||
export default {
|
||||
name: "SprayGraph",
|
||||
props: {
|
||||
spray: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
watch(() => props.spray, () => {
|
||||
// console.log(props.spray)
|
||||
})
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => props.spray,
|
||||
() => {
|
||||
// console.log(props.spray)
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
@@ -6,138 +6,158 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts/core';
|
||||
import {GridComponent, LegendComponent, TooltipComponent} from 'echarts/components';
|
||||
import {BarChart} from 'echarts/charts';
|
||||
import {CanvasRenderer} from 'echarts/renderers';
|
||||
import {onMounted, onUnmounted, ref} from "vue";
|
||||
import {checkStatEmpty, getPlayerArr} from "../utils";
|
||||
import {useStore} from "vuex";
|
||||
import * as echarts from "echarts/core";
|
||||
import {
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent,
|
||||
} from "echarts/components";
|
||||
import { BarChart } from "echarts/charts";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import { checkStatEmpty, getPlayerArr } from "../utils";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "FlashChart",
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
let myChart1, myChart2
|
||||
let myChart1, myChart2;
|
||||
|
||||
const getWindowWidth = () => {
|
||||
const windowWidth = window.innerWidth
|
||||
if (windowWidth <= 750)
|
||||
return windowWidth
|
||||
else
|
||||
return 650
|
||||
}
|
||||
const windowWidth = window.innerWidth;
|
||||
if (windowWidth <= 750) return windowWidth;
|
||||
else return 650;
|
||||
};
|
||||
|
||||
const setHeight = () => {
|
||||
const windowWidth = getWindowWidth()
|
||||
if (windowWidth >= 751)
|
||||
return windowWidth * 3 / 7.5
|
||||
const windowWidth = getWindowWidth();
|
||||
if (windowWidth >= 751) return (windowWidth * 3) / 7.5;
|
||||
else if (windowWidth >= 501 && windowWidth <= 750)
|
||||
return windowWidth * 3 / 6.5
|
||||
else
|
||||
return windowWidth * 3 / 5.5
|
||||
}
|
||||
return (windowWidth * 3) / 6.5;
|
||||
else return (windowWidth * 3) / 5.5;
|
||||
};
|
||||
|
||||
const width = ref(getWindowWidth())
|
||||
const height = ref(setHeight())
|
||||
const width = ref(getWindowWidth());
|
||||
const height = ref(setHeight());
|
||||
|
||||
const dataArr = (stats, team, prop) => {
|
||||
if (['team', 'enemy', 'self'].indexOf(prop) > -1) {
|
||||
let arr = []
|
||||
if (["team", "enemy", "self"].indexOf(prop) > -1) {
|
||||
let arr = [];
|
||||
for (let i = (team - 1) * 5; i < team * 5; i++) {
|
||||
arr.push({
|
||||
value: checkStatEmpty(Function('return(function(stats, i){ return stats[i].dmg.' + prop + '})')()(stats, i)) * (prop === 'enemy' ? 1 : -1),
|
||||
value:
|
||||
checkStatEmpty(
|
||||
Function(
|
||||
"return(function(stats, i){ return stats[i].dmg." +
|
||||
prop +
|
||||
"})"
|
||||
)()(stats, i)
|
||||
) * (prop === "enemy" ? 1 : -1),
|
||||
itemStyle: {
|
||||
color: prop === 'enemy' ? getComputedStyle(document.documentElement).getPropertyValue(`--csgo-${stats[i].color}`) : 'firebrick'
|
||||
}
|
||||
})
|
||||
color:
|
||||
prop === "enemy"
|
||||
? getComputedStyle(document.documentElement).getPropertyValue(
|
||||
`--csgo-${stats[i].color}`
|
||||
)
|
||||
: "firebrick",
|
||||
},
|
||||
});
|
||||
}
|
||||
arr.reverse()
|
||||
return arr
|
||||
arr.reverse();
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const optionGen = (team) => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
type: "shadow",
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
show: false,
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
left: "3%",
|
||||
right: "4%",
|
||||
bottom: "3%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
min: -300
|
||||
}
|
||||
type: "value",
|
||||
min: -300,
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
type: "category",
|
||||
axisTick: {
|
||||
show: false
|
||||
show: false,
|
||||
},
|
||||
data: getPlayerArr(store.state.matchDetails.stats, team)
|
||||
}
|
||||
data: getPlayerArr(store.state.matchDetails.stats, team),
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'Team',
|
||||
type: 'bar',
|
||||
stack: 'Total',
|
||||
name: "Team",
|
||||
type: "bar",
|
||||
stack: "Total",
|
||||
label: {
|
||||
show: true,
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
focus: "series",
|
||||
},
|
||||
data: dataArr(store.state.matchDetails.stats, team, 'team')
|
||||
data: dataArr(store.state.matchDetails.stats, team, "team"),
|
||||
},
|
||||
{
|
||||
name: 'Enemy',
|
||||
type: 'bar',
|
||||
stack: 'Total',
|
||||
name: "Enemy",
|
||||
type: "bar",
|
||||
stack: "Total",
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inside'
|
||||
position: "inside",
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
focus: "series",
|
||||
},
|
||||
data: dataArr(store.state.matchDetails.stats, team, 'enemy')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
data: dataArr(store.state.matchDetails.stats, team, "enemy"),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const disposeCharts = () => {
|
||||
if (myChart1 != null && myChart1 !== '' && myChart1 !== undefined) {
|
||||
myChart1.dispose()
|
||||
if (myChart1 != null && myChart1 !== "" && myChart1 !== undefined) {
|
||||
myChart1.dispose();
|
||||
}
|
||||
if (myChart2 != null && myChart2 !== '' && myChart2 !== undefined) {
|
||||
myChart2.dispose()
|
||||
if (myChart2 != null && myChart2 !== "" && myChart2 !== undefined) {
|
||||
myChart2.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildCharts = () => {
|
||||
disposeCharts()
|
||||
disposeCharts();
|
||||
|
||||
myChart1 = echarts.init(document.getElementById('dmg-chart-1'), {}, {width: width.value, height: height.value});
|
||||
myChart1 = echarts.init(
|
||||
document.getElementById("dmg-chart-1"),
|
||||
{},
|
||||
{ width: width.value, height: height.value }
|
||||
);
|
||||
myChart1.setOption(optionGen(1));
|
||||
|
||||
myChart2 = echarts.init(document.getElementById('dmg-chart-2'), {}, {width: width.value, height: height.value});
|
||||
myChart2 = echarts.init(
|
||||
document.getElementById("dmg-chart-2"),
|
||||
{},
|
||||
{ width: width.value, height: height.value }
|
||||
);
|
||||
myChart2.setOption(optionGen(2));
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (store.state.matchDetails.stats) {
|
||||
@@ -146,27 +166,27 @@ export default {
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
BarChart,
|
||||
CanvasRenderer
|
||||
CanvasRenderer,
|
||||
]);
|
||||
|
||||
buildCharts()
|
||||
buildCharts();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
disposeCharts()
|
||||
})
|
||||
disposeCharts();
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
if (window.innerWidth <= 750) {
|
||||
width.value = getWindowWidth() - 20
|
||||
height.value = setHeight()
|
||||
width.value = getWindowWidth() - 20;
|
||||
height.value = setHeight();
|
||||
}
|
||||
|
||||
buildCharts()
|
||||
}
|
||||
}
|
||||
}
|
||||
buildCharts();
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -186,6 +206,5 @@ export default {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,17 +1,20 @@
|
||||
<template>
|
||||
<div class="toggle-btn text-muted">
|
||||
<div @click.prevent="$emit('translated', handleBtnClick())"
|
||||
class="d-flex">
|
||||
<div class="d-flex" @click.prevent="$emit('translated', handleBtnClick())">
|
||||
<span class="text-center mx-2">
|
||||
<i id="toggle-off" class="fa fa-toggle-off show"/>
|
||||
<i id="toggle-on" class="fa fa-toggle-on"/>
|
||||
<i id="toggle-off" class="fa fa-toggle-off show" />
|
||||
<i id="toggle-on" class="fa fa-toggle-on" />
|
||||
</span>
|
||||
<div>
|
||||
<span :class="toggle === 'translated' ? 'text-warning' : ''"
|
||||
class="float-start">
|
||||
<span class="text-uppercase">Translate to {{data.browserLang}}</span>
|
||||
<span
|
||||
:class="toggle === 'translated' ? 'text-warning' : ''"
|
||||
class="float-start"
|
||||
>
|
||||
<span class="text-uppercase"
|
||||
>Translate to {{ data.browserLang }}</span
|
||||
>
|
||||
<span class="loading-icon ms-2" title="Translating..">
|
||||
<i class="fa fa-spinner fa-pulse fa-fw"/>
|
||||
<i class="fa fa-spinner fa-pulse fa-fw" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -20,82 +23,85 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import ISO6391 from 'iso-639-1'
|
||||
import {GetChatHistoryTranslated} from "@/utils";
|
||||
import {useStore} from "vuex";
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import ISO6391 from "iso-639-1";
|
||||
import { GetChatHistoryTranslated } from "/src/utils";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'TranslateChatButton',
|
||||
name: "TranslateChatButton",
|
||||
props: {
|
||||
translated: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
const data = reactive({
|
||||
browserIsoCode: '',
|
||||
browserLangCode: '',
|
||||
browserLang: '',
|
||||
})
|
||||
browserIsoCode: "",
|
||||
browserLangCode: "",
|
||||
browserLang: "",
|
||||
});
|
||||
|
||||
const toggle = ref('original')
|
||||
const toggle = ref("original");
|
||||
|
||||
const setLanguageVariables = () => {
|
||||
const navLangs = navigator.languages
|
||||
const navLangs = navigator.languages;
|
||||
|
||||
data.browserIsoCode = navLangs.find((l) => l.length === 5)
|
||||
data.browserLangCode = navLangs[0]
|
||||
data.browserIsoCode = navLangs.find((l) => l.length === 5);
|
||||
data.browserLangCode = navLangs[0];
|
||||
|
||||
if (ISO6391.validate(data.browserLangCode)) {
|
||||
data.browserLang = ISO6391.getNativeName(data.browserLangCode)
|
||||
data.browserLang = ISO6391.getNativeName(data.browserLangCode);
|
||||
} else {
|
||||
data.browserIsoCode = 'en-US'
|
||||
data.browserLangCode = 'en'
|
||||
data.browserLang = 'English'
|
||||
data.browserIsoCode = "en-US";
|
||||
data.browserLangCode = "en";
|
||||
data.browserLang = "English";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleBtnClick = async () => {
|
||||
let response
|
||||
let response;
|
||||
|
||||
const refreshButton = document.querySelector('.loading-icon .fa-spinner')
|
||||
refreshButton.classList.add('show')
|
||||
const refreshButton = document.querySelector(".loading-icon .fa-spinner");
|
||||
refreshButton.classList.add("show");
|
||||
|
||||
toggleShow()
|
||||
toggleShow();
|
||||
|
||||
response = await GetChatHistoryTranslated(store, store.state.matchDetails.match_id)
|
||||
response = await GetChatHistoryTranslated(
|
||||
store,
|
||||
store.state.matchDetails.match_id
|
||||
);
|
||||
|
||||
if (refreshButton.classList.contains('show'))
|
||||
refreshButton.classList.remove('show')
|
||||
if (refreshButton.classList.contains("show"))
|
||||
refreshButton.classList.remove("show");
|
||||
|
||||
return [response, toggle.value]
|
||||
}
|
||||
return [response, toggle.value];
|
||||
};
|
||||
|
||||
const toggleShow = () => {
|
||||
const offBtn = document.getElementById('toggle-off')
|
||||
const onBtn = document.getElementById('toggle-on')
|
||||
const offBtn = document.getElementById("toggle-off");
|
||||
const onBtn = document.getElementById("toggle-on");
|
||||
|
||||
if (offBtn.classList.contains('show')) {
|
||||
offBtn.classList.remove('show')
|
||||
onBtn.classList.add('show')
|
||||
toggle.value = 'translated'
|
||||
} else if (onBtn.classList.contains('show')) {
|
||||
onBtn.classList.remove('show')
|
||||
offBtn.classList.add('show')
|
||||
toggle.value = 'original'
|
||||
if (offBtn.classList.contains("show")) {
|
||||
offBtn.classList.remove("show");
|
||||
onBtn.classList.add("show");
|
||||
toggle.value = "translated";
|
||||
} else if (onBtn.classList.contains("show")) {
|
||||
onBtn.classList.remove("show");
|
||||
offBtn.classList.add("show");
|
||||
toggle.value = "original";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
setLanguageVariables()
|
||||
})
|
||||
return {data, toggle, handleBtnClick}
|
||||
setLanguageVariables();
|
||||
});
|
||||
return { data, toggle, handleBtnClick };
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -1,8 +1,14 @@
|
||||
<template>
|
||||
<div :style="props.ud.flames || props.ud.flash || props.ud.he ? 'display: flex' : 'display: none'"
|
||||
class="player-utility">
|
||||
<div
|
||||
:style="
|
||||
props.ud.flames || props.ud.flash || props.ud.he
|
||||
? 'display: flex'
|
||||
: 'display: none'
|
||||
"
|
||||
class="player-utility"
|
||||
>
|
||||
<div class="heading">
|
||||
<img :src="props.avatar" alt="Player avatar" class="avatar">
|
||||
<img :src="props.avatar" alt="Player avatar" class="avatar" />
|
||||
<h4>{{ props.name }}</h4>
|
||||
</div>
|
||||
<div :id="'utility-chart-' + props.id"></div>
|
||||
@@ -10,13 +16,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts/core';
|
||||
import {LegendComponent, TooltipComponent} from 'echarts/components';
|
||||
import {PieChart} from 'echarts/charts';
|
||||
import {LabelLayout} from 'echarts/features';
|
||||
import {CanvasRenderer} from 'echarts/renderers';
|
||||
import { TitleComponent } from 'echarts/components';
|
||||
import {onMounted} from "vue";
|
||||
import * as echarts from "echarts/core";
|
||||
import {
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
} from "echarts/components";
|
||||
import { PieChart } from "echarts/charts";
|
||||
import { LabelLayout } from "echarts/features";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
export default {
|
||||
name: "FlashChart",
|
||||
@@ -24,21 +33,21 @@ export default {
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
default: "",
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
default: "",
|
||||
required: true,
|
||||
},
|
||||
ud: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
@@ -49,87 +58,100 @@ export default {
|
||||
PieChart,
|
||||
CanvasRenderer,
|
||||
TitleComponent,
|
||||
LabelLayout
|
||||
LabelLayout,
|
||||
]);
|
||||
|
||||
let myChart = echarts.init(document.getElementById(`utility-chart-${props.id}`), {}, {width: 500, height: 300});
|
||||
let option
|
||||
let myChart = echarts.init(
|
||||
document.getElementById(`utility-chart-${props.id}`),
|
||||
{},
|
||||
{ width: 500, height: 300 }
|
||||
);
|
||||
let option;
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
trigger: "item",
|
||||
formatter: "{a} <br/>{b}: {c} ({d}%)",
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
show: false,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Utility Damage',
|
||||
type: 'pie',
|
||||
radius: [0, '65%'],
|
||||
name: "Utility Damage",
|
||||
type: "pie",
|
||||
radius: [0, "65%"],
|
||||
avoidLabelOverlap: true,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#000',
|
||||
borderWidth: 3
|
||||
borderColor: "#000",
|
||||
borderWidth: 3,
|
||||
},
|
||||
label: {
|
||||
position: 'inside',
|
||||
position: "inside",
|
||||
fontsize: 36,
|
||||
fontWeight: 'bold'
|
||||
fontWeight: "bold",
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
show: false,
|
||||
},
|
||||
data: [
|
||||
(props.ud.flames ? {
|
||||
value: props.ud.flames ? props.ud.flames : null,
|
||||
name: 'Flames',
|
||||
itemStyle: {
|
||||
color: '#FF4343FF'
|
||||
}
|
||||
} : {}),
|
||||
(props.ud.he ? {
|
||||
value: props.ud.he ? props.ud.he : null,
|
||||
name: 'HE',
|
||||
itemStyle: {
|
||||
color: '#62c265'
|
||||
}
|
||||
} : {})
|
||||
,
|
||||
(props.ud.flash ? {
|
||||
value: props.ud.flash ? props.ud.flash : null,
|
||||
name: 'Flash',
|
||||
itemStyle: {
|
||||
color: '#18cff3'
|
||||
}
|
||||
} : {}),
|
||||
(props.ud.smoke ? {
|
||||
value: props.ud.smoke ? props.ud.smoke : null,
|
||||
name: 'Smoke',
|
||||
itemStyle: {
|
||||
color: '#6e6b78'
|
||||
}
|
||||
} : {}),
|
||||
(props.ud.decoy ? {
|
||||
value: props.ud.decoy ? props.ud.decoy : null,
|
||||
name: 'Decoy',
|
||||
itemStyle: {
|
||||
color: '#e28428'
|
||||
}
|
||||
} : {})
|
||||
]
|
||||
}
|
||||
]
|
||||
props.ud.flames
|
||||
? {
|
||||
value: props.ud.flames ? props.ud.flames : null,
|
||||
name: "Flames",
|
||||
itemStyle: {
|
||||
color: "#FF4343FF",
|
||||
},
|
||||
}
|
||||
: {},
|
||||
props.ud.he
|
||||
? {
|
||||
value: props.ud.he ? props.ud.he : null,
|
||||
name: "HE",
|
||||
itemStyle: {
|
||||
color: "#62c265",
|
||||
},
|
||||
}
|
||||
: {},
|
||||
props.ud.flash
|
||||
? {
|
||||
value: props.ud.flash ? props.ud.flash : null,
|
||||
name: "Flash",
|
||||
itemStyle: {
|
||||
color: "#18cff3",
|
||||
},
|
||||
}
|
||||
: {},
|
||||
props.ud.smoke
|
||||
? {
|
||||
value: props.ud.smoke ? props.ud.smoke : null,
|
||||
name: "Smoke",
|
||||
itemStyle: {
|
||||
color: "#6e6b78",
|
||||
},
|
||||
}
|
||||
: {},
|
||||
props.ud.decoy
|
||||
? {
|
||||
value: props.ud.decoy ? props.ud.decoy : null,
|
||||
name: "Decoy",
|
||||
itemStyle: {
|
||||
color: "#e28428",
|
||||
},
|
||||
}
|
||||
: {},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
})
|
||||
});
|
||||
|
||||
return {props}
|
||||
}
|
||||
}
|
||||
return { props };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -1,61 +1,67 @@
|
||||
<template>
|
||||
<div class="utility-chart-total" v-if="props.stats">
|
||||
<div v-if="props.stats" class="utility-chart-total">
|
||||
<div class="heading">
|
||||
<h4>Total Utility Damage</h4>
|
||||
</div>
|
||||
<div id="utility-chart-total"></div>
|
||||
<hr>
|
||||
<hr />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts/core';
|
||||
import {GridComponent, LegendComponent, TooltipComponent} from 'echarts/components';
|
||||
import {BarChart} from 'echarts/charts';
|
||||
import {CanvasRenderer} from 'echarts/renderers';
|
||||
import {onMounted} from "vue";
|
||||
import * as echarts from "echarts/core";
|
||||
import {
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
TooltipComponent,
|
||||
} from "echarts/components";
|
||||
import { BarChart } from "echarts/charts";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
export default {
|
||||
name: "FlashChart",
|
||||
props: {
|
||||
stats: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const checkStatEmpty = (stat) => {
|
||||
if (stat)
|
||||
return stat
|
||||
else
|
||||
return 0
|
||||
}
|
||||
if (stat) return stat;
|
||||
else return 0;
|
||||
};
|
||||
|
||||
const seriesArr = (stats) => {
|
||||
let arr = []
|
||||
let arr = [];
|
||||
for (let i = 0; i < stats.length; i++) {
|
||||
const sum = checkStatEmpty(stats[i].dmg.ud.flames) + checkStatEmpty(stats[i].dmg.ud.flash) + checkStatEmpty(stats[i].dmg.ud.he) + checkStatEmpty(stats[i].dmg.ud.smoke)
|
||||
const sum =
|
||||
checkStatEmpty(stats[i].dmg.ud.flames) +
|
||||
checkStatEmpty(stats[i].dmg.ud.flash) +
|
||||
checkStatEmpty(stats[i].dmg.ud.he) +
|
||||
checkStatEmpty(stats[i].dmg.ud.smoke);
|
||||
|
||||
if (sum !== 0) {
|
||||
arr.push({
|
||||
name: stats[i].player.name,
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
label: {
|
||||
show: true
|
||||
show: true,
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
focus: "series",
|
||||
},
|
||||
data: [sum]
|
||||
})
|
||||
data: [sum],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
arr.sort((a, b) => parseFloat(b.data[0]) - parseFloat(a.data[0]))
|
||||
arr.sort((a, b) => parseFloat(b.data[0]) - parseFloat(a.data[0]));
|
||||
|
||||
return arr
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
echarts.use([
|
||||
@@ -63,58 +69,62 @@ export default {
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
BarChart,
|
||||
CanvasRenderer
|
||||
CanvasRenderer,
|
||||
]);
|
||||
|
||||
let myChart = echarts.init(document.getElementById('utility-chart-total'), {}, {width: 800, height: 200});
|
||||
let option
|
||||
let myChart = echarts.init(
|
||||
document.getElementById("utility-chart-total"),
|
||||
{},
|
||||
{ width: 800, height: 200 }
|
||||
);
|
||||
let option;
|
||||
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
// Use axis to trigger tooltip
|
||||
type: 'shadow' // 'shadow' as default; can also be 'line'
|
||||
}
|
||||
type: "shadow", // 'shadow' as default; can also be 'line'
|
||||
},
|
||||
},
|
||||
// color: ['#143147', '#39546c', '#617a94', '#89a2bd', '#b3cce8', '#eac65c', '#bd9d2c', '#917501', '#685000', '#412c00'],
|
||||
// color: ['#003470', '#005a9b', '#0982c7', '#4bace5', '#90d3fe', '#febf4a', '#d7931c', '#ac6a01', '#804400', '#572000'],
|
||||
// color: ['#888F98', '#10121A', '#1B2732', '#5F7892', '#C3A235'],
|
||||
legend: {
|
||||
textStyle: {
|
||||
color: 'white'
|
||||
}
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
left: "3%",
|
||||
right: "4%",
|
||||
bottom: "3%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value'
|
||||
type: "value",
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: ['Total']
|
||||
type: "category",
|
||||
data: ["Total"],
|
||||
},
|
||||
aria: {
|
||||
enabled: true,
|
||||
show: true,
|
||||
decal: {
|
||||
show: true
|
||||
}
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
|
||||
series: seriesArr(props.stats)
|
||||
series: seriesArr(props.stats),
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
})
|
||||
});
|
||||
|
||||
return {props}
|
||||
}
|
||||
}
|
||||
return { props };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@@ -1,5 +0,0 @@
|
||||
export const SHARECODE_REGEX = /^CSGO(?:-?[ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789]{5}){5}$/
|
||||
export const AUTHCODE_REGEX = /^[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{4}-[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{5}-[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{4}$/
|
||||
|
||||
export const NAV_HEIGHT = 70
|
||||
export const FOOTER_HEIGHT = 200
|
7
src/constants/index.ts
Normal file
7
src/constants/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const SHARECODE_REGEX =
|
||||
/^CSGO(?:-?[ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789]{5}){5}$/;
|
||||
export const AUTHCODE_REGEX =
|
||||
/^[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{4}-[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{5}-[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{4}$/;
|
||||
|
||||
export const NAV_HEIGHT = 70;
|
||||
export const FOOTER_HEIGHT = 200;
|
24
src/main.js
24
src/main.js
@@ -1,24 +0,0 @@
|
||||
import {createApp} from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import 'bootstrap'
|
||||
import '@/scss/custom.scss'
|
||||
import VueMatomo from 'vue-matomo'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(store)
|
||||
app.use(router)
|
||||
|
||||
if (process.env.VUE_APP_TRACKING) {
|
||||
app.use(
|
||||
VueMatomo, {
|
||||
host: process.env.VUE_APP_TRACK_URL,
|
||||
siteId: process.env.VUE_APP_TRACK_ID,
|
||||
router: router,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
app.mount('#app')
|
31
src/main.ts
Normal file
31
src/main.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { createApp } from "vue";
|
||||
//import { createPinia } from 'pinia'
|
||||
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import VueMatomo from "vue-matomo";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import store from "./store";
|
||||
|
||||
import "bootstrap";
|
||||
import "bootstrap-icons/font/bootstrap-icons.css";
|
||||
import "/src/scss/custom.scss";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
//app.use(createPinia())
|
||||
app.use(router);
|
||||
app.use(store);
|
||||
|
||||
if (import.meta.env.VITE_TRACKING) {
|
||||
app.use(VueMatomo, {
|
||||
host: import.meta.env.VITE_TRACK_URL,
|
||||
siteId: import.meta.env.VITE_TRACK_ID,
|
||||
router: router,
|
||||
});
|
||||
}
|
||||
|
||||
app.mount("#app");
|
@@ -1,130 +0,0 @@
|
||||
import {createRouter, createWebHistory} from 'vue-router'
|
||||
|
||||
function lazyLoadView(view) {
|
||||
return () => import(`@/views/${view}.vue`)
|
||||
}
|
||||
|
||||
function lazyLoadComponent(view) {
|
||||
return () => import(`@/components/${view}.vue`)
|
||||
}
|
||||
|
||||
function lazyLoadErrorPages(view) {
|
||||
return () => import(`@/views/errorPages/${view}.vue`)
|
||||
}
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
components: {
|
||||
main: lazyLoadView('Home')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/privacy-policy',
|
||||
name: 'PrivacyPolicy',
|
||||
components: {
|
||||
main: lazyLoadView('PrivacyPolicy')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/matches',
|
||||
name: 'Explore',
|
||||
components: {
|
||||
main: lazyLoadView('Explore')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/player/:id',
|
||||
name: 'Player',
|
||||
components: {
|
||||
main: lazyLoadView('Player'),
|
||||
},
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/match/:match_id',
|
||||
name: 'Match',
|
||||
components: {
|
||||
main: lazyLoadView('Match')
|
||||
},
|
||||
props: true,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
components: {
|
||||
score: lazyLoadComponent('ScoreTeam')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'economy',
|
||||
components: {
|
||||
score: lazyLoadComponent('EqValueGraph')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'details',
|
||||
components: {
|
||||
score: lazyLoadComponent('Details')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'flashes',
|
||||
components: {
|
||||
score: lazyLoadComponent('FlashChart')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'damage',
|
||||
components: {
|
||||
score: lazyLoadComponent('DamageSite')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'chat',
|
||||
components: {
|
||||
score: lazyLoadComponent('MatchChatHistory')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
name: '404',
|
||||
components: {
|
||||
main: lazyLoadErrorPages('404')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/500',
|
||||
name: '500',
|
||||
components: {
|
||||
main: lazyLoadErrorPages('500')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/502',
|
||||
name: '502',
|
||||
components: {
|
||||
main: lazyLoadErrorPages('502')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
redirect: '/'
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(process.env.BASE_URL),
|
||||
routes,
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
return {x: 0, y: 0}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
130
src/router/index.ts
Normal file
130
src/router/index.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
|
||||
function lazyLoadView(view: string) {
|
||||
return () => import(`/src/views/${view}.vue`);
|
||||
}
|
||||
|
||||
function lazyLoadErrorPages(view: string) {
|
||||
return () => import(`/src/views/errorPages/${view}.vue`);
|
||||
}
|
||||
|
||||
function lazyLoadComponent(view: string) {
|
||||
return () => import(`/src/components/${view}.vue`);
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
name: "Home",
|
||||
components: {
|
||||
main: lazyLoadView("HomeView"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/privacy-policy",
|
||||
name: "PrivacyPolicy",
|
||||
components: {
|
||||
main: lazyLoadView("PrivacyPolicy"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/matches",
|
||||
name: "Explore",
|
||||
components: {
|
||||
main: lazyLoadView("ExploreView"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/player/:id",
|
||||
name: "Player",
|
||||
components: {
|
||||
main: lazyLoadView("PlayerView"),
|
||||
},
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "/match/:match_id",
|
||||
name: "Match",
|
||||
components: {
|
||||
main: lazyLoadView("MatchView"),
|
||||
},
|
||||
props: true,
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
components: {
|
||||
score: lazyLoadComponent("ScoreTeam"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "economy",
|
||||
components: {
|
||||
score: lazyLoadComponent("EqValueGraph"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "details",
|
||||
components: {
|
||||
score: lazyLoadComponent("DetailsComponent"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "flashes",
|
||||
components: {
|
||||
score: lazyLoadComponent("FlashChart"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "damage",
|
||||
components: {
|
||||
score: lazyLoadComponent("DamageSite"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "chat",
|
||||
components: {
|
||||
score: lazyLoadComponent("MatchChatHistory"),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/404",
|
||||
name: "404",
|
||||
components: {
|
||||
main: lazyLoadErrorPages("404Page"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/500",
|
||||
name: "500",
|
||||
components: {
|
||||
main: lazyLoadErrorPages("500Page"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/502",
|
||||
name: "502",
|
||||
components: {
|
||||
main: lazyLoadErrorPages("502Page"),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
redirect: "/",
|
||||
},
|
||||
],
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
} else {
|
||||
return { x: 0, y: 0 };
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default router;
|
@@ -2,6 +2,7 @@
|
||||
cursor: help;
|
||||
text-decoration: underline dotted grey;
|
||||
}
|
||||
|
||||
.helpicon {
|
||||
cursor: help;
|
||||
}
|
||||
@@ -19,7 +20,12 @@
|
||||
}
|
||||
|
||||
.placeholder-wave-alt {
|
||||
mask-image: linear-gradient(130deg, black 55%, rgba(0, 0, 0, (1 - 0.2)) 75%, black 95%);
|
||||
mask-image: linear-gradient(
|
||||
130deg,
|
||||
black 55%,
|
||||
rgba(0, 0, 0, (1 - 0.2)) 75%,
|
||||
black 95%
|
||||
);
|
||||
mask-size: 200% 100%;
|
||||
animation: placeholder-wave-alt 2.5s linear infinite;
|
||||
}
|
||||
|
@@ -2,38 +2,37 @@
|
||||
//@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');
|
||||
@font-face {
|
||||
font-family: "OpenSans";
|
||||
src: local('OpenSans'),
|
||||
url("/fonts/OpenSans-VariableFont_wdth,wght.woff2") format("woff2"),
|
||||
url("/fonts/OpenSans-VariableFont_wdth,wght.ttf") format("truetype");
|
||||
src: local("OpenSans"),
|
||||
url("/fonts/OpenSans-VariableFont_wdth,wght.woff2") format("woff2"),
|
||||
url("/fonts/OpenSans-VariableFont_wdth,wght.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "OpenSansItalic";
|
||||
src: local('OpenSansItalic'),
|
||||
url("/fonts/OpenSans-Italic-VariableFont_wdth,wght.woff2") format("woff2"),
|
||||
url("/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf") format("truetype");
|
||||
src: local("OpenSansItalic"),
|
||||
url("/fonts/OpenSans-Italic-VariableFont_wdth,wght.woff2") format("woff2"),
|
||||
url("/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "CSRegular";
|
||||
src: local('CSRegular'),
|
||||
url("/fonts/cs_regular.woff2") format("woff2"),
|
||||
url("/fonts/cs_regular.ttf") format("truetype");
|
||||
src: local("CSRegular"), url("/fonts/cs_regular.woff2") format("woff2"),
|
||||
url("/fonts/cs_regular.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Orbitron";
|
||||
src: local('Orbitron'),
|
||||
url("/fonts/Orbitron-VariableFont_wght.woff2") format("woff2"),
|
||||
url("/fonts/Orbitron-VariableFont_wght.ttf") format("truetype");
|
||||
src: local("Orbitron"),
|
||||
url("/fonts/Orbitron-VariableFont_wght.woff2") format("woff2"),
|
||||
url("/fonts/Orbitron-VariableFont_wght.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
// Default variable overrides
|
||||
$font-family-base: 'OpenSans';
|
||||
$font-family-base: "OpenSans";
|
||||
$body-color: white;
|
||||
|
||||
$primary: #888f98;
|
||||
@@ -59,11 +58,11 @@ $success: #609926;
|
||||
|
||||
:root {
|
||||
// CSGO COLORS
|
||||
--csgo-orange: #FE9A28;
|
||||
--csgo-blue: #5BA7FE;
|
||||
--csgo-yellow: #F7F52F;
|
||||
--csgo-purple: #A01BEF;
|
||||
--csgo-green: #04B462;
|
||||
--csgo-orange: #fe9a28;
|
||||
--csgo-blue: #5ba7fe;
|
||||
--csgo-yellow: #f7f52f;
|
||||
--csgo-purple: #a01bef;
|
||||
--csgo-green: #04b462;
|
||||
--csgo-grey: #5a5a5a;
|
||||
}
|
||||
|
||||
|
@@ -1,66 +1,63 @@
|
||||
import { createStore } from 'vuex'
|
||||
import { createStore } from "vuex";
|
||||
|
||||
export default createStore({
|
||||
state: {
|
||||
id64: '',
|
||||
vanityUrl: '',
|
||||
id64: "",
|
||||
vanityUrl: "",
|
||||
matchDetails: {},
|
||||
playerDetails: {},
|
||||
playersArr: [],
|
||||
scroll_state: 0,
|
||||
info: []
|
||||
info: [],
|
||||
},
|
||||
mutations: {
|
||||
changeId64(state, payload) {
|
||||
state.id64 = payload.id
|
||||
state.id64 = payload.id;
|
||||
},
|
||||
changeVanityUrl(state, payload) {
|
||||
state.vanityUrl = payload.id
|
||||
state.vanityUrl = payload.id;
|
||||
},
|
||||
changeMatchDetails(state, payload) {
|
||||
state.matchDetails = payload.data
|
||||
state.matchDetails = payload.data;
|
||||
},
|
||||
changePlayerDetails(state, payload) {
|
||||
state.playerDetails = payload.data
|
||||
state.playerDetails = payload.data;
|
||||
},
|
||||
changePlayersArr(state, payload) {
|
||||
state.playersArr = payload.data
|
||||
state.playersArr = payload.data;
|
||||
},
|
||||
changeScrollState(state, payload) {
|
||||
state.scroll_state = payload
|
||||
state.scroll_state = payload;
|
||||
},
|
||||
changeInfoState(state, payload) {
|
||||
state.info.push(payload.data)
|
||||
state.info.push(payload.data);
|
||||
},
|
||||
resetId64(state) {
|
||||
state.id64 = ''
|
||||
state.id64 = "";
|
||||
},
|
||||
resetVanityUrl(state) {
|
||||
state.vanityUrl = ''
|
||||
state.vanityUrl = "";
|
||||
},
|
||||
resetMatchDetails(state) {
|
||||
state.matchDetails = {}
|
||||
state.matchDetails = {};
|
||||
},
|
||||
resetPlayerDetails(state) {
|
||||
state.playerDetails = {}
|
||||
state.playerDetails = {};
|
||||
},
|
||||
resetPlayersArr(state) {
|
||||
state.playersArr = []
|
||||
state.playersArr = [];
|
||||
},
|
||||
resetScrollState(state) {
|
||||
state.scroll_state = 0
|
||||
state.scroll_state = 0;
|
||||
},
|
||||
resetInfoState(state) {
|
||||
state.info = []
|
||||
state.info = [];
|
||||
},
|
||||
removeInfoState(state, id) {
|
||||
state.info.splice(id, 1)
|
||||
}
|
||||
state.info.splice(id, 1);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
},
|
||||
modules: {
|
||||
},
|
||||
getters: {
|
||||
}
|
||||
})
|
||||
actions: {},
|
||||
modules: {},
|
||||
getters: {},
|
||||
});
|
||||
|
16
src/stores/counter.ts
Normal file
16
src/stores/counter.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore({
|
||||
id: 'counter',
|
||||
state: () => ({
|
||||
counter: 0
|
||||
}),
|
||||
getters: {
|
||||
doubleCount: (state) => state.counter * 2
|
||||
},
|
||||
actions: {
|
||||
increment() {
|
||||
this.counter++
|
||||
}
|
||||
}
|
||||
})
|
@@ -1,450 +1,444 @@
|
||||
import axios from "axios";
|
||||
import {StatusCodes as STATUS} from "http-status-codes";
|
||||
import {AUTHCODE_REGEX, SHARECODE_REGEX} from "@/constants";
|
||||
import { StatusCodes as STATUS } from "http-status-codes";
|
||||
import { AUTHCODE_REGEX, SHARECODE_REGEX } from "/src/constants";
|
||||
|
||||
const API_URL = process.env.VUE_APP_API_URL
|
||||
const API_URL = import.meta.env.VITE_API_URL;
|
||||
|
||||
// /player/<id> GET returns player <id> details (last 10 matches)
|
||||
export const GetUser = async (store, id) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/player/${id}`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Bad request'
|
||||
break
|
||||
message = "Bad request";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Player not found'
|
||||
break
|
||||
message = "Player not found";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to get meta-stats or player'
|
||||
break
|
||||
message = "Unable to get meta-stats or player";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /player/<id>/meta/<limit> GET returns player <id> meta-stats with <limit>
|
||||
export const GetPlayerMeta = async (store, player_id, limit = 4) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/player/${player_id}/meta/${limit}`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Bad request'
|
||||
break
|
||||
message = "Bad request";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Player not found'
|
||||
break
|
||||
message = "Player not found";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to get player meta'
|
||||
break
|
||||
message = "Unable to get player meta";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /player/<id>/next/<unix> GET returns 20 matches after <unix> for player <id>
|
||||
export const LoadMoreMatches = async (store, player_id, date) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/player/${player_id}/next/${date}`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Bad request'
|
||||
break
|
||||
message = "Bad request";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Player not found'
|
||||
break
|
||||
message = "Player not found";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to get meta-stats or player'
|
||||
break
|
||||
message = "Unable to get meta-stats or player";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /player/<id>/track POST Track player <id> FORM_DATA: authcode, [sharecode]
|
||||
export const TrackMe = async (store, id64, authcode, sharecode = '') => {
|
||||
let status = null
|
||||
let message = ''
|
||||
export const TrackMe = async (store, id64, authcode, sharecode = "") => {
|
||||
let status = null;
|
||||
let message = "";
|
||||
|
||||
if (sharecode !== '' && !SHARECODE_REGEX.test(sharecode)) {
|
||||
status = STATUS.IM_A_TEAPOT
|
||||
message = 'Sharecode is invalid'
|
||||
if (sharecode !== "" && !SHARECODE_REGEX.test(sharecode)) {
|
||||
status = STATUS.IM_A_TEAPOT;
|
||||
message = "Sharecode is invalid";
|
||||
}
|
||||
if (authcode === '' || !AUTHCODE_REGEX.test(authcode.toUpperCase())) {
|
||||
status = STATUS.IM_A_TEAPOT
|
||||
message = 'Authcode is invalid'
|
||||
if (authcode === "" || !AUTHCODE_REGEX.test(authcode.toUpperCase())) {
|
||||
status = STATUS.IM_A_TEAPOT;
|
||||
message = "Authcode is invalid";
|
||||
}
|
||||
|
||||
if (status === null && message === '') {
|
||||
if (status === null && message === "") {
|
||||
await axios
|
||||
.post(`${API_URL}/player/${id64}/track`, `authcode=${authcode.toUpperCase()}&sharecode=${sharecode}`)
|
||||
.post(
|
||||
`${API_URL}/player/${id64}/track`,
|
||||
`authcode=${authcode.toUpperCase()}&sharecode=${sharecode}`
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.ACCEPTED) {
|
||||
status = STATUS.ACCEPTED
|
||||
message = 'Tracking successful'
|
||||
status = STATUS.ACCEPTED;
|
||||
message = "Tracking successful";
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Invalid arguments'
|
||||
break
|
||||
message = "Invalid arguments";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Player not found'
|
||||
break
|
||||
message = "Player not found";
|
||||
break;
|
||||
case STATUS.SERVICE_UNAVAILABLE:
|
||||
message = 'Service currently unavailable - Please try again later'
|
||||
break
|
||||
message = "Service currently unavailable - Please try again later";
|
||||
break;
|
||||
case STATUS.UNAUTHORIZED:
|
||||
message = 'Authcode is invalid'
|
||||
break
|
||||
message = "Authcode is invalid";
|
||||
break;
|
||||
case STATUS.PRECONDITION_FAILED:
|
||||
message = 'Sharecode is invalid or missing'
|
||||
break
|
||||
message = "Sharecode is invalid or missing";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Service is currently unavailable - Please try again later'
|
||||
break
|
||||
message =
|
||||
"Service is currently unavailable - Please try again later";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
status = err.response.status
|
||||
})
|
||||
status = err.response.status;
|
||||
});
|
||||
}
|
||||
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
return status
|
||||
}
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
return status;
|
||||
};
|
||||
|
||||
// /match/<id> GET returns details for match <id>
|
||||
export const GetMatchDetails = async (store, match_id) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/match/${match_id}`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Error parsing matchID'
|
||||
break
|
||||
message = "Error parsing matchID";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Match not found'
|
||||
break
|
||||
message = "Match not found";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to get match data'
|
||||
break
|
||||
message = "Unable to get match data";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /match/<id>/rounds GET returns round-stats for match <id>
|
||||
export const GetPlayerValue = async (store, match_id) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/match/${match_id}/rounds`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Error parsing matchID'
|
||||
break
|
||||
message = "Error parsing matchID";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Match not found'
|
||||
break
|
||||
message = "Match not found";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to get match data'
|
||||
break
|
||||
message = "Unable to get match data";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /match/<id>/weapons GET returns weapon-stats for match <id>
|
||||
export const GetWeaponDmg = async (store, match_id) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/match/${match_id}/weapons`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Bad request'
|
||||
break
|
||||
message = "Bad request";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Weapon damage not found'
|
||||
break
|
||||
message = "Weapon damage not found";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to get weapon damage'
|
||||
break
|
||||
message = "Unable to get weapon damage";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /match/<id>/chat GET returns chat history for match <id>
|
||||
export const GetChatHistory = async (store, match_id) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/match/${match_id}/chat`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Bad request'
|
||||
break
|
||||
message = "Bad request";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Weapon damage not found'
|
||||
break
|
||||
message = "Weapon damage not found";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to get weapon damage'
|
||||
break
|
||||
message = "Unable to get weapon damage";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /matches/<id>/chat/<langCode> GET returns chat history for match <id> with translated sections
|
||||
export const GetChatHistoryTranslated = async (store, match_id) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/match/${match_id}/chat?translate=1`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Bad request'
|
||||
break
|
||||
message = "Bad request";
|
||||
break;
|
||||
case STATUS.NOT_FOUND:
|
||||
message = 'Chat was not found'
|
||||
break
|
||||
message = "Chat was not found";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to get chat'
|
||||
break
|
||||
message = "Unable to get chat";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /matches GET returns last 20 matches in DB
|
||||
export const GetMatches = async (store) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/matches`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Bad request'
|
||||
break
|
||||
message = "Bad request";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to marshal JSON'
|
||||
break
|
||||
message = "Unable to marshal JSON";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
// /matches/next/<unix> GET returns 20 matches after time <unix>
|
||||
export const LoadMoreMatchesExplore = async (store, date) => {
|
||||
let response = null
|
||||
let response = null;
|
||||
|
||||
await axios
|
||||
.get(`${API_URL}/matches/next/${date}`)
|
||||
.then((res) => {
|
||||
if (res.status === STATUS.OK)
|
||||
response = res.data
|
||||
if (res.status === STATUS.OK) response = res.data;
|
||||
})
|
||||
.catch((err) => {
|
||||
let message = ''
|
||||
let message = "";
|
||||
|
||||
switch (err.response.status) {
|
||||
case STATUS.BAD_REQUEST:
|
||||
message = 'Bad request'
|
||||
break
|
||||
message = "Bad request";
|
||||
break;
|
||||
case STATUS.INTERNAL_SERVER_ERROR:
|
||||
message = 'Unable to load more matches'
|
||||
break
|
||||
message = "Unable to load more matches";
|
||||
break;
|
||||
default:
|
||||
message = 'An unknown error occurred'
|
||||
message = "An unknown error occurred";
|
||||
}
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: err.response.status,
|
||||
message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
})
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
@@ -1,69 +1,77 @@
|
||||
import {DateTime, Duration} from "luxon/build/es6/luxon";
|
||||
import { DateTime, Duration } from "luxon/build/es6/luxon";
|
||||
|
||||
export const ConvertTickToTime = (tick, rate = 64) => {
|
||||
const time = Duration.fromObject({hours: 0, minutes: 0, seconds: tick / rate || 0})
|
||||
const time = Duration.fromObject({
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
seconds: tick / rate || 0,
|
||||
});
|
||||
|
||||
if (time.hours > 1)
|
||||
return time.toFormat('hh:mm:ss')
|
||||
else if (time.hours < 1)
|
||||
return time.toFormat('mm:ss')
|
||||
}
|
||||
if (time.hours > 1) return time.toFormat("hh:mm:ss");
|
||||
else if (time.hours < 1) return time.toFormat("mm:ss");
|
||||
};
|
||||
|
||||
export const FormatDuration = (d) => {
|
||||
const duration = Duration.fromObject({hours: 0, minutes: 0, seconds: d}).normalize().toObject()
|
||||
const duration = Duration.fromObject({ hours: 0, minutes: 0, seconds: d })
|
||||
.normalize()
|
||||
.toObject();
|
||||
|
||||
if (duration.hours > 1)
|
||||
return `${duration.hours} h ${duration.minutes} min`
|
||||
else if (duration.hours < 1)
|
||||
return `${duration.minutes} min`
|
||||
}
|
||||
if (duration.hours > 1) return `${duration.hours} h ${duration.minutes} min`;
|
||||
else if (duration.hours < 1) return `${duration.minutes} min`;
|
||||
};
|
||||
|
||||
export const FormatFullDuration = (d) => {
|
||||
const duration = Duration.fromObject({hours: 0, minutes: 0, seconds: d}).normalize()
|
||||
const duration = Duration.fromObject({
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
seconds: d,
|
||||
}).normalize();
|
||||
|
||||
if (duration.hours > 1)
|
||||
return duration.toFormat('hh:mm:ss')
|
||||
else if (duration.hours < 1)
|
||||
return duration.toFormat('mm:ss')
|
||||
}
|
||||
if (duration.hours > 1) return duration.toFormat("hh:mm:ss");
|
||||
else if (duration.hours < 1) return duration.toFormat("mm:ss");
|
||||
};
|
||||
|
||||
export const FormatDate = (date) => {
|
||||
const matchDate = DateTime.fromSeconds(date || 0)
|
||||
const diff = DateTime.now().diff(matchDate)
|
||||
const matchDate = DateTime.fromSeconds(date || 0);
|
||||
const diff = DateTime.now().diff(matchDate);
|
||||
|
||||
if (diff.as('days') > 8)
|
||||
return matchDate.toLocaleString({weekday: 'short', day: '2-digit', month: '2-digit', year: 'numeric'})
|
||||
else
|
||||
return matchDate.toRelative()
|
||||
}
|
||||
if (diff.as("days") > 8)
|
||||
return matchDate.toLocaleString({
|
||||
weekday: "short",
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
else return matchDate.toRelative();
|
||||
};
|
||||
|
||||
export const FormatFullDate = (date) => {
|
||||
const matchDate = DateTime.fromSeconds(date || 0)
|
||||
const matchDate = DateTime.fromSeconds(date || 0);
|
||||
|
||||
return matchDate.toLocaleString({
|
||||
weekday: 'short',
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
})
|
||||
}
|
||||
weekday: "short",
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
});
|
||||
};
|
||||
|
||||
export const FormatVacDate = (date, match) => {
|
||||
const vacDate = DateTime.fromSeconds(date || 0)
|
||||
const matchDate = DateTime.fromSeconds(match || 0)
|
||||
const vacDate = DateTime.fromSeconds(date || 0);
|
||||
const matchDate = DateTime.fromSeconds(match || 0);
|
||||
|
||||
if (vacDate.diff(matchDate).as('days') >= -30) {
|
||||
return vacDate.toRelative()
|
||||
if (vacDate.diff(matchDate).as("days") >= -30) {
|
||||
return vacDate.toRelative();
|
||||
} else {
|
||||
return ''
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const MatchNotParsedTime = (match) => {
|
||||
const matchDate = DateTime.fromSeconds(match || 0)
|
||||
const matchDate = DateTime.fromSeconds(match || 0);
|
||||
|
||||
return matchDate.diffNow().as('hours') >= -2;
|
||||
}
|
||||
return matchDate.diffNow().as("hours") >= -2;
|
||||
};
|
||||
|
@@ -1,89 +1,89 @@
|
||||
export const DisplayRank = (rankNr = 0) => {
|
||||
const rankMap = new Map([
|
||||
[0, 'Unranked'],
|
||||
[1, 'Silver I'],
|
||||
[2, 'Silver II'],
|
||||
[3, 'Silver III'],
|
||||
[4, 'Silver IV'],
|
||||
[5, 'Silver Elite'],
|
||||
[6, 'Silver Elite Master'],
|
||||
[7, 'Gold Nova I'],
|
||||
[8, 'Gold Nova II'],
|
||||
[9, 'Gold Nova III'],
|
||||
[10, 'Gold Nova IV'],
|
||||
[11, 'Master Guardian I'],
|
||||
[12, 'Master Guardian II'],
|
||||
[13, 'Master Guardian Elite'],
|
||||
[14, 'Distinguished Master Guardian'],
|
||||
[15, 'Legendary Eagle'],
|
||||
[16, 'Legendary Eagle Master'],
|
||||
[17, 'Supreme Master First Class'],
|
||||
[18, 'Global Elite'],
|
||||
])
|
||||
return [`/images/rank_icons/skillgroup${rankNr}.svg`, rankMap.get(rankNr)]
|
||||
}
|
||||
const rankMap = new Map([
|
||||
[0, "Unranked"],
|
||||
[1, "Silver I"],
|
||||
[2, "Silver II"],
|
||||
[3, "Silver III"],
|
||||
[4, "Silver IV"],
|
||||
[5, "Silver Elite"],
|
||||
[6, "Silver Elite Master"],
|
||||
[7, "Gold Nova I"],
|
||||
[8, "Gold Nova II"],
|
||||
[9, "Gold Nova III"],
|
||||
[10, "Gold Nova IV"],
|
||||
[11, "Master Guardian I"],
|
||||
[12, "Master Guardian II"],
|
||||
[13, "Master Guardian Elite"],
|
||||
[14, "Distinguished Master Guardian"],
|
||||
[15, "Legendary Eagle"],
|
||||
[16, "Legendary Eagle Master"],
|
||||
[17, "Supreme Master First Class"],
|
||||
[18, "Global Elite"],
|
||||
]);
|
||||
return [`/images/rank_icons/skillgroup${rankNr}.svg`, rankMap.get(rankNr)];
|
||||
};
|
||||
|
||||
export const DisplayWeapon = (weaponId) => {
|
||||
const wepaonMap = new Map([
|
||||
[1, 'p2000'],
|
||||
[2, 'glock'],
|
||||
[3, 'p250'],
|
||||
[4, 'deagle'],
|
||||
[5, 'fiveseven'],
|
||||
[6, 'elite'],
|
||||
[7, 'tec9'],
|
||||
[8, 'cz75a'],
|
||||
[9, 'usp_silencer'],
|
||||
[10, 'revolver'],
|
||||
[1, "p2000"],
|
||||
[2, "glock"],
|
||||
[3, "p250"],
|
||||
[4, "deagle"],
|
||||
[5, "fiveseven"],
|
||||
[6, "elite"],
|
||||
[7, "tec9"],
|
||||
[8, "cz75a"],
|
||||
[9, "usp_silencer"],
|
||||
[10, "revolver"],
|
||||
|
||||
[101, 'mp7'],
|
||||
[102, 'mp9'],
|
||||
[103, 'bizon'],
|
||||
[104, 'mac10'],
|
||||
[105, 'ump45'],
|
||||
[106, 'p90'],
|
||||
[107, 'mp5sd'],
|
||||
[101, "mp7"],
|
||||
[102, "mp9"],
|
||||
[103, "bizon"],
|
||||
[104, "mac10"],
|
||||
[105, "ump45"],
|
||||
[106, "p90"],
|
||||
[107, "mp5sd"],
|
||||
|
||||
[201, 'sawedoff'],
|
||||
[202, 'nova'],
|
||||
[203, 'mag7'],
|
||||
[204, 'xm1014'],
|
||||
[205, 'm249'],
|
||||
[206, 'negev'],
|
||||
[201, "sawedoff"],
|
||||
[202, "nova"],
|
||||
[203, "mag7"],
|
||||
[204, "xm1014"],
|
||||
[205, "m249"],
|
||||
[206, "negev"],
|
||||
|
||||
[301, 'galilar'],
|
||||
[302, 'famas'],
|
||||
[303, 'ak47'],
|
||||
[304, 'm4a1'],
|
||||
[305, 'm4a1_silencer'],
|
||||
[306, 'ssg08'],
|
||||
[307, 'sg556'],
|
||||
[308, 'aug'],
|
||||
[309, 'awp'],
|
||||
[310, 'scar20'],
|
||||
[311, 'g3sg1'],
|
||||
])
|
||||
if (wepaonMap.get(weaponId)){
|
||||
return `/images/weapons/${wepaonMap.get(weaponId)}.svg`
|
||||
[301, "galilar"],
|
||||
[302, "famas"],
|
||||
[303, "ak47"],
|
||||
[304, "m4a1"],
|
||||
[305, "m4a1_silencer"],
|
||||
[306, "ssg08"],
|
||||
[307, "sg556"],
|
||||
[308, "aug"],
|
||||
[309, "awp"],
|
||||
[310, "scar20"],
|
||||
[311, "g3sg1"],
|
||||
]);
|
||||
if (wepaonMap.get(weaponId)) {
|
||||
return `/images/weapons/${wepaonMap.get(weaponId)}.svg`;
|
||||
} else {
|
||||
weaponId
|
||||
weaponId;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const LoadImage = (mapName) => {
|
||||
let img = new Image()
|
||||
let background = document.querySelector('.bg-img')
|
||||
let img = new Image();
|
||||
let background = document.querySelector(".bg-img");
|
||||
|
||||
img.onload = function() {
|
||||
if (background) {
|
||||
background.src = img.src
|
||||
}
|
||||
img.onload = function () {
|
||||
if (background) {
|
||||
background.src = img.src;
|
||||
}
|
||||
};
|
||||
|
||||
img.onerror = function () {
|
||||
img.src = `/images/map_screenshots/${mapName}.jpg`
|
||||
img.onerror = null
|
||||
}
|
||||
img.onerror = function () {
|
||||
img.src = `/images/map_screenshots/${mapName}.jpg`;
|
||||
img.onerror = null;
|
||||
};
|
||||
|
||||
img.src = `/images/map_screenshots/${mapName}.webp`
|
||||
}
|
||||
img.src = `/images/map_screenshots/${mapName}.webp`;
|
||||
};
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import router from "../router";
|
||||
|
||||
export const GoToMatch = (id) => {
|
||||
router.push({name: 'Match', params: {match_id: id}})
|
||||
}
|
||||
router.push({ name: "Match", params: { match_id: id } });
|
||||
};
|
||||
|
||||
export const GoToPlayer = (id) => {
|
||||
router.push({name: 'Player', params: {id: id}})
|
||||
}
|
||||
router.push({ name: "Player", params: { id: id } });
|
||||
};
|
||||
|
||||
export const GoToError = (code) => {
|
||||
router.push({name: code})
|
||||
}
|
||||
router.push({ name: code });
|
||||
};
|
||||
|
||||
export const GoToLink = (link) => {
|
||||
router.replace(link)
|
||||
}
|
||||
router.replace(link);
|
||||
};
|
||||
|
@@ -1,12 +1,24 @@
|
||||
export const GetHLTV_1 = (kills = 0, rounds, deaths = 0, k2 = 0, k3 = 0, k4 = 0, k5 = 0) => {
|
||||
const k1 = kills - k2 - k3 - k4 - k5
|
||||
const Weight_KPR = 0.679 // weight kills per round
|
||||
const Weight_SPR = 0.317 // weight survived rounds per round
|
||||
const Weight_RMK = 1.277 // weight value calculated from rounds with multiple kills (1k + 4*2k + 9*3k + 16*4k + 25*5k)
|
||||
export const GetHLTV_1 = (
|
||||
kills = 0,
|
||||
rounds,
|
||||
deaths = 0,
|
||||
k2 = 0,
|
||||
k3 = 0,
|
||||
k4 = 0,
|
||||
k5 = 0
|
||||
) => {
|
||||
const k1 = kills - k2 - k3 - k4 - k5;
|
||||
const Weight_KPR = 0.679; // weight kills per round
|
||||
const Weight_SPR = 0.317; // weight survived rounds per round
|
||||
const Weight_RMK = 1.277; // weight value calculated from rounds with multiple kills (1k + 4*2k + 9*3k + 16*4k + 25*5k)
|
||||
|
||||
const KillRating = kills / rounds / Weight_KPR
|
||||
const SurvivalRating = (rounds - deaths) / rounds / Weight_SPR
|
||||
const RoundsWithMultipleKillsRating = (k1 + 4 * k2 + 9 * k3 + 16 * k4 + 25 * k5) / rounds / Weight_RMK
|
||||
const KillRating = kills / rounds / Weight_KPR;
|
||||
const SurvivalRating = (rounds - deaths) / rounds / Weight_SPR;
|
||||
const RoundsWithMultipleKillsRating =
|
||||
(k1 + 4 * k2 + 9 * k3 + 16 * k4 + 25 * k5) / rounds / Weight_RMK;
|
||||
|
||||
return ((KillRating + 0.7 * SurvivalRating + RoundsWithMultipleKillsRating) / 2.7).toFixed(2)
|
||||
}
|
||||
return (
|
||||
(KillRating + 0.7 * SurvivalRating + RoundsWithMultipleKillsRating) /
|
||||
2.7
|
||||
).toFixed(2);
|
||||
};
|
||||
|
@@ -1,25 +1,31 @@
|
||||
export const SaveLastVisitedToLocalStorage = (data) => {
|
||||
let a = JSON.parse(localStorage.getItem('recent-visited')) || [];
|
||||
let a = JSON.parse(localStorage.getItem("recent-visited")) || [];
|
||||
|
||||
if (a.length === 0) {
|
||||
a.unshift(data);
|
||||
} else if (a.length === 9) {
|
||||
if (a.find(p => p.steamid64 === data.steamid64)) {
|
||||
a.shift()
|
||||
a.splice(a.findIndex(i => i.steamid64 === data.steamid64), 1)
|
||||
a.unshift(data)
|
||||
} else if (!a.find(p => p.steamid64 === data.steamid64)) {
|
||||
a.shift()
|
||||
a.unshift(data)
|
||||
}
|
||||
} else if (a.length > 0 && a.length < 9) {
|
||||
if (a.find(p => p.steamid64 === data.steamid64)) {
|
||||
a.splice(a.findIndex(i => i.steamid64 === data.steamid64), 1)
|
||||
a.unshift(data)
|
||||
} else if (!a.find(p => p.steamid64 === data.steamid64)) {
|
||||
a.unshift(data)
|
||||
}
|
||||
if (a.length === 0) {
|
||||
a.unshift(data);
|
||||
} else if (a.length === 9) {
|
||||
if (a.find((p) => p.steamid64 === data.steamid64)) {
|
||||
a.shift();
|
||||
a.splice(
|
||||
a.findIndex((i) => i.steamid64 === data.steamid64),
|
||||
1
|
||||
);
|
||||
a.unshift(data);
|
||||
} else if (!a.find((p) => p.steamid64 === data.steamid64)) {
|
||||
a.shift();
|
||||
a.unshift(data);
|
||||
}
|
||||
} else if (a.length > 0 && a.length < 9) {
|
||||
if (a.find((p) => p.steamid64 === data.steamid64)) {
|
||||
a.splice(
|
||||
a.findIndex((i) => i.steamid64 === data.steamid64),
|
||||
1
|
||||
);
|
||||
a.unshift(data);
|
||||
} else if (!a.find((p) => p.steamid64 === data.steamid64)) {
|
||||
a.unshift(data);
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem('recent-visited', JSON.stringify(a));
|
||||
}
|
||||
localStorage.setItem("recent-visited", JSON.stringify(a));
|
||||
};
|
||||
|
@@ -1,127 +1,125 @@
|
||||
import {GoToError} from "@/utils/GoTo";
|
||||
import { GoToError } from "/src/utils/GoTo";
|
||||
|
||||
export const errorHandling = (code) => {
|
||||
if (code === 404) {
|
||||
GoToError('404')
|
||||
GoToError("404");
|
||||
} else if (code === 500) {
|
||||
GoToError('500')
|
||||
GoToError("500");
|
||||
} else if (code === 502) {
|
||||
GoToError('502')
|
||||
GoToError("502");
|
||||
} else {
|
||||
GoToError('404')
|
||||
GoToError("404");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const setTitle = (title) => {
|
||||
document.title = `${title} | csgoWTF`
|
||||
}
|
||||
document.title = `${title} | csgoWTF`;
|
||||
};
|
||||
|
||||
export const closeNav = (navSelector) => {
|
||||
const nav = document.getElementById(navSelector)
|
||||
if (nav)
|
||||
if (nav.classList.contains('show'))
|
||||
nav.classList.remove('show')
|
||||
}
|
||||
const nav = document.getElementById(navSelector);
|
||||
if (nav) if (nav.classList.contains("show")) nav.classList.remove("show");
|
||||
};
|
||||
|
||||
export const GetWinLoss = (matchResult, teamId) => {
|
||||
if (matchResult === teamId) {
|
||||
return 'win'
|
||||
return "win";
|
||||
} else if (matchResult === 0) {
|
||||
return 'draw'
|
||||
return "draw";
|
||||
} else {
|
||||
return 'loss'
|
||||
return "loss";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const truncate = (str, len, ending) => {
|
||||
if (len == null)
|
||||
len = 100
|
||||
if (len == null) len = 100;
|
||||
|
||||
if (ending == null)
|
||||
ending = '..'
|
||||
if (ending == null) ending = "..";
|
||||
|
||||
if (str.length > len)
|
||||
return str.substring(0, len - ending.length) + ending
|
||||
else
|
||||
return str
|
||||
}
|
||||
if (str.length > len) return str.substring(0, len - ending.length) + ending;
|
||||
else return str;
|
||||
};
|
||||
|
||||
export const checkStatEmpty = (stat) => {
|
||||
if (stat)
|
||||
return stat
|
||||
return 0
|
||||
}
|
||||
if (stat) return stat;
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const FixMapName = (map) => {
|
||||
return map.split('_')[1].replace(/^\w/, c => c.toUpperCase());
|
||||
}
|
||||
return map.split("_")[1].replace(/^\w/, (c) => c.toUpperCase());
|
||||
};
|
||||
|
||||
export const getPlayerArr = (stats, team, color) => {
|
||||
let arr = []
|
||||
let arr = [];
|
||||
for (let i = (team - 1) * 5; i < team * 5; i++) {
|
||||
arr.push({
|
||||
value: truncate(stats[i].player.name, 12),
|
||||
textStyle: {
|
||||
color: color ? getComputedStyle(document.documentElement).getPropertyValue(`--csgo-${stats[i].color}`) : 'white'
|
||||
}
|
||||
})
|
||||
color: color
|
||||
? getComputedStyle(document.documentElement).getPropertyValue(
|
||||
`--csgo-${stats[i].color}`
|
||||
)
|
||||
: "white",
|
||||
},
|
||||
});
|
||||
}
|
||||
arr.reverse()
|
||||
return arr
|
||||
}
|
||||
arr.reverse();
|
||||
return arr;
|
||||
};
|
||||
|
||||
export const constructAvatarUrl = (hash, size) => {
|
||||
const base = 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars'
|
||||
const imgSize = size ? `_${size}` : ''
|
||||
const base =
|
||||
"https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars";
|
||||
const imgSize = size ? `_${size}` : "";
|
||||
|
||||
if (hash) {
|
||||
const hashDir = hash.substring(0, 2)
|
||||
const hashDir = hash.substring(0, 2);
|
||||
|
||||
return `${base}/${hashDir}/${hash}${imgSize}.jpg`
|
||||
return `${base}/${hashDir}/${hash}${imgSize}.jpg`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const sortObjectValue = (obj, direction = 'asc') => {
|
||||
const sortable = []
|
||||
export const sortObjectValue = (obj, direction = "asc") => {
|
||||
const sortable = [];
|
||||
for (let key in obj) {
|
||||
sortable.push([key, obj[key]])
|
||||
sortable.push([key, obj[key]]);
|
||||
}
|
||||
|
||||
if (direction === 'asc') {
|
||||
if (direction === "asc") {
|
||||
sortable.sort((a, b) => {
|
||||
return a[1] - b[1]
|
||||
})
|
||||
return a[1] - b[1];
|
||||
});
|
||||
}
|
||||
if (direction === 'desc') {
|
||||
if (direction === "desc") {
|
||||
sortable.sort((a, b) => {
|
||||
return b[1] - a[1]
|
||||
})
|
||||
return b[1] - a[1];
|
||||
});
|
||||
}
|
||||
|
||||
return sortable
|
||||
}
|
||||
return sortable;
|
||||
};
|
||||
|
||||
export const CreatePlayersArray = (stats) => {
|
||||
let arr = []
|
||||
let arr = [];
|
||||
for (let i in stats) {
|
||||
arr.push({team_id: stats[i].team_id, player: stats[i].player})
|
||||
arr.push({ team_id: stats[i].team_id, player: stats[i].player });
|
||||
}
|
||||
return arr
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
export const scrollToPos = (pos = 0) => {
|
||||
window.scrollTo({
|
||||
top: pos,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
export const StripControlCodes = (str = '') => {
|
||||
export const StripControlCodes = (str = "") => {
|
||||
const regexpControl = /\p{C}/gu;
|
||||
return str.replace(regexpControl, '')
|
||||
}
|
||||
return str.replace(regexpControl, "");
|
||||
};
|
||||
|
||||
export const ProcessName = (str = '') => {
|
||||
return StripControlCodes(str).trim()
|
||||
}
|
||||
export const ProcessName = (str = "") => {
|
||||
return StripControlCodes(str).trim();
|
||||
};
|
||||
|
@@ -1,17 +1,19 @@
|
||||
import {
|
||||
ConvertTickToTime,
|
||||
FormatDate,
|
||||
FormatDuration,
|
||||
FormatFullDate,
|
||||
FormatFullDuration,
|
||||
FormatVacDate,
|
||||
MatchNotParsedTime,
|
||||
ConvertTickToTime
|
||||
} from "./DateTime";
|
||||
import {GoToLink, GoToMatch, GoToPlayer} from "./GoTo";
|
||||
import {SaveLastVisitedToLocalStorage} from "./LocalStorage";
|
||||
import {GetHLTV_1} from "./HLTV";
|
||||
import {DisplayRank, LoadImage, DisplayWeapon} from "./Display";
|
||||
import { GoToLink, GoToMatch, GoToPlayer } from "./GoTo";
|
||||
import { SaveLastVisitedToLocalStorage } from "./LocalStorage";
|
||||
import { GetHLTV_1 } from "./HLTV";
|
||||
import { DisplayRank, DisplayWeapon, LoadImage } from "./Display";
|
||||
import {
|
||||
GetChatHistory,
|
||||
GetChatHistoryTranslated,
|
||||
GetMatchDetails,
|
||||
GetMatches,
|
||||
GetPlayerMeta,
|
||||
@@ -20,25 +22,23 @@ import {
|
||||
GetWeaponDmg,
|
||||
LoadMoreMatches,
|
||||
LoadMoreMatchesExplore,
|
||||
GetChatHistory,
|
||||
GetChatHistoryTranslated,
|
||||
TrackMe
|
||||
TrackMe,
|
||||
} from "./ApiRequests";
|
||||
import {
|
||||
checkStatEmpty,
|
||||
closeNav,
|
||||
constructAvatarUrl,
|
||||
CreatePlayersArray,
|
||||
errorHandling,
|
||||
FixMapName,
|
||||
getPlayerArr,
|
||||
GetWinLoss,
|
||||
ProcessName,
|
||||
scrollToPos,
|
||||
setTitle,
|
||||
sortObjectValue,
|
||||
truncate,
|
||||
scrollToPos,
|
||||
StripControlCodes,
|
||||
ProcessName,
|
||||
errorHandling
|
||||
truncate,
|
||||
} from "./Utils";
|
||||
|
||||
export {
|
||||
@@ -81,5 +81,5 @@ export {
|
||||
scrollToPos,
|
||||
StripControlCodes,
|
||||
ProcessName,
|
||||
errorHandling
|
||||
}
|
||||
errorHandling,
|
||||
};
|
||||
|
@@ -1,113 +0,0 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="container-lg text-center">
|
||||
<h3>Recent matches</h3>
|
||||
<div v-if="data.matches">
|
||||
<MatchesTable :key="data.matches" :explore="true" :matches="data.matches" />
|
||||
|
||||
<div class="load-more text-center">
|
||||
<button :key="scrollToPos(store.state.scroll_state)" class="btn border-2 btn-outline-info"
|
||||
@click="setMoreMatches">Load More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<hr>
|
||||
<h6>There seems to be a problem loading the content</h6>
|
||||
<h6>Please try again later</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {onBeforeUnmount, onMounted, reactive} from "vue";
|
||||
import {GetMatches, LoadImage, LoadMoreMatchesExplore, MatchNotParsedTime, scrollToPos} from "@/utils";
|
||||
import MatchesTable from "@/components/MatchesTable";
|
||||
import {useStore} from "vuex";
|
||||
import router from "@/router";
|
||||
|
||||
export default {
|
||||
name: 'Explore',
|
||||
components: {MatchesTable},
|
||||
setup() {
|
||||
document.title = "Matches | csgoWTF"
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const data = reactive({
|
||||
matches: []
|
||||
})
|
||||
|
||||
const setMoreMatches = async () => {
|
||||
const res = await LoadMoreMatchesExplore(store, data.matches[data.matches.length - 1].date)
|
||||
|
||||
if (res !== null)
|
||||
res.forEach(e => data.matches.push(e))
|
||||
|
||||
scrollToPos(window.scrollY)
|
||||
|
||||
// console.log(data.matches)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
data.matches = await GetMatches(store)
|
||||
|
||||
if (data.matches !== null) {
|
||||
if (data.matches[0].map) {
|
||||
await LoadImage(data.matches[0].map)
|
||||
} else if (!data.matches[0].map && MatchNotParsedTime(data.matches[0].date) && data.matches[1].map) {
|
||||
await LoadImage(data.matches[1].map)
|
||||
} else {
|
||||
await LoadImage('random')
|
||||
}
|
||||
} else {
|
||||
document.querySelector('.bg-img').style.display = 'none'
|
||||
}
|
||||
|
||||
scrollToPos(store.state.scroll_state)
|
||||
|
||||
// if (data.matches) {
|
||||
// console.log(data.matches)
|
||||
// }
|
||||
|
||||
document.getElementById('app').style.background = 'rgba(0, 0, 0, .7)'
|
||||
document.querySelector('.bg-img').style.display = 'initial'
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.commit('changeScrollState', window.scrollY)
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (!to.fullPath.match('/match/') && !from.fullPath.match('/match/')) {
|
||||
store.commit('changeScrollState', 0)
|
||||
}
|
||||
next()
|
||||
})
|
||||
})
|
||||
|
||||
return {data, setMoreMatches, store, scrollToPos}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-lg {
|
||||
padding: 2rem;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.container-lg {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
134
src/views/ExploreView.vue
Normal file
134
src/views/ExploreView.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="container-lg text-center">
|
||||
<h3>Recent matches</h3>
|
||||
<div v-if="data.matches">
|
||||
<MatchesTable
|
||||
:key="data.matches"
|
||||
:explore="true"
|
||||
:matches="data.matches"
|
||||
/>
|
||||
|
||||
<div class="load-more text-center">
|
||||
<button
|
||||
:key="scrollToPos(store.state.scroll_state)"
|
||||
class="btn border-2 btn-outline-info"
|
||||
@click="setMoreMatches"
|
||||
>
|
||||
Load More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<hr />
|
||||
<h6>There seems to be a problem loading the content</h6>
|
||||
<h6>Please try again later</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { onBeforeUnmount, onMounted, reactive } from "vue";
|
||||
import {
|
||||
GetMatches,
|
||||
LoadImage,
|
||||
LoadMoreMatchesExplore,
|
||||
MatchNotParsedTime,
|
||||
scrollToPos,
|
||||
} from "/src/utils";
|
||||
|
||||
import { useStore } from "vuex";
|
||||
import router from "/src/router";
|
||||
import MatchesTable from "/src/components/MatchesTable";
|
||||
|
||||
export default {
|
||||
name: "ExploreView",
|
||||
components: { MatchesTable },
|
||||
setup() {
|
||||
document.title = "Matches | csgoWTF";
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const data = reactive({
|
||||
matches: [],
|
||||
});
|
||||
|
||||
const setMoreMatches = async () => {
|
||||
const res = await LoadMoreMatchesExplore(
|
||||
store,
|
||||
data.matches[data.matches.length - 1].date
|
||||
);
|
||||
|
||||
if (res !== null) res.forEach((e) => data.matches.push(e));
|
||||
|
||||
scrollToPos(window.scrollY);
|
||||
|
||||
// console.log(data.matches)
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
data.matches = await GetMatches(store);
|
||||
|
||||
if (data.matches !== null) {
|
||||
if (data.matches[0].map) {
|
||||
await LoadImage(data.matches[0].map);
|
||||
} else if (
|
||||
!data.matches[0].map &&
|
||||
MatchNotParsedTime(data.matches[0].date) &&
|
||||
data.matches[1].map
|
||||
) {
|
||||
await LoadImage(data.matches[1].map);
|
||||
} else {
|
||||
await LoadImage("random");
|
||||
}
|
||||
} else {
|
||||
document.querySelector(".bg-img").style.display = "none";
|
||||
}
|
||||
|
||||
scrollToPos(store.state.scroll_state);
|
||||
|
||||
// if (data.matches) {
|
||||
// console.log(data.matches)
|
||||
// }
|
||||
|
||||
document.getElementById("app").style.background = "rgba(0, 0, 0, .7)";
|
||||
document.querySelector(".bg-img").style.display = "initial";
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.commit("changeScrollState", window.scrollY);
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (!to.fullPath.match("/match/") && !from.fullPath.match("/match/")) {
|
||||
store.commit("changeScrollState", 0);
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
return { data, setMoreMatches, store, scrollToPos };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-lg {
|
||||
padding: 2rem;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.container-lg {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,278 +0,0 @@
|
||||
<template>
|
||||
<div class="main-content content text-center">
|
||||
<div class="head pt-4 pb-4">
|
||||
<img alt="logo"
|
||||
class="logo mt-lg-5 mt-3 mb-3"
|
||||
src="/images/logo.svg">
|
||||
<h3 class="mb-lg-4">Open source CSGO data platform</h3>
|
||||
</div>
|
||||
<div v-if="recentVisited !== null" class="recent-search mt-5 mb-5 row gap-2 justify-content-center">
|
||||
<div v-for="(player, id) in recentVisited" :key="player.steamid64" class="player-card" tabindex="0"
|
||||
@keyup.enter="GoToPlayer(player.vanity_url || player.steamid64)">
|
||||
<div class="p-2" @click="GoToPlayer(player.vanity_url || player.steamid64)">
|
||||
<div class="col-md-4 m-auto">
|
||||
<img :alt="player.name" :src="player.avatar">
|
||||
</div>
|
||||
<div class="col-md-8 m-auto">
|
||||
<p>{{ player.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<i class="delete fa fa-times" tabindex="0" @click="removeRecentVisited(id)"></i>
|
||||
</div>
|
||||
</div>
|
||||
<hr v-if="recentVisited !== null" class="m-auto text-muted">
|
||||
<div class="body container m-auto row mt-5 mb-5 justify-content-center">
|
||||
<table class="table table-borderless">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<i class="fa fa-code-fork"/>
|
||||
</th>
|
||||
<th>
|
||||
<i class="fa fa-liberapay"/>
|
||||
</th>
|
||||
<th>
|
||||
<i class="fa fa-pie-chart"/>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="align-middle">
|
||||
<td>
|
||||
<h4 class="fw-light">Open Source</h4>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://liberapay.com/CSGOWTF/donate" target="_blank">
|
||||
<img alt="Donate using Liberapay"
|
||||
src="https://liberapay.com/assets/widgets/donate.svg"
|
||||
style="height: 35px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<h4 class="fw-light">In-Depth Data</h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p class="fw-light">Everything is open source and under GPL licence. Contributions welcome.</p>
|
||||
</td>
|
||||
<td>
|
||||
<p class="fw-light">We develop this site in our spare time. If you want to support us, donations are
|
||||
appreciated!</p>
|
||||
</td>
|
||||
<td>
|
||||
<p class="fw-light">Matches with parsed replay provide additional match data.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td/>
|
||||
<td>
|
||||
<img alt="liberapay patrons" src="https://img.shields.io/liberapay/patrons/CSGOWTF.svg"
|
||||
style="height: 25px"/>
|
||||
</td>
|
||||
<td/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {GoToPlayer, SaveLastVisitedToLocalStorage, setTitle} from "@/utils";
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {useStore} from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
setup() {
|
||||
setTitle('Home')
|
||||
const store = useStore()
|
||||
|
||||
const recentVisited = ref([])
|
||||
|
||||
const loadRecentVisited = () => {
|
||||
recentVisited.value = JSON.parse(localStorage.getItem('recent-visited'))
|
||||
|
||||
if (recentVisited.value !== null) {
|
||||
if (window.innerWidth < 768) {
|
||||
recentVisited.value = recentVisited.value.filter(i => recentVisited.value.indexOf(i) < 6)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const removeRecentVisited = (key) => {
|
||||
if (recentVisited.value !== null) {
|
||||
recentVisited.value.splice(key, 1)
|
||||
recentVisited.value.reverse()
|
||||
|
||||
localStorage.clear()
|
||||
|
||||
if (recentVisited.value !== []) {
|
||||
recentVisited.value.map(p => {
|
||||
SaveLastVisitedToLocalStorage(p)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
loadRecentVisited()
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
loadRecentVisited()
|
||||
store.commit('resetPlayerDetails')
|
||||
|
||||
document.getElementById('app').style.background = 'none'
|
||||
document.querySelector('.bg-img').style.display = 'none'
|
||||
})
|
||||
|
||||
return {recentVisited, GoToPlayer, removeRecentVisited}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
table {
|
||||
td {
|
||||
p {
|
||||
max-width: 40ch;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.fa {
|
||||
font-size: 5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
.head {
|
||||
// display jpg
|
||||
background-image: url("/images/map_screenshots/default.jpg");
|
||||
}
|
||||
|
||||
.head {
|
||||
// display webp if possible
|
||||
background-image: url("/images/map_screenshots/default.webp");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
|
||||
.logo {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.text-up {
|
||||
font-family: "OpenSans", sans-serif;
|
||||
font-size: 40%;
|
||||
vertical-align: top;
|
||||
text-shadow: 10px -5px 1rem rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: lighter;
|
||||
}
|
||||
}
|
||||
|
||||
.recent-search {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
|
||||
.player-card {
|
||||
width: 180px;
|
||||
height: 75px;
|
||||
background: var(--bs-blue);
|
||||
border-radius: 15% 5%;
|
||||
position: relative;
|
||||
|
||||
.delete {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--bs-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background: var(--bs-warning) !important;
|
||||
}
|
||||
|
||||
&:hover > .delete {
|
||||
display: initial;
|
||||
position: absolute;
|
||||
font-size: 1rem;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
|
||||
&:hover {
|
||||
color: maroon;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: .9rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.logo {
|
||||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.head {
|
||||
.logo {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2rem;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
.recent-search {
|
||||
.player-card {
|
||||
height: 60px;
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.delete {
|
||||
display: initial;
|
||||
position: absolute;
|
||||
font-size: 1rem;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
color: maroon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
p {
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.fas {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
310
src/views/HomeView.vue
Normal file
310
src/views/HomeView.vue
Normal file
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<div class="main-content content text-center">
|
||||
<div class="head pt-4 pb-4">
|
||||
<img alt="logo" class="logo mt-lg-5 mt-3 mb-3" src="/images/logo.svg" />
|
||||
<h3 class="mb-lg-4">Open source CSGO data platform</h3>
|
||||
</div>
|
||||
<div
|
||||
v-if="recentVisited !== null"
|
||||
class="recent-search mt-5 mb-5 row gap-2 justify-content-center"
|
||||
>
|
||||
<div
|
||||
v-for="(player, id) in recentVisited"
|
||||
:key="player.steamid64"
|
||||
class="player-card"
|
||||
tabindex="0"
|
||||
@keyup.enter="GoToPlayer(player.vanity_url || player.steamid64)"
|
||||
>
|
||||
<div
|
||||
class="p-2"
|
||||
@click="GoToPlayer(player.vanity_url || player.steamid64)"
|
||||
>
|
||||
<div class="col-md-4 m-auto">
|
||||
<img :alt="player.name" :src="player.avatar" />
|
||||
</div>
|
||||
<div class="col-md-8 m-auto">
|
||||
<p>{{ player.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<i
|
||||
class="delete fa fa-times"
|
||||
tabindex="0"
|
||||
@click="removeRecentVisited(id)"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<hr v-if="recentVisited !== null" class="m-auto text-muted" />
|
||||
<div class="body container m-auto row mt-5 mb-5 justify-content-center">
|
||||
<table class="table table-borderless">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<i class="fa fa-code-fork" />
|
||||
</th>
|
||||
<th>
|
||||
<i class="fa fa-liberapay" />
|
||||
</th>
|
||||
<th>
|
||||
<i class="fa fa-pie-chart" />
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="align-middle">
|
||||
<td>
|
||||
<h4 class="fw-light">Open Source</h4>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://liberapay.com/CSGOWTF/donate" target="_blank">
|
||||
<img
|
||||
alt="Donate using Liberapay"
|
||||
src="https://liberapay.com/assets/widgets/donate.svg"
|
||||
style="height: 35px"
|
||||
/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<h4 class="fw-light">In-Depth Data</h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p class="fw-light">
|
||||
Everything is open source and under GPL licence. Contributions
|
||||
welcome.
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p class="fw-light">
|
||||
We develop this site in our spare time. If you want to support
|
||||
us, donations are appreciated!
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p class="fw-light">
|
||||
Matches with parsed replay provide additional match data.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td />
|
||||
<td>
|
||||
<img
|
||||
alt="liberapay patrons"
|
||||
src="https://img.shields.io/liberapay/patrons/CSGOWTF.svg"
|
||||
style="height: 25px"
|
||||
/>
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
GoToPlayer,
|
||||
SaveLastVisitedToLocalStorage,
|
||||
setTitle,
|
||||
} from "/src/utils";
|
||||
import { onBeforeMount, ref } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "HomeView",
|
||||
setup() {
|
||||
setTitle("Home");
|
||||
const store = useStore();
|
||||
|
||||
const recentVisited = ref([]);
|
||||
|
||||
const loadRecentVisited = () => {
|
||||
recentVisited.value = JSON.parse(localStorage.getItem("recent-visited"));
|
||||
|
||||
if (recentVisited.value !== null) {
|
||||
if (window.innerWidth < 768) {
|
||||
recentVisited.value = recentVisited.value.filter(
|
||||
(i) => recentVisited.value.indexOf(i) < 6
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const removeRecentVisited = (key) => {
|
||||
if (recentVisited.value !== null) {
|
||||
recentVisited.value.splice(key, 1);
|
||||
recentVisited.value.reverse();
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
if (recentVisited.value !== []) {
|
||||
recentVisited.value.map((p) => {
|
||||
SaveLastVisitedToLocalStorage(p);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadRecentVisited();
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
loadRecentVisited();
|
||||
store.commit("resetPlayerDetails");
|
||||
|
||||
document.getElementById("app").style.background = "none";
|
||||
document.querySelector(".bg-img").style.display = "none";
|
||||
});
|
||||
|
||||
return { recentVisited, GoToPlayer, removeRecentVisited };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
table {
|
||||
td {
|
||||
p {
|
||||
max-width: 40ch;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
font-size: 5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
.head {
|
||||
// display jpg
|
||||
background-image: url("/images/map_screenshots/default.jpg");
|
||||
}
|
||||
|
||||
.head {
|
||||
// display webp if possible
|
||||
background-image: url("/images/map_screenshots/default.webp");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
|
||||
.logo {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.text-up {
|
||||
font-family: "OpenSans", sans-serif;
|
||||
font-size: 40%;
|
||||
vertical-align: top;
|
||||
text-shadow: 10px -5px 1rem rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: lighter;
|
||||
}
|
||||
}
|
||||
|
||||
.recent-search {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
|
||||
.player-card {
|
||||
width: 180px;
|
||||
height: 75px;
|
||||
background: var(--bs-blue);
|
||||
border-radius: 15% 5%;
|
||||
position: relative;
|
||||
|
||||
.delete {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--bs-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background: var(--bs-warning) !important;
|
||||
}
|
||||
|
||||
&:hover > .delete {
|
||||
display: initial;
|
||||
position: absolute;
|
||||
font-size: 1rem;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
|
||||
&:hover {
|
||||
color: maroon;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.9rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.logo {
|
||||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.head {
|
||||
.logo {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2rem;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
.recent-search {
|
||||
.player-card {
|
||||
height: 60px;
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.delete {
|
||||
display: initial;
|
||||
position: absolute;
|
||||
font-size: 1rem;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
color: maroon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
p {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.fas {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,582 +0,0 @@
|
||||
<template>
|
||||
<div class="overlay" :style="{minHeight: pHeight + 'px'}">
|
||||
<div class="match-wrapper">
|
||||
<div class="head row m-auto text-center">
|
||||
<div class="map-score">
|
||||
<div class="score-team-1">
|
||||
<h1 :class="data.matchDetails.match_result === 1 ? 'text-success' : data.matchDetails.match_result === 0 ? 'text-warning' : 'text-danger'">{{data.score[0]}}</h1>
|
||||
<div class="team-1">
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg">
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg">
|
||||
</div>
|
||||
<div class="team-avg-rank">
|
||||
<img v-if="data.matchDetails.parsed"
|
||||
:alt="DisplayRank(Math.floor(data.team1Avg || 0))[1]"
|
||||
:src="DisplayRank(Math.floor(data.team1Avg || 0))[0]"
|
||||
:title="'Average Team-Rank: ' + DisplayRank(Math.floor(data.team1Avg || 0))[1]"
|
||||
class="team-avg-rank-icon helpicon"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-auto map">
|
||||
<img v-if="data.matchDetails.map" :alt="data.matchDetails.map"
|
||||
:src="'/images/map_icons/map_icon_' + data.matchDetails.map + '.svg'"
|
||||
:title="FixMapName(data.matchDetails.map)" class="map-icon"
|
||||
>
|
||||
<img v-if="!data.matchDetails.map" :src="'/images/map_icons/map_icon_lobby_mapveto.svg'"
|
||||
alt="Map icon"
|
||||
class="map-icon" title="Map unknown"
|
||||
>
|
||||
</div>
|
||||
<div class="score-team-2">
|
||||
<h1 :class="data.matchDetails.match_result === 2 ? 'text-success' : data.matchDetails.match_result === 0 ? 'text-warning' : 'text-danger'">{{ data.score[1] }}</h1>
|
||||
<div class="team-2">
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg">
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg">
|
||||
</div>
|
||||
<div class="team-avg-rank">
|
||||
<img v-if="data.matchDetails.parsed"
|
||||
:alt="DisplayRank(Math.floor(data.team2Avg || 0))[1]"
|
||||
:src="DisplayRank(Math.floor(data.team2Avg || 0))[0]"
|
||||
:title="'Average Team-Rank: ' + DisplayRank(Math.floor(data.team2Avg || 0))[1]"
|
||||
class="team-avg-rank-icon helpicon"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text">
|
||||
<p class="text-center text-muted fs-6 mb-1">
|
||||
Match lasted for
|
||||
<span class="text-white">{{ FormatDuration(data.matchDetails.duration) }}</span>
|
||||
</p>
|
||||
<p class="text-center text-muted fs-6">
|
||||
on
|
||||
<span class="text-white">{{ FormatFullDate(data.matchDetails.date) }}</span>
|
||||
</p>
|
||||
<div class="text-center fs-6">
|
||||
<img v-if="data.matchDetails.max_rounds === 16" alt="Match length" class="match-len helpicon"
|
||||
src="/images/icons/timer_short.svg" title="Short Match">
|
||||
<img v-if="data.matchDetails.max_rounds === 30 || !data.matchDetails.max_rounds" alt="Match length"
|
||||
class="match-len helpicon"
|
||||
src="/images/icons/timer_long.svg" title="Long Match">
|
||||
|
||||
<span v-if="data.matchDetails.parsed" class="text-muted px-2">—</span>
|
||||
|
||||
<img v-if="data.matchDetails.parsed"
|
||||
:alt="DisplayRank(Math.floor(data.matchDetails.avg_rank || 0))[1]"
|
||||
:src="DisplayRank(Math.floor(data.matchDetails.avg_rank || 0))[0]"
|
||||
:title="'Average Rank: ' + DisplayRank(Math.floor(data.matchDetails.avg_rank || 0))[1]"
|
||||
class="rank-icon helpicon"/>
|
||||
|
||||
<span v-if="data.matchDetails.parsed && data.matchDetails.replay_url" class="text-muted px-2">—</span>
|
||||
|
||||
<div v-if="data.matchDetails.parsed && data.matchDetails.replay_url" class="btn-group">
|
||||
<i id="downloadMenuBtn" aria-hidden="true" class="fa fa-ellipsis-h mx-2"
|
||||
title="Click for more" @click.prevent="handleDownloadMenu"></i>
|
||||
<div id="downloadGroup" class="group">
|
||||
<a v-if="data.matchDetails.replay_url" :href="data.matchDetails.replay_url" target="_blank"
|
||||
title="Download Demo">
|
||||
<i id="downloadDemo" aria-hidden="true" class="fa fa-download mx-2"></i>
|
||||
</a>
|
||||
<a v-if="data.matchDetails.share_code"
|
||||
:href="'steam://rungame/730/76561202255233023/+csgo_download_match ' + data.matchDetails.share_code"
|
||||
target="_blank" title="Watch Demo">
|
||||
<i id="replayDemo" aria-hidden="true" class="fa fa-television mx-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav navbar-dark navbar-expand-lg">
|
||||
<button aria-controls="matchNav" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler"
|
||||
data-bs-target="#matchNav" data-bs-toggle="collapse" type="button">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div id="matchNav" class="collapse navbar-collapse justify-content-between">
|
||||
<ul class="list-unstyled d-flex m-auto">
|
||||
<li :title="!data.matchDetails.parsed ? 'This demo has not been parsed' : ''"
|
||||
class="list-item nav-item">
|
||||
<router-link :to="'/match/' + data.matchDetails.match_id" class="nav-link"
|
||||
replace>Scoreboard
|
||||
</router-link>
|
||||
</li>
|
||||
<li :title="!data.matchDetails.parsed ? 'This demo has not been parsed' : ''"
|
||||
class="list-item nav-item">
|
||||
<router-link :class="!data.matchDetails.parsed ? 'disabled' : ''" :disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/economy'" class="nav-link"
|
||||
replace>Economy
|
||||
</router-link>
|
||||
</li>
|
||||
<li :title="!data.matchDetails.parsed ? 'This demo has not been parsed' : ''"
|
||||
class="list-item nav-item">
|
||||
<router-link :class="!data.matchDetails.parsed ? 'disabled' : ''" :disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/details'" class="nav-link"
|
||||
replace>Details
|
||||
</router-link>
|
||||
</li>
|
||||
<li :title="!data.matchDetails.parsed ? 'This demo has not been parsed' : ''"
|
||||
class="list-item nav-item">
|
||||
<router-link :class="!data.matchDetails.parsed ? 'disabled' : ''" :disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/flashes'" class="nav-link"
|
||||
replace>Flashes
|
||||
</router-link>
|
||||
</li>
|
||||
<li :title="!data.matchDetails.parsed ? 'This demo has not been parsed' : ''"
|
||||
class="list-item nav-item">
|
||||
<router-link :class="!data.matchDetails.parsed ? 'disabled' : ''" :disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/damage'" class="nav-link"
|
||||
replace>Damage
|
||||
</router-link>
|
||||
</li>
|
||||
<li :title="!data.matchDetails.parsed ? 'This demo has not been parsed' : ''"
|
||||
class="list-item nav-item">
|
||||
<router-link :class="!data.matchDetails.parsed ? 'disabled' : ''" :disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/chat'" class="nav-link"
|
||||
replace>Chat
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="scoreWrapper" class="scoreboard">
|
||||
<router-view v-if="data.score.length === 2 && data.stats" name="score"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, watch} from "vue";
|
||||
import {
|
||||
closeNav,
|
||||
CreatePlayersArray,
|
||||
DisplayRank,
|
||||
errorHandling,
|
||||
FixMapName,
|
||||
FormatDuration,
|
||||
FormatFullDate,
|
||||
GetMatchDetails,
|
||||
GoToLink,
|
||||
LoadImage,
|
||||
ProcessName
|
||||
} from "@/utils";
|
||||
import {useStore} from "vuex";
|
||||
import {useRoute} from 'vue-router'
|
||||
import {DateTime} from "luxon/build/es6/luxon";
|
||||
import {FOOTER_HEIGHT, NAV_HEIGHT} from "@/constants";
|
||||
|
||||
export default {
|
||||
name: 'Match',
|
||||
props: ['match_id'],
|
||||
setup(props) {
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const pHeight = ref(0)
|
||||
|
||||
const matchIdPattern = /^\d{19}$/
|
||||
|
||||
// Refs
|
||||
const data = reactive({
|
||||
player_id: '',
|
||||
matchDetails: {},
|
||||
stats: [],
|
||||
score: [0],
|
||||
team1Avg: 0,
|
||||
team2Avg: 0
|
||||
})
|
||||
|
||||
const getWindowHeight = () => {
|
||||
const navHeight = document.getElementsByTagName('nav')[0].clientHeight
|
||||
const footerHeight = document.getElementsByTagName('footer')[0].clientHeight
|
||||
|
||||
// 70 = nav-height | 108.5 = footer-height
|
||||
return window.innerHeight - navHeight - footerHeight
|
||||
}
|
||||
|
||||
pHeight.value = getWindowHeight()
|
||||
|
||||
// Functions
|
||||
const GetMatch = async () => {
|
||||
if (matchIdPattern.test(props.match_id)) {
|
||||
const res = await GetMatchDetails(store, props.match_id)
|
||||
|
||||
if (res !== null) {
|
||||
if (res.map)
|
||||
document.title = `${FixMapName(res.map)} ► ${res.score[0]} : ${res.score[1]} ◄ ${DateTime.fromSeconds(res.date).toLocaleString(DateTime.DATETIME_SHORT)} | csgoWTF`
|
||||
else
|
||||
document.title = `Match-Details | csgoWTF`
|
||||
|
||||
store.commit({
|
||||
type: 'changeMatchDetails',
|
||||
data: res
|
||||
})
|
||||
|
||||
checkRoute()
|
||||
data.matchDetails = store.state.matchDetails
|
||||
|
||||
data.matchDetails.stats.forEach(p => {
|
||||
p.player.name = ProcessName(p.player.name)
|
||||
})
|
||||
|
||||
data.stats = data.matchDetails.stats
|
||||
data.score = data.matchDetails.score
|
||||
|
||||
// Set avg team ranks
|
||||
let pCount = 1
|
||||
data.team1Avg = Math.floor(getTeamAvgRank(1).reduce((a, b) => {
|
||||
if (a !== 0 && b !== 0)
|
||||
pCount++
|
||||
return (a + b)
|
||||
})) / pCount
|
||||
|
||||
pCount = 1
|
||||
data.team2Avg = Math.floor(getTeamAvgRank(2).reduce((a, b) => {
|
||||
if (a !== 0 && b !== 0)
|
||||
pCount++
|
||||
return (a + b)
|
||||
})) / pCount
|
||||
|
||||
LoadImage(data.matchDetails.map ? data.matchDetails.map : 'random')
|
||||
|
||||
store.commit({
|
||||
type: 'changePlayersArr',
|
||||
data: CreatePlayersArray(data.stats)
|
||||
})
|
||||
|
||||
// console.log(data.matchDetails)
|
||||
} else {
|
||||
document.querySelector('.bg-img').style.display = 'none'
|
||||
}
|
||||
} else {
|
||||
errorHandling(404)
|
||||
}
|
||||
}
|
||||
|
||||
const checkRoute = () => {
|
||||
if (route.fullPath.split('/')[3]) {
|
||||
const sub = route.fullPath.split('/')[3]
|
||||
if (matchIdPattern.test(props.match_id)) {
|
||||
GoToLink(`/match/${props.match_id}/${sub}`)
|
||||
} else {
|
||||
errorHandling(404)
|
||||
}
|
||||
} else {
|
||||
if (matchIdPattern.test(props.match_id))
|
||||
GoToLink(`/match/${props.match_id}`)
|
||||
else {
|
||||
errorHandling(404)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getTeamAvgRank = (team) => {
|
||||
let arr = []
|
||||
for (let i = (team - 1) * 5; i < team * 5; i++) {
|
||||
arr.push(data.matchDetails.stats[i].rank?.old !== undefined ? data.matchDetails.stats[i].rank?.old : 0)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
const handleDownloadMenu = () => {
|
||||
const downloadGroup = document.getElementById('downloadGroup')
|
||||
const menuBtn = document.getElementById('downloadMenuBtn')
|
||||
let opacity = window.getComputedStyle(menuBtn).getPropertyValue('opacity')
|
||||
|
||||
function show() {
|
||||
if (opacity < 1) {
|
||||
opacity = opacity + 0.1
|
||||
downloadGroup.style.opacity = opacity
|
||||
} else {
|
||||
clearInterval(0)
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (opacity > 0) {
|
||||
opacity = opacity - 0.1
|
||||
menuBtn.style.opacity = opacity
|
||||
} else {
|
||||
menuBtn.style.display = 'none'
|
||||
downloadGroup.style.opacity = 0
|
||||
downloadGroup.style.display = 'block'
|
||||
setInterval(show, 35)
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(hide, 35)
|
||||
}
|
||||
|
||||
// Watchers
|
||||
watch(() => props.match_id, GetMatch)
|
||||
|
||||
// Run on create
|
||||
onBeforeMount(() => {
|
||||
GetMatch()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.commit('resetMatchDetails')
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const headHeight = 230
|
||||
const navHeight = 42
|
||||
|
||||
const height = window.innerHeight - NAV_HEIGHT - FOOTER_HEIGHT - headHeight - navHeight
|
||||
const scoreWrapper = document.getElementById('scoreWrapper')
|
||||
scoreWrapper.style.minHeight = height + 'px'
|
||||
|
||||
document.getElementById('app').style.background = 'linear-gradient(90deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.85) 30%, rgba(0, 0, 0, 0.85) 70%, rgba(0, 0, 0, .6) 100%)'
|
||||
document.querySelector('.bg-img').style.display = 'initial'
|
||||
})
|
||||
|
||||
window.onresize = () => {
|
||||
pHeight.value = getWindowHeight()
|
||||
}
|
||||
|
||||
document.addEventListener('click', () => {
|
||||
closeNav('matchNav')
|
||||
})
|
||||
|
||||
return {
|
||||
data, DisplayRank, FormatFullDate, FormatDuration, FixMapName, route, pHeight, handleDownloadMenu, getTeamAvgRank
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.head {
|
||||
height: 230px;
|
||||
background: linear-gradient(90deg,
|
||||
rgba(0, 0, 0, 0.3) 0%,
|
||||
rgba(0, 0, 0, 0.55) 30%,
|
||||
rgba(0, 0, 0, 0.55) 70%,
|
||||
rgba(0, 0, 0, .3) 100%
|
||||
);
|
||||
|
||||
.map-score {
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
.map img {
|
||||
width: auto;
|
||||
height: 100px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.score-team-1,
|
||||
.score-team-2 {
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
|
||||
h1 {
|
||||
margin: 0 auto .5rem;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.team-avg-rank {
|
||||
margin: 3.5rem auto 0;
|
||||
|
||||
.team-avg-rank-icon {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.team-1,
|
||||
.team-2 {
|
||||
position: relative;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
opacity: .8;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
&:first-child {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: 20px;
|
||||
z-index: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.team-1 {
|
||||
right: 1.4rem;
|
||||
}
|
||||
|
||||
.team-2 {
|
||||
left: -1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.score-team-1 {
|
||||
left: 25%;
|
||||
}
|
||||
|
||||
.score-team-2 {
|
||||
right: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
.rank-icon {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.match-len {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
#downloadMenuBtn {
|
||||
cursor: pointer;
|
||||
font-size: 1.3rem;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: none;
|
||||
margin-left: -5px;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
font-size: 1.3rem;
|
||||
|
||||
&:hover, &:focus {
|
||||
color: var(--bs-warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
max-width: 100vw;
|
||||
min-height: 42px;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
background: linear-gradient(90deg,
|
||||
rgba(0, 0, 0, 0.7) 0%,
|
||||
rgba(0, 0, 0, 0.95) 30%,
|
||||
rgba(0, 0, 0, 0.95) 70%,
|
||||
rgba(0, 0, 0, .7) 100%
|
||||
);
|
||||
border-top: 1px solid rgba(255, 255, 255, .2);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .2);
|
||||
|
||||
.nav-link {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: var(--bs-info);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.router-link-exact-active {
|
||||
background: var(--bs-info)
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: #585858;
|
||||
|
||||
&:hover {
|
||||
background: lime;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#scoreWrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.score-team-1,
|
||||
.score-team-2 {
|
||||
top: 1rem !important;
|
||||
|
||||
h1 {
|
||||
font-size: 2.8rem !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.team-avg-rank {
|
||||
margin: 2rem auto 0 !important;
|
||||
|
||||
.team-avg-rank-icon {
|
||||
width: 50px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.team-1, .team-2 {
|
||||
img {
|
||||
width: 25px !important;
|
||||
height: 25px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.team-2 {
|
||||
left: -1.3rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.score-team-1 {
|
||||
left: 10% !important;
|
||||
}
|
||||
|
||||
.score-team-2 {
|
||||
right: 10% !important;
|
||||
}
|
||||
|
||||
.nav {
|
||||
button {
|
||||
outline: 1px solid var(--bs-primary);
|
||||
margin-left: auto;
|
||||
float: right;
|
||||
margin-right: 1rem;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
outline: 1px solid var(--bs-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--bs-primary);
|
||||
|
||||
ul {
|
||||
flex-direction: column;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#scoreWrapper {
|
||||
justify-content: flex-start;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
769
src/views/MatchView.vue
Normal file
769
src/views/MatchView.vue
Normal file
@@ -0,0 +1,769 @@
|
||||
<template>
|
||||
<div :style="{ minHeight: pHeight + 'px' }" class="overlay">
|
||||
<div class="match-wrapper">
|
||||
<div class="head row m-auto text-center">
|
||||
<div class="map-score">
|
||||
<div class="score-team-1">
|
||||
<h1
|
||||
:class="
|
||||
data.matchDetails.match_result === 1
|
||||
? 'text-success'
|
||||
: data.matchDetails.match_result === 0
|
||||
? 'text-warning'
|
||||
: 'text-danger'
|
||||
"
|
||||
>
|
||||
{{ data.score[0] }}
|
||||
</h1>
|
||||
<div class="team-1">
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg" />
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg" />
|
||||
</div>
|
||||
<div class="team-avg-rank">
|
||||
<img
|
||||
v-if="data.matchDetails.parsed"
|
||||
:alt="DisplayRank(Math.floor(data.team1Avg || 0))[1]"
|
||||
:src="DisplayRank(Math.floor(data.team1Avg || 0))[0]"
|
||||
:title="
|
||||
'Average Team-Rank: ' +
|
||||
DisplayRank(Math.floor(data.team1Avg || 0))[1]
|
||||
"
|
||||
class="team-avg-rank-icon helpicon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-auto map">
|
||||
<img
|
||||
v-if="data.matchDetails.map"
|
||||
:alt="data.matchDetails.map"
|
||||
:src="
|
||||
'/images/map_icons/map_icon_' + data.matchDetails.map + '.svg'
|
||||
"
|
||||
:title="FixMapName(data.matchDetails.map)"
|
||||
class="map-icon"
|
||||
/>
|
||||
<img
|
||||
v-if="!data.matchDetails.map"
|
||||
:src="'/images/map_icons/map_icon_lobby_mapveto.svg'"
|
||||
alt="Map icon"
|
||||
class="map-icon"
|
||||
title="Map unknown"
|
||||
/>
|
||||
</div>
|
||||
<div class="score-team-2">
|
||||
<h1
|
||||
:class="
|
||||
data.matchDetails.match_result === 2
|
||||
? 'text-success'
|
||||
: data.matchDetails.match_result === 0
|
||||
? 'text-warning'
|
||||
: 'text-danger'
|
||||
"
|
||||
>
|
||||
{{ data.score[1] }}
|
||||
</h1>
|
||||
<div class="team-2">
|
||||
<img alt="T logo" src="/images/icons/t_logo.svg" />
|
||||
<img alt="CT logo" src="/images/icons/ct_logo.svg" />
|
||||
</div>
|
||||
<div class="team-avg-rank">
|
||||
<img
|
||||
v-if="data.matchDetails.parsed"
|
||||
:alt="DisplayRank(Math.floor(data.team2Avg || 0))[1]"
|
||||
:src="DisplayRank(Math.floor(data.team2Avg || 0))[0]"
|
||||
:title="
|
||||
'Average Team-Rank: ' +
|
||||
DisplayRank(Math.floor(data.team2Avg || 0))[1]
|
||||
"
|
||||
class="team-avg-rank-icon helpicon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text">
|
||||
<p class="text-center text-muted fs-6 mb-1">
|
||||
Match lasted for
|
||||
<span class="text-white">{{
|
||||
FormatDuration(data.matchDetails.duration)
|
||||
}}</span>
|
||||
</p>
|
||||
<p class="text-center text-muted fs-6">
|
||||
on
|
||||
<span class="text-white">{{
|
||||
FormatFullDate(data.matchDetails.date)
|
||||
}}</span>
|
||||
</p>
|
||||
<div class="text-center fs-6">
|
||||
<img
|
||||
v-if="data.matchDetails.max_rounds === 16"
|
||||
alt="Match length"
|
||||
class="match-len helpicon"
|
||||
src="/images/icons/timer_short.svg"
|
||||
title="Short Match"
|
||||
/>
|
||||
<img
|
||||
v-if="
|
||||
data.matchDetails.max_rounds === 30 ||
|
||||
!data.matchDetails.max_rounds
|
||||
"
|
||||
alt="Match length"
|
||||
class="match-len helpicon"
|
||||
src="/images/icons/timer_long.svg"
|
||||
title="Long Match"
|
||||
/>
|
||||
|
||||
<span v-if="data.matchDetails.parsed" class="text-muted px-2"
|
||||
>—</span
|
||||
>
|
||||
|
||||
<img
|
||||
v-if="data.matchDetails.parsed"
|
||||
:alt="DisplayRank(Math.floor(data.matchDetails.avg_rank || 0))[1]"
|
||||
:src="DisplayRank(Math.floor(data.matchDetails.avg_rank || 0))[0]"
|
||||
:title="
|
||||
'Average Rank: ' +
|
||||
DisplayRank(Math.floor(data.matchDetails.avg_rank || 0))[1]
|
||||
"
|
||||
class="rank-icon helpicon"
|
||||
/>
|
||||
|
||||
<span
|
||||
v-if="data.matchDetails.parsed && data.matchDetails.replay_url"
|
||||
class="text-muted px-2"
|
||||
>—</span
|
||||
>
|
||||
|
||||
<div
|
||||
v-if="data.matchDetails.parsed && data.matchDetails.replay_url"
|
||||
class="btn-group"
|
||||
>
|
||||
<i
|
||||
id="downloadMenuBtn"
|
||||
aria-hidden="true"
|
||||
class="fa fa-ellipsis-h mx-2"
|
||||
title="Click for more"
|
||||
@click.prevent="handleDownloadMenu"
|
||||
></i>
|
||||
<div id="downloadGroup" class="group">
|
||||
<a
|
||||
v-if="data.matchDetails.replay_url"
|
||||
:href="data.matchDetails.replay_url"
|
||||
target="_blank"
|
||||
title="Download Demo"
|
||||
>
|
||||
<i
|
||||
id="downloadDemo"
|
||||
aria-hidden="true"
|
||||
class="fa fa-download mx-2"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
v-if="data.matchDetails.share_code"
|
||||
:href="
|
||||
'steam://rungame/730/76561202255233023/+csgo_download_match ' +
|
||||
data.matchDetails.share_code
|
||||
"
|
||||
target="_blank"
|
||||
title="Watch Demo"
|
||||
>
|
||||
<i
|
||||
id="replayDemo"
|
||||
aria-hidden="true"
|
||||
class="fa fa-television mx-2"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav navbar-dark navbar-expand-lg">
|
||||
<button
|
||||
aria-controls="matchNav"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
class="navbar-toggler"
|
||||
data-bs-target="#matchNav"
|
||||
data-bs-toggle="collapse"
|
||||
type="button"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div
|
||||
id="matchNav"
|
||||
class="collapse navbar-collapse justify-content-between"
|
||||
>
|
||||
<ul class="list-unstyled d-flex m-auto">
|
||||
<li
|
||||
:title="
|
||||
!data.matchDetails.parsed ? 'This demo has not been parsed' : ''
|
||||
"
|
||||
class="list-item nav-item"
|
||||
>
|
||||
<router-link
|
||||
:to="'/match/' + data.matchDetails.match_id"
|
||||
class="nav-link"
|
||||
replace
|
||||
>Scoreboard
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
:title="
|
||||
!data.matchDetails.parsed ? 'This demo has not been parsed' : ''
|
||||
"
|
||||
class="list-item nav-item"
|
||||
>
|
||||
<router-link
|
||||
:class="!data.matchDetails.parsed ? 'disabled' : ''"
|
||||
:disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/economy'"
|
||||
class="nav-link"
|
||||
replace
|
||||
>Economy
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
:title="
|
||||
!data.matchDetails.parsed ? 'This demo has not been parsed' : ''
|
||||
"
|
||||
class="list-item nav-item"
|
||||
>
|
||||
<router-link
|
||||
:class="!data.matchDetails.parsed ? 'disabled' : ''"
|
||||
:disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/details'"
|
||||
class="nav-link"
|
||||
replace
|
||||
>Details
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
:title="
|
||||
!data.matchDetails.parsed ? 'This demo has not been parsed' : ''
|
||||
"
|
||||
class="list-item nav-item"
|
||||
>
|
||||
<router-link
|
||||
:class="!data.matchDetails.parsed ? 'disabled' : ''"
|
||||
:disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/flashes'"
|
||||
class="nav-link"
|
||||
replace
|
||||
>Flashes
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
:title="
|
||||
!data.matchDetails.parsed ? 'This demo has not been parsed' : ''
|
||||
"
|
||||
class="list-item nav-item"
|
||||
>
|
||||
<router-link
|
||||
:class="!data.matchDetails.parsed ? 'disabled' : ''"
|
||||
:disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/damage'"
|
||||
class="nav-link"
|
||||
replace
|
||||
>Damage
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
:title="
|
||||
!data.matchDetails.parsed ? 'This demo has not been parsed' : ''
|
||||
"
|
||||
class="list-item nav-item"
|
||||
>
|
||||
<router-link
|
||||
:class="!data.matchDetails.parsed ? 'disabled' : ''"
|
||||
:disabled="!data.matchDetails.parsed"
|
||||
:to="'/match/' + data.matchDetails.match_id + '/chat'"
|
||||
class="nav-link"
|
||||
replace
|
||||
>Chat
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="scoreWrapper" class="scoreboard">
|
||||
<router-view v-if="data.score.length === 2 && data.stats" name="score" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
reactive,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
import {
|
||||
closeNav,
|
||||
CreatePlayersArray,
|
||||
DisplayRank,
|
||||
errorHandling,
|
||||
FixMapName,
|
||||
FormatDuration,
|
||||
FormatFullDate,
|
||||
GetMatchDetails,
|
||||
GoToLink,
|
||||
LoadImage,
|
||||
ProcessName,
|
||||
} from "/src/utils";
|
||||
import { useStore } from "vuex";
|
||||
import { useRoute } from "vue-router";
|
||||
import { DateTime } from "luxon/build/es6/luxon";
|
||||
import { FOOTER_HEIGHT, NAV_HEIGHT } from "/src/constants";
|
||||
|
||||
export default {
|
||||
name: "MatchView",
|
||||
props: ["match_id"],
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const pHeight = ref(0);
|
||||
|
||||
const matchIdPattern = /^\d{19}$/;
|
||||
|
||||
// Refs
|
||||
const data = reactive({
|
||||
player_id: "",
|
||||
matchDetails: {},
|
||||
stats: [],
|
||||
score: [0],
|
||||
team1Avg: 0,
|
||||
team2Avg: 0,
|
||||
});
|
||||
|
||||
const getWindowHeight = () => {
|
||||
const navHeight = document.getElementsByTagName("nav")[0].clientHeight;
|
||||
const footerHeight =
|
||||
document.getElementsByTagName("footer")[0].clientHeight;
|
||||
|
||||
// 70 = nav-height | 108.5 = footer-height
|
||||
return window.innerHeight - navHeight - footerHeight;
|
||||
};
|
||||
|
||||
pHeight.value = getWindowHeight();
|
||||
|
||||
// Functions
|
||||
const GetMatch = async () => {
|
||||
if (matchIdPattern.test(props.match_id)) {
|
||||
const res = await GetMatchDetails(store, props.match_id);
|
||||
|
||||
if (res !== null) {
|
||||
if (res.map)
|
||||
document.title = `${FixMapName(res.map)} ► ${res.score[0]} : ${
|
||||
res.score[1]
|
||||
} ◄ ${DateTime.fromSeconds(res.date).toLocaleString(
|
||||
DateTime.DATETIME_SHORT
|
||||
)} | csgoWTF`;
|
||||
else document.title = `Match-Details | csgoWTF`;
|
||||
|
||||
store.commit({
|
||||
type: "changeMatchDetails",
|
||||
data: res,
|
||||
});
|
||||
|
||||
checkRoute();
|
||||
data.matchDetails = store.state.matchDetails;
|
||||
|
||||
data.matchDetails.stats.forEach((p) => {
|
||||
p.player.name = ProcessName(p.player.name);
|
||||
});
|
||||
|
||||
data.stats = data.matchDetails.stats;
|
||||
data.score = data.matchDetails.score;
|
||||
|
||||
// Set avg team ranks
|
||||
let pCount = 1;
|
||||
data.team1Avg =
|
||||
Math.floor(
|
||||
getTeamAvgRank(1).reduce((a, b) => {
|
||||
if (a !== 0 && b !== 0) pCount++;
|
||||
return a + b;
|
||||
})
|
||||
) / pCount;
|
||||
|
||||
pCount = 1;
|
||||
data.team2Avg =
|
||||
Math.floor(
|
||||
getTeamAvgRank(2).reduce((a, b) => {
|
||||
if (a !== 0 && b !== 0) pCount++;
|
||||
return a + b;
|
||||
})
|
||||
) / pCount;
|
||||
|
||||
LoadImage(data.matchDetails.map ? data.matchDetails.map : "random");
|
||||
|
||||
store.commit({
|
||||
type: "changePlayersArr",
|
||||
data: CreatePlayersArray(data.stats),
|
||||
});
|
||||
|
||||
// console.log(data.matchDetails)
|
||||
} else {
|
||||
document.querySelector(".bg-img").style.display = "none";
|
||||
}
|
||||
} else {
|
||||
errorHandling(404);
|
||||
}
|
||||
};
|
||||
|
||||
const checkRoute = () => {
|
||||
if (route.fullPath.split("/")[3]) {
|
||||
const sub = route.fullPath.split("/")[3];
|
||||
if (matchIdPattern.test(props.match_id)) {
|
||||
GoToLink(`/match/${props.match_id}/${sub}`);
|
||||
} else {
|
||||
errorHandling(404);
|
||||
}
|
||||
} else {
|
||||
if (matchIdPattern.test(props.match_id))
|
||||
GoToLink(`/match/${props.match_id}`);
|
||||
else {
|
||||
errorHandling(404);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getTeamAvgRank = (team) => {
|
||||
let arr = [];
|
||||
for (let i = (team - 1) * 5; i < team * 5; i++) {
|
||||
arr.push(
|
||||
data.matchDetails.stats[i].rank?.old !== undefined
|
||||
? data.matchDetails.stats[i].rank?.old
|
||||
: 0
|
||||
);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
const handleDownloadMenu = () => {
|
||||
const downloadGroup = document.getElementById("downloadGroup");
|
||||
const menuBtn = document.getElementById("downloadMenuBtn");
|
||||
let opacity = window
|
||||
.getComputedStyle(menuBtn)
|
||||
.getPropertyValue("opacity");
|
||||
|
||||
function show() {
|
||||
if (opacity < 1) {
|
||||
opacity = opacity + 0.1;
|
||||
downloadGroup.style.opacity = opacity;
|
||||
} else {
|
||||
clearInterval(0);
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (opacity > 0) {
|
||||
opacity = opacity - 0.1;
|
||||
menuBtn.style.opacity = opacity;
|
||||
} else {
|
||||
menuBtn.style.display = "none";
|
||||
downloadGroup.style.opacity = 0;
|
||||
downloadGroup.style.display = "block";
|
||||
setInterval(show, 35);
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(hide, 35);
|
||||
};
|
||||
|
||||
// Watchers
|
||||
watch(() => props.match_id, GetMatch);
|
||||
|
||||
// Run on create
|
||||
onBeforeMount(() => {
|
||||
GetMatch();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.commit("resetMatchDetails");
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const headHeight = 230;
|
||||
const navHeight = 42;
|
||||
|
||||
const height =
|
||||
window.innerHeight -
|
||||
NAV_HEIGHT -
|
||||
FOOTER_HEIGHT -
|
||||
headHeight -
|
||||
navHeight;
|
||||
const scoreWrapper = document.getElementById("scoreWrapper");
|
||||
scoreWrapper.style.minHeight = height + "px";
|
||||
|
||||
document.getElementById("app").style.background =
|
||||
"linear-gradient(90deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.85) 30%, rgba(0, 0, 0, 0.85) 70%, rgba(0, 0, 0, .6) 100%)";
|
||||
document.querySelector(".bg-img").style.display = "initial";
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
pHeight.value = getWindowHeight();
|
||||
};
|
||||
|
||||
document.addEventListener("click", () => {
|
||||
closeNav("matchNav");
|
||||
});
|
||||
|
||||
return {
|
||||
data,
|
||||
DisplayRank,
|
||||
FormatFullDate,
|
||||
FormatDuration,
|
||||
FixMapName,
|
||||
route,
|
||||
pHeight,
|
||||
handleDownloadMenu,
|
||||
getTeamAvgRank,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.head {
|
||||
height: 230px;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 0, 0, 0.3) 0%,
|
||||
rgba(0, 0, 0, 0.55) 30%,
|
||||
rgba(0, 0, 0, 0.55) 70%,
|
||||
rgba(0, 0, 0, 0.3) 100%
|
||||
);
|
||||
|
||||
.map-score {
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
.map img {
|
||||
width: auto;
|
||||
height: 100px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.score-team-1,
|
||||
.score-team-2 {
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
|
||||
h1 {
|
||||
margin: 0 auto 0.5rem;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.team-avg-rank {
|
||||
margin: 3.5rem auto 0;
|
||||
|
||||
.team-avg-rank-icon {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.team-1,
|
||||
.team-2 {
|
||||
position: relative;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
opacity: 0.8;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
&:first-child {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: 20px;
|
||||
z-index: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.team-1 {
|
||||
right: 1.4rem;
|
||||
}
|
||||
|
||||
.team-2 {
|
||||
left: -1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.score-team-1 {
|
||||
left: 25%;
|
||||
}
|
||||
|
||||
.score-team-2 {
|
||||
right: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
.rank-icon {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.match-len {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
#downloadMenuBtn {
|
||||
cursor: pointer;
|
||||
font-size: 1.3rem;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: none;
|
||||
margin-left: -5px;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
font-size: 1.3rem;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--bs-warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
max-width: 100vw;
|
||||
min-height: 42px;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 0, 0, 0.7) 0%,
|
||||
rgba(0, 0, 0, 0.95) 30%,
|
||||
rgba(0, 0, 0, 0.95) 70%,
|
||||
rgba(0, 0, 0, 0.7) 100%
|
||||
);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
|
||||
.nav-link {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: var(--bs-info);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.router-link-exact-active {
|
||||
background: var(--bs-info);
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: #585858;
|
||||
|
||||
&:hover {
|
||||
background: lime;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#scoreWrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.score-team-1,
|
||||
.score-team-2 {
|
||||
top: 1rem !important;
|
||||
|
||||
h1 {
|
||||
font-size: 2.8rem !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.team-avg-rank {
|
||||
margin: 2rem auto 0 !important;
|
||||
|
||||
.team-avg-rank-icon {
|
||||
width: 50px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.team-1,
|
||||
.team-2 {
|
||||
img {
|
||||
width: 25px !important;
|
||||
height: 25px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.team-2 {
|
||||
left: -1.3rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.score-team-1 {
|
||||
left: 10% !important;
|
||||
}
|
||||
|
||||
.score-team-2 {
|
||||
right: 10% !important;
|
||||
}
|
||||
|
||||
.nav {
|
||||
button {
|
||||
outline: 1px solid var(--bs-primary);
|
||||
margin-left: auto;
|
||||
float: right;
|
||||
margin-right: 1rem;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
outline: 1px solid var(--bs-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--bs-primary);
|
||||
|
||||
ul {
|
||||
flex-direction: column;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#scoreWrapper {
|
||||
justify-content: flex-start;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,543 +0,0 @@
|
||||
<template>
|
||||
<div class="wrapper" :style="{minHeight: pHeight + 'px'}">
|
||||
<div class="container-lg">
|
||||
<div v-if="store.state.playerDetails.name">
|
||||
<div class="card mb-3 bg-transparent border-0">
|
||||
<div class="row g-0">
|
||||
<div class="img-container col-md-2 pt-3">
|
||||
<img
|
||||
:class="data.tracked ? 'tracked' : ''"
|
||||
:src="constructAvatarUrl(store.state.playerDetails.avatar, 'full')"
|
||||
:title="data.tracked ? 'Tracked' : ''"
|
||||
alt="Player avatar"
|
||||
class="img-fluid avatar">
|
||||
</div>
|
||||
<div class="col-md-8 d-flex">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title"><a
|
||||
:href="/^\d{17}$/.test(props.id) ? 'https://steamcommunity.com/profiles/' + props.id : 'https://steamcommunity.com/id/' + props.id"
|
||||
class="text-decoration-none text-white"
|
||||
target="_blank"
|
||||
title="Open steam profile">{{
|
||||
store.state.playerDetails.name
|
||||
}}
|
||||
<i class="fa fa-steam"></i>
|
||||
</a></h3>
|
||||
<table class="table table-borderless text-center">
|
||||
<tr>
|
||||
<th class="wlt-win text-uppercase text-muted">Wins</th>
|
||||
<th class="wlt-loss text-uppercase text-muted">Losses</th>
|
||||
<th class="wlt-tie text-uppercase text-muted">Ties</th>
|
||||
<th class="wlt-win-rate text-uppercase text-muted">Win-Rate</th>
|
||||
<th class="wlt-tie-rate text-uppercase text-muted">Tie-Rate</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="wlt-win">{{ data.match_stats.win }}</td>
|
||||
<td class="wlt-loss">{{ data.match_stats.loss }}</td>
|
||||
<td class="wlt-tie">{{ data.match_stats.tie }}</td>
|
||||
<td class="wlt-win-rate">{{
|
||||
data.match_stats.win > 0 ? (data.match_stats.win / data.match_stats.total * 100).toFixed(0) : 0
|
||||
}}%
|
||||
</td>
|
||||
<td class="wlt-tie-rate">{{
|
||||
data.match_stats.tie > 0 ? (data.match_stats.tie / data.match_stats.total * 100).toFixed(0) : 0
|
||||
}}%
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="badges">
|
||||
<img v-if="store.state.playerDetails.vac"
|
||||
:title="'VAC-Ban: ' + FormatVacDate(store.state.playerDetails.vac_date, store.state.matchDetails.date)"
|
||||
alt="Vac banned"
|
||||
src="/images/icons/vac_banned.svg">
|
||||
<img v-if="store.state.playerDetails.game_ban"
|
||||
:title="'Game-Ban: ' + FormatVacDate(store.state.playerDetails.game_ban_date, store.state.matchDetails.date)"
|
||||
alt="Game banned"
|
||||
src="/images/icons/game_banned.svg">
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!data.tracked" class="dropdown trackme-btn">
|
||||
<button
|
||||
id="login-dropdown"
|
||||
aria-expanded="false"
|
||||
class="btn border-2 btn-outline-info"
|
||||
data-bs-toggle="dropdown"
|
||||
type="button"
|
||||
>
|
||||
Track Me!
|
||||
</button>
|
||||
<div aria-labelledby="login-dropdown" class="dropdown-menu mt-2 border-2 border-primary bg-body"
|
||||
style="width: 320px">
|
||||
<form class="px-4 py-3">
|
||||
<!-- AuthCode input -->
|
||||
<div class="form-outline mb-4">
|
||||
<input id="track-authcode" v-model="data.userData.authcode" class="form-control bg-secondary"
|
||||
placeholder="AuthCode (required)"
|
||||
required type="text"/>
|
||||
</div>
|
||||
|
||||
<!-- ShareCode input -->
|
||||
<div class="form-outline mb-2">
|
||||
<input id="track-sharecode" v-model="data.userData.sharecode" class="form-control bg-secondary"
|
||||
:placeholder="store.state.playerDetails.matches ? 'ShareCode (optional)' : 'ShareCode (required)'"
|
||||
:required="!store.state.playerDetails.matches"
|
||||
type="text"/>
|
||||
</div>
|
||||
|
||||
<div class="form-outline mb-4">
|
||||
<small>
|
||||
<a href="https://help.steampowered.com/en/wizard/HelpWithGameIssue/?appid=730&issueid=128"
|
||||
target="_blank">
|
||||
Here you can find your AuthCode and ShareCode
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Submit button -->
|
||||
<button class="btn btn-outline-warning border-2" type="submit"
|
||||
@click.prevent="TrackPlayer">
|
||||
TrackMe
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="data.tracked" class="refresh-btn" title="Refresh Match-List" @click="RefreshData">
|
||||
<i class="fa fa-refresh fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="match-container d-flex">
|
||||
<div class="matches">
|
||||
|
||||
<MatchesTable v-if="store.state.playerDetails.matches" :matches="store.state.playerDetails.matches" color-front />
|
||||
<h5 v-else>Track yourself to see your matches</h5>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="store.state.playerDetails.matches" class="side-info-container">
|
||||
|
||||
<PlayerSideInfo :player_meta="data.playerMeta"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="load-more col-lg-9 col-md-12 text-center">
|
||||
<button v-if="data.match_stats.total !== data.matches.length" :key="scrollToPos(store.state.scroll_state)"
|
||||
class="btn border-2 btn-outline-info" @click="setMoreMatches">Load More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center pt-5">
|
||||
<h3>Player-Page</h3>
|
||||
<hr>
|
||||
<h6>There seems to be a problem loading the player</h6>
|
||||
<h6>Please try again later</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, watch} from "vue";
|
||||
import {useStore} from "vuex";
|
||||
import {
|
||||
constructAvatarUrl,
|
||||
DisplayRank,
|
||||
FixMapName,
|
||||
FormatVacDate,
|
||||
GetPlayerMeta,
|
||||
GetUser,
|
||||
GetWinLoss,
|
||||
GoToPlayer,
|
||||
LoadImage,
|
||||
LoadMoreMatches,
|
||||
MatchNotParsedTime,
|
||||
ProcessName,
|
||||
SaveLastVisitedToLocalStorage,
|
||||
scrollToPos,
|
||||
setTitle,
|
||||
TrackMe
|
||||
} from "@/utils";
|
||||
import {FOOTER_HEIGHT, NAV_HEIGHT} from "@/constants";
|
||||
import MatchesTable from "@/components/MatchesTable";
|
||||
import router from "@/router";
|
||||
import PlayerSideInfo from "@/components/PlayerSideInfo";
|
||||
import {StatusCodes as STATUS} from "http-status-codes";
|
||||
|
||||
export default {
|
||||
name: 'Player',
|
||||
components: {PlayerSideInfo, MatchesTable},
|
||||
props: ['id'],
|
||||
setup(props) {
|
||||
// Variables
|
||||
const store = useStore()
|
||||
const pHeight = ref(0)
|
||||
const displayCounter = 3
|
||||
|
||||
const data = reactive({
|
||||
userData: {
|
||||
authcode: '',
|
||||
sharecode: ''
|
||||
},
|
||||
tracked: false,
|
||||
matches: [],
|
||||
match_stats: {
|
||||
loss: 0,
|
||||
win: 0,
|
||||
tie: 0,
|
||||
total: 0
|
||||
},
|
||||
playerMeta: {},
|
||||
})
|
||||
|
||||
const getWindowHeight = () => {
|
||||
const navHeight = document.getElementsByTagName('nav')[0].clientHeight
|
||||
const footerHeight = document.getElementsByTagName('footer')[0].clientHeight
|
||||
|
||||
// 70 = nav-height | 108.5 = footer-height
|
||||
return window.innerHeight - navHeight - footerHeight
|
||||
}
|
||||
|
||||
pHeight.value = getWindowHeight()
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (Object.entries(store.state.playerDetails).length === 0) {
|
||||
GetPlayer()
|
||||
} else {
|
||||
// console.log(store.state.playerDetails)
|
||||
SetPlayerData()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const SetPlayerData = async () => {
|
||||
data.tracked = store.state.playerDetails.tracked
|
||||
if (store.state.playerDetails.matches)
|
||||
data.matches = store.state.playerDetails.matches
|
||||
if (store.state.playerDetails.match_stats) {
|
||||
data.match_stats.loss = store.state.playerDetails.match_stats.loss || 0
|
||||
data.match_stats.win = store.state.playerDetails.match_stats.win || 0
|
||||
data.match_stats.tie = store.state.playerDetails.match_stats.tie || 0
|
||||
data.match_stats.total = data.match_stats.loss + data.match_stats.win + data.match_stats.tie
|
||||
}
|
||||
|
||||
store.commit({
|
||||
type: 'changeId64',
|
||||
id: store.state.playerDetails.steamid64
|
||||
})
|
||||
store.commit({
|
||||
type: 'changeVanityUrl',
|
||||
id: store.state.playerDetails.vanity_url || ''
|
||||
})
|
||||
|
||||
if (store.state.playerDetails.matches) {
|
||||
if (data.matches[0].map) {
|
||||
await LoadImage(data.matches[0].map)
|
||||
} else if (!data.matches[0].map && MatchNotParsedTime(data.matches[0].date) && data.matches[1].map) {
|
||||
await LoadImage(data.matches[1].map)
|
||||
} else {
|
||||
await LoadImage('random')
|
||||
}
|
||||
} else {
|
||||
await LoadImage('random')
|
||||
}
|
||||
|
||||
document.querySelector('.bg-img').style.display = 'initial'
|
||||
document.getElementById('app').style.background = 'rgba(0, 0, 0, .7)'
|
||||
|
||||
let player = {
|
||||
'steamid64': store.state.playerDetails.steamid64,
|
||||
'vanity_url': store.state.playerDetails.vanity_url || '',
|
||||
'name': store.state.playerDetails.name,
|
||||
'avatar': constructAvatarUrl(store.state.playerDetails.avatar, 'medium')
|
||||
}
|
||||
SaveLastVisitedToLocalStorage(player)
|
||||
|
||||
setTitle(store.state.playerDetails.name)
|
||||
}
|
||||
|
||||
const GetPlayer = async (reset = false) => {
|
||||
if (props.id) {
|
||||
const resData = await GetUser(store, props.id)
|
||||
|
||||
if (resData !== null) {
|
||||
if (resData.steamid64 !== store.state.playerDetails.steamid64 || reset) {
|
||||
resData.name = ProcessName(resData.name)
|
||||
|
||||
store.commit('resetPlayerDetails')
|
||||
store.commit({
|
||||
type: 'changePlayerDetails',
|
||||
data: resData
|
||||
})
|
||||
}
|
||||
|
||||
await SetPlayerData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setMoreMatches = async () => {
|
||||
const res = await LoadMoreMatches(store, store.state.playerDetails.steamid64, data.matches[data.matches.length - 1].date)
|
||||
|
||||
if (res !== null)
|
||||
await res.matches.forEach(e => data.matches.push(e))
|
||||
|
||||
scrollToPos(window.scrollY)
|
||||
|
||||
// console.log(store.state.playerDetails)
|
||||
}
|
||||
|
||||
const RefreshData = async () => {
|
||||
const refreshButton = document.querySelector('.refresh-btn .fa')
|
||||
refreshButton.classList.add('fa-spin')
|
||||
refreshButton.classList.add('fa-fw')
|
||||
refreshButton.classList.remove('fa-refresh')
|
||||
refreshButton.classList.add('fa-spinner')
|
||||
scrollToPos(0)
|
||||
|
||||
await GetPlayer(true).then(() => {
|
||||
setTimeout(() => {
|
||||
refreshButton.classList.remove('fa-spin')
|
||||
refreshButton.classList.remove('fa-fw')
|
||||
refreshButton.classList.add('fa-refresh')
|
||||
refreshButton.classList.remove('fa-spinner')
|
||||
}, 2000)
|
||||
})
|
||||
|
||||
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter)
|
||||
if (data.playerMeta === null)
|
||||
data.playerMeta = {}
|
||||
}
|
||||
|
||||
const TrackPlayer = async () => {
|
||||
let message = ''
|
||||
|
||||
if (data.matches.length === 0) {
|
||||
if (data.userData.sharecode === '') {
|
||||
message = 'Sharecode is missing'
|
||||
}
|
||||
if (data.userData.authcode === '') {
|
||||
message = 'Authcode is missing'
|
||||
}
|
||||
} else {
|
||||
if (data.userData.authcode === '') {
|
||||
message = 'Authcode is missing'
|
||||
}
|
||||
}
|
||||
|
||||
if (message !== '') {
|
||||
store.commit({
|
||||
type: 'changeInfoState',
|
||||
data: {
|
||||
statuscode: STATUS.IM_A_TEAPOT,
|
||||
message: message,
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const res = await TrackMe(store, store.state.playerDetails.steamid64, data.userData.authcode, data.userData.sharecode)
|
||||
|
||||
if (res !== null && res === STATUS.ACCEPTED) {
|
||||
location.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.id, async () => {
|
||||
await GetPlayer()
|
||||
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter)
|
||||
if (data.playerMeta === null)
|
||||
data.playerMeta = {}
|
||||
})
|
||||
|
||||
// watch(() => data.playerMeta, () => {
|
||||
// console.log(data.playerMeta)
|
||||
// })
|
||||
|
||||
onMounted(async () => {
|
||||
const height = window.innerHeight - NAV_HEIGHT - FOOTER_HEIGHT
|
||||
const wrapper = document.querySelector('.wrapper')
|
||||
wrapper.style.minHeight = height + 'px'
|
||||
|
||||
await GetPlayer()
|
||||
|
||||
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter)
|
||||
if (data.playerMeta === null)
|
||||
data.playerMeta = {}
|
||||
|
||||
scrollToPos(store.state.scroll_state)
|
||||
|
||||
// console.log(store.state.playerDetails)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.commit('changeScrollState', window.scrollY)
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.fullPath.match('/player/') && from.fullPath.match('/player/')) {
|
||||
store.commit('changeScrollState', 0)
|
||||
}
|
||||
next()
|
||||
})
|
||||
})
|
||||
|
||||
window.onresize = () => {
|
||||
pHeight.value = getWindowHeight()
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
store,
|
||||
pHeight,
|
||||
props,
|
||||
TrackPlayer,
|
||||
RefreshData,
|
||||
TrackMe,
|
||||
GetWinLoss,
|
||||
DisplayRank,
|
||||
constructAvatarUrl,
|
||||
FormatVacDate,
|
||||
FixMapName,
|
||||
GoToPlayer,
|
||||
MatchNotParsedTime,
|
||||
scrollToPos,
|
||||
setMoreMatches
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrapper {
|
||||
.load-more {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.trackme-btn,
|
||||
.refresh-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
.fa-refresh {
|
||||
color: var(--bs-warning);
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding-top: 10px;
|
||||
|
||||
.badges {
|
||||
height: 30px;
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
box-shadow: 0 0 10px black;
|
||||
|
||||
&.tracked {
|
||||
box-shadow: 0 0 20px 5px var(--bs-success);
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
font-size: .75rem;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table {
|
||||
max-width: 500px;
|
||||
|
||||
.wlt-win, .wlt-loss, .wlt-tie {
|
||||
text-align: start;
|
||||
max-width: 70px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wlt-tie-rate, .wlt-win-rate {
|
||||
text-align: end;
|
||||
max-width: 90px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.match-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
|
||||
.matches {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.side-info-container {
|
||||
width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.card {
|
||||
.avatar {
|
||||
height: 75px !important;
|
||||
width: 75px !important;
|
||||
}
|
||||
}
|
||||
.trackme-btn,
|
||||
.refresh-btn {
|
||||
top: 25px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
&:hover,
|
||||
&:focus {
|
||||
.fa {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 991px) {
|
||||
.card .avatar {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
}
|
||||
.match-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
|
||||
.matches {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.side-info-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
643
src/views/PlayerView.vue
Normal file
643
src/views/PlayerView.vue
Normal file
@@ -0,0 +1,643 @@
|
||||
<template>
|
||||
<div :style="{ minHeight: pHeight + 'px' }" class="wrapper">
|
||||
<div class="container-lg">
|
||||
<div v-if="store.state.playerDetails.name">
|
||||
<div class="card mb-3 bg-transparent border-0">
|
||||
<div class="row g-0">
|
||||
<div class="img-container col-md-2 pt-3">
|
||||
<img
|
||||
:class="data.tracked ? 'tracked' : ''"
|
||||
:src="
|
||||
constructAvatarUrl(store.state.playerDetails.avatar, 'full')
|
||||
"
|
||||
:title="data.tracked ? 'Tracked' : ''"
|
||||
alt="Player avatar"
|
||||
class="img-fluid avatar"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-8 d-flex">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">
|
||||
<a
|
||||
:href="
|
||||
/^\d{17}$/.test(props.id)
|
||||
? 'https://steamcommunity.com/profiles/' + props.id
|
||||
: 'https://steamcommunity.com/id/' + props.id
|
||||
"
|
||||
class="text-decoration-none text-white"
|
||||
target="_blank"
|
||||
title="Open steam profile"
|
||||
>{{ store.state.playerDetails.name }}
|
||||
<i class="fa fa-steam"></i>
|
||||
</a>
|
||||
</h3>
|
||||
<table class="table table-borderless text-center">
|
||||
<tr>
|
||||
<th class="wlt-win text-uppercase text-muted">Wins</th>
|
||||
<th class="wlt-loss text-uppercase text-muted">Losses</th>
|
||||
<th class="wlt-tie text-uppercase text-muted">Ties</th>
|
||||
<th class="wlt-win-rate text-uppercase text-muted">
|
||||
Win-Rate
|
||||
</th>
|
||||
<th class="wlt-tie-rate text-uppercase text-muted">
|
||||
Tie-Rate
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="wlt-win">{{ data.match_stats.win }}</td>
|
||||
<td class="wlt-loss">{{ data.match_stats.loss }}</td>
|
||||
<td class="wlt-tie">{{ data.match_stats.tie }}</td>
|
||||
<td class="wlt-win-rate">
|
||||
{{
|
||||
data.match_stats.win > 0
|
||||
? (
|
||||
(data.match_stats.win / data.match_stats.total) *
|
||||
100
|
||||
).toFixed(0)
|
||||
: 0
|
||||
}}%
|
||||
</td>
|
||||
<td class="wlt-tie-rate">
|
||||
{{
|
||||
data.match_stats.tie > 0
|
||||
? (
|
||||
(data.match_stats.tie / data.match_stats.total) *
|
||||
100
|
||||
).toFixed(0)
|
||||
: 0
|
||||
}}%
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="badges">
|
||||
<img
|
||||
v-if="store.state.playerDetails.vac"
|
||||
:title="
|
||||
'VAC-Ban: ' +
|
||||
FormatVacDate(
|
||||
store.state.playerDetails.vac_date,
|
||||
store.state.matchDetails.date
|
||||
)
|
||||
"
|
||||
alt="Vac banned"
|
||||
src="/images/icons/vac_banned.svg"
|
||||
/>
|
||||
<img
|
||||
v-if="store.state.playerDetails.game_ban"
|
||||
:title="
|
||||
'Game-Ban: ' +
|
||||
FormatVacDate(
|
||||
store.state.playerDetails.game_ban_date,
|
||||
store.state.matchDetails.date
|
||||
)
|
||||
"
|
||||
alt="Game banned"
|
||||
src="/images/icons/game_banned.svg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!data.tracked" class="dropdown trackme-btn">
|
||||
<button
|
||||
id="login-dropdown"
|
||||
aria-expanded="false"
|
||||
class="btn border-2 btn-outline-info"
|
||||
data-bs-toggle="dropdown"
|
||||
type="button"
|
||||
>
|
||||
Track Me!
|
||||
</button>
|
||||
<div
|
||||
aria-labelledby="login-dropdown"
|
||||
class="dropdown-menu mt-2 border-2 border-primary bg-body"
|
||||
style="width: 320px"
|
||||
>
|
||||
<form class="px-4 py-3">
|
||||
<!-- AuthCode input -->
|
||||
<div class="form-outline mb-4">
|
||||
<input
|
||||
id="track-authcode"
|
||||
v-model="data.userData.authcode"
|
||||
class="form-control bg-secondary"
|
||||
placeholder="AuthCode (required)"
|
||||
required
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- ShareCode input -->
|
||||
<div class="form-outline mb-2">
|
||||
<input
|
||||
id="track-sharecode"
|
||||
v-model="data.userData.sharecode"
|
||||
:placeholder="
|
||||
store.state.playerDetails.matches
|
||||
? 'ShareCode (optional)'
|
||||
: 'ShareCode (required)'
|
||||
"
|
||||
:required="!store.state.playerDetails.matches"
|
||||
class="form-control bg-secondary"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-outline mb-4">
|
||||
<small>
|
||||
<a
|
||||
href="https://help.steampowered.com/en/wizard/HelpWithGameIssue/?appid=730&issueid=128"
|
||||
target="_blank"
|
||||
>
|
||||
Here you can find your AuthCode and ShareCode
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Submit button -->
|
||||
<button
|
||||
class="btn btn-outline-warning border-2"
|
||||
type="submit"
|
||||
@click.prevent="TrackPlayer"
|
||||
>
|
||||
TrackMe
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="data.tracked"
|
||||
class="refresh-btn"
|
||||
title="Refresh Match-List"
|
||||
@click="RefreshData"
|
||||
>
|
||||
<i class="fa fa-refresh fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="match-container d-flex">
|
||||
<div class="matches">
|
||||
<MatchesTable
|
||||
v-if="store.state.playerDetails.matches"
|
||||
:matches="store.state.playerDetails.matches"
|
||||
color-front
|
||||
/>
|
||||
<h5 v-else>Track yourself to see your matches</h5>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="store.state.playerDetails.matches"
|
||||
class="side-info-container"
|
||||
>
|
||||
<PlayerSideInfo :player_meta="data.playerMeta" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="load-more col-lg-9 col-md-12 text-center">
|
||||
<button
|
||||
v-if="data.match_stats.total !== data.matches.length"
|
||||
:key="scrollToPos(store.state.scroll_state)"
|
||||
class="btn border-2 btn-outline-info"
|
||||
@click="setMoreMatches"
|
||||
>
|
||||
Load More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center pt-5">
|
||||
<h3>Player-Page</h3>
|
||||
<hr />
|
||||
<h6>There seems to be a problem loading the player</h6>
|
||||
<h6>Please try again later</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
reactive,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { useStore } from "vuex";
|
||||
import {
|
||||
constructAvatarUrl,
|
||||
DisplayRank,
|
||||
FixMapName,
|
||||
FormatVacDate,
|
||||
GetPlayerMeta,
|
||||
GetUser,
|
||||
GetWinLoss,
|
||||
GoToPlayer,
|
||||
LoadImage,
|
||||
LoadMoreMatches,
|
||||
MatchNotParsedTime,
|
||||
ProcessName,
|
||||
SaveLastVisitedToLocalStorage,
|
||||
scrollToPos,
|
||||
setTitle,
|
||||
TrackMe,
|
||||
} from "/src/utils";
|
||||
import { FOOTER_HEIGHT, NAV_HEIGHT } from "/src/constants";
|
||||
import MatchesTable from "/src/components/MatchesTable";
|
||||
import router from "/src/router";
|
||||
import PlayerSideInfo from "/src/components/PlayerSideInfo";
|
||||
import { StatusCodes as STATUS } from "http-status-codes";
|
||||
|
||||
export default {
|
||||
name: "PlayerView",
|
||||
components: { PlayerSideInfo, MatchesTable },
|
||||
props: ["id"],
|
||||
setup(props) {
|
||||
// Variables
|
||||
const store = useStore();
|
||||
const pHeight = ref(0);
|
||||
const displayCounter = 3;
|
||||
|
||||
const data = reactive({
|
||||
userData: {
|
||||
authcode: "",
|
||||
sharecode: "",
|
||||
},
|
||||
tracked: false,
|
||||
matches: [],
|
||||
match_stats: {
|
||||
loss: 0,
|
||||
win: 0,
|
||||
tie: 0,
|
||||
total: 0,
|
||||
},
|
||||
playerMeta: {},
|
||||
});
|
||||
|
||||
const getWindowHeight = () => {
|
||||
const navHeight = document.getElementsByTagName("nav")[0].clientHeight;
|
||||
const footerHeight =
|
||||
document.getElementsByTagName("footer")[0].clientHeight;
|
||||
|
||||
// 70 = nav-height | 108.5 = footer-height
|
||||
return window.innerHeight - navHeight - footerHeight;
|
||||
};
|
||||
|
||||
pHeight.value = getWindowHeight();
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (Object.entries(store.state.playerDetails).length === 0) {
|
||||
GetPlayer();
|
||||
} else {
|
||||
// console.log(store.state.playerDetails)
|
||||
SetPlayerData();
|
||||
}
|
||||
});
|
||||
|
||||
const SetPlayerData = async () => {
|
||||
data.tracked = store.state.playerDetails.tracked;
|
||||
if (store.state.playerDetails.matches)
|
||||
data.matches = store.state.playerDetails.matches;
|
||||
if (store.state.playerDetails.match_stats) {
|
||||
data.match_stats.loss = store.state.playerDetails.match_stats.loss || 0;
|
||||
data.match_stats.win = store.state.playerDetails.match_stats.win || 0;
|
||||
data.match_stats.tie = store.state.playerDetails.match_stats.tie || 0;
|
||||
data.match_stats.total =
|
||||
data.match_stats.loss + data.match_stats.win + data.match_stats.tie;
|
||||
}
|
||||
|
||||
store.commit({
|
||||
type: "changeId64",
|
||||
id: store.state.playerDetails.steamid64,
|
||||
});
|
||||
store.commit({
|
||||
type: "changeVanityUrl",
|
||||
id: store.state.playerDetails.vanity_url || "",
|
||||
});
|
||||
|
||||
if (store.state.playerDetails.matches) {
|
||||
if (data.matches[0].map) {
|
||||
await LoadImage(data.matches[0].map);
|
||||
} else if (
|
||||
!data.matches[0].map &&
|
||||
MatchNotParsedTime(data.matches[0].date) &&
|
||||
data.matches[1].map
|
||||
) {
|
||||
await LoadImage(data.matches[1].map);
|
||||
} else {
|
||||
await LoadImage("random");
|
||||
}
|
||||
} else {
|
||||
await LoadImage("random");
|
||||
}
|
||||
|
||||
document.querySelector(".bg-img").style.display = "initial";
|
||||
document.getElementById("app").style.background = "rgba(0, 0, 0, .7)";
|
||||
|
||||
let player = {
|
||||
steamid64: store.state.playerDetails.steamid64,
|
||||
vanity_url: store.state.playerDetails.vanity_url || "",
|
||||
name: store.state.playerDetails.name,
|
||||
avatar: constructAvatarUrl(store.state.playerDetails.avatar, "medium"),
|
||||
};
|
||||
SaveLastVisitedToLocalStorage(player);
|
||||
|
||||
setTitle(store.state.playerDetails.name);
|
||||
};
|
||||
|
||||
const GetPlayer = async (reset = false) => {
|
||||
if (props.id) {
|
||||
const resData = await GetUser(store, props.id);
|
||||
|
||||
if (resData !== null) {
|
||||
if (
|
||||
resData.steamid64 !== store.state.playerDetails.steamid64 ||
|
||||
reset
|
||||
) {
|
||||
resData.name = ProcessName(resData.name);
|
||||
|
||||
store.commit("resetPlayerDetails");
|
||||
store.commit({
|
||||
type: "changePlayerDetails",
|
||||
data: resData,
|
||||
});
|
||||
}
|
||||
|
||||
await SetPlayerData();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setMoreMatches = async () => {
|
||||
const res = await LoadMoreMatches(
|
||||
store,
|
||||
store.state.playerDetails.steamid64,
|
||||
data.matches[data.matches.length - 1].date
|
||||
);
|
||||
|
||||
if (res !== null) await res.matches.forEach((e) => data.matches.push(e));
|
||||
|
||||
scrollToPos(window.scrollY);
|
||||
|
||||
// console.log(store.state.playerDetails)
|
||||
};
|
||||
|
||||
const RefreshData = async () => {
|
||||
const refreshButton = document.querySelector(".refresh-btn .fa");
|
||||
refreshButton.classList.add("fa-spin");
|
||||
refreshButton.classList.add("fa-fw");
|
||||
refreshButton.classList.remove("fa-refresh");
|
||||
refreshButton.classList.add("fa-spinner");
|
||||
scrollToPos(0);
|
||||
|
||||
await GetPlayer(true).then(() => {
|
||||
setTimeout(() => {
|
||||
refreshButton.classList.remove("fa-spin");
|
||||
refreshButton.classList.remove("fa-fw");
|
||||
refreshButton.classList.add("fa-refresh");
|
||||
refreshButton.classList.remove("fa-spinner");
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter);
|
||||
if (data.playerMeta === null) data.playerMeta = {};
|
||||
};
|
||||
|
||||
const TrackPlayer = async () => {
|
||||
let message = "";
|
||||
|
||||
if (data.matches.length === 0) {
|
||||
if (data.userData.sharecode === "") {
|
||||
message = "Sharecode is missing";
|
||||
}
|
||||
if (data.userData.authcode === "") {
|
||||
message = "Authcode is missing";
|
||||
}
|
||||
} else {
|
||||
if (data.userData.authcode === "") {
|
||||
message = "Authcode is missing";
|
||||
}
|
||||
}
|
||||
|
||||
if (message !== "") {
|
||||
store.commit({
|
||||
type: "changeInfoState",
|
||||
data: {
|
||||
statuscode: STATUS.IM_A_TEAPOT,
|
||||
message: message,
|
||||
type: "error",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const res = await TrackMe(
|
||||
store,
|
||||
store.state.playerDetails.steamid64,
|
||||
data.userData.authcode,
|
||||
data.userData.sharecode
|
||||
);
|
||||
|
||||
if (res !== null && res === STATUS.ACCEPTED) {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.id,
|
||||
async () => {
|
||||
await GetPlayer();
|
||||
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter);
|
||||
if (data.playerMeta === null) data.playerMeta = {};
|
||||
}
|
||||
);
|
||||
|
||||
// watch(() => data.playerMeta, () => {
|
||||
// console.log(data.playerMeta)
|
||||
// })
|
||||
|
||||
onMounted(async () => {
|
||||
const height = window.innerHeight - NAV_HEIGHT - FOOTER_HEIGHT;
|
||||
const wrapper = document.querySelector(".wrapper");
|
||||
wrapper.style.minHeight = height + "px";
|
||||
|
||||
await GetPlayer();
|
||||
|
||||
data.playerMeta = await GetPlayerMeta(store, props.id, displayCounter);
|
||||
if (data.playerMeta === null) data.playerMeta = {};
|
||||
|
||||
scrollToPos(store.state.scroll_state);
|
||||
|
||||
// console.log(store.state.playerDetails)
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.commit("changeScrollState", window.scrollY);
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.fullPath.match("/player/") && from.fullPath.match("/player/")) {
|
||||
store.commit("changeScrollState", 0);
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
pHeight.value = getWindowHeight();
|
||||
};
|
||||
|
||||
return {
|
||||
data,
|
||||
store,
|
||||
pHeight,
|
||||
props,
|
||||
TrackPlayer,
|
||||
RefreshData,
|
||||
TrackMe,
|
||||
GetWinLoss,
|
||||
DisplayRank,
|
||||
constructAvatarUrl,
|
||||
FormatVacDate,
|
||||
FixMapName,
|
||||
GoToPlayer,
|
||||
MatchNotParsedTime,
|
||||
scrollToPos,
|
||||
setMoreMatches,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrapper {
|
||||
.load-more {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.trackme-btn,
|
||||
.refresh-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
.fa-refresh {
|
||||
color: var(--bs-warning);
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding-top: 10px;
|
||||
|
||||
.badges {
|
||||
height: 30px;
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
box-shadow: 0 0 10px black;
|
||||
|
||||
&.tracked {
|
||||
box-shadow: 0 0 20px 5px var(--bs-success);
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
font-size: 0.75rem;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table {
|
||||
max-width: 500px;
|
||||
|
||||
.wlt-win,
|
||||
.wlt-loss,
|
||||
.wlt-tie {
|
||||
text-align: start;
|
||||
max-width: 70px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wlt-tie-rate,
|
||||
.wlt-win-rate {
|
||||
text-align: end;
|
||||
max-width: 90px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.match-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
|
||||
.matches {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.side-info-container {
|
||||
width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.card {
|
||||
.avatar {
|
||||
height: 75px !important;
|
||||
width: 75px !important;
|
||||
}
|
||||
}
|
||||
.trackme-btn,
|
||||
.refresh-btn {
|
||||
top: 25px;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
&:hover,
|
||||
&:focus {
|
||||
.fa {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 991px) {
|
||||
.card .avatar {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
}
|
||||
.match-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
|
||||
.matches {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.side-info-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
@@ -4,13 +4,3 @@
|
||||
<h4 class="mt-4">The page you were looking for was not found!</h4>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "404"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -4,13 +4,3 @@
|
||||
<h4 class="mt-4">An internal server error occurred!</h4>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "500"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -4,13 +4,3 @@
|
||||
<h4 class="mt-4">You reached a bad gateway!</h4>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "502"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.vite-config.json"
|
||||
}
|
||||
]
|
||||
}
|
8
tsconfig.vite-config.json
Normal file
8
tsconfig.vite-config.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.node.json",
|
||||
"include": ["vite.config.*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
14
vite.config.ts
Normal file
14
vite.config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { fileURLToPath, URL } from 'url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
@@ -1,10 +0,0 @@
|
||||
const Dotenv = require('dotenv-webpack');
|
||||
process.env.VUE_APP_VERSION = require('./package.json').version
|
||||
|
||||
module.exports = {
|
||||
configureWebpack: {
|
||||
plugins: [
|
||||
new Dotenv()
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user