feat: MenuItem에 funcCall 타입 추가후 처리

This commit is contained in:
2023-03-22 22:44:42 +09:00
parent 4d2b5ced82
commit ca3e64f3b9
6 changed files with 158 additions and 55 deletions
+3
View File
@@ -12,6 +12,9 @@ class MenuItem extends DTO{
public readonly string $name,
public readonly string $url,
#[NullIsUndefined]
public ?string $funcCall = null,
#[NullIsUndefined]
public ?string $icon = null,
#[NullIsUndefined]
+4 -1
View File
@@ -3,6 +3,7 @@
namespace sammo;
use sammo\DTO\MenuItem;
use sammo\DTO\MenuLine;
use sammo\DTO\MenuMulti;
use sammo\DTO\MenuSplit;
@@ -11,7 +12,7 @@ use sammo\DTO\MenuSplit;
class GlobalMenu {
/** @var (MenuItem|MenuMulti|MenuSplit)[] */
const version = 1;
const version = 2;
static ?array $menu = null;
public static function getMenu(): array{
if(static::$menu !== null){
@@ -23,6 +24,7 @@ class GlobalMenu {
new MenuItem('세력일람', 'a_kingdomList.php', newTab: true),
new MenuItem('장수일람', 'a_genList.php', newTab: true),
new MenuItem('명장일람', 'a_bestGeneral.php', newTab: true),
new MenuLine(),
new MenuItem('명예의전당', 'a_hallOfFame.php', newTab: true),
new MenuItem('왕조일람', 'a_emperior.php', newTab: true),
]),
@@ -32,6 +34,7 @@ class GlobalMenu {
[
new MenuItem('건의/제안', '/board/request', newTab: true),
new MenuItem('팁/강좌', '/board/tip', newTab: true),
new MenuLine(),
new MenuItem('패치 내역', '/board/patch', newTab: true),
]
),
+16
View File
@@ -10,6 +10,7 @@
:globalInfo="globalInfo"
:modelValue="globalMenu"
variant="sammo-base2"
@reqCall="reqMenuCall"
/>
</div>
<GameInfo
@@ -220,6 +221,21 @@ const storeP = getGameConstStore().then((store) => {
gameConstStore.value = store;
});
const menuCallList: Record<string, ()=>unknown> = {
showVersion:()=>{
console.log('TODO version 출력');
},
}
function reqMenuCall(key:string){
if(!(key in menuCallList)){
console.error(`메뉴 호출 실패: ${key}`);
return;
}
const func = menuCallList[key];
void func();
}
const showSecret = computed(() => {
if (!frontInfo.value) {
return false;
+63 -38
View File
@@ -7,54 +7,71 @@
:variant="variant"
:href="item.url"
:target="item.newTab ? '_blank' : undefined"
@click="menuClick($event, item)"
>{{ item.name }}</BButton
>
<template v-else-if="item.type === 'multi'">
<BDropdown :variant="variant" :text="item.name" class="col">
<BDropdown v-else-if="item.type === 'multi'" :variant="variant" :text="item.name" class="col">
<template v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
<template v-if="subItem.type === 'item'">
<BDropdownItem
:variant="variant"
:href="subItem.url"
:target="subItem.newTab ? '_blank' : undefined"
@click="menuClick($event, subItem)"
>{{ subItem.name }}</BDropdownItem
>
</template>
<BDropdownDivider v-else />
</template>
</BDropdown>
<BDropdown
v-else-if="item.type === 'split'"
split
class="col"
:variant="variant"
:text="item.main.name"
:splitHref="item.main.url"
@click="menuClick($event, item.main)"
>
<template v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
<BDropdownItem
v-for="(subItem, subIdx) in item.subMenu"
:key="subIdx"
v-if="subItem.type === 'item'"
:variant="variant"
:href="subItem.url"
:target="subItem.newTab ? '_blank' : undefined"
@click="menuClick($event, subItem)"
>{{ 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>
<BDropdownDivider v-else />
</template>
</BDropdown>
</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-next";
import type {
GetFrontInfoResponse,
GetMenuResponse,
MenuItem,
MenuLine,
MenuMulti,
MenuSplit,
} from "@/defs/API/Global";
import { BButton, BDropdown, BDropdownItem, BDropdownDivider, type ButtonVariant } from "bootstrap-vue-next";
import { isArray } from "lodash-es";
import { computed, toRef } from "vue";
const props = defineProps<{
modelValue: GetMenuResponse["menu"];
globalInfo: GetFrontInfoResponse["global"];
variant: ButtonVariant | 'sammo-base2',
mobileRowSize?: number,
desktopRowSize?: number,
variant: ButtonVariant | "sammo-base2";
mobileRowSize?: number;
desktopRowSize?: number;
}>();
const emit = defineEmits<{
(event: "reqCall", value: string): void;
}>();
const mobileRowSize = computed(() => {
@@ -72,7 +89,7 @@ const variant = computed(() => {
const modelValue = toRef(props, "modelValue");
const globalInfo = toRef(props, "globalInfo");
type MenuVariant = MenuItem | MenuSplit | MenuMulti;
type MenuVariant = MenuItem | MenuSplit | MenuMulti | MenuLine;
function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVariant[] | undefined {
if (isArray(menu)) {
@@ -122,15 +139,23 @@ function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVarian
const filteredMenu = computed(() => filterMenu(modelValue.value) as GetMenuResponse["menu"]);
function splitClick(menu: MenuItem) {
return (e: Event) => {
if (!menu.newTab) {
return;
}
function menuClick(e: Event, menu: MenuItem) {
if (menu.funcCall) {
//TODO: CTRL+클릭도 대응해야하는지 고민이 필요함
e.preventDefault();
e.stopPropagation();
window.open(menu.url);
};
emit("reqCall", menu.url);
return;
}
if (!menu.url) {
e.preventDefault();
return;
}
if (!menu.newTab) {
//자동 핸들러를 따름
return;
}
e.preventDefault();
window.open(menu.url);
}
</script>
+71 -16
View File
@@ -2,7 +2,12 @@
<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">
<a
class="dropdown-item"
:href="item.url"
:target="item.newTab ? '_blank' : undefined"
@click="menuClick($event, item)"
>
{{ item.name }}
</a>
</li>
@@ -10,30 +15,57 @@
<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 v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
<li v-if="subItem.type === 'item'">
<a
class="dropdown-item subItem"
:href="subItem.url"
:target="subItem.newTab ? '_blank' : undefined"
@click="menuClick($event, subItem)"
>{{ subItem.name }}</a
>
</li>
<hr v-else class="dropdown-divider" />
</template>
<hr class="dropdown-divider" />
</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>
<a
class="dropdown-item"
:href="item.main.url"
:target="item.main.newTab ? '_blank' : undefined"
@click="menuClick($event, item.main)"
>{{ item.main.name }}</a
>
</li>
<template v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
<li v-if="subItem.type === 'item'">
<a
class="dropdown-item subItem"
:href="subItem.url"
:target="subItem.newTab ? '_blank' : undefined"
@click="menuClick($event, subItem)"
>{{ subItem.name }}</a
>
</li>
<hr v-else class="dropdown-divider" />
</template>
<hr class="dropdown-divider" />
</template>
</template>
</ul>
</template>
<script setup lang="ts">
import type { GetFrontInfoResponse, GetMenuResponse, MenuItem, MenuMulti, MenuSplit } from "@/defs/API/Global";
import type {
GetFrontInfoResponse,
GetMenuResponse,
MenuItem,
MenuLine,
MenuMulti,
MenuSplit,
} from "@/defs/API/Global";
import { isArray } from "lodash-es";
import { computed, toRef } from "vue";
@@ -45,6 +77,10 @@ const props = defineProps<{
columns?: number;
}>();
const emit = defineEmits<{
(event: "reqCall", value: string): void;
}>();
const columns = computed(() => {
return props.columns ?? 3;
});
@@ -52,7 +88,7 @@ const columns = computed(() => {
const modelValue = toRef(props, "modelValue");
const globalInfo = toRef(props, "globalInfo");
type MenuVariant = MenuItem | MenuSplit | MenuMulti;
type MenuVariant = MenuItem | MenuSplit | MenuMulti | MenuLine;
function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVariant[] | undefined {
if (isArray(menu)) {
@@ -101,6 +137,25 @@ function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVarian
}
const filteredMenu = computed(() => filterMenu(modelValue.value) as GetMenuResponse["menu"]);
function menuClick(e: Event, menu: MenuItem) {
if (menu.funcCall) {
//TODO: CTRL+클릭도 대응해야하는지 고민이 필요함
e.preventDefault();
emit("reqCall", menu.url);
return;
}
if (!menu.url) {
e.preventDefault();
return;
}
if (!menu.newTab) {
//자동 핸들러를 따름
return;
}
e.preventDefault();
window.open(menu.url);
}
</script>
<style lang="scss" scoped>
+1
View File
@@ -231,6 +231,7 @@ export type MenuItem = {
type: 'item';
name: string;
url: string;
funcCall?: string;
icon?: string;
newTab?: boolean;
condHightlightVar?: string;