From 40fea74555d01a8379ab5152af5aa8bad446dd9a Mon Sep 17 00:00:00 2001 From: Kiho Kim Date: Sun, 10 Dec 2023 11:51:35 +0900 Subject: [PATCH] =?UTF-8?q?spa=EB=A1=9C=20=EC=9E=AC=EA=B5=AC=EC=84=B1=20(#?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * firebase login * spa 로 변경, webapp로 이동 * empty login * 재검사시에 mythic player 테이블도 업데이트 * 최초스캔전일 경우 not null 컬럼에 - 세팅 * analytics id * devcontainer ssh * remove static html * 기본 메뉴 변경 * cloudflare -> google analytics * analytics id * spa 로 변경, webapp로 이동 --- src/main/resources/static/menu.js | 29 -- src/main/resources/static/recent.js | 116 ------- src/main/resources/static/relation.js | 148 -------- src/main/resources/static/score-timeline.js | 301 ----------------- src/main/webapp/app.js | 0 src/main/webapp/character-list.js | 67 ++++ .../static => webapp}/css/vis.min.css | 0 src/main/webapp/index.html | 40 +++ .../static => webapp}/js/vis.min.js | 0 .../{resources/static => webapp}/login.js | 0 .../static/index.html => webapp/recent.html} | 4 +- src/main/webapp/recent.js | 120 +++++++ src/main/webapp/relation.html | 16 + src/main/webapp/relation.js | 161 +++++++++ src/main/webapp/score-timeline.html | 16 + src/main/webapp/score-timeline.js | 318 ++++++++++++++++++ .../{resources/static => webapp}/utils.js | 0 17 files changed, 740 insertions(+), 596 deletions(-) delete mode 100644 src/main/resources/static/menu.js delete mode 100644 src/main/resources/static/recent.js delete mode 100644 src/main/resources/static/relation.js delete mode 100644 src/main/resources/static/score-timeline.js create mode 100644 src/main/webapp/app.js create mode 100644 src/main/webapp/character-list.js rename src/main/{resources/static => webapp}/css/vis.min.css (100%) create mode 100644 src/main/webapp/index.html rename src/main/{resources/static => webapp}/js/vis.min.js (100%) rename src/main/{resources/static => webapp}/login.js (100%) rename src/main/{resources/static/index.html => webapp/recent.html} (74%) create mode 100644 src/main/webapp/recent.js create mode 100644 src/main/webapp/relation.html create mode 100644 src/main/webapp/relation.js create mode 100644 src/main/webapp/score-timeline.html create mode 100644 src/main/webapp/score-timeline.js rename src/main/{resources/static => webapp}/utils.js (100%) diff --git a/src/main/resources/static/menu.js b/src/main/resources/static/menu.js deleted file mode 100644 index c3820d0..0000000 --- a/src/main/resources/static/menu.js +++ /dev/null @@ -1,29 +0,0 @@ - -export default { - data() { - return { - 'items': [ - { 'title': '쐐기 기록', 'url': 'recent.html' }, - { 'title': '친구 그래프', 'url': 'relation.html' }, - { 'title': '쐐기평점 타임라인', 'url': 'score-timeline.html' }, - ] - } - }, - methods: { - menu_selected(index) { - location.href = this.items[index].url; - } - }, - template: ` - - - - - {{ item.title }} - - - - ` -} diff --git a/src/main/resources/static/recent.js b/src/main/resources/static/recent.js deleted file mode 100644 index 060bebf..0000000 --- a/src/main/resources/static/recent.js +++ /dev/null @@ -1,116 +0,0 @@ -import Login from './login.js' -import Menu from './menu.js' -import Utils from './utils.js' - -export default { - data() { - return { - 'characterName': '', - 'server': '', - 'servers': {}, - 'serverNames': [], - 'dungeons': {}, - 'player': null, - 'records': [], - 'showOption': true, - } - }, - components: { - Login, Menu - }, - methods: { - search() { - if (this.characterName == '') { - return; - } - this.characterName = this.characterName.substring(0, 1).toUpperCase() + - this.characterName.substring(1).toLowerCase(); - - if (window.localStorage) { - window.localStorage.setItem('relation_server', this.server); - window.localStorage.setItem('relation_character', this.characterName); - } - - fetch('char/recent/' + encodeURI(this.server) + '/' + encodeURI(this.characterName)).then(async resp => { - const data = await resp.json(); - this.player = data.player; - this.records = data.data; - }); - }, - scan() { - if (this.characterName == '') { - return; - } - this.characterName = this.characterName.substring(0, 1).toUpperCase() + - this.characterName.substring(1).toLowerCase(); - - fetch('char/scan/' + encodeURI(this.server) + '/' + encodeURI(this.characterName)); - }, - formatDate(d) { - return Utils.formatDate(d); - }, - }, - beforeMount() { - if (window.localStorage) { - this.server = window.localStorage.getItem('relation_server') - this.characterName = window.localStorage.getItem('relation_character') - } - if (this.server == '' || this.server == null) { - this.server = '아즈샤라'; - } - }, - mounted() { - fetch('form/realms').then(async resp => { - this.servers = await resp.json() - for (var key in this.servers) { - this.serverNames.push(this.servers[key]); - } - if (this.server == '') { - this.server = this.serverNames[0]['value'] - } - }); - fetch('form/dungeons').then(async resp => { - this.dungeons = await resp.json() - }) - }, - template: ` - - - - - - - - - - - - 검색 - {{ showOption ? "검색 조건 닫기" : "검색 조건 열기" }} - - - 재검사 - - - - -
마지막 갱신 일자: {{ formatDate(player?.lastUpdateTs ?? 0) }}
-
-
- - - {{ formatDate(record.completedTimestamp) }} - - - {{ dungeons[record.dungeonId] }} - - - {{ record.keystoneLevel }} - - - {{ record.keystoneUpgrade }} - - - - `, -} diff --git a/src/main/resources/static/relation.js b/src/main/resources/static/relation.js deleted file mode 100644 index 65d059b..0000000 --- a/src/main/resources/static/relation.js +++ /dev/null @@ -1,148 +0,0 @@ -import Login from './login.js' -import Menu from './menu.js' - -export default { - data() { - return { - 'characterName': '', - 'server': '', - 'servers': {}, - 'serverNames': [], - 'minimumRun': 1, - 'players': new vis.DataSet([]), - 'relations': new vis.DataSet([]), - 'nextId': 1, - 'showOption': true, - } - }, - components: { - Login, Menu - }, - methods: { - search() { - if (this.characterName == '') { - return; - } - this.characterName = this.characterName.substring(0, 1).toUpperCase() + - this.characterName.substring(1).toLowerCase(); - - if (window.localStorage) { - window.localStorage.setItem('relation_server', this.server); - window.localStorage.setItem('relation_character', this.characterName); - window.localStorage.setItem('relation_run', this.minimumRun); - } - - fetch('char/relation/' + encodeURI(this.server) + '/' + encodeURI(this.characterName) + '/' + this.minimumRun).then(async resp => { - const data = await resp.json(); - let i1 = this.players.get().find(p => p.label == this.characterName + '-' + this.server); - if (i1 === undefined) { - i1 = { - id: this.nextId++, - label: this.characterName + '-' + this.server - }; - this.players.add(i1); - } - - data.forEach(dd => { - if (dd.value >= this.minimumRun) { - let i2 = this.players.get().find(p => p.label == dd.name + '-' + dd.realm); - if (i2 === undefined) { - i2 = { - id: this.nextId++, - label: dd.name + '-' + dd.realm - }; - this.players.add(i2); - } - - var exists = this.relations.get().some(rel => (rel.from == i1.id && rel.to == i2.id) || (rel.from == i2.id && rel.to == i1.id)); - // this.relations.forEach(rel => { - // if (rel.from == i1.id && rel.to == i2.id) { - // exists = true - // } else if (rel.from == i2.id && rel.to == i1.id) { - // exists = true - // } - // }) - if (!exists) { - this.relations.add({from: i1.id, to: i2.id, value: dd.value, title: dd.value }) - } - } - }) - }) - }, - }, - beforeMount() { - if (window.localStorage) { - const server = window.localStorage.getItem('relation_server') - const run = parseInt(window.localStorage.getItem('relation_run')) - this.server = server - this.characterName = window.localStorage.getItem('relation_character') - if (run > 0) { - this.minimumRun = run - } - } - if (this.server == '' || this.server == null) { - this.server = '아즈샤라'; - } - }, - mounted() { - fetch('form/realms').then(async resp => { - this.servers = await resp.json() - for (var key in this.servers) { - this.serverNames.push(this.servers[key]); - // { - // "label": this.servers[key], - // "value": this.servers[key], - // }) - } - if (this.server == '') { - this.server = this.serverNames[0]['value'] - } - }) - - var container = document.getElementById('relations') - var data = { - nodes: this.players, - edges: this.relations - } - var options = {} - var network = new vis.Network(container, data, options) - network.on('doubleClick', e => { - if (e.nodes.length > 0) { - var id = e.nodes[0]; - var pl = this.players.get().find(p => p.id == id); - var name = pl.label; - this.server = name.substring(name.indexOf('-') + 1); - this.characterName = name.substring(0, name.indexOf('-')); - this.search(); - } - }) - }, - template: ` - - - - - - - - - - - - - - - 검색 - {{ showOption ? "검색 조건 닫기" : "검색 조건 열기" }} - - - - - - -
-
-
- - `, -} diff --git a/src/main/resources/static/score-timeline.js b/src/main/resources/static/score-timeline.js deleted file mode 100644 index 9a415da..0000000 --- a/src/main/resources/static/score-timeline.js +++ /dev/null @@ -1,301 +0,0 @@ -import Login from './login.js' -import Menu from './menu.js' - -export default { - data() { - return { - 'characterName': '', - 'server': '', - 'servers': {}, - 'serverNames': [], - 'showOption': true, - 'showDungeons': true, - 'allSeason': false, - 'graph2d': null, - 'graphs': { - '1': { - 'items': [], - 'groups': [{ - id: '0', - content: "total", - options: { - excludeFromStacking: true - } - }], - 'options': { - style: 'bar', - stack: true, - legend: true, - locale: 'en', - // zoomable: false, - // barChart: { width: 50, align: 'center' }, - drawPoints: { - onRender: function(item, group, grap2d) { - return item.label != null; - }, - style: 'circle' - }, - dataAxis: { - icons: true, - left: { range: { min: 0, max: 0 } } - }, - height: 900 - } - }, - '0': { - 'items': [], - 'groups': [{ - id: '0', - content: "total", - options: { - excludeFromStacking: true - } - }], - 'options': { - style: 'bar', - stack: false, - legend: false, - locale: 'en', - // zoomable: false, - // barChart: { width: 50, align: 'center' }, - drawPoints: { - onRender: function(item, group, grap2d) { - return item.label != null; - }, - style: 'circle' - }, - dataAxis: { - icons: true, - left: { range: { min: 0, max: 0 } } - }, - height: 900 - } - } - } - } - }, - components: { - Login, Menu - }, - watch: { - showDungeons(newVal, oldVal) { - if (this.graph2d == null) { - return; - } - const g = this.graphs[this.showDungeons ? 1 : 0]; - this.graph2d.setItems(g.items); - this.graph2d.setGroups(g.groups); - this.graph2d.setOptions(g.options); - } - }, - methods: { - async search() { - if (this.characterName == '') { - return; - } - this.characterName = this.characterName.substring(0, 1).toUpperCase() + - this.characterName.substring(1).toLowerCase(); - - if (window.localStorage) { - window.localStorage.setItem('relation_server', this.server); - window.localStorage.setItem('relation_character', this.characterName); - } - - const resp = await fetch(`char/mythic_rating/${encodeURI(this.server)}/${encodeURI(this.characterName)}?season=${this.allSeason?1:0}`); - const body = await resp.json(); - const data = body; - - let season = 0; - let period = 0; - let dungeonScore = {}; - let minTimestamp = 0; - let maxTimestamp = 0; - let periodTimestamp = 0; - const oneWeek = 7*24*3600*1000; - - this.graphs['1'].items = []; - this.graphs['1'].groups = [{ - id: '0', - content: "total", - options: { - excludeFromStacking: true - } - }]; - this.graphs['0'].items = []; - const items1 = this.graphs['1'].items; - const groups = this.graphs['1'].groups; - const items2 = this.graphs['0'].items; - - const addSummary = () => { - let score = 0 - for (let did in dungeonScore) { - const arr = dungeonScore[did] - const dscore = Math.max(arr[0], arr[1]) * 1.5 + Math.min(arr[0], arr[1]) * 0.5 - score += dscore - - const dname = groups.find(g => g.id == String(did)).content - - items1.push({ - x: periodTimestamp, - y: dscore, - end: periodTimestamp + oneWeek, - group: String(did), - label: { - content: String(Math.round(dscore)) + '(' + dname.substring(0, 1) + ')', - xOffset: 0, - yOffset: 20 - } - }) - } - if (score > 0) { - items1.push({ - x: periodTimestamp, - y: score, - end: periodTimestamp + oneWeek, - group: '0', - label: { - content: String(Math.round(score)), - xOffset: 0, - yOffset: -20 - } - }) - items2.push({ - x: periodTimestamp, - y: score, - end: periodTimestamp + oneWeek, - group: '0', - label: { - content: String(Math.round(score)), - xOffset: 0, - yOffset: -20 - } - }) - } - } - - data.forEach(data => { - if (data.season != season) { - dungeonScore = {}; - season = data.season; - period = 0; - } - if (data.period != period) { - if (minTimestamp == 0) { - minTimestamp = data.timestamp; - } else { - while (period < data.period) { - addSummary(); - period++; - periodTimestamp += oneWeek; - } - } - period = data.period - periodTimestamp = data.timestamp - maxTimestamp = data.timestamp - } - if (!dungeonScore[data.dungeon_id]) { - dungeonScore[data.dungeon_id] = [0, 0] - } - dungeonScore[data.dungeon_id][period % 2] = Math.max(dungeonScore[data.dungeon_id][period % 2], data.mythic_rating) - if (!(groups.some(g => g.id == String(data.dungeon_id)))) { - groups.push({ - id: String(data.dungeon_id), - content: data.dungeon_name - }) - } - }) - addSummary() - if (items1.length >= 0) { - const timelineMargin = oneWeek; - const container = document.getElementById('timeline'); - container.innerHTML = ''; - const g = this.graphs[this.showDungeons ? 1 : 0]; - - const xRangeMin = minTimestamp - timelineMargin; - const xRangeMax = maxTimestamp + timelineMargin * 2; - const yRangeMax = Math.ceil(Math.max.apply(null, items1.filter(it => it.group == '0').map(it => it.y)) / 100) * 100 + 200; - - this.graphs['0'].options.min = xRangeMin; - this.graphs['0'].options.max = xRangeMax; - this.graphs['0'].options.dataAxis.left.range.max = yRangeMax; - this.graphs['1'].options.min = xRangeMin; - this.graphs['1'].options.max = xRangeMax; - this.graphs['1'].options.dataAxis.left.range.max = yRangeMax; - - this.graph2d = new vis.Graph2d(container, g.items, g.groups, g.options); - this.graph2d.setWindow(xRangeMin, xRangeMax); - } - }, - - scan() { - if (this.characterName == '') { - return; - } - this.characterName = this.characterName.substring(0, 1).toUpperCase() + - this.characterName.substring(1).toLowerCase(); - - fetch('char/scan/' + encodeURI(this.server) + '/' + encodeURI(this.characterName)); - }, - - toggleDungeon() { - this.showDungeons = !this.showDungeons; - }, - - toggleSeason() { - this.allSeason = !this.allSeason; - }, - }, - beforeMount() { - if (window.localStorage) { - const server = window.localStorage.getItem('relation_server') - const run = parseInt(window.localStorage.getItem('relation_run')) - this.server = server - this.characterName = window.localStorage.getItem('relation_character') - if (run > 0) { - this.minimumRun = run - } - } - if (this.server == '' || this.server == null) { - this.server = '아즈샤라'; - } - }, - mounted() { - fetch('form/realms').then(async resp => { - this.servers = await resp.json() - for (let key in this.servers) { - this.serverNames.push(this.servers[key]); - } - if (this.server == '') { - this.server = this.serverNames[0]['value'] - } - }) - }, - template: ` - - - - - - - - - - - - 검색 - {{ showOption ? "검색 조건 닫기" : "검색 조건 열기" }} - - - {{ showDungeons ? "던전별 표시" : "총점 표시" }} - {{ allSeason ? "전체시즌" : "현재시즌" }} - 재검사 - - - - -
-
-
- - `, -}; diff --git a/src/main/webapp/app.js b/src/main/webapp/app.js new file mode 100644 index 0000000..e69de29 diff --git a/src/main/webapp/character-list.js b/src/main/webapp/character-list.js new file mode 100644 index 0000000..6a8de5d --- /dev/null +++ b/src/main/webapp/character-list.js @@ -0,0 +1,67 @@ +export default { + data() { + return { + characterName: "", + server: "", + }; + }, + components: {}, + props: ["serverNames", "characters"], + methods: { + insert() { + if (this.characterName == "") { + return; + } + this.characterName = + this.characterName.substring(0, 1).toUpperCase() + + this.characterName.substring(1).toLowerCase(); + + this.$emit("added", { + name: this.characterName, + server: this.server, + }); + }, + remove(index) { + this.$emit("removed", index); + }, + }, + beforeMount() { + if (window.localStorage) { + const server = window.localStorage.getItem("relation_server"); + const run = parseInt(window.localStorage.getItem("relation_run")); + this.server = server; + this.characterName = window.localStorage.getItem("relation_character"); + } + if (this.server == "" || this.server == null) { + this.server = "아즈샤라"; + } + }, + mounted() {}, + template: ` + + + + + + + + + + 추가 + + + + + + {{ item.name }} + + + {{ item.server }} + + + 삭제 + + + + `, +}; diff --git a/src/main/resources/static/css/vis.min.css b/src/main/webapp/css/vis.min.css similarity index 100% rename from src/main/resources/static/css/vis.min.css rename to src/main/webapp/css/vis.min.css diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 0000000..5a8d2b1 --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,40 @@ + + + + +Mythic+ + + + + + + + + + + +
+ + + + + + diff --git a/src/main/resources/static/js/vis.min.js b/src/main/webapp/js/vis.min.js similarity index 100% rename from src/main/resources/static/js/vis.min.js rename to src/main/webapp/js/vis.min.js diff --git a/src/main/resources/static/login.js b/src/main/webapp/login.js similarity index 100% rename from src/main/resources/static/login.js rename to src/main/webapp/login.js diff --git a/src/main/resources/static/index.html b/src/main/webapp/recent.html similarity index 74% rename from src/main/resources/static/index.html rename to src/main/webapp/recent.html index 31a0f92..cc1df71 100644 --- a/src/main/resources/static/index.html +++ b/src/main/webapp/recent.html @@ -9,8 +9,8 @@ - + \ No newline at end of file diff --git a/src/main/webapp/recent.js b/src/main/webapp/recent.js new file mode 100644 index 0000000..7de9e98 --- /dev/null +++ b/src/main/webapp/recent.js @@ -0,0 +1,120 @@ +import Utils from "./utils.js"; + +export default { + data() { + return { + player: null, + records: [], + }; + }, + components: {}, + props: [ + "serverNames", + "dungeons", + "characters", + "characterName", + "server", + "showOption", + ], + methods: { + search() { + if (this.characterName == "") { + return; + } + this.$emit("preSearch"); + + setTimeout(() => { + fetch( + "char/recent/" + + encodeURI(this.server) + + "/" + + encodeURI(this.characterName) + ).then(async (resp) => { + const data = await resp.json(); + this.player = data.player; + this.records = data.data; + }); + }, 100); + }, + scan() { + if (this.characterName == "") { + return; + } + this.$emit("preSearch"); + + setTimeout(() => { + fetch( + "char/scan/" + + encodeURI(this.server) + + "/" + + encodeURI(this.characterName) + ); + }, 100); + }, + quickSearch(index) { + this.$emit("characterSelected", index); + this.search(); + }, + formatDate(d) { + return Utils.formatDate(d); + }, + nameUpdated(event) { + this.$emit("nameUpdated", event); + }, + serverUpdated(event) { + this.$emit("serverUpdated", event); + }, + showOptionToggled() { + this.$emit("showOptionToggled"); + }, + }, + mounted() {}, + template: ` + + + + + + + + + + + + + + + + {{ item.name }} - {{ item.server }} + + + + 검색 + {{ showOption ? "검색 조건 닫기" : "검색 조건 열기" }} + 재검사 + + + + +
마지막 갱신 일자: {{ formatDate(player?.lastUpdateTs ?? 0) }}
+
+
+ + + {{ formatDate(record.completedTimestamp) }} + + + {{ dungeons[record.dungeonId] }} + + + {{ record.keystoneLevel }} + + + {{ record.keystoneUpgrade }} + + +
+ `, +}; diff --git a/src/main/webapp/relation.html b/src/main/webapp/relation.html new file mode 100644 index 0000000..6273b75 --- /dev/null +++ b/src/main/webapp/relation.html @@ -0,0 +1,16 @@ + + + + +Mythic+ + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/relation.js b/src/main/webapp/relation.js new file mode 100644 index 0000000..20e9975 --- /dev/null +++ b/src/main/webapp/relation.js @@ -0,0 +1,161 @@ +export default { + data() { + return { + minimumRun: 1, + players: new vis.DataSet([]), + relations: new vis.DataSet([]), + nextId: 1, + }; + }, + components: {}, + props: ["serverNames", "characters", "characterName", "server", "showOption"], + methods: { + search() { + if (this.characterName == "") { + return; + } + + this.$emit("preSearch"); + if (window.localStorage) { + window.localStorage.setItem("relation_run", this.minimumRun); + } + setTimeout(() => this.search2(), 100); + }, + search2() { + fetch( + "char/relation/" + + encodeURI(this.server) + + "/" + + encodeURI(this.characterName) + + "/" + + this.minimumRun + ).then(async (resp) => { + const data = await resp.json(); + let i1 = this.players + .get() + .find((p) => p.label == this.characterName + "-" + this.server); + if (i1 === undefined) { + i1 = { + id: this.nextId++, + label: this.characterName + "-" + this.server, + }; + this.players.add(i1); + } + + data.forEach((dd) => { + if (dd.value >= this.minimumRun) { + let i2 = this.players + .get() + .find((p) => p.label == dd.name + "-" + dd.realm); + if (i2 === undefined) { + i2 = { + id: this.nextId++, + label: dd.name + "-" + dd.realm, + }; + this.players.add(i2); + } + + var exists = this.relations + .get() + .some( + (rel) => + (rel.from == i1.id && rel.to == i2.id) || + (rel.from == i2.id && rel.to == i1.id) + ); + // this.relations.forEach(rel => { + // if (rel.from == i1.id && rel.to == i2.id) { + // exists = true + // } else if (rel.from == i2.id && rel.to == i1.id) { + // exists = true + // } + // }) + if (!exists) { + this.relations.add({ + from: i1.id, + to: i2.id, + value: dd.value, + title: dd.value, + }); + } + } + }); + }); + }, + quickSearch(index) { + this.$emit("characterSelected", index); + this.search(); + }, + nameUpdated(event) { + this.$emit("nameUpdated", event); + }, + serverUpdated(event) { + this.$emit("serverUpdated", event); + }, + showOptionToggled() { + this.$emit("showOptionToggled"); + }, + }, + beforeMount() { + if (window.localStorage) { + const run = parseInt(window.localStorage.getItem("relation_run")); + if (run > 0) { + this.minimumRun = run; + } + } + }, + mounted() { + var container = document.getElementById("relations"); + var data = { + nodes: this.players, + edges: this.relations, + }; + var options = {}; + var network = new vis.Network(container, data, options); + network.on("doubleClick", (e) => { + if (e.nodes.length > 0) { + var id = e.nodes[0]; + var pl = this.players.get().find((p) => p.id == id); + var name = pl.label; + this.nameUpdated(name.substring(0, name.indexOf("-"))); + this.serverUpdated(name.substring(name.indexOf("-") + 1)); + this.search(); + } + }); + }, + template: ` + + + + + + + + + + + + + + + + + + + {{ item.name }} - {{ item.server }} + + + + 검색 + {{ showOption ? "검색 조건 닫기" : "검색 조건 열기" }} + + + + +
+
+
+
+ `, +}; diff --git a/src/main/webapp/score-timeline.html b/src/main/webapp/score-timeline.html new file mode 100644 index 0000000..d78ebc6 --- /dev/null +++ b/src/main/webapp/score-timeline.html @@ -0,0 +1,16 @@ + + + + +Mythic+ + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/score-timeline.js b/src/main/webapp/score-timeline.js new file mode 100644 index 0000000..39cfacd --- /dev/null +++ b/src/main/webapp/score-timeline.js @@ -0,0 +1,318 @@ +export default { + data() { + return { + showDungeons: true, + allSeason: false, + graph2d: null, + graphs: { + 1: { + items: [], + groups: [ + { + id: "0", + content: "total", + options: { + excludeFromStacking: true, + }, + }, + ], + options: { + style: "bar", + stack: true, + legend: true, + locale: "en", + // zoomable: false, + // barChart: { width: 50, align: 'center' }, + drawPoints: { + onRender: function (item, group, grap2d) { + return item.label != null; + }, + style: "circle", + }, + dataAxis: { + icons: true, + left: { range: { min: 0, max: 0 } }, + }, + height: 900, + }, + }, + 0: { + items: [], + groups: [ + { + id: "0", + content: "total", + options: { + excludeFromStacking: true, + }, + }, + ], + options: { + style: "bar", + stack: false, + legend: false, + locale: "en", + // zoomable: false, + // barChart: { width: 50, align: 'center' }, + drawPoints: { + onRender: function (item, group, grap2d) { + return item.label != null; + }, + style: "circle", + }, + dataAxis: { + icons: true, + left: { range: { min: 0, max: 0 } }, + }, + height: 900, + }, + }, + }, + }; + }, + components: {}, + props: [ + "serverNames", + "dungeons", + "characters", + "characterName", + "server", + "showOption", + ], + watch: { + showDungeons(newVal, oldVal) { + if (this.graph2d == null) { + return; + } + const g = this.graphs[this.showDungeons ? 1 : 0]; + this.graph2d.setItems(g.items); + this.graph2d.setGroups(g.groups); + this.graph2d.setOptions(g.options); + }, + }, + methods: { + async search() { + if (this.characterName == "") { + return; + } + this.$emit("preSearch"); + setTimeout(() => this.search2(), 100); + }, + async search2() { + const resp = await fetch( + `char/mythic_rating/${encodeURI(this.server)}/${encodeURI( + this.characterName + )}?season=${this.allSeason ? 1 : 0}` + ); + const body = await resp.json(); + const data = body; + + let season = 0; + let period = 0; + let dungeonScore = {}; + let minTimestamp = 0; + let maxTimestamp = 0; + let periodTimestamp = 0; + const oneWeek = 7 * 24 * 3600 * 1000; + + this.graphs["1"].items = []; + this.graphs["1"].groups = [ + { + id: "0", + content: "total", + options: { + excludeFromStacking: true, + }, + }, + ]; + this.graphs["0"].items = []; + const items1 = this.graphs["1"].items; + const groups = this.graphs["1"].groups; + const items2 = this.graphs["0"].items; + + const addSummary = () => { + let score = 0; + for (let did in dungeonScore) { + const arr = dungeonScore[did]; + const dscore = + Math.max(arr[0], arr[1]) * 1.5 + Math.min(arr[0], arr[1]) * 0.5; + score += dscore; + + const dname = groups.find((g) => g.id == String(did)).content; + + items1.push({ + x: periodTimestamp, + y: dscore, + end: periodTimestamp + oneWeek, + group: String(did), + label: { + content: + String(Math.round(dscore)) + "(" + dname.substring(0, 1) + ")", + xOffset: 0, + yOffset: 20, + }, + }); + } + if (score > 0) { + items1.push({ + x: periodTimestamp, + y: score, + end: periodTimestamp + oneWeek, + group: "0", + label: { + content: String(Math.round(score)), + xOffset: 0, + yOffset: -20, + }, + }); + items2.push({ + x: periodTimestamp, + y: score, + end: periodTimestamp + oneWeek, + group: "0", + label: { + content: String(Math.round(score)), + xOffset: 0, + yOffset: -20, + }, + }); + } + }; + + data.forEach((data) => { + if (data.season != season) { + dungeonScore = {}; + season = data.season; + period = 0; + } + if (data.period != period) { + if (minTimestamp == 0) { + minTimestamp = data.timestamp; + } else { + while (period < data.period) { + addSummary(); + period++; + periodTimestamp += oneWeek; + } + } + period = data.period; + periodTimestamp = data.timestamp; + maxTimestamp = data.timestamp; + } + if (!dungeonScore[data.dungeon_id]) { + dungeonScore[data.dungeon_id] = [0, 0]; + } + dungeonScore[data.dungeon_id][period % 2] = Math.max( + dungeonScore[data.dungeon_id][period % 2], + data.mythic_rating + ); + if (!groups.some((g) => g.id == String(data.dungeon_id))) { + groups.push({ + id: String(data.dungeon_id), + content: data.dungeon_name, + }); + } + }); + addSummary(); + if (items1.length >= 0) { + const timelineMargin = oneWeek; + const container = document.getElementById("timeline"); + container.innerHTML = ""; + const g = this.graphs[this.showDungeons ? 1 : 0]; + + const xRangeMin = minTimestamp - timelineMargin; + const xRangeMax = maxTimestamp + timelineMargin * 2; + const yRangeMax = + Math.ceil( + Math.max.apply( + null, + items1.filter((it) => it.group == "0").map((it) => it.y) + ) / 100 + ) * + 100 + + 200; + + this.graphs["0"].options.min = xRangeMin; + this.graphs["0"].options.max = xRangeMax; + this.graphs["0"].options.dataAxis.left.range.max = yRangeMax; + this.graphs["1"].options.min = xRangeMin; + this.graphs["1"].options.max = xRangeMax; + this.graphs["1"].options.dataAxis.left.range.max = yRangeMax; + + this.graph2d = new vis.Graph2d(container, g.items, g.groups, g.options); + this.graph2d.setWindow(xRangeMin, xRangeMax); + } + }, + + scan() { + if (this.characterName == "") { + return; + } + this.characterName = + this.characterName.substring(0, 1).toUpperCase() + + this.characterName.substring(1).toLowerCase(); + + fetch( + "char/scan/" + + encodeURI(this.server) + + "/" + + encodeURI(this.characterName) + ); + }, + quickSearch(index) { + this.$emit("characterSelected", index); + this.search(); + }, + nameUpdated(event) { + this.$emit("nameUpdated", event); + }, + serverUpdated(event) { + this.$emit("serverUpdated", event); + }, + showOptionToggled() { + this.$emit("showOptionToggled"); + }, + toggleDungeon() { + this.showDungeons = !this.showDungeons; + }, + toggleSeason() { + this.allSeason = !this.allSeason; + }, + }, + template: ` + + + + + + + + + + + + + + + + {{ item.name }} - {{ item.server }} + + + + 검색 + {{ showOption ? "검색 조건 닫기" : "검색 조건 열기" }} + {{ showDungeons ? "던전별 표시" : "총점 표시" }} + {{ allSeason ? "전체시즌" : "현재시즌" }} + 재검사 + + + + +
+
+
+
+ `, +}; diff --git a/src/main/resources/static/utils.js b/src/main/webapp/utils.js similarity index 100% rename from src/main/resources/static/utils.js rename to src/main/webapp/utils.js