Files
reader-star/apps/user/src/layouts/modules/global-tab/context-menu.vue
2026-01-16 13:06:44 +08:00

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>