382 lines
11 KiB
Vue
382 lines
11 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: "",
|
|
},
|
|
],
|
|
});
|
|
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"
|
|
>
|
|
<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
|
|
class="cursor-pointer relative group"
|
|
v-for="(item, index) in appList"
|
|
:key="index"
|
|
>
|
|
{{ 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">
|
|
<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="cursor-pointer scale-80">广州魔创未来科技有限公司</div>
|
|
</div>
|
|
<div class="justify-between text-xs text-gray-400 mt-2 scale-80">
|
|
<div class="cursor-pointer scale-80">京ICP备2023015442号</div>
|
|
</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>
|