148 lines
3.2 KiB
Vue
148 lines
3.2 KiB
Vue
<script setup lang="ts">
|
|
import type { VNode } from 'vue'
|
|
import { computed } from 'vue'
|
|
import { useSvgIcon } from '@/hooks/common/icon'
|
|
import { $t } from '@/locales'
|
|
import { useTabStore } from '@/store/modules/tab'
|
|
|
|
defineOptions({
|
|
name: 'ContextMenu',
|
|
})
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
excludeKeys: () => [],
|
|
disabledKeys: () => [],
|
|
})
|
|
|
|
interface Props {
|
|
/** ClientX */
|
|
x: number
|
|
/** ClientY */
|
|
y: number
|
|
tabId: string
|
|
excludeKeys?: App.Global.DropdownKey[]
|
|
disabledKeys?: App.Global.DropdownKey[]
|
|
}
|
|
|
|
const visible = defineModel<boolean>('visible')
|
|
|
|
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs, fixTab, unfixTab, isTabRetain, homeTab } = useTabStore()
|
|
const { SvgIconVNode } = useSvgIcon()
|
|
|
|
interface DropdownOption {
|
|
key: App.Global.DropdownKey
|
|
label: string
|
|
icon?: () => VNode
|
|
disabled?: boolean
|
|
}
|
|
|
|
const options = computed(() => {
|
|
const opts: DropdownOption[] = [
|
|
{
|
|
key: 'closeCurrent',
|
|
label: $t('dropdown.closeCurrent'),
|
|
icon: SvgIconVNode({ icon: 'ant-design:close-outlined', fontSize: 18 }),
|
|
},
|
|
{
|
|
key: 'closeOther',
|
|
label: $t('dropdown.closeOther'),
|
|
icon: SvgIconVNode({ icon: 'ant-design:column-width-outlined', fontSize: 18 }),
|
|
},
|
|
{
|
|
key: 'closeLeft',
|
|
label: $t('dropdown.closeLeft'),
|
|
icon: SvgIconVNode({ icon: 'mdi:format-horizontal-align-left', fontSize: 18 }),
|
|
},
|
|
{
|
|
key: 'closeRight',
|
|
label: $t('dropdown.closeRight'),
|
|
icon: SvgIconVNode({ icon: 'mdi:format-horizontal-align-right', fontSize: 18 }),
|
|
},
|
|
{
|
|
key: 'closeAll',
|
|
label: $t('dropdown.closeAll'),
|
|
icon: SvgIconVNode({ icon: 'ant-design:line-outlined', fontSize: 18 }),
|
|
},
|
|
]
|
|
|
|
if (props.tabId !== homeTab?.id) {
|
|
if (isTabRetain(props.tabId)) {
|
|
opts.push({
|
|
key: 'unpin',
|
|
label: $t('dropdown.unpin'),
|
|
icon: SvgIconVNode({ icon: 'mdi:pin-off-outline', fontSize: 18 }),
|
|
})
|
|
}
|
|
else {
|
|
opts.push({
|
|
key: 'pin',
|
|
label: $t('dropdown.pin'),
|
|
icon: SvgIconVNode({ icon: 'mdi:pin-outline', fontSize: 18 }),
|
|
})
|
|
}
|
|
}
|
|
|
|
const { excludeKeys, disabledKeys } = props
|
|
|
|
const result = opts.filter(opt => !excludeKeys.includes(opt.key))
|
|
|
|
disabledKeys.forEach((key) => {
|
|
const opt = result.find(item => item.key === key)
|
|
|
|
if (opt) {
|
|
opt.disabled = true
|
|
}
|
|
})
|
|
|
|
return result
|
|
})
|
|
|
|
function hideDropdown() {
|
|
visible.value = false
|
|
}
|
|
|
|
const dropdownAction: Record<App.Global.DropdownKey, () => void> = {
|
|
closeCurrent() {
|
|
removeTab(props.tabId)
|
|
},
|
|
closeOther() {
|
|
clearTabs([props.tabId])
|
|
},
|
|
closeLeft() {
|
|
clearLeftTabs(props.tabId)
|
|
},
|
|
closeRight() {
|
|
clearRightTabs(props.tabId)
|
|
},
|
|
closeAll() {
|
|
clearTabs()
|
|
},
|
|
pin() {
|
|
fixTab(props.tabId)
|
|
},
|
|
unpin() {
|
|
unfixTab(props.tabId)
|
|
},
|
|
}
|
|
|
|
function handleDropdown(optionKey: App.Global.DropdownKey) {
|
|
dropdownAction[optionKey]?.()
|
|
hideDropdown()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<NDropdown
|
|
:show="visible"
|
|
placement="bottom-start"
|
|
trigger="manual"
|
|
:x="x"
|
|
:y="y"
|
|
:options="options"
|
|
@clickoutside="hideDropdown"
|
|
@select="handleDropdown"
|
|
/>
|
|
</template>
|
|
|
|
<style scoped></style>
|