1. 右键菜单通用组件封装
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled

2. 文件管理优化功能(后续需要新增分组、个人收藏)
3. 优化订单页面、诊所管理页面的布局,更加集中,减少表长度
4. 新增诊所的时候业务员支持气泡卡片点击选择,不用再输入业务码
5. 接口日志筛选优化
This commit is contained in:
李琦
2026-06-18 16:42:51 +08:00
parent 073f7f882b
commit 75dd690188
16 changed files with 1282 additions and 46 deletions

View File

@@ -0,0 +1,166 @@
<script lang="ts" setup>
import type { ContextMenuItem } from './types';
import MIcon from '#/components/icon/icon.vue';
import {
contextMenuState,
getSubmenuPosition,
hideContextMenu,
MENU_ITEM_HEIGHT,
MENU_WIDTH,
showSubmenu,
submenuState,
} from './use-context-menu';
defineOptions({ name: 'GlobalContextMenu' });
function onItemEnter(item: ContextMenuItem, e: MouseEvent) {
if (!item.children?.length || item.disabled) {
showSubmenu('', [], 0, 0);
return;
}
const target = e.currentTarget as HTMLElement;
const rect = target.getBoundingClientRect();
const pos = getSubmenuPosition(rect, item.children.length);
showSubmenu(item.key, item.children, pos.left, pos.top);
}
function handleClick(item: ContextMenuItem) {
if (item.disabled || item.children?.length) {
return;
}
item.handler?.(contextMenuState.payload);
hideContextMenu();
}
function handleSubmenuClick(item: ContextMenuItem) {
if (item.disabled) {
return;
}
item.handler?.(contextMenuState.payload);
hideContextMenu();
}
</script>
<template>
<Teleport to="body">
<div
v-if="contextMenuState.visible"
class="global-context-menu"
:style="{ left: `${contextMenuState.x}px`, top: `${contextMenuState.y}px`, width: `${MENU_WIDTH}px` }"
@contextmenu.prevent
>
<div
v-for="item in contextMenuState.items"
:key="item.key"
class="context-menu-item"
:class="{ disabled: item.disabled, 'has-children': !!item.children?.length }"
@click.stop="handleClick(item)"
@mouseenter="onItemEnter(item, $event)"
>
<span class="item-icon">
<component :is="item.icon" v-if="item.icon" />
<MIcon v-else-if="item.iconName" :icon="item.iconName" size="14" />
</span>
<span class="item-label">{{ item.label }}</span>
<span v-if="item.children?.length" class="item-arrow">›</span>
</div>
</div>
<div
v-if="contextMenuState.visible && submenuState.activeKey && submenuState.items.length"
class="global-context-menu submenu"
:style="{ left: `${submenuState.left}px`, top: `${submenuState.top}px`, width: `${MENU_WIDTH}px` }"
@contextmenu.prevent
>
<div
v-for="child in submenuState.items"
:key="child.key"
class="context-menu-item"
:class="{ disabled: child.disabled }"
@click.stop="handleSubmenuClick(child)"
>
<span class="item-icon">
<component :is="child.icon" v-if="child.icon" />
<MIcon v-else-if="child.iconName" :icon="child.iconName" size="14" />
</span>
<span class="item-label">{{ child.label }}</span>
</div>
</div>
</Teleport>
</template>
<style scoped lang="scss">
.global-context-menu {
position: fixed;
z-index: 9999;
padding: 4px 0;
background: #fff;
border: 1px solid #e5e6eb;
border-radius: 8px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
.context-menu-item {
display: flex;
align-items: center;
gap: 8px;
min-height: 36px;
padding: 0 12px;
font-size: 13px;
color: #1d2129;
cursor: pointer;
user-select: none;
&:hover:not(.disabled) {
background: #f2f7ff;
color: #165dff;
}
&.disabled {
color: #c9cdd4;
cursor: not-allowed;
}
&.has-children {
padding-right: 8px;
}
}
.item-icon {
display: inline-flex;
width: 16px;
height: 16px;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.item-label {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item-arrow {
color: #86909c;
font-size: 14px;
}
.dark {
.global-context-menu {
background: #1f2937;
border-color: #374151;
}
.context-menu-item {
color: #e5e7eb;
&:hover:not(.disabled) {
background: #374151;
color: #93c5fd;
}
}
}
</style>