feat: GlobalMenu Component(wip)
This commit is contained in:
+36
-5
@@ -15,7 +15,14 @@
|
|||||||
<!-- eslint-disable-next-line vue/max-attributes-per-line -->
|
<!-- eslint-disable-next-line vue/max-attributes-per-line -->
|
||||||
<BContainer v-if="asyncReady" id="container" :position="'position-relative'" :toast="{ root: true }" class="bg0">
|
<BContainer v-if="asyncReady" id="container" :position="'position-relative'" :toast="{ root: true }" class="bg0">
|
||||||
<main class="gx-0">
|
<main class="gx-0">
|
||||||
<div class="commonToolbar">툴바?</div>
|
<div class="commonToolbar">
|
||||||
|
<GlobalMenu
|
||||||
|
v-if="globalMenu && globalInfo"
|
||||||
|
:globalInfo="globalInfo"
|
||||||
|
:modelValue="globalMenu"
|
||||||
|
variant="sammo-base2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="scenarioName">시나리오</div>
|
<div class="scenarioName">시나리오</div>
|
||||||
<div v-if="frontInfo" class="gameInfo">
|
<div v-if="frontInfo" class="gameInfo">
|
||||||
<div class="subScenarioName">{{ globalInfo.scenarioText }}</div>
|
<div class="subScenarioName">{{ globalInfo.scenarioText }}</div>
|
||||||
@@ -111,7 +118,7 @@
|
|||||||
:showSecret="showSecret"
|
:showSecret="showSecret"
|
||||||
:myLevel="generalInfo.officerLevel"
|
:myLevel="generalInfo.officerLevel"
|
||||||
:nationLevel="nationStaticInfo?.level ?? 0"
|
:nationLevel="nationStaticInfo?.level ?? 0"
|
||||||
:nationColor="nationStaticInfo?.color.substring(1,7) ?? '000000'"
|
:nationColor="nationStaticInfo?.color.substring(1, 7) ?? '000000'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div id="actionMiniPlateSub" class="gx-0 row">
|
<div id="actionMiniPlateSub" class="gx-0 row">
|
||||||
@@ -162,7 +169,14 @@
|
|||||||
:permissionLevel="generalInfo.permission"
|
:permissionLevel="generalInfo.permission"
|
||||||
ref="msgPanel"
|
ref="msgPanel"
|
||||||
/>
|
/>
|
||||||
<div class="commonToolbar">툴바?</div>
|
<div class="commonToolbar">
|
||||||
|
<GlobalMenu
|
||||||
|
v-if="globalMenu && globalInfo"
|
||||||
|
:globalInfo="globalInfo"
|
||||||
|
:modelValue="globalMenu"
|
||||||
|
variant="sammo-base2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</BContainer>
|
</BContainer>
|
||||||
<div v-else>서버 갱신 중입니다.</div>
|
<div v-else>서버 갱신 중입니다.</div>
|
||||||
@@ -188,14 +202,14 @@ declare const formatCityInfo: (city: MapCityParsedRaw) => MapCityParsed;
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { BContainer, BButton, useToast } from "bootstrap-vue-3";
|
import { BContainer, BButton, useToast } from "bootstrap-vue-3";
|
||||||
import { isString } from "lodash";
|
import { isString } from "lodash";
|
||||||
import { computed, provide, ref, watch } from "vue";
|
import { computed, onMounted, provide, ref, watch } from "vue";
|
||||||
import { GameConstStore, getGameConstStore } from "./GameConstStore";
|
import { GameConstStore, getGameConstStore } from "./GameConstStore";
|
||||||
import { SammoAPI, type InvalidResponse } from "./SammoAPI";
|
import { SammoAPI, type InvalidResponse } from "./SammoAPI";
|
||||||
import { parseTime } from "./util/parseTime";
|
import { parseTime } from "./util/parseTime";
|
||||||
import { unwrap } from "./util/unwrap";
|
import { unwrap } from "./util/unwrap";
|
||||||
import Denque from "denque";
|
import Denque from "denque";
|
||||||
import { formatLog } from "@/utilGame/formatLog";
|
import { formatLog } from "@/utilGame/formatLog";
|
||||||
import type { ExecuteResponse, GetFrontInfoResponse } from "./defs/API/Global";
|
import type { ExecuteResponse, GetFrontInfoResponse, GetMenuResponse } from "./defs/API/Global";
|
||||||
import { delay } from "./util/delay";
|
import { delay } from "./util/delay";
|
||||||
import CityBasicCard from "./components/CityBasicCard.vue";
|
import CityBasicCard from "./components/CityBasicCard.vue";
|
||||||
import NationBasicCard from "./components/NationBasicCard.vue";
|
import NationBasicCard from "./components/NationBasicCard.vue";
|
||||||
@@ -210,6 +224,7 @@ import PartialReservedCommand from "./PartialReservedCommand.vue";
|
|||||||
import AutorunInfo from "./components/AutorunInfo.vue";
|
import AutorunInfo from "./components/AutorunInfo.vue";
|
||||||
import { scrollHardTo } from "./util/scrollHardTo";
|
import { scrollHardTo } from "./util/scrollHardTo";
|
||||||
import MainControlBar from "./components/MainControlBar.vue";
|
import MainControlBar from "./components/MainControlBar.vue";
|
||||||
|
import GlobalMenu from "./components/GlobalMenu.vue";
|
||||||
|
|
||||||
const { serverName, serverNick, serverID } = staticValues;
|
const { serverName, serverNick, serverID } = staticValues;
|
||||||
|
|
||||||
@@ -393,6 +408,22 @@ watch(refreshCounter, async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const globalMenu = ref<GetMenuResponse["menu"]>();
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const response = await SammoAPI.Global.GetGlobalMenu();
|
||||||
|
globalMenu.value = response.menu;
|
||||||
|
} catch (e) {
|
||||||
|
if (isString(e)) {
|
||||||
|
toasts.danger({
|
||||||
|
title: "메뉴 갱신 실패",
|
||||||
|
body: `${e}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const frontInfo = ref<GetFrontInfoResponse>();
|
const frontInfo = ref<GetFrontInfoResponse>();
|
||||||
const globalInfo = ref<GetFrontInfoResponse["global"]>({} as GetFrontInfoResponse["global"]);
|
const globalInfo = ref<GetFrontInfoResponse["global"]>({} as GetFrontInfoResponse["global"]);
|
||||||
const generalInfo = ref<GetFrontInfoResponse["general"]>();
|
const generalInfo = ref<GetFrontInfoResponse["general"]>();
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
<template>
|
||||||
|
<div class="global-menu">
|
||||||
|
<template v-for="(item, idx) in filteredMenu" :key="idx">
|
||||||
|
<BButton
|
||||||
|
v-if="item.type === 'item'"
|
||||||
|
class="col"
|
||||||
|
:variant="variant"
|
||||||
|
:href="item.url"
|
||||||
|
:target="item.newTab ? '_blank' : undefined"
|
||||||
|
>{{ item.name }}</BButton
|
||||||
|
>
|
||||||
|
<template v-else-if="item.type === 'multi'">
|
||||||
|
<BDropdown :variant="variant" :text="item.name" class="col">
|
||||||
|
<BDropdownItem
|
||||||
|
v-for="(subItem, subIdx) in item.subMenu"
|
||||||
|
:key="subIdx"
|
||||||
|
:variant="variant"
|
||||||
|
:href="subItem.url"
|
||||||
|
:target="subItem.newTab ? '_blank' : undefined"
|
||||||
|
>{{ subItem.name }}</BDropdownItem
|
||||||
|
>
|
||||||
|
</BDropdown>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.type === 'split'">
|
||||||
|
<BDropdown
|
||||||
|
split
|
||||||
|
class="col"
|
||||||
|
:variant="variant"
|
||||||
|
:text="item.main.name"
|
||||||
|
:splitHref="item.main.url"
|
||||||
|
@click="splitClick(item.main)($event)"
|
||||||
|
>
|
||||||
|
<BDropdownItem
|
||||||
|
v-for="(subItem, subIdx) in item.subMenu"
|
||||||
|
:key="subIdx"
|
||||||
|
:href="subItem.url"
|
||||||
|
:target="subItem.newTab ? '_blank' : undefined"
|
||||||
|
>{{ subItem.name }}</BDropdownItem
|
||||||
|
>
|
||||||
|
</BDropdown>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { GetFrontInfoResponse, GetMenuResponse, MenuItem, MenuMulti, MenuSplit } from "@/defs/API/Global";
|
||||||
|
import { BButton, BDropdown, BDropdownItem, type ButtonVariant } from "bootstrap-vue-3";
|
||||||
|
import { isArray } from "lodash";
|
||||||
|
import { computed, toRef } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: GetMenuResponse["menu"];
|
||||||
|
globalInfo: GetFrontInfoResponse["global"];
|
||||||
|
variant: ButtonVariant | 'sammo-base2',
|
||||||
|
mobileRowSize?: number,
|
||||||
|
desktopRowSize?: number,
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const mobileRowSize = computed(() => {
|
||||||
|
return props.mobileRowSize || 4;
|
||||||
|
});
|
||||||
|
|
||||||
|
const desktopRowSize = computed(() => {
|
||||||
|
return props.desktopRowSize || 8;
|
||||||
|
});
|
||||||
|
|
||||||
|
const variant = computed(() => {
|
||||||
|
return props.variant as ButtonVariant;
|
||||||
|
});
|
||||||
|
|
||||||
|
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"]);
|
||||||
|
|
||||||
|
function splitClick(menu: MenuItem) {
|
||||||
|
return (e: Event) => {
|
||||||
|
if (!menu.newTab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
window.open(menu.url);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@scss/common/break_500px.scss";
|
||||||
|
.global-menu {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-1000px {
|
||||||
|
.global-menu {
|
||||||
|
grid-template-columns: repeat(v-bind(desktopRowSize), 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@include media-500px {
|
||||||
|
.global-menu {
|
||||||
|
grid-template-columns: repeat(v-bind(mobileRowSize), 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user