feat: PageFront에 모바일용 BottomBar 재구현
This commit is contained in:
+22
-22
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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> </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>
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
|
||||
@@ -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">내 정보&설정</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>
|
||||
Reference in New Issue
Block a user