初始化

This commit is contained in:
2026-03-26 20:18:20 +08:00
commit 120a5b4dfd
368 changed files with 35926 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
<script setup lang="ts">
import { computed } from 'vue';
import { createReusableTemplate } from '@vueuse/core';
import { SimpleScrollbar } from '@sa/materials';
import { transformColorWithOpacity } from '@sa/color';
defineOptions({ name: 'FirstLevelMenu' });
interface Props {
menus: App.Global.Menu[];
activeMenuKey?: string;
inverted?: boolean;
siderCollapse?: boolean;
darkMode?: boolean;
themeColor: string;
}
const props = defineProps<Props>();
interface Emits {
(e: 'select', menu: App.Global.Menu): boolean;
(e: 'toggleSiderCollapse'): void;
}
const emit = defineEmits<Emits>();
interface MixMenuItemProps {
/** Menu item label */
label: App.Global.Menu['label'];
/** Menu item icon */
icon: App.Global.Menu['icon'];
/** Active menu item */
active: boolean;
/** Mini size */
isMini?: boolean;
}
const [DefineMixMenuItem, MixMenuItem] = createReusableTemplate<MixMenuItemProps>();
const selectedBgColor = computed(() => {
const { darkMode, themeColor } = props;
const light = transformColorWithOpacity(themeColor, 0.1, '#ffffff');
const dark = transformColorWithOpacity(themeColor, 0.3, '#000000');
return darkMode ? dark : light;
});
function handleClickMixMenu(menu: App.Global.Menu) {
emit('select', menu);
}
function toggleSiderCollapse() {
emit('toggleSiderCollapse');
}
</script>
<template>
<!-- define component: MixMenuItem -->
<DefineMixMenuItem v-slot="{ label, icon, active, isMini }">
<div
class="mx-4px mb-6px flex-col-center cursor-pointer rounded-8px bg-transparent px-4px py-8px transition-300 hover:bg-[rgb(0,0,0,0.08)]"
:class="{
'text-primary selected-mix-menu': active,
'text-white:65 hover:text-white': inverted,
'!text-white !bg-primary': active && inverted
}"
>
<component :is="icon" :class="[isMini ? 'text-icon-small' : 'text-icon-large']" />
<p
class="w-full ellipsis-text text-center text-12px transition-height-300"
:class="[isMini ? 'h-0 pt-0' : 'h-20px pt-4px']"
>
{{ label }}
</p>
</div>
</DefineMixMenuItem>
<!-- define component end: MixMenuItem -->
<div class="h-full flex-col-stretch flex-1-hidden">
<slot></slot>
<SimpleScrollbar>
<MixMenuItem
v-for="menu in menus"
:key="menu.key"
:label="menu.label"
:icon="menu.icon"
:active="menu.key === activeMenuKey"
:is-mini="siderCollapse"
@click="handleClickMixMenu(menu)"
/>
</SimpleScrollbar>
<MenuToggler
arrow-icon
:collapsed="siderCollapse"
:z-index="99"
:class="{ 'text-white:88 !hover:text-white': inverted }"
@click="toggleSiderCollapse"
/>
</div>
</template>
<style scoped>
.selected-mix-menu {
background-color: v-bind(selectedBgColor);
}
</style>

View File

@@ -0,0 +1,36 @@
<script setup lang="ts">
interface Props {
item: App.Global.Menu;
}
const { item } = defineProps<Props>();
const hasChildren = item.children && item.children.length > 0;
</script>
<template>
<ElSubMenu v-if="hasChildren" :index="item.key">
<template #title>
<ElIcon>
<component :is="item.icon" />
</ElIcon>
<span class="ib-ellipsis">{{ item.label }}</span>
</template>
<MenuItem v-for="child in item.children" :key="child.key" :item="child" :index="child.key"></MenuItem>
</ElSubMenu>
<ElMenuItem v-else>
<ElIcon>
<component :is="item.icon" />
</ElIcon>
<span class="ib-ellipsis">{{ item.label }}</span>
</ElMenuItem>
</template>
<style scoped>
.ib-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
position: relative;
}
</style>