feat: PageFront에 모바일용 BottomBar 재구현

This commit is contained in:
2023-03-09 02:20:24 +09:00
parent bff1c3a08a
commit b5c02d83bc
5 changed files with 398 additions and 26 deletions
+22 -22
View File
@@ -1,5 +1,5 @@
<template>
<div id="outBlock">
<div id="outBlock" :class="`sam-color-${nationStaticInfo?.color.substring(1, 7) ?? '000000'}`">
<div id="outBlock2">
<!-- eslint-disable-next-line vue/max-attributes-per-line -->
<BContainer v-if="asyncReady" id="container" :position="'position-relative'" :toast="{ root: true }" class="bg0">
@@ -89,7 +89,7 @@
<div class="col">
<div class="gx-1 row">
<div class="col-3 d-grid">
<button type="button" class="btn btn-dark" @click="scrollHardTo('reservedCommandPanel')">
<button type="button" class="btn btn-dark" @click="scrollToSelector('#reservedCommandPanel')">
명령으로
</button>
</div>
@@ -155,10 +155,12 @@
</BContainer>
<div v-else>서버 갱신 중입니다.</div>
</div>
<nav id="mobileBottomBar">
<BButton>하단 </BButton>
<BButton>하단 바2</BButton>
</nav>
<GameBottomBar
v-if="frontInfo && globalMenu"
id="mobileBottomBar"
:frontInfo="frontInfo"
:globalMenu="globalMenu"
/>
</div>
</template>
<script lang="ts">
@@ -192,11 +194,11 @@ import MessagePanel from "./components/MessagePanel.vue";
import type { MapResult, NationStaticItem } from "./defs";
import MapViewer, { type CityPositionMap, type MapCityParsed, type MapCityParsedRaw } from "./components/MapViewer.vue";
import PartialReservedCommand from "./PartialReservedCommand.vue";
import { scrollHardTo } from "./util/scrollHardTo";
import { scrollToSelector } from "./util/scrollToSelector";
import MainControlBar from "./components/MainControlBar.vue";
import GlobalMenu from "./components/GlobalMenu.vue";
import GameInfo from "./components/GameInfo.vue";
import { formatDate } from "@vueuse/shared";
import GameBottomBar from "./components/GameBottomBar.vue";
const { serverName, serverNick, serverID } = staticValues;
@@ -325,7 +327,6 @@ const generalRecords = ref(new Denque<[number, string]>());
const globalRecords = ref(new Denque<[number, string]>());
const worldHistory = ref(new Denque<[number, string]>());
let generalInfoLock = false;
watch(refreshCounter, async () => {
@@ -364,14 +365,14 @@ watch(refreshCounter, async () => {
const recentRecord = response.recentRecord;
const recordIter = [
['flushGeneral', generalRecords, 'general', lastGeneralRecordID],
['flushGlobal', globalRecords, 'global', lastGeneralRecordID],
['flushHistory', worldHistory, 'history', lastWorldHistoryID],
["flushGeneral", generalRecords, "general", lastGeneralRecordID],
["flushGlobal", globalRecords, "global", lastGeneralRecordID],
["flushHistory", worldHistory, "history", lastWorldHistoryID],
] as const;
let haveNewRecord = false;
for(const [flushKey, recordRef, recordKey, lastRecordID] of recordIter) {
for (const [flushKey, recordRef, recordKey, lastRecordID] of recordIter) {
if (recentRecord[flushKey]) {
recordRef.value = new Denque<[number, string]>();
haveNewRecord = true;
@@ -381,26 +382,25 @@ watch(refreshCounter, async () => {
lastRecordID.value = Math.max(lastRecordID.value, subRecord[0][0]);
haveNewRecord = true;
}
while(subRecord.length){
while (subRecord.length) {
const [id, record] = unwrap(subRecord.pop());
if(!recordRef.value.isEmpty() && id <= unwrap(recordRef.value.get(0))[0]){
if (!recordRef.value.isEmpty() && id <= unwrap(recordRef.value.get(0))[0]) {
continue;
}
if(recordRef.value.length >= 15){
if (recordRef.value.length >= 15) {
recordRef.value.pop();
}
recordRef.value.unshift([id, formatLog(record)]);
}
}
if(haveNewRecord){
if (haveNewRecord) {
toasts.info({
title: '갱신 완료',
title: "갱신 완료",
body: `동향 변경이 있습니다.`,
});
}
else{
} else {
toasts.success({
title: '갱신 완료',
title: "갱신 완료",
});
}
} catch (e) {
@@ -456,7 +456,7 @@ watch(refreshCounter, async () => {
</script>
<style lang="scss" scoped>
@import "@scss/common/break_500px.scss";
:deep(){
:deep() {
@import "@scss/gameEvent.scss";
}
+188
View File
@@ -0,0 +1,188 @@
<template>
<nav class="gameBottomBar navbar-expand navbar-dark bg-dark d-sm-block d-md-none p-0">
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mx-auto">
<li class="nav-item dropup">
<div
id="navbarGlobal"
class="dropdown-toggle text-white btn btn-sammo-base2"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
외부 메뉴
</div>
<GlobalMenuBar
id="navbarGlobalItems"
aria-labelledby="navbarGlobal"
:globalInfo="globalInfo"
:modelValue="globalMenu"
:columns="3"
/>
</li>
<li class="nav-item dropup">
<div
id="navbarNation"
class="dropdown-toggle btn btn-sammo-nation controlBar"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
국가 메뉴
</div>
<MainControlDropdown
id="navbarNationItems"
aria-labelledby="navbarNation"
:showSecret="showSecret"
:permission="frontInfo.general.permission"
:myLevel="frontInfo.general.officerLevel"
:nationLevel="nationInfo.level"
/>
</li>
<li class="nav-item dropup">
<div
id="navbarQuick"
class="dropdown-toggle text-white btn btn-dark"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
빠른 이동
</div>
<ul id="navbarQuickItems" class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item disabled">국가 정보</a></li>
<hr class="dropdown-divider" />
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.nationNotice')">방침</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('#reservedCommandPanel')">명령</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.nationInfo')">국가</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.generalInfo')">장수</button>
</li>
<li><button type="button" class="dropdown-item" @click="scrollToSelector('.cityInfo')">도시</button></li>
<li><a class="dropdown-item disabled">동향 정보</a></li>
<hr class="dropdown-divider" />
<li><button type="button" class="dropdown-item" @click="scrollToSelector('.mapView')">지도</button></li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.PublicRecord')">
동향
</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.GeneralLog')">개인</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.WorldHistory')">정세</button>
</li>
<li><span>&nbsp;</span></li>
<li><a class="dropdown-item disabled">메시지</a></li>
<hr class="dropdown-divider" />
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.PublicTalk')">전체</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.NationalTalk')">국가</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.PrivateTalk')">개인</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="scrollToSelector('.DiplomacyTalk')">외교</button>
</li>
<li>
<button type="button" class="btn btn-sammo-base2" @click="moveLobby">로비로</button>
</li>
</ul>
</li>
<li class="nav-item">
<a class="refreshPage btn btn-sammo-base2 text-white" role="button">갱신</a>
</li>
</ul>
</div>
</nav>
</template>
<script setup lang="ts">
import type { GetFrontInfoResponse, GetMenuResponse } from "@/defs/API/Global";
import { scrollToSelector } from "@/util/scrollToSelector";
import { toRefs, watch, ref, computed } from "vue";
import GlobalMenuBar from "./GlobalMenuDropdown.vue";
import MainControlDropdown from "./MainControlDropdown.vue";
//FIXME: scrollToSelector
const props = defineProps<{
frontInfo: GetFrontInfoResponse;
globalMenu: GetMenuResponse["menu"];
}>();
const { frontInfo, globalMenu } = toRefs(props);
const globalInfo = ref(frontInfo.value.global);
watch(frontInfo, (frontInfo) => {
globalInfo.value = frontInfo.global;
});
const nationInfo = ref(frontInfo.value.nation);
watch(frontInfo, (frontInfo) => {
nationInfo.value = frontInfo.nation;
});
function moveLobby() {
location.replace("../");
}
const showSecret = computed(() => {
if (!frontInfo.value) {
return false;
}
if (frontInfo.value.general.permission >= 1) {
return true;
}
if (frontInfo.value.general.officerLevel >= 2) {
return true;
}
return false;
});
</script>
<style lang="scss" scoped>
@import "@scss/common/break_500px.scss";
@import "@scss/common/base.scss";
@include media-500px {
.gameBottomBar {
.nav-item ul.dropdown-menu {
max-height: calc(100vh - 50px);
overflow-y: auto;
li {
font-size: 16px;
}
}
.nav-item > .btn {
text-align: center;
width: 125px;
font-size: 16px;
}
box-shadow: 0 -1px 0 $dark;
border-left: none !important;
border-right: none !important;
}
#navbarNationItems {
columns: 3;
}
#navbarQuickItems {
columns: 3;
}
}
</style>
+116
View File
@@ -0,0 +1,116 @@
<template>
<ul class="dropdown-menu dropdown-menu-start">
<template v-for="(item, idx) in filteredMenu" :key="idx">
<li v-if="item.type === 'item'">
<a class="dropdown-item" :href="item.url" :target="item.newTab ? '_blank' : undefined">
{{ item.name }}
</a>
</li>
<template v-else-if="item.type === 'multi'">
<li :style="{ orphans: item.subMenu.length + 1 }">
<span class="dropdown-item disabled">{{ item.name }}</span>
</li>
<li v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
<a class="dropdown-item subItem" :href="subItem.url" :target="subItem.newTab ? '_blank' : undefined">{{
subItem.name
}}</a>
</li>
</template>
<template v-else-if="item.type === 'split'">
<li :style="{ orphans: item.subMenu.length + 1 }">
<a class="dropdown-item" :href="item.main.url" :target="item.main.newTab ? '_blank' : undefined">{{
item.main.name
}}</a>
</li>
<li v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
<a class="dropdown-item subItem" :href="subItem.url" :target="subItem.newTab ? '_blank' : undefined">{{
subItem.name
}}</a>
</li>
</template>
</template>
</ul>
</template>
<script setup lang="ts">
import type { GetFrontInfoResponse, GetMenuResponse, MenuItem, MenuMulti, MenuSplit } from "@/defs/API/Global";
import { isArray } from "lodash";
import { computed, toRef } from "vue";
const props = defineProps<{
modelValue: GetMenuResponse["menu"];
globalInfo: GetFrontInfoResponse["global"];
mobileRowSize?: number;
desktopRowSize?: number;
columns?: number;
}>();
const columns = computed(() => {
return props.columns ?? 3;
});
const modelValue = toRef(props, "modelValue");
const globalInfo = toRef(props, "globalInfo");
type MenuVariant = MenuItem | MenuSplit | MenuMulti;
function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVariant[] | undefined {
if (isArray(menu)) {
return menu.filter(filterMenu) as MenuVariant[];
}
if (menu.type === "item") {
if (!menu.condShowVar) {
return menu;
}
const cond = menu.condShowVar;
if (cond in globalInfo.value) {
if (cond.startsWith("!")) {
if (!globalInfo.value[cond.slice(1) as keyof GetFrontInfoResponse["global"]]) {
return menu;
}
} else if (globalInfo.value[cond as keyof GetFrontInfoResponse["global"]]) {
return menu;
}
}
return undefined;
}
if (menu.type === "multi") {
const filtered = menu.subMenu.filter(filterMenu) as MenuVariant[];
if (filtered.length === 0) {
return undefined;
}
if (filtered.length === 1) {
return filtered[0];
}
return filtered;
}
if (menu.type === "split") {
const filterMain = filterMenu(menu.main);
if (!filterMain) {
return undefined;
}
const filtered = menu.subMenu.filter(filterMenu) as MenuVariant[];
if (filtered.length === 0) {
return filterMain;
}
return filtered;
}
}
const filteredMenu = computed(() => filterMenu(modelValue.value) as GetMenuResponse["menu"]);
</script>
<style lang="scss" scoped>
@import "@scss/common/break_500px.scss";
.subItem {
padding-left: 1.5rem;
}
.dropdown-menu {
columns: v-bind(columns);
}
</style>
+3 -4
View File
@@ -1,5 +1,5 @@
<template>
<div :class="`sam-color-${nationColor} controlBar`">
<div class="controlBar">
<a href="v_board.php" :class="`commandButton btn btn-sammo-nation ${myLevel >= 1 ? '' : 'disabled'}`"> </a>
<a
href="v_board.php?isSecret=true"
@@ -84,10 +84,9 @@ const props = defineProps<{
permission: number;
myLevel: number;
nationLevel: number;
nationColor: string;
}>();
const { showSecret, permission, myLevel, nationLevel, nationColor } = toRefs(props);
const { showSecret, permission, myLevel, nationLevel } = toRefs(props);
</script>
<style lang="scss" scoped>
@@ -104,7 +103,7 @@ const { showSecret, permission, myLevel, nationLevel, nationColor } = toRefs(pro
}
}
@include media-1000px{
@include media-1000px {
.controlBar {
display: grid;
grid-template-columns: repeat(10, 1fr);
+69
View File
@@ -0,0 +1,69 @@
<template>
<ul class="dropdown-menu dromdown-menu-start">
<li><a href="v_board.php" :class="`dropdown-item ${myLevel >= 1 ? '' : 'disabled'}`">회의실</a></li>
<li>
<a href="v_board.php?isSecret=true" :class="`dropdown-item ${permission >= 2 ? '' : 'disabled'} `">기밀실</a>
</li>
<li>
<a href="v_troop.php" :class="`dropdown-item ${myLevel >= 1 && nationLevel >= 1 ? '' : 'disabled'} `">부대 편성</a>
</li>
<li><a href="t_diplomacy.php" :class="`dropdown-item ${showSecret ? '' : 'disabled'} `">외교부</a></li>
<li><a href="b_myBossInfo.php" :class="`dropdown-item ${myLevel >= 1 ? '' : 'disabled'} `">인사부</a></li>
<li><a href="v_nationStratFinan.php" :class="`dropdown-item ${showSecret ? '' : 'disabled'} `">내무부</a></li>
<li><a href="v_chiefCenter.php" :class="`dropdown-item ${showSecret ? '' : 'disabled'} `">사령부</a></li>
<li><a href="v_NPCControl.php" :class="`dropdown-item ${showSecret ? '' : 'disabled'} `">NPC 정책</a></li>
<li>
<a href="b_genList.php" target="_blank" :class="`dropdown-item open-window ${showSecret ? '' : 'disabled'}`"
>암행부</a
>
</li>
<li><a href="b_tournament.php" class="dropdown-item" target="_blank">토너먼트</a></li>
<li><a href="b_myKingdomInfo.php" :class="`dropdown-item ${myLevel >= 1 ? '' : 'disabled'}`">세력 정보</a></li>
<li>
<a href="b_myCityInfo.php" :class="`dropdown-item ${myLevel >= 1 && nationLevel >= 1 ? '' : 'disabled'}`"
>세력도시</a
>
</li>
<li><a href="v_nationGeneral.php" :class="`dropdown-item ${myLevel >= 1 ? '' : 'disabled'}`">세력 장수</a></li>
<li><a href="v_globalDiplomacy.php" class="dropdown-item">중원 정보</a></li>
<li><a href="b_currentCity.php" class="dropdown-item">현재 도시</a></li>
<li>
<a href="v_battleCenter.php" target="_blank" :class="`dropdown-item open-window ${showSecret ? '' : 'disabled'}`"
>감찰부</a
>
</li>
<li><a href="v_inheritPoint.php" class="dropdown-item">유산 관리</a></li>
<li><a href="b_myPage.php" class="dropdown-item"> 정보&amp;설정</a></li>
<li style="orphans: 2">
<a href="v_auction.php" target="_blank" class="dropdown-item open-window">/ 경매장</a>
</li>
<li><a href="v_auction.php?type=unique" target="_blank" class="dropdown-item open-window">유니크 경매장</a></li>
<li><a href="b_betting.php" class="dropdown-item" target="_blank">베팅장</a></li>
</ul>
</template>
<script setup lang="ts">
import { computed, toRefs } from "vue";
const props = defineProps<{
showSecret: boolean;
permission: number;
myLevel: number;
nationLevel: number;
columns?: number;
}>();
const columns = computed(() => {
return props.columns ?? 4;
});
const { showSecret, permission, myLevel, nationLevel } = toRefs(props);
</script>
<style lang="scss" scoped>
.subItem {
padding-left: 1.5rem;
}
.dropdown-menu {
columns: v-bind(columns);
}
</style>