17 changed files with 457 additions and 22 deletions
After Width: | Height: | Size: 4.9 KiB |
@ -0,0 +1,137 @@ |
|||||||
|
<template> |
||||||
|
<teleport to="#app"> |
||||||
|
<div class="drawer" @click="show = false" v-show="show" |
||||||
|
@contextmenu.prevent="console.log('禁用浏览器默认右键功能')"> |
||||||
|
<div class="content" :style="style" @click.native.stop> |
||||||
|
<div class="title" v-if="showHeader"> |
||||||
|
<span>{{ title }}</span> |
||||||
|
<i class="close" @click="show = false; emit('close')"></i> |
||||||
|
</div> |
||||||
|
<slot /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</teleport> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts" setup> |
||||||
|
import { useStore } from "vuex"; |
||||||
|
import { watch, onMounted, ref, onBeforeUnmount } from "vue"; |
||||||
|
import { useI18n } from "vue3-i18n"; |
||||||
|
|
||||||
|
const { t } = useI18n(); |
||||||
|
const { state, commit, dispatch } = useStore(); |
||||||
|
const show = ref(false); |
||||||
|
const style = ref({}) |
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'close']) |
||||||
|
const prop = defineProps({ |
||||||
|
modelValue: { |
||||||
|
type: Boolean, |
||||||
|
default: false |
||||||
|
}, |
||||||
|
showHeader: { |
||||||
|
type: Boolean, |
||||||
|
default: true |
||||||
|
}, |
||||||
|
title: { |
||||||
|
type: String, |
||||||
|
defalut: "" |
||||||
|
}, |
||||||
|
width: { |
||||||
|
type: String, |
||||||
|
default: '' |
||||||
|
}, |
||||||
|
height: { |
||||||
|
type: String, |
||||||
|
default: '' |
||||||
|
}, |
||||||
|
direction: { |
||||||
|
type: String, |
||||||
|
default: 'left' |
||||||
|
}, |
||||||
|
padding: { |
||||||
|
type: String, |
||||||
|
default: '10px' |
||||||
|
}, |
||||||
|
}) |
||||||
|
watch(show, (n, o) => { |
||||||
|
emit('update:modelValue', n); |
||||||
|
}) |
||||||
|
watch(() => prop.modelValue, (n, o) => { |
||||||
|
show.value = n; |
||||||
|
}) |
||||||
|
|
||||||
|
const setDirection = () => { |
||||||
|
switch (prop.direction) { |
||||||
|
case 'left': |
||||||
|
style.value = { left: '0', width: prop.width, height: '100%' }; |
||||||
|
break; |
||||||
|
case 'right': |
||||||
|
style.value = { right: '0', width: prop.width, height: '100%' }; |
||||||
|
break; |
||||||
|
case 'top': |
||||||
|
style.value = { top: '0', width: '100%', height: prop.height }; |
||||||
|
break; |
||||||
|
case 'bottom': |
||||||
|
style.value = { bottom: '0', width: '100%', height: prop.height }; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
watch(() => prop.direction, setDirection); |
||||||
|
|
||||||
|
const keydown = (e) => { |
||||||
|
if (e.keyCode == 27) { |
||||||
|
show.value = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
setDirection(); |
||||||
|
document.addEventListener('keydown', keydown) |
||||||
|
}); |
||||||
|
onBeforeUnmount(() => { |
||||||
|
document.removeEventListener('keydown', keydown) |
||||||
|
}); |
||||||
|
</script> |
||||||
|
<style lang="scss" scoped> |
||||||
|
.drawer { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
position: absolute; |
||||||
|
z-index: 9; |
||||||
|
background: rgba(0, 0, 0, 0.5); |
||||||
|
|
||||||
|
.content { |
||||||
|
position: absolute; |
||||||
|
background: rgba(0, 0, 0, 0.7); |
||||||
|
padding: v-bind('prop.padding'); |
||||||
|
width: v-bind('prop.width'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.title { |
||||||
|
display: flex; |
||||||
|
position: relative; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
font-size: 1.8rem; |
||||||
|
height: 3.5rem; |
||||||
|
line-height: 3.5rem; |
||||||
|
width: 100%; |
||||||
|
color: white; |
||||||
|
margin-bottom: 0.5rem; |
||||||
|
|
||||||
|
.close { |
||||||
|
cursor: pointer; |
||||||
|
position: absolute; |
||||||
|
top: 0.5rem; |
||||||
|
right: 0.5rem; |
||||||
|
display: block; |
||||||
|
width: 2rem; |
||||||
|
height: 2rem; |
||||||
|
background-image: url(@/assets/icons/close.png); |
||||||
|
background-size: cover; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,4 @@ |
|||||||
|
import Tab from './tab.vue'; |
||||||
|
import Tabs from './tabs.vue'; |
||||||
|
|
||||||
|
export { Tabs, Tab }; |
@ -0,0 +1,36 @@ |
|||||||
|
<template> |
||||||
|
<div v-show="isSelected" class="tab__pane"> |
||||||
|
<slot /> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts" setup> |
||||||
|
import { useStore } from "vuex"; |
||||||
|
import { getCurrentInstance, onMounted, ref, computed, watch } from "vue"; |
||||||
|
import { useI18n } from "vue3-i18n"; |
||||||
|
|
||||||
|
const { t } = useI18n(); |
||||||
|
const { state, commit, dispatch } = useStore(); |
||||||
|
const parent: any = getCurrentInstance()?.parent; |
||||||
|
const name = ref(""); |
||||||
|
|
||||||
|
const props = defineProps({ |
||||||
|
label: { |
||||||
|
type: String, |
||||||
|
default: '' |
||||||
|
}, |
||||||
|
name: { |
||||||
|
type: String, |
||||||
|
default: '' |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
const isSelected = computed(() => { |
||||||
|
return parent.exposed.curActive.value == props.name; |
||||||
|
}) |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
name.value = props.name; |
||||||
|
}); |
||||||
|
</script> |
||||||
|
<style lang="scss" scoped></style> |
@ -0,0 +1,102 @@ |
|||||||
|
<template> |
||||||
|
<div class="tabs"> |
||||||
|
<div class="tabs__nav"> |
||||||
|
<div v-for="item in tabs" :key="item.name" ref="tabs" @click="curActive = item.name" class="tab" |
||||||
|
:class="{ 'tab--active': item.name === curActive }"> |
||||||
|
<span>{{ item.label || item.name }}</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<slot /> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts" setup> |
||||||
|
import { useStore } from "vuex"; |
||||||
|
import { watch, onMounted, ref } from "vue"; |
||||||
|
import { useI18n } from "vue3-i18n"; |
||||||
|
|
||||||
|
const { t } = useI18n(); |
||||||
|
const { state, commit, dispatch } = useStore(); |
||||||
|
const curActive = ref(''); |
||||||
|
const emit = defineEmits(['update:modelValue']) |
||||||
|
|
||||||
|
const props = defineProps({ |
||||||
|
modelValue: { |
||||||
|
type: String, |
||||||
|
default: '' |
||||||
|
}, |
||||||
|
active: { |
||||||
|
type: String, |
||||||
|
default: '' |
||||||
|
}, |
||||||
|
tabs: { |
||||||
|
type: Array as () => any[], |
||||||
|
default: [] |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
watch(curActive, (n, o) => { |
||||||
|
emit('update:modelValue', n); |
||||||
|
}) |
||||||
|
watch(() => props.modelValue, (n, o) => { |
||||||
|
curActive.value = n; |
||||||
|
}) |
||||||
|
|
||||||
|
defineExpose({ curActive }) |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
curActive.value = props.active || props.tabs[0]?.name; |
||||||
|
}); |
||||||
|
</script> |
||||||
|
<style lang="scss" scoped> |
||||||
|
*{ |
||||||
|
color: white; |
||||||
|
} |
||||||
|
.tab { |
||||||
|
// flex: 1; |
||||||
|
cursor: pointer; |
||||||
|
min-width: 0; |
||||||
|
padding: 0.3rem 0.3rem; |
||||||
|
font-size: 14px; |
||||||
|
position: relative; |
||||||
|
color: #000; |
||||||
|
line-height: 50px; |
||||||
|
text-align: center; |
||||||
|
box-sizing: border-box; |
||||||
|
margin-right: 2rem; |
||||||
|
|
||||||
|
span { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
|
||||||
|
&--active { |
||||||
|
font-weight: 500 !important; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.tab--active { |
||||||
|
border-bottom: 2px solid #ccc; |
||||||
|
} |
||||||
|
|
||||||
|
.tabs { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
&__nav { |
||||||
|
display: flex; |
||||||
|
user-select: none; |
||||||
|
position: relative; |
||||||
|
height: 100%; |
||||||
|
box-sizing: content-box; |
||||||
|
} |
||||||
|
|
||||||
|
&__line { |
||||||
|
z-index: 1; |
||||||
|
left: 0; |
||||||
|
bottom: 0px; |
||||||
|
height: 3px; |
||||||
|
position: absolute; |
||||||
|
border-radius: 3px; |
||||||
|
background-color: red; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,76 @@ |
|||||||
|
<template> |
||||||
|
<div class="title">{{ t('quality.' + quality) }}{{ t(type + '.type') }} |
||||||
|
</div> |
||||||
|
<div class="equips"> |
||||||
|
<div class="equip" v-for="item in categorys" :key="item.name"> |
||||||
|
<EquipIcon :equip="getShowEquip(item)" /> |
||||||
|
<div class="name">{{ t(type + '.' + item.name + '.0') }}</div> |
||||||
|
<div class="entry" v-for="e in item.entry">{{ t(e.type + '.0') }}</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts" setup> |
||||||
|
import { useStore } from "vuex"; |
||||||
|
import { reactive, onMounted, ref } from "vue"; |
||||||
|
import { useI18n } from "vue3-i18n"; |
||||||
|
import { EquipIcon } from "@/components"; |
||||||
|
import { quality_collor } from "@/config"; |
||||||
|
|
||||||
|
const { t } = useI18n(); |
||||||
|
const { state, commit, dispatch } = useStore(); |
||||||
|
const color = ref('#FFF'); |
||||||
|
|
||||||
|
const props = defineProps({ |
||||||
|
type: { |
||||||
|
type: String, |
||||||
|
default: '' |
||||||
|
}, |
||||||
|
quality: { |
||||||
|
type: String, |
||||||
|
default: '' |
||||||
|
}, |
||||||
|
categorys: { |
||||||
|
type: Array as () => any[], |
||||||
|
default: [], |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const getShowEquip = (category) => { |
||||||
|
const quality = props.quality; |
||||||
|
const equip: any = new Object(); |
||||||
|
equip.quality = { quality: quality, color: quality_collor[quality] }; |
||||||
|
equip.base = { icon: category.icon }; |
||||||
|
return equip; |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
color.value = quality_collor[props.quality]; |
||||||
|
}); |
||||||
|
</script> |
||||||
|
<style lang="scss" scoped> |
||||||
|
.title { |
||||||
|
height: 2.5rem; |
||||||
|
font-size: 1.5rem; |
||||||
|
color: v-bind('color'); |
||||||
|
} |
||||||
|
|
||||||
|
.equips { |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
width: 50rem; |
||||||
|
|
||||||
|
.equip { |
||||||
|
align-items: center; |
||||||
|
text-align: center; |
||||||
|
justify-items: center; |
||||||
|
width: 10rem; |
||||||
|
margin-bottom: 1rem; |
||||||
|
|
||||||
|
.name { |
||||||
|
padding-bottom: 0.5rem; |
||||||
|
color: v-bind('color'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,69 @@ |
|||||||
|
<template> |
||||||
|
<Tooltip :infos="[t('illustrated.0')]" width="8rem"> |
||||||
|
<img class="menu-img" :src="menu_icons.illustrated" @click="showMenu"> |
||||||
|
</Tooltip> |
||||||
|
|
||||||
|
<Drawer :title="t('illustrated.0')" v-model="showIllustrated"> |
||||||
|
<Tabs :tabs="tabs"> |
||||||
|
<tab v-for="item in tabs" :name="item.name"> |
||||||
|
<Equips :type="item.name" quality="unique" :categorys="uniques[item.name]" /> |
||||||
|
<Equips :type="item.name" quality="epic" :categorys="epics[item.name]" /> |
||||||
|
</tab> |
||||||
|
</Tabs> |
||||||
|
</Drawer> |
||||||
|
|
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts" setup> |
||||||
|
import { useStore } from "vuex"; |
||||||
|
import { computed, onMounted, ref, onBeforeUnmount } from "vue"; |
||||||
|
import { useI18n } from "vue3-i18n"; |
||||||
|
import { Tooltip, Drawer, Tabs, Tab } from "@/components"; |
||||||
|
import { menu_icons, weaponCategorys, weaponUniqueCategorys, armorUniqueCategorys, armorCategorys } from "@/config"; |
||||||
|
import { neckUniqueCategorys, neckCategorys, ringUniqueCategorys, ringCategorys } from "@/config"; |
||||||
|
import Equips from "./equips.vue"; |
||||||
|
|
||||||
|
const { t } = useI18n(); |
||||||
|
const { state, commit, dispatch } = useStore(); |
||||||
|
const showIllustrated = computed(() => { |
||||||
|
return state.curMenu == 'illustrated'; |
||||||
|
}); |
||||||
|
const uniques = { |
||||||
|
weapon: weaponUniqueCategorys, |
||||||
|
armor: armorUniqueCategorys, |
||||||
|
neck: neckUniqueCategorys, |
||||||
|
ring: ringUniqueCategorys |
||||||
|
} |
||||||
|
const epics = { |
||||||
|
weapon: weaponCategorys, |
||||||
|
armor: armorCategorys, |
||||||
|
neck: neckCategorys, |
||||||
|
ring: ringCategorys |
||||||
|
} |
||||||
|
|
||||||
|
const tabs = [ |
||||||
|
{ label: t('weapon.type'), name: 'weapon' }, |
||||||
|
{ label: t('armor.type'), name: 'armor' }, |
||||||
|
{ label: t('neck.type'), name: 'neck' }, |
||||||
|
{ label: t('ring.type'), name: 'ring' } |
||||||
|
] |
||||||
|
|
||||||
|
const showMenu = () => { |
||||||
|
state.curMenu = showIllustrated.value ? null : 'illustrated'; |
||||||
|
} |
||||||
|
|
||||||
|
const keydown = (e) => { |
||||||
|
if (e.keyCode == 73 && !e.ctrlKey) { |
||||||
|
showMenu(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
document.addEventListener('keydown', keydown); |
||||||
|
}); |
||||||
|
onBeforeUnmount(() => { |
||||||
|
document.removeEventListener('keydown', keydown); |
||||||
|
}) |
||||||
|
|
||||||
|
</script> |
||||||
|
<style lang="scss" scoped></style> |
@ -0,0 +1,3 @@ |
|||||||
|
import Illustrated from './illustrated.vue'; |
||||||
|
|
||||||
|
export default Illustrated; |
Loading…
Reference in new issue