Compare commits

...

2 Commits

Author SHA1 Message Date
qiuyuan
8d345598cf Merge branch 'main' of https://gitlab.guxuan.icu/Leo_Ding/GPU_Web 2025-11-20 16:33:47 +08:00
qiuyuan
2bf9f2cbae 代码修改 2025-11-20 16:33:46 +08:00
8 changed files with 325 additions and 135 deletions

View File

@ -1,17 +1,30 @@
<!-- src/App.vue -->
<template>
<router-view />
<div id="app">
<Header />
<router-view />
</div>
</template>
<script setup>
import Header from '@/components/Header.vue'
</script>
<style>
/* 全局重置 */
* {
box-sizing: border-box;
}
html, body, #app {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
box-sizing: border-box;
}
html, body {
height: 100%;
}
#app {
min-height: 100vh;
margin-top: 66px;
}
</style>

123
src/components/Header.vue Normal file
View File

@ -0,0 +1,123 @@
<!-- src/components/Header.vue -->
<template>
<div class="header">
<!-- 左侧导航 -->
<nav class="nav-left">
<a href="/" class="nav-item">首页</a>
<a href="/compute" class="nav-item">算力中心</a>
<a href="/cloud-server" class="nav-item">云主机</a>
</nav>
<!-- 右侧导航 -->
<nav class="nav-right">
<a href="/docs" class="nav-item">用户文档</a>
<a href="/controlPanel" class="nav-item active">控制台</a>
<button class="login-btn" @click="goToLogin">登录/注册</button>
</nav>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
// 使 Vue Router
const goToLogin = () => {
router.push('/login')
}
</script>
<style scoped>
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 40px;
background-color: #fff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
height: 64px;
box-sizing: border-box;
}
.nav-left,
.nav-right {
display: flex;
align-items: center;
gap: 32px;
}
.nav-left {
padding-left: 8px; /* 增加左侧内边距 */
}
.nav-right {
padding-right: 8px; /* 增加右侧内边距 */
}
.nav-item {
font-size: 15px;
color: #333;
text-decoration: none;
transition: all 0.2s ease;
padding: 8px 4px;
position: relative;
font-weight: 500;
}
.nav-item:hover {
color: #1890ff;
}
.nav-item.active {
color: #1890ff;
font-weight: 600;
}
.nav-item.active::after {
content: '';
position: absolute;
bottom: -2px;
left: 4px;
right: 4px;
height: 2px;
background-color: #1890ff;
border-radius: 1px;
}
.login-btn {
padding: 8px 20px;
background-color: #1890ff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
}
.login-btn:hover {
background-color: #096dd9;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(24, 144, 255, 0.3);
}
/* 响应式调整 */
@media (max-width: 768px) {
.header {
padding: 16px 20px;
}
.nav-left,
.nav-right {
gap: 20px;
}
}
</style>

View File

@ -1,51 +1,49 @@
<!-- src/components/Layout.vue -->
<template>
<div class="layout">
<div class="sidebar-container">
<Sidebar />
<div class="layout">
<!-- 侧边栏 -->
<div class="sidebar-container custom-sidebar">
<Sidebar />
</div>
<!-- 主要内容区域 -->
<div class="main-content">
<div class="content">
<router-view />
</div>
</div>
</div>
<div class="main-content">
<div class="content">
<router-view />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import Sidebar from './sidebar.vue'
import Sidebar from './Sidebar.vue'
</script>
<style scoped>
.layout {
display: flex;
min-height: 100vh;
width: 100%;
margin: 0;
padding: 0;
display: flex;
min-height: 100vh;
width: 100%;
}
.sidebar-container {
width: 200px;
flex-shrink: 0;
height: 100vh;
position: sticky;
top: 0;
left: 0;
width: 240px;
flex-shrink: 0;
height: 100vh;
position: sticky;
top: 0;
left: 0;
}
.main-content {
flex: 1;
min-width: 0;
background: #f5f5f5;
flex: 1;
min-width: 0;
background: #f5f5f5;
}
.content {
padding: 20px;
background: #fff;
min-height: calc(100vh - 40px);
padding: 20px;
background: #fff;
min-height: calc(100vh - 40px);
}
</style>

View File

@ -7,26 +7,33 @@
:open-keys="openKeys"
@click="handleMenuClick"
theme="light"
class="custom-menu"
>
<template v-for="item in menuItems" :key="item.path">
<!-- 有子菜单的项 -->
<a-sub-menu v-if="item.children && item.children.length" :key="item.path">
<template #title>
<span class="menu-item-content">
<span class="icon">{{ item.icon }}</span>
<span>{{ item.name }}</span>
<span class="icon">
<component :is="item.icon" />
</span>
<span class="menu-text">{{ item.name }}</span>
</span>
</template>
<a-menu-item v-for="child in item.children" :key="child.path">
<router-link :to="child.path">{{ child.name }}</router-link>
<router-link :to="child.path" class="submenu-item">
<span class="menu-text">{{ child.name }}</span>
</router-link>
</a-menu-item>
</a-sub-menu>
<!-- 没有子菜单的项 -->
<a-menu-item v-else :key="item.path">
<router-link :to="item.path" class="menu-item-content">
<span class="icon">{{ item.icon }}</span>
<span>{{ item.name }}</span>
<span class="icon">
<component :is="item.icon" />
</span>
<span class="menu-text">{{ item.name }}</span>
</router-link>
</a-menu-item>
</template>
@ -37,6 +44,7 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { HomeOutlined,FolderOpenOutlined,ConsoleSqlOutlined ,GlobalOutlined,LaptopOutlined,MoneyCollectOutlined,TeamOutlined} from '@ant-design/icons-vue'
const route = useRoute()
const router = useRouter()
@ -44,20 +52,20 @@ const router = useRouter()
interface MenuItem {
path: string
name: string
icon: string
icon: any
children?: { path: string; name: string }[]
}
// 使 /controlPanel
const menuItems: MenuItem[] = [
{ path: '/controlPanel/fileStore', name: '文件存储', icon: '💾' },
{ path: '/controlPanel/container', name: '容器实例', icon: '🐳' },
{ path: '/controlPanel/image', name: '镜像', icon: '🖼️' },
{ path: '/controlPanel/publicData', name: '公开数据', icon: '📚' },
{ path: '/controlPanel/overview', name: '总览', icon: HomeOutlined },
{ path: '/controlPanel/container', name: '容器实例', icon: ConsoleSqlOutlined },
{ path: '/controlPanel/fileStore', name: '文件存储', icon: FolderOpenOutlined },
{ path: '/controlPanel/image', name: '镜像', icon: GlobalOutlined },
{ path: '/controlPanel/publicData', name: '公开数据', icon: LaptopOutlined },
{
path: '/controlPanel/fee',
name: '费用',
icon: '💰',
icon: MoneyCollectOutlined,
children: [
{ path: '/controlPanel/fee/detail', name: '详情' },
{ path: '/controlPanel/fee/bill', name: '账单' }
@ -66,7 +74,7 @@ const menuItems: MenuItem[] = [
{
path: '/controlPanel/account',
name: '账号',
icon: '👤',
icon: TeamOutlined,
children: [
{ path: '/controlPanel/account/profile', name: '个人资料' },
{ path: '/controlPanel/account/security', name: '安全设置' }
@ -74,12 +82,8 @@ const menuItems: MenuItem[] = [
}
]
//
const selectedKeys = computed(() => {
return [route.path]
})
const selectedKeys = computed(() => [route.path])
//
const openKeys = computed(() => {
const keys: string[] = []
if (route.path.startsWith('/controlPanel/fee')) keys.push('/controlPanel/fee')
@ -87,7 +91,6 @@ const openKeys = computed(() => {
return keys
})
//
const handleMenuClick = ({ key }: { key: string }) => {
router.push(key)
}
@ -95,7 +98,7 @@ const handleMenuClick = ({ key }: { key: string }) => {
<style scoped>
.sidebar {
width: 200px;
width: 240px;
height: 100vh;
background: #fff;
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1);
@ -105,23 +108,99 @@ const handleMenuClick = ({ key }: { key: string }) => {
.menu-item-content {
display: flex;
align-items: center;
}
.icon {
margin-right: 8px;
width: 20px;
text-align: center;
}
/* 修复链接样式 */
:deep(.ant-menu-item a) {
color: inherit;
text-decoration: none;
display: block;
width: 100%;
}
:deep(.ant-menu-submenu-title) {
.icon {
margin-right: 12px; /* 增加图标和文字的间距 */
width: 20px;
text-align: center;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 16px; /* 图标大小 */
}
.menu-text {
font-size: 14px; /* 文字大小 */
font-weight: 500; /* 中等字重 */
line-height: 1.4;
}
.submenu-item {
display: block;
width: 100%;
padding-left: 32px; /* 子菜单项缩进 */
}
/* 自定义菜单样式 */
:deep(.custom-menu.ant-menu) {
font-size: 14px;
border: none;
}
:deep(.custom-menu.ant-menu-inline) {
background: transparent;
}
:deep(.custom-menu .ant-menu-item) {
height: 48px; /* 增加菜单项高度 */
line-height: 48px;
margin: 0;
border-radius: 0;
width: 100%;
}
:deep(.custom-menu .ant-menu-item:not(:last-child)) {
margin-bottom: 0;
}
:deep(.custom-menu .ant-menu-submenu-title) {
height: 48px; /* 增加子菜单标题高度 */
line-height: 48px;
padding-right: 12px !important;
border-radius: 0;
margin: 0;
}
:deep(.custom-menu .ant-menu-submenu .ant-menu-item) {
height: 40px; /* 子菜单项稍矮一些 */
line-height: 40px;
padding-left: 48px !important; /* 子菜单项缩进 */
}
:deep(.custom-menu .ant-menu-item-selected) {
background-color: #e6f7ff; /* 选中背景色 */
border-right: 3px solid #1890ff; /* 选中指示条 */
}
:deep(.custom-menu .ant-menu-item:active),
:deep(.custom-menu .ant-menu-submenu-title:active) {
background-color: #f0f0f0;
}
:deep(.custom-menu .ant-menu-item a) {
color: inherit;
text-decoration: none;
display: flex;
align-items: center;
width: 100%;
height: 100%;
}
:deep(.custom-menu .ant-menu-submenu-arrow) {
color: rgba(0, 0, 0, 0.45);
}
/* 鼠标悬停效果 */
:deep(.custom-menu .ant-menu-item:hover),
:deep(.custom-menu .ant-menu-submenu-title:hover) {
background-color: #f5f5f5;
color: #1890ff;
}
:deep(.custom-menu .ant-menu-item:hover .icon),
:deep(.custom-menu .ant-menu-submenu-title:hover .icon) {
color: #1890ff;
}
</style>

View File

@ -3,20 +3,13 @@ import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 引入 Ant Design Vue 组件
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'
// 引入更多必要的组件
import {
Menu,
MenuItem,
SubMenu
} from 'ant-design-vue'
const app = createApp(App)
// 注册组件
app.use(Menu)
app.use(MenuItem)
app.use(SubMenu)
app.use(router)
app.use(Antd) // 全局注册所有 Ant Design 组件
app.mount('#app')

View File

@ -2,40 +2,38 @@
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Layout from '@/components/Layout.vue'
// 按需导入视图组件
const HomeView = () => import('@/views/home/index.vue')
const FileStore = () => import('@/views/controlPanel/fileStore/index.vue')
const Container = () => import('@/views/controlPanel/container/index.vue')
const Image = () => import('@/views/controlPanel/image/index.vue')
// 其他视图组件...
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
component: HomeView // 这个页面独立,不包含侧边栏
component: HomeView // 这个页面独立,不使用Layout
},
{
path: '/controlPanel',
component: Layout, // 这个路径下的所有页面都使用 Layout包含侧边栏
redirect: '/controlPanel/fileStore', // 添加重定向到默认子路由
path: '/fileStore',
name: 'FileStore',
component: Layout, // 使用Layout包装FileStore
children: [
{
path: '/controlPanel', // 或者写成 'fileStore'(相对路径)
name: 'FileStore',
component: FileStore,
},
path: '', // 空路径,访问 /fileStore 时显示FileStore组件
name: 'FileStoreContent',
component: FileStore
}
]
},
// 可以继续添加其他使用Layout的路由
{
path: '/container',
name: 'Container',
component: Layout,
children: [
{
path: '/controlPanel/container',
name: 'Container',
component: Container,
},
{
path: '/controlPanel/image',
name: 'Image',
component: Image,
},
// 可以继续添加其他子路由...
path: '',
name: 'ContainerContent',
component: () => import('@/views/controlPanel/container/index.vue')
}
]
}
]

View File

@ -1,15 +1,10 @@
<script setup lang="ts">
</script>
<!-- src/views/controlPanel/fileStore/index.vue -->
<template>
<div style="padding: 20px;">
<h1>📁 File Store</h1>
<p>这里只有文件存储的内容没有 logo没有 HelloWorld</p>
<div>
<h1>文件存储管理</h1>
<p>这个页面显示在Layout中包含侧边栏</p>
<router-link to="/" style="color: blue; text-decoration: underline;">
返回首页无侧边栏
</router-link>
</div>
</template>
<style scoped>
</style>
</template>

View File

@ -1,22 +1,13 @@
<!-- src/views/HomeView.vue -->
<script setup lang="ts">
// import HelloWorld from '@/components/HelloWorld.vue'
</script>
<!-- src/views/home/index.vue -->
<template>
<div>
<div class="links">
<router-link to="/controlPanel/fileStore">File Store</router-link>
</div>
<div >
<h1>首页 - 独立页面</h1>
<p>这个页面没有侧边栏</p>
<router-link to="/fileStore" style="color: blue; text-decoration: underline;">
点击进入文件存储有侧边栏
</router-link>
</div>
</template>
<style scoped>
.logo { height: 6em; padding: 1.5em; }
.logo:hover { filter: drop-shadow(0 0 2em #646cffaa); }
.logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); }
</style>
<script setup>
</script>