mcwl-pc/app/layouts/default.vue

406 lines
12 KiB
Vue

<script setup lang="ts">
import {
Binary,
Code2,
Crown,
Earth,
Image,
LayoutGrid,
Lightbulb,
Maximize,
Network,
User,
Workflow,
} from 'lucide-vue-next'
// import { useRouter } from 'vue-router'
const userStore = useUserStore()
// definePageMeta({
// middleware:[
// function (to, from ){
// if(to.path === "/personal-center" && !userStore.isLoggedIn){
// return abortNavigation()
// }
// }
// ]
// })
const modalStore = useModalStore()
const menuStore = useMenuStore()
// const router = useRouter()
// 路径到图标的映射
const iconMap: any = {
'/model-square': LayoutGrid,
'/picture-square': Lightbulb,
'/work-square': Workflow,
'/web-ui': Image,
'/comfy-ui': Workflow,
'/training-lora': Binary,
'/high-availability': Maximize,
'/api-platform': Code2,
'/creator-center': Code2,
'/personal-center': User,
'/member-center': Crown,
}
const route = useRoute()
// 监听路由变化
watch(
() => route.path,
(path) => {
menuStore.setActiveMenu(path)
},
{ immediate: true }, // 这样一进入页面就会执行一次
)
const menuItems1 = ref({
title: '探索',
list: [
{
icon: LayoutGrid,
text: '模型广场',
route: '/model-square',
},
{
icon: Lightbulb,
text: '作品灵感',
route: '/picture-square',
},
{
icon: Workflow,
text: '工作流',
route: '/work-square',
},
],
})
const menuItems2 = ref({
title: '创作',
list: [
{
icon: Network,
text: '在线工作流',
route: '',
desc: 'Comfy UI',
},
{
icon: Earth,
text: '魔创星球',
route: '/planet',
},
],
})
const menuItems3 = ref({
title: '其他',
list: [
{
icon: User,
text: '个人中心',
route: '/personal-center',
},
{
icon: Crown,
text: '会员中心',
route: '/member-center',
},
],
})
// 更新菜单项中的图标
menuStore.menuItems = menuStore.menuItems.map((item: any) => ({
...item,
LucideIcon: iconMap[item.path], // 添加 Lucide 图标组件
}))
function handleSide(event: Event, path: string) {
if (path === '/member-center') {
if (!userStore.isLoggedIn) {
modalStore.showLoginModal()
}
else {
event.preventDefault() // 阻止默认行为
event.stopPropagation() // 阻止事件冒泡
const baseUrl = window.location.origin
window.open(`${baseUrl}/member-center`, '_blank', 'noopener,noreferrer')
}
// 确保当前路由不变
// nextTick(() => {
// if (route.path !== window.location.pathname) {
// navigateTo(route.path, { replace: true })
// }
// })
}
else if (path === '/personal-center' && !userStore.isLoggedIn) {
modalStore.showLoginModal()
}
else {
menuStore.setActiveMenu(path)
}
}
const appList = ref([])
async function getAppList() {
try {
const res = await request.get(`/app/list`)
if (res.code === 200) {
appList.value = res.data
}
}
catch (err) {
console.log(err)
}
}
getAppList()
function toUs(url: string) {
const baseUrl = window.location.origin
window.open(`${baseUrl}/us/${url}`, '_blank', 'noopener,noreferrer')
}
</script>
<template>
<div class="flex h-screen flex-col bg-white dark:bg-dark-800">
<!-- Header -->
<Header />
<!-- Main Content -->
<div class="flex flex-1 overflow-hidden">
<!-- Sidebar -->
<nav
class="w-[230px] border-r border-gray-100 bg-gray-50/50 dark:border-dark-700 dark:bg-dark-800/50 overflow-y-auto scrollbar-hide [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
>
<div class="space-y-1 px-2">
<div class="text-[#000] p-4">
<div class="px-3 pt-4">
<span class="text-gray-400 text-xs">
{{ menuItems1.title }}
</span>
</div>
<div class="py-4 space-y-2 border-b border-b-solid border-b-gray-200">
<NuxtLink
v-for="item in menuItems1.list"
:key="item.route"
class="py-3 px-2 w-full block hover:bg-gray-100 rounded-lg"
:to="item.route"
:class="[
menuStore.activeMenu === item.route
? 'bg-blue-500/8 bg-gray-100 dark:bg-blue-500/10 dark:text-blue-400'
: 'dark:text-gray-300 dark:hover:bg-dark-700/50',
]"
@click="(event: Event) => handleSide(event, item.route)"
>
<div class="flex items-center">
<component
:is="item.icon"
class="h-[18px] w-[18px] mr-2"
:class="
menuStore.activeMenu === item.route
? 'text-blue-600 dark:text-blue-400'
: 'dark:text-gray-400'
"
/>
<span class="text-sm">
{{ item.text }}
</span>
</div>
</NuxtLink>
</div>
</div>
<div class="text-[#000] px-4">
<div class="px-3 py-4">
<span class="text-gray-400 text-xs">
{{ menuItems2.title }}
</span>
</div>
<div>
<NuxtLink
v-for="item in menuItems2.list"
:key="item.route"
:to="item.route"
target="_blank"
@click="(event: Event) => handleSide(event, item.route)"
>
<div
class="cursor-pointer mb-3 flex flex-col justify-center py-3 px-2 w-full block hover:bg-gray-100 border-[#ccc] rounded-lg border-dashed border"
>
<div class="flex items-center">
<component :is="item.icon" class="h-[18px] w-[18px] mr-[10px]" />
<div class="text-sm">
{{ item.text }}
</div>
</div>
<div v-if="item.desc" class="text-xs text-gray-500 mt-1 ml-[28px]">
{{ item.desc }}
</div>
</div>
</NuxtLink>
</div>
</div>
<div class="text-[#000] px-4">
<div class="px-3 pt-4">
<span class="text-gray-400 text-xs">
{{ menuItems3.title }}
</span>
</div>
<div class="py-4 space-y-2">
<NuxtLink
v-for="item in menuItems3.list"
:key="item.route"
class="py-3 px-2 w-full block hover:bg-gray-100 rounded-lg"
:to="item.route"
:class="[
menuStore.activeMenu === item.route
? 'bg-blue-500/8 bg-gray-100 dark:bg-blue-500/10 dark:text-blue-400'
: 'dark:text-gray-300 dark:hover:bg-dark-700/50',
]"
@click="(event: Event) => handleSide(event, item.route)"
>
<div class="flex items-center">
<component
:is="item.icon"
class="h-[18px] w-[18px] mr-2"
:class="
menuStore.activeMenu === item.route
? 'text-blue-600 dark:text-blue-400'
: 'dark:text-gray-400'
"
/>
<span class="text-sm">
{{ item.text }}
</span>
</div>
</NuxtLink>
</div>
</div>
<div class="px-4">
<div class="text-xs text-gray-500 flex flex-wrap gap-2">
<div
v-for="(item, index) in appList"
:key="index"
class="cursor-pointer relative group"
>
{{ item.name }}
<div
class="flex flex-col justify-center items-center gap-2 absolute bottom-4 left-0 w-[90px] h-[115px] hidden group-hover:block bg-white p-2 z-index-999 border border-solid border-[#ccc] rounded-lg"
>
<div class="text-xs text-center mb-2">
扫码关注
</div>
<img class="w-[80px] h-[70px]" :src="item.url" alt="">
</div>
</div>
</div>
<div class="flex justify-between text-xs text-gray-400 mt-2 mb-2">
<div class="cursor-pointer" @click="toUs('agreement')">
用户协议
</div>
<div class="cursor-pointer" @click="toUs('privacy')">
隐私政策
</div>
<div class="cursor-pointer" @click="toUs('aboutUs')">
关于我们
</div>
</div>
<div class="justify-between text-xs text-gray-400 mt-2 scale-80">
<div class="scale-80">
魔创未来(盘锦)量子科技有限公司
</div>
</div>
<div class="justify-between text-xs text-gray-400 mt-2 scale-80">
<!-- <a class="cursor-pointer scale-80">
辽ICP备2024045484号-1
</div> -->
<a class="text-xs leading-7 hover:opacity-80 mt-[8px]" href="https://beian.miit.gov.cn" rel="noreferrer" target="_blank">辽ICP备2024045484号-1</a>
</div>
<!-- <div class="justify-between text-xs text-gray-400 mt-2 scale-80">
<div class="cursor-pointer scale-80">
网信算备
</div>
<div class="cursor-pointer scale-80">
110112129623601230015号
</div>
</div>
<div class="justify-between text-xs text-gray-400 mt-2 scale-80">
<div class="cursor-pointer scale-80">
备案号: Beijing-PianYu-202402
</div>
</div> -->
</div>
<!-- <NuxtLink
v-for="item in menuStore.menuItems"
:key="item.path"
:to="item.path"
class="flex items-center gap-3 rounded-lg px-4 py-4 text-[15px] font-medium no-underline transition-colors"
:class="[
menuStore.activeMenu === item.path
? 'bg-blue-500/8 text-blue-600 dark:bg-blue-500/10 dark:text-blue-400'
: 'text-gray-600 hover:bg-gray-500/5 dark:text-gray-300 dark:hover:bg-dark-700/50',
]"
@click="(event: Event) => handleSide(event, item.path)"
>
<component
:is="item.LucideIcon"
class="h-[14px] w-[14px]"
:class="menuStore.activeMenu === item.path
? 'text-blue-600 dark:text-blue-400'
: 'text-gray-500 dark:text-gray-400'"
/>
<span>{{ item.label }}</span>
</NuxtLink> -->
</div>
</nav>
<!-- Page Content -->
<main class="flex-1 overflow-auto">
<slot />
</main>
<!-- 登录框组件 -->
</div>
</div>
</template>
<style>
:root {
--primary: rgb(59, 130, 246);
--primary-hover: rgb(37, 99, 235);
}
::selection {
/* cursor: pointer; */
background: var(--primary);
color: white;
/* position: absolute; */
/* border: dashed; */
}
/* 自定义滚动条 */
::-webkit-scrollbar {
justify-content: center;
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #e5e7eb;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #d1d5db;
}
.dark ::-webkit-scrollbar-thumb {
background: #374151;
}
.dark ::-webkit-scrollbar-thumb:hover {
background: #4b5563;
}
</style>