feat(projects): 工作台小组件设计

This commit is contained in:
2026-05-28 08:20:01 +08:00
parent 3988eaf910
commit 4ed4b537ad
54 changed files with 4726 additions and 2720 deletions

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import type { VNode } from 'vue';
import { computed, ref } from 'vue';
import { objectContextDomainConfigs } from '@/constants/object-context';
import { useRouteStore } from '@/store/modules/route';
import { useWorkbenchStore } from '@/store/modules/workbench';
import { useRouterPush } from '@/hooks/common/router';
@@ -28,20 +30,27 @@ const { routerPushByKey } = useRouterPush();
interface FlatMenu {
key: string;
label: string;
icon?: string;
/** 来自 routeStore.menus 的 icon VNodeSvgIconVNode 渲染产物,与侧栏菜单同源) */
icon?: VNode;
}
// 与 picker 保持一致对象域入口project_list / product_list当叶子
// 其下的对象上下文页不进入快捷入口候选范围。
const OBJECT_DOMAIN_ENTRY_KEYS = new Set(objectContextDomainConfigs.map(c => c.entryRouteKey));
function flatten(menus: typeof routeStore.menus): FlatMenu[] {
const out: FlatMenu[] = [];
function walk(list: typeof menus) {
list.forEach((m: any) => {
if (m.children && m.children.length) {
const isDomainEntry = OBJECT_DOMAIN_ENTRY_KEYS.has(m.key);
const hasChildren = !isDomainEntry && m.children && m.children.length > 0;
if (hasChildren) {
walk(m.children);
} else {
out.push({
key: m.key,
label: m.label as string,
icon: m.i18nKey || m.icon || ''
icon: m.icon
});
}
});
@@ -90,8 +99,15 @@ function handleConfirm(keys: string[]) {
</div>
<div v-else class="shortcut-grid">
<button v-for="item in selected" :key="item.key" class="shortcut-item" @click="handleClick(item.key)">
<SvgIcon icon="mdi:link-variant" />
<span>{{ item.label }}</span>
<ElIcon v-if="item.icon" class="shortcut-item__icon">
<component :is="item.icon" />
</ElIcon>
<SvgIcon v-else icon="mdi:link-variant" class="shortcut-item__icon" />
<span class="shortcut-item__label">{{ item.label }}</span>
</button>
<button class="shortcut-item shortcut-item--add" title="添加快捷入口" @click="openPicker">
<SvgIcon icon="mdi:plus" />
<span>添加</span>
</button>
</div>
@@ -121,11 +137,37 @@ function handleConfirm(keys: string[]) {
transition: all 120ms;
}
.shortcut-item__icon {
font-size: 20px;
color: var(--el-color-primary);
transition: color 120ms;
}
.shortcut-item__label {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.shortcut-item:hover {
border-color: var(--el-color-primary);
color: var(--el-color-primary);
}
.shortcut-item--add {
border-style: dashed;
border-color: var(--el-border-color);
background: transparent;
color: var(--el-text-color-secondary);
}
.shortcut-item--add:hover {
border-color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
.shortcut-empty {
padding: 20px 0;
}