feat: MenuItem에 funcCall 타입 추가후 처리
This commit is contained in:
@@ -12,6 +12,9 @@ class MenuItem extends DTO{
|
|||||||
public readonly string $name,
|
public readonly string $name,
|
||||||
public readonly string $url,
|
public readonly string $url,
|
||||||
|
|
||||||
|
#[NullIsUndefined]
|
||||||
|
public ?string $funcCall = null,
|
||||||
|
|
||||||
#[NullIsUndefined]
|
#[NullIsUndefined]
|
||||||
public ?string $icon = null,
|
public ?string $icon = null,
|
||||||
#[NullIsUndefined]
|
#[NullIsUndefined]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace sammo;
|
namespace sammo;
|
||||||
|
|
||||||
use sammo\DTO\MenuItem;
|
use sammo\DTO\MenuItem;
|
||||||
|
use sammo\DTO\MenuLine;
|
||||||
use sammo\DTO\MenuMulti;
|
use sammo\DTO\MenuMulti;
|
||||||
use sammo\DTO\MenuSplit;
|
use sammo\DTO\MenuSplit;
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ use sammo\DTO\MenuSplit;
|
|||||||
class GlobalMenu {
|
class GlobalMenu {
|
||||||
/** @var (MenuItem|MenuMulti|MenuSplit)[] */
|
/** @var (MenuItem|MenuMulti|MenuSplit)[] */
|
||||||
|
|
||||||
const version = 1;
|
const version = 2;
|
||||||
static ?array $menu = null;
|
static ?array $menu = null;
|
||||||
public static function getMenu(): array{
|
public static function getMenu(): array{
|
||||||
if(static::$menu !== null){
|
if(static::$menu !== null){
|
||||||
@@ -23,6 +24,7 @@ class GlobalMenu {
|
|||||||
new MenuItem('세력일람', 'a_kingdomList.php', newTab: true),
|
new MenuItem('세력일람', 'a_kingdomList.php', newTab: true),
|
||||||
new MenuItem('장수일람', 'a_genList.php', newTab: true),
|
new MenuItem('장수일람', 'a_genList.php', newTab: true),
|
||||||
new MenuItem('명장일람', 'a_bestGeneral.php', newTab: true),
|
new MenuItem('명장일람', 'a_bestGeneral.php', newTab: true),
|
||||||
|
new MenuLine(),
|
||||||
new MenuItem('명예의전당', 'a_hallOfFame.php', newTab: true),
|
new MenuItem('명예의전당', 'a_hallOfFame.php', newTab: true),
|
||||||
new MenuItem('왕조일람', 'a_emperior.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/request', newTab: true),
|
||||||
new MenuItem('팁/강좌', '/board/tip', newTab: true),
|
new MenuItem('팁/강좌', '/board/tip', newTab: true),
|
||||||
|
new MenuLine(),
|
||||||
new MenuItem('패치 내역', '/board/patch', newTab: true),
|
new MenuItem('패치 내역', '/board/patch', newTab: true),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
:globalInfo="globalInfo"
|
:globalInfo="globalInfo"
|
||||||
:modelValue="globalMenu"
|
:modelValue="globalMenu"
|
||||||
variant="sammo-base2"
|
variant="sammo-base2"
|
||||||
|
@reqCall="reqMenuCall"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<GameInfo
|
<GameInfo
|
||||||
@@ -220,6 +221,21 @@ const storeP = getGameConstStore().then((store) => {
|
|||||||
gameConstStore.value = 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(() => {
|
const showSecret = computed(() => {
|
||||||
if (!frontInfo.value) {
|
if (!frontInfo.value) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -7,54 +7,71 @@
|
|||||||
:variant="variant"
|
:variant="variant"
|
||||||
:href="item.url"
|
:href="item.url"
|
||||||
:target="item.newTab ? '_blank' : undefined"
|
:target="item.newTab ? '_blank' : undefined"
|
||||||
|
@click="menuClick($event, item)"
|
||||||
>{{ item.name }}</BButton
|
>{{ item.name }}</BButton
|
||||||
>
|
>
|
||||||
<template v-else-if="item.type === 'multi'">
|
<BDropdown v-else-if="item.type === 'multi'" :variant="variant" :text="item.name" class="col">
|
||||||
<BDropdown :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
|
<BDropdownItem
|
||||||
v-for="(subItem, subIdx) in item.subMenu"
|
v-if="subItem.type === 'item'"
|
||||||
:key="subIdx"
|
|
||||||
:variant="variant"
|
:variant="variant"
|
||||||
:href="subItem.url"
|
:href="subItem.url"
|
||||||
:target="subItem.newTab ? '_blank' : undefined"
|
:target="subItem.newTab ? '_blank' : undefined"
|
||||||
|
@click="menuClick($event, subItem)"
|
||||||
>{{ subItem.name }}</BDropdownItem
|
>{{ subItem.name }}</BDropdownItem
|
||||||
>
|
>
|
||||||
</BDropdown>
|
<BDropdownDivider v-else />
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="item.type === 'split'">
|
</BDropdown>
|
||||||
<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>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { GetFrontInfoResponse, GetMenuResponse, MenuItem, MenuMulti, MenuSplit } from "@/defs/API/Global";
|
import type {
|
||||||
import { BButton, BDropdown, BDropdownItem, type ButtonVariant } from "bootstrap-vue-next";
|
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 { isArray } from "lodash-es";
|
||||||
import { computed, toRef } from "vue";
|
import { computed, toRef } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: GetMenuResponse["menu"];
|
modelValue: GetMenuResponse["menu"];
|
||||||
globalInfo: GetFrontInfoResponse["global"];
|
globalInfo: GetFrontInfoResponse["global"];
|
||||||
variant: ButtonVariant | 'sammo-base2',
|
variant: ButtonVariant | "sammo-base2";
|
||||||
mobileRowSize?: number,
|
mobileRowSize?: number;
|
||||||
desktopRowSize?: number,
|
desktopRowSize?: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: "reqCall", value: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const mobileRowSize = computed(() => {
|
const mobileRowSize = computed(() => {
|
||||||
@@ -72,7 +89,7 @@ const variant = computed(() => {
|
|||||||
const modelValue = toRef(props, "modelValue");
|
const modelValue = toRef(props, "modelValue");
|
||||||
const globalInfo = toRef(props, "globalInfo");
|
const globalInfo = toRef(props, "globalInfo");
|
||||||
|
|
||||||
type MenuVariant = MenuItem | MenuSplit | MenuMulti;
|
type MenuVariant = MenuItem | MenuSplit | MenuMulti | MenuLine;
|
||||||
|
|
||||||
function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVariant[] | undefined {
|
function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVariant[] | undefined {
|
||||||
if (isArray(menu)) {
|
if (isArray(menu)) {
|
||||||
@@ -122,15 +139,23 @@ function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVarian
|
|||||||
|
|
||||||
const filteredMenu = computed(() => filterMenu(modelValue.value) as GetMenuResponse["menu"]);
|
const filteredMenu = computed(() => filterMenu(modelValue.value) as GetMenuResponse["menu"]);
|
||||||
|
|
||||||
function splitClick(menu: MenuItem) {
|
function menuClick(e: Event, menu: MenuItem) {
|
||||||
return (e: Event) => {
|
if (menu.funcCall) {
|
||||||
if (!menu.newTab) {
|
//TODO: CTRL+클릭도 대응해야하는지 고민이 필요함
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
emit("reqCall", menu.url);
|
||||||
window.open(menu.url);
|
return;
|
||||||
};
|
}
|
||||||
|
if (!menu.url) {
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!menu.newTab) {
|
||||||
|
//자동 핸들러를 따름
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
window.open(menu.url);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,12 @@
|
|||||||
<ul class="dropdown-menu dropdown-menu-start">
|
<ul class="dropdown-menu dropdown-menu-start">
|
||||||
<template v-for="(item, idx) in filteredMenu" :key="idx">
|
<template v-for="(item, idx) in filteredMenu" :key="idx">
|
||||||
<li v-if="item.type === 'item'">
|
<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 }}
|
{{ item.name }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -10,30 +15,57 @@
|
|||||||
<li :style="{ orphans: item.subMenu.length + 1 }">
|
<li :style="{ orphans: item.subMenu.length + 1 }">
|
||||||
<span class="dropdown-item disabled">{{ item.name }}</span>
|
<span class="dropdown-item disabled">{{ item.name }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
|
<template v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
|
||||||
<a class="dropdown-item subItem" :href="subItem.url" :target="subItem.newTab ? '_blank' : undefined">{{
|
<li v-if="subItem.type === 'item'">
|
||||||
subItem.name
|
<a
|
||||||
}}</a>
|
class="dropdown-item subItem"
|
||||||
</li>
|
: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>
|
||||||
<template v-else-if="item.type === 'split'">
|
<template v-else-if="item.type === 'split'">
|
||||||
<li :style="{ orphans: item.subMenu.length + 1 }">
|
<li :style="{ orphans: item.subMenu.length + 1 }">
|
||||||
<a class="dropdown-item" :href="item.main.url" :target="item.main.newTab ? '_blank' : undefined">{{
|
<a
|
||||||
item.main.name
|
class="dropdown-item"
|
||||||
}}</a>
|
:href="item.main.url"
|
||||||
</li>
|
:target="item.main.newTab ? '_blank' : undefined"
|
||||||
<li v-for="(subItem, subIdx) in item.subMenu" :key="subIdx">
|
@click="menuClick($event, item.main)"
|
||||||
<a class="dropdown-item subItem" :href="subItem.url" :target="subItem.newTab ? '_blank' : undefined">{{
|
>{{ item.main.name }}</a
|
||||||
subItem.name
|
>
|
||||||
}}</a>
|
|
||||||
</li>
|
</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>
|
||||||
</template>
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 { isArray } from "lodash-es";
|
||||||
import { computed, toRef } from "vue";
|
import { computed, toRef } from "vue";
|
||||||
|
|
||||||
@@ -45,6 +77,10 @@ const props = defineProps<{
|
|||||||
columns?: number;
|
columns?: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: "reqCall", value: string): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
const columns = computed(() => {
|
const columns = computed(() => {
|
||||||
return props.columns ?? 3;
|
return props.columns ?? 3;
|
||||||
});
|
});
|
||||||
@@ -52,7 +88,7 @@ const columns = computed(() => {
|
|||||||
const modelValue = toRef(props, "modelValue");
|
const modelValue = toRef(props, "modelValue");
|
||||||
const globalInfo = toRef(props, "globalInfo");
|
const globalInfo = toRef(props, "globalInfo");
|
||||||
|
|
||||||
type MenuVariant = MenuItem | MenuSplit | MenuMulti;
|
type MenuVariant = MenuItem | MenuSplit | MenuMulti | MenuLine;
|
||||||
|
|
||||||
function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVariant[] | undefined {
|
function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVariant[] | undefined {
|
||||||
if (isArray(menu)) {
|
if (isArray(menu)) {
|
||||||
@@ -101,6 +137,25 @@ function filterMenu(menu: MenuVariant | MenuVariant[]): MenuVariant | MenuVarian
|
|||||||
}
|
}
|
||||||
|
|
||||||
const filteredMenu = computed(() => filterMenu(modelValue.value) as GetMenuResponse["menu"]);
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ export type MenuItem = {
|
|||||||
type: 'item';
|
type: 'item';
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
funcCall?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
newTab?: boolean;
|
newTab?: boolean;
|
||||||
condHightlightVar?: string;
|
condHightlightVar?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user