补充dashboard
This commit is contained in:
parent
783972ecab
commit
fb832df8e1
81
devcloud/web/package-lock.json
generated
81
devcloud/web/package-lock.json
generated
@ -11,6 +11,7 @@
|
||||
"@vueuse/core": "^13.6.0",
|
||||
"axios": "^1.11.0",
|
||||
"vue": "^3.5.18",
|
||||
"vue-echarts": "^7.0.3",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -2388,6 +2389,24 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/echarts": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
|
||||
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "2.3.0",
|
||||
"zrender": "5.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/echarts/node_modules/tslib": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
|
||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.194",
|
||||
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz",
|
||||
@ -4647,6 +4666,51 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-demi": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-echarts": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-7.0.3.tgz",
|
||||
"integrity": "sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue-demi": "^0.13.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/runtime-core": "^3.0.0",
|
||||
"echarts": "^5.5.1",
|
||||
"vue": "^2.7.0 || ^3.1.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/runtime-core": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-eslint-parser": {
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz",
|
||||
@ -4771,6 +4835,23 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zrender": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",
|
||||
"integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/zrender/node_modules/tslib": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
|
||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
"@vueuse/core": "^13.6.0",
|
||||
"axios": "^1.11.0",
|
||||
"vue": "^3.5.18",
|
||||
"vue-echarts": "^7.0.3",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
154
devcloud/web/src/layout/DashboardLayout.vue
Normal file
154
devcloud/web/src/layout/DashboardLayout.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div class="layout-container">
|
||||
<!-- 背景装饰元素 -->
|
||||
<div class="decoration-circle circle-1"></div>
|
||||
<div class="decoration-circle circle-2"></div>
|
||||
<div class="decoration-wave"></div>
|
||||
|
||||
<!-- 使用顶部导航组件 -->
|
||||
<HeaderNav :active-key="activeMenuKey" @menu-change="handleMenuChange" @user-option-click="handleUserOption" />
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div class="main-content-wrapper">
|
||||
<!-- 内容滚动区域 -->
|
||||
<div class="scrollable-content">
|
||||
<div class="content-scroll-wrapper">
|
||||
<!-- 主内容区 -->
|
||||
<a-watermark :content="token.user_name" :font="{ color: 'rgba(0, 0, 0, 0.06)' }">
|
||||
<main class="router-view-wrapper">
|
||||
<router-view />
|
||||
</main>
|
||||
</a-watermark>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="layout-footer">
|
||||
<p>© 2025 研发交付平台 · 让软件交付更高效</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import HeaderNav from './components/HeaderNav.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import token from '@/storage/token'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const activeMenuKey = ref('DashBoard');
|
||||
|
||||
const handleMenuChange = (key) => {
|
||||
activeMenuKey.value = key;
|
||||
router.push({ name: key })
|
||||
// 这里可以添加路由跳转逻辑
|
||||
console.log('菜单切换:', key);
|
||||
};
|
||||
|
||||
const handleUserOption = (option) => {
|
||||
console.log('用户操作:', option);
|
||||
// 根据不同的option执行不同的操作
|
||||
switch (option) {
|
||||
case 'profile':
|
||||
// 跳转到个人中心
|
||||
break;
|
||||
case 'settings':
|
||||
// 跳转到系统设置
|
||||
break;
|
||||
case 'logout':
|
||||
// 执行退出登录
|
||||
break;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/* 基础布局 */
|
||||
.layout-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.decoration-circle {
|
||||
position: fixed;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, rgba(24, 144, 255, 0.1) 0%, rgba(24, 144, 255, 0.05) 100%);
|
||||
z-index: 0;
|
||||
|
||||
&.circle-1 {
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
top: -200px;
|
||||
left: -200px;
|
||||
}
|
||||
|
||||
&.circle-2 {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
bottom: -100px;
|
||||
right: -100px;
|
||||
}
|
||||
}
|
||||
|
||||
.decoration-wave {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
background: url('@/assets/svg/wave.svg') repeat-x;
|
||||
background-size: 1000px 100px;
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content-wrapper {
|
||||
padding-top: 60px;
|
||||
height: calc(100vh - 60px);
|
||||
}
|
||||
|
||||
/* 内容滚动区域 */
|
||||
.scrollable-content {
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
-ms-overflow-style: none;
|
||||
/* IE/Edge */
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
/* Chrome/Safari */
|
||||
}
|
||||
|
||||
.content-scroll-wrapper {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* 路由视图容器 */
|
||||
.router-view-wrapper {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
min-height: calc(100vh - 180px);
|
||||
/* 调整最小高度 */
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.layout-footer {
|
||||
padding: 16px 24px;
|
||||
margin-top: auto;
|
||||
/* 关键:使页脚始终在底部 */
|
||||
text-align: center;
|
||||
color: var(--color-text-3);
|
||||
font-size: 12px;
|
||||
border-top: 1px solid var(--color-border-2);
|
||||
background: var(--color-bg-2);
|
||||
}
|
||||
</style>
|
||||
192
devcloud/web/src/layout/FrontendLayout.vue
Normal file
192
devcloud/web/src/layout/FrontendLayout.vue
Normal file
@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div class="layout-container">
|
||||
<!-- 背景装饰元素 -->
|
||||
<div class="decoration-circle circle-1"></div>
|
||||
<div class="decoration-circle circle-2"></div>
|
||||
<div class="decoration-wave"></div>
|
||||
|
||||
<!-- 使用顶部导航组件 -->
|
||||
<div class="fixed-header">
|
||||
<div class="header-content">
|
||||
<div class="logo-section">
|
||||
<h1 class="platform-name">研发交付平台</h1>
|
||||
</div>
|
||||
<div class="main-nav-section">
|
||||
<a-menu mode="horizontal" :default-selected-keys="[activeKey]">
|
||||
<a-menu-item v-for="item in menuItems" :key="item.key" @click="handleMenuClick(item)">
|
||||
{{ item.label }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
<div class="user-section">
|
||||
<a-button type="text" @click="() => $router.push({ name: 'LoginPage' })">登录</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div class="main-content-wrapper">
|
||||
<!-- 内容滚动区域 -->
|
||||
<div class="scrollable-content">
|
||||
<div class="content-scroll-wrapper">
|
||||
<!-- 主内容区 -->
|
||||
<main class="router-view-wrapper">
|
||||
<router-view />
|
||||
</main>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="layout-footer">
|
||||
<p>© 2025 研发交付平台 · 让软件交付更高效</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/* 基础布局 */
|
||||
.layout-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.decoration-circle {
|
||||
position: fixed;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, rgba(24, 144, 255, 0.1) 0%, rgba(24, 144, 255, 0.05) 100%);
|
||||
z-index: 0;
|
||||
|
||||
&.circle-1 {
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
top: -200px;
|
||||
left: -200px;
|
||||
}
|
||||
|
||||
&.circle-2 {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
bottom: -100px;
|
||||
right: -100px;
|
||||
}
|
||||
}
|
||||
|
||||
.decoration-wave {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
background: url('@/assets/svg/wave.svg') repeat-x;
|
||||
background-size: 1000px 100px;
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
z-index: 100;
|
||||
|
||||
.header-content {
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 24px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.logo-section {
|
||||
.platform-name {
|
||||
color: var(--color-text-1);
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.main-nav-section {
|
||||
flex: 1;
|
||||
margin: 0 40px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
:deep(.arco-menu-horizontal) {
|
||||
height: 60px;
|
||||
background: transparent;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
background-color: var(--color-primary-light-3);
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-primary-light-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content-wrapper {
|
||||
padding-top: 60px;
|
||||
height: calc(100vh - 60px);
|
||||
}
|
||||
|
||||
/* 内容滚动区域 */
|
||||
.scrollable-content {
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
-ms-overflow-style: none;
|
||||
/* IE/Edge */
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
/* Chrome/Safari */
|
||||
}
|
||||
|
||||
.content-scroll-wrapper {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* 路由视图容器 */
|
||||
.router-view-wrapper {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
min-height: calc(100vh - 180px);
|
||||
/* 调整最小高度 */
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.layout-footer {
|
||||
padding: 16px 24px;
|
||||
margin-top: auto;
|
||||
/* 关键:使页脚始终在底部 */
|
||||
text-align: center;
|
||||
color: var(--color-text-3);
|
||||
font-size: 12px;
|
||||
border-top: 1px solid var(--color-border-2);
|
||||
background: var(--color-bg-2);
|
||||
}
|
||||
</style>
|
||||
@ -5,63 +5,21 @@
|
||||
<div class="decoration-circle circle-2"></div>
|
||||
<div class="decoration-wave"></div>
|
||||
|
||||
<!-- 固定顶部导航栏 -->
|
||||
<div class="fixed-header">
|
||||
<div class="header-content">
|
||||
<div class="logo-section">
|
||||
<h1 class="platform-name">研发交付平台</h1>
|
||||
</div>
|
||||
<div class="main-nav-section">
|
||||
<a-menu mode="horizontal" :default-selected-keys="['1']">
|
||||
<a-menu-item key="1">工作台</a-menu-item>
|
||||
<a-menu-item key="2">项目管理</a-menu-item>
|
||||
<a-menu-item key="3">研发交付</a-menu-item>
|
||||
<a-menu-item key="4">制品库</a-menu-item>
|
||||
<a-menu-item key="5">测试中心</a-menu-item>
|
||||
<a-menu-item key="6">运维中心</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
<div class="user-section">
|
||||
<a-dropdown position="bottom">
|
||||
<a-avatar :size="32" class="user-avatar">
|
||||
<icon-user />
|
||||
</a-avatar>
|
||||
<template #content>
|
||||
<a-doption>个人中心</a-doption>
|
||||
<a-doption>系统设置</a-doption>
|
||||
<a-doption>退出登录</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 使用新的顶部导航组件 -->
|
||||
<HeaderNav :active-key="activeMenuKey" @menu-change="handleMenuChange" @user-option-click="handleUserOption" />
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div class="main-content-wrapper">
|
||||
<!-- 可收缩侧边栏 -->
|
||||
<div class="fixed-sidebar" :class="{ 'collapsed': isSidebarCollapsed }">
|
||||
<!-- 使用原生a-menu确保折叠效果 -->
|
||||
<a-menu :theme="light" :default-selected-keys="['1']" :collapsed="isSidebarCollapsed" :collapsed-width="64"
|
||||
<a-menu theme="light" :default-selected-keys="['1']" :collapsed="isSidebarCollapsed" :collapsed-width="64"
|
||||
:style="{ width: '100%', height: '100%' }">
|
||||
<a-menu-item key="1">
|
||||
<template #icon><icon-dashboard /></template>
|
||||
<span class="menu-title">流水线列表</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2">
|
||||
<template #icon><icon-branch /></template>
|
||||
<span class="menu-title">分支管理</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="3">
|
||||
<template #icon><icon-history /></template>
|
||||
<span class="menu-title">执行历史</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="4">
|
||||
<template #icon><icon-settings /></template>
|
||||
<span class="menu-title">流水线模板</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="5">
|
||||
<template #icon><icon-monitor /></template>
|
||||
<span class="menu-title">监控中心</span>
|
||||
<a-menu-item v-for="menu in currentMenus" :key="menu.key">
|
||||
<template #icon>
|
||||
<component :is="menu.icon"></component>
|
||||
</template>
|
||||
<span class="menu-title">{{ menu.title }}</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
|
||||
@ -89,7 +47,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<a-watermark content="arco.design" :font="{ color: 'rgba(0, 0, 0, 0.06)' }">
|
||||
<a-watermark :content="token.user_name" :font="{ color: 'rgba(0, 0, 0, 0.06)' }">
|
||||
<main class="router-view-wrapper">
|
||||
<router-view />
|
||||
</main>
|
||||
@ -106,13 +64,86 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { computed, ref, shallowReactive } from 'vue';
|
||||
import HeaderNav from './components/HeaderNav.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { IconApps, IconBranch, IconHistory, IconSettings, IconTags } from '@arco-design/web-vue/es/icon';
|
||||
import token from '@/storage/token'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const isSidebarCollapsed = ref(false);
|
||||
const activeMenuKey = ref('ProjectSystem');
|
||||
|
||||
const toggleSidebar = () => {
|
||||
isSidebarCollapsed.value = !isSidebarCollapsed.value;
|
||||
};
|
||||
|
||||
const handleMenuChange = (key) => {
|
||||
activeMenuKey.value = key;
|
||||
router.push({ name: key })
|
||||
// 这里可以添加路由跳转逻辑
|
||||
console.log('菜单切换:', key);
|
||||
};
|
||||
|
||||
const handleUserOption = (option) => {
|
||||
console.log('用户操作:', option);
|
||||
// 根据不同的option执行不同的操作
|
||||
switch (option) {
|
||||
case 'profile':
|
||||
// 跳转到个人中心
|
||||
break;
|
||||
case 'settings':
|
||||
// 跳转到系统设置
|
||||
break;
|
||||
case 'logout':
|
||||
// 执行退出登录
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const currentMenus = computed(() => {
|
||||
return systemMenus[activeMenuKey.value]
|
||||
})
|
||||
|
||||
const systemMenus = shallowReactive({
|
||||
ProjectSystem: [
|
||||
{
|
||||
key: '1',
|
||||
icon: IconApps,
|
||||
title: '应用管理'
|
||||
},
|
||||
],
|
||||
DevelopSystem: [
|
||||
{
|
||||
key: '1',
|
||||
icon: IconTags,
|
||||
title: '版本迭代'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
icon: IconBranch,
|
||||
title: '分支管理'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
icon: IconHistory,
|
||||
title: '执行历史'
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
icon: IconSettings,
|
||||
title: '流水线模板'
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
title: '监控中心'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
125
devcloud/web/src/layout/components/HeaderNav.vue
Normal file
125
devcloud/web/src/layout/components/HeaderNav.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div class="fixed-header">
|
||||
<div class="header-content">
|
||||
<div class="logo-section">
|
||||
<h1 class="platform-name">研发交付平台</h1>
|
||||
</div>
|
||||
<div class="main-nav-section">
|
||||
<a-menu mode="horizontal" :default-selected-keys="[activeKey]">
|
||||
<a-menu-item v-for="item in menuItems" :key="item.key" @click="handleMenuClick(item)">
|
||||
{{ item.label }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
<div class="user-section">
|
||||
<a-dropdown position="bottom">
|
||||
<a-avatar :size="32" class="user-avatar">
|
||||
<icon-user />
|
||||
</a-avatar>
|
||||
<template #content>
|
||||
<a-doption v-for="option in userOptions" :key="option.key" @click="option.handler">
|
||||
{{ option.label }}
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import token from '@/storage/token'
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
defineProps({
|
||||
activeKey: {
|
||||
type: String,
|
||||
default: 'HomePage'
|
||||
}
|
||||
});
|
||||
|
||||
const router = useRouter()
|
||||
const emit = defineEmits(['menu-change', 'user-option-click']);
|
||||
|
||||
const menuItems = ref([
|
||||
{ key: 'DashBoard', label: '工作台' },
|
||||
{ key: 'ProjectSystem', label: '项目管理' },
|
||||
{ key: 'DevelopSystem', label: '研发交付' },
|
||||
{ key: '4', label: '制品库' },
|
||||
{ key: '5', label: '测试中心' },
|
||||
{ key: '6', label: '运维中心' }
|
||||
]);
|
||||
|
||||
const userOptions = ref([
|
||||
{ key: 'profile', label: '个人中心', handler: () => emit('user-option-click', 'profile') },
|
||||
{ key: 'settings', label: '系统设置', handler: () => emit('user-option-click', 'settings') },
|
||||
{
|
||||
key: 'logout', label: '退出登录', handler: () => {
|
||||
// 登录的状态去掉
|
||||
token.value = null
|
||||
router.push({ name: 'LoginPage' })
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const handleMenuClick = (menuItem) => {
|
||||
emit('menu-change', menuItem.key);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
z-index: 100;
|
||||
|
||||
.header-content {
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 24px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.logo-section {
|
||||
.platform-name {
|
||||
color: var(--color-text-1);
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.main-nav-section {
|
||||
flex: 1;
|
||||
margin: 0 40px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
:deep(.arco-menu-horizontal) {
|
||||
height: 60px;
|
||||
background: transparent;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
background-color: var(--color-primary-light-3);
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-primary-light-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-space>
|
||||
<a-button type="primary">Primary</a-button>
|
||||
<a-button>Secondary</a-button>
|
||||
<a-button type="dashed">Dashed</a-button>
|
||||
<a-button type="outline">Outline</a-button>
|
||||
<a-button type="text">Text</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped></style>
|
||||
@ -93,7 +93,7 @@ const handleSubmit = async (data) => {
|
||||
if (data.errors == null) {
|
||||
const resp = await mcenter.Login(data.values);
|
||||
token.value = resp;
|
||||
router.push({ name: 'HomePage' });
|
||||
router.push({ name: 'DashBoard' });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
337
devcloud/web/src/pages/dashboard/DashboardPage.vue
Normal file
337
devcloud/web/src/pages/dashboard/DashboardPage.vue
Normal file
@ -0,0 +1,337 @@
|
||||
<template>
|
||||
<div class="dashboard-container">
|
||||
<!-- 顶部数据概览 -->
|
||||
<section class="metrics-section">
|
||||
<a-row :gutter="20">
|
||||
<a-col :xs="24" :sm="12" :md="6" v-for="metric in metrics" :key="metric.title">
|
||||
<a-card class="metric-card" hoverable>
|
||||
<div class="metric-content">
|
||||
<div class="metric-icon" :style="{ backgroundColor: metric.color }">
|
||||
<component :is="metric.icon" size="20" />
|
||||
</div>
|
||||
<div class="metric-info">
|
||||
<a-typography-text class="metric-title">{{ metric.title }}</a-typography-text>
|
||||
<a-typography-title :heading="4" class="metric-value">{{ metric.value }}</a-typography-title>
|
||||
<a-typography-text class="metric-trend" :type="metric.trend.type">
|
||||
{{ metric.trend.value }}
|
||||
<icon-caret-up v-if="metric.trend.direction === 'up'" />
|
||||
<icon-caret-down v-else />
|
||||
</a-typography-text>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</section>
|
||||
|
||||
<!-- 构建状态和活动流水线 -->
|
||||
<section class="main-section">
|
||||
<a-row :gutter="20">
|
||||
<!-- 构建状态图表 -->
|
||||
<a-col :xs="24" :md="16">
|
||||
<a-card title="构建状态统计" :bordered="false" class="chart-card">
|
||||
<template #extra>
|
||||
<a-select v-model="buildTimeRange" size="small" style="width: 120px">
|
||||
<a-option value="week">最近一周</a-option>
|
||||
<a-option value="month">最近一月</a-option>
|
||||
<a-option value="quarter">最近三月</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<BuildStatusChar :data="buildData" />
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<!-- 活动流水线 -->
|
||||
<a-col :xs="24" :md="8">
|
||||
<a-card title="活动流水线" :bordered="false" class="pipeline-card">
|
||||
<template #extra>
|
||||
<a-link>查看全部</a-link>
|
||||
</template>
|
||||
<a-list :bordered="false" :split="false">
|
||||
<a-list-item v-for="pipeline in activePipelines" :key="pipeline.id">
|
||||
<a-list-item-meta>
|
||||
<template #avatar>
|
||||
<a-badge :status="pipeline.status" />
|
||||
</template>
|
||||
<template #title>
|
||||
<a-link>{{ pipeline.name }}</a-link>
|
||||
</template>
|
||||
<template #description>
|
||||
<a-space>
|
||||
<a-typography-text type="secondary">#{{ pipeline.id }}</a-typography-text>
|
||||
<a-typography-text type="secondary">{{ pipeline.duration }}</a-typography-text>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
<a-tag :color="pipeline.tagColor">{{ pipeline.stage }}</a-tag>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</section>
|
||||
|
||||
<!-- 部署统计和最近活动 -->
|
||||
<section class="secondary-section">
|
||||
<a-row :gutter="20">
|
||||
<!-- 部署统计 -->
|
||||
<a-col :xs="24" :md="12">
|
||||
<a-card title="部署统计" :bordered="false" class="chart-card">
|
||||
<DeployChart :data="deployData" />
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<!-- 最近活动 -->
|
||||
<a-col :xs="24" :md="12">
|
||||
<a-card title="最近活动" :bordered="false" class="activity-card">
|
||||
<a-timeline>
|
||||
<a-timeline-item v-for="activity in recentActivities" :key="activity.id" :color="activity.color">
|
||||
<a-space direction="vertical" size="2">
|
||||
<div class="activity-content">
|
||||
<a-typography-text strong>{{ activity.user }}</a-typography-text>
|
||||
<a-typography-text type="secondary">{{ activity.action }}</a-typography-text>
|
||||
<a-link>{{ activity.target }}</a-link>
|
||||
</div>
|
||||
<a-typography-text type="secondary" class="activity-time">{{ activity.time }}</a-typography-text>
|
||||
</a-space>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import {
|
||||
IconCheckCircle,
|
||||
IconClockCircle,
|
||||
IconCloseCircle,
|
||||
IconCode,
|
||||
IconCaretUp,
|
||||
IconCaretDown
|
||||
} from '@arco-design/web-vue/es/icon';
|
||||
|
||||
import DeployChart from './components/DeployChart.vue';
|
||||
import BuildStatusChar from './components/BuildStatusChar.vue';
|
||||
|
||||
|
||||
|
||||
// 数据
|
||||
const buildTimeRange = ref('week');
|
||||
|
||||
const metrics = ref([
|
||||
{
|
||||
title: '成功构建',
|
||||
value: '1,248',
|
||||
icon: IconCheckCircle,
|
||||
color: 'var(--color-success-light-1)',
|
||||
trend: {
|
||||
value: '12.5%',
|
||||
direction: 'up',
|
||||
type: 'success'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '失败构建',
|
||||
value: '56',
|
||||
icon: IconCloseCircle,
|
||||
color: 'var(--color-danger-light-1)',
|
||||
trend: {
|
||||
value: '3.2%',
|
||||
direction: 'down',
|
||||
type: 'success'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '平均构建时间',
|
||||
value: '2m 45s',
|
||||
icon: IconClockCircle,
|
||||
color: 'var(--color-warning-light-1)',
|
||||
trend: {
|
||||
value: '5.1%',
|
||||
direction: 'down',
|
||||
type: 'success'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '今日部署',
|
||||
value: '42',
|
||||
icon: IconCode,
|
||||
color: 'var(--color-primary-light-1)',
|
||||
trend: {
|
||||
value: '8.7%',
|
||||
direction: 'up',
|
||||
type: 'success'
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const activePipelines = ref([
|
||||
{
|
||||
id: '2356',
|
||||
name: 'frontend-web',
|
||||
status: 'success',
|
||||
duration: '2m 12s',
|
||||
stage: '部署生产',
|
||||
tagColor: 'green'
|
||||
},
|
||||
{
|
||||
id: '2355',
|
||||
name: 'backend-service',
|
||||
status: 'processing',
|
||||
duration: '1m 45s',
|
||||
stage: '运行测试',
|
||||
tagColor: 'orange'
|
||||
},
|
||||
{
|
||||
id: '2354',
|
||||
name: 'mobile-app',
|
||||
status: 'error',
|
||||
duration: '3m 28s',
|
||||
stage: '构建失败',
|
||||
tagColor: 'red'
|
||||
},
|
||||
{
|
||||
id: '2353',
|
||||
name: 'data-pipeline',
|
||||
status: 'warning',
|
||||
duration: '4m 15s',
|
||||
stage: '等待审批',
|
||||
tagColor: 'gold'
|
||||
}
|
||||
]);
|
||||
|
||||
const buildData = ref({/* 图表数据 */ });
|
||||
const deployData = ref({/* 图表数据 */ });
|
||||
|
||||
const recentActivities = ref([
|
||||
{
|
||||
id: 1,
|
||||
user: '张开发',
|
||||
action: '触发了构建',
|
||||
target: 'frontend-web #2356',
|
||||
time: '10分钟前',
|
||||
color: 'green'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
user: '李测试',
|
||||
action: '部署了版本',
|
||||
target: 'backend-service v1.2.3',
|
||||
time: '25分钟前',
|
||||
color: 'blue'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
user: '王运维',
|
||||
action: '创建了新环境',
|
||||
target: 'staging-environment',
|
||||
time: '1小时前',
|
||||
color: 'purple'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
user: '系统',
|
||||
action: '完成了扫描',
|
||||
target: 'security-scan #142',
|
||||
time: '2小时前',
|
||||
color: 'gray'
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dashboard-container {
|
||||
padding: 16px;
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 指标卡片 */
|
||||
.metrics-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.metric-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.metric-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.metric-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.metric-title {
|
||||
display: block;
|
||||
color: var(--color-text-2);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.metric-trend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.main-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
margin-bottom: 20px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.pipeline-card {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
/* 活动时间线 */
|
||||
.activity-card {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.activity-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.activity-time {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.dashboard-container {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div style="height: 300px">构建状态图表</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div style="height: 300px">部署统计图表</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
13
devcloud/web/src/pages/develop/SprintPage.vue
Normal file
13
devcloud/web/src/pages/develop/SprintPage.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<a-steps type="arrow" :current="2">
|
||||
<a-step description="This is a description">Succeeded</a-step>
|
||||
<a-step description="This is a description">Processing</a-step>
|
||||
<a-step description="This is a description">Pending</a-step>
|
||||
</a-steps>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped></style>
|
||||
248
devcloud/web/src/pages/frontend/ProductPage.vue
Normal file
248
devcloud/web/src/pages/frontend/ProductPage.vue
Normal file
@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<div class="product-page">
|
||||
<!-- 核心价值部分 -->
|
||||
<section class="section">
|
||||
<a-divider class="driver" orientation="center">
|
||||
<a-typography-title :heading="4">平台核心价值</a-typography-title>
|
||||
</a-divider>
|
||||
|
||||
<a-row :gutter="24" justify="center" class="value-cards">
|
||||
<a-col :xs="24" :sm="12" :md="8" v-for="(value, index) in values" :key="index">
|
||||
<a-card class="value-card" hoverable>
|
||||
<div class="card-icon">
|
||||
<component :is="value.icon" size="48" />
|
||||
</div>
|
||||
<a-card-meta :title="value.title" :description="value.description" class="card-meta" />
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</section>
|
||||
|
||||
<!-- 功能特性部分 -->
|
||||
<section class="section">
|
||||
<a-divider class="driver" orientation="center">
|
||||
<a-typography-title :heading="4">功能特性</a-typography-title>
|
||||
</a-divider>
|
||||
|
||||
<a-list :grid="{ gutter: 16, xs: 1, sm: 2, lg: 2, xl: 2 }" :data="features" class="feature-list">
|
||||
<template #item="{ item }">
|
||||
<a-list-item>
|
||||
<a-card hoverable class="feature-card">
|
||||
<a-list-item-meta>
|
||||
<template #avatar>
|
||||
<a-avatar :size="40" :style="{ backgroundColor: 'rgb(var(--primary-6))' }">
|
||||
<icon-check />
|
||||
</a-avatar>
|
||||
</template>
|
||||
<template #title>
|
||||
<a-typography-text strong>{{ item.title }}</a-typography-text>
|
||||
</template>
|
||||
<template #description>
|
||||
<a-typography-paragraph :ellipsis="{ rows: 2 }">{{ item.description }}</a-typography-paragraph>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-card>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</section>
|
||||
|
||||
<!-- 客户案例部分 -->
|
||||
<!-- <section class="section">
|
||||
<a-divider orientation="center">
|
||||
<a-typography-title :heading="4">客户案例</a-typography-title>
|
||||
</a-divider>
|
||||
|
||||
<div class="case-container">
|
||||
<a-carousel :auto-play="true" :interval="4000" animation-name="fade" show-arrow="hover" indicator-type="line"
|
||||
class="case-carousel">
|
||||
<a-carousel-item v-for="caseItem in cases" :key="caseItem.id">
|
||||
<a-card :title="caseItem.company" class="case-card" hoverable>
|
||||
<a-typography-paragraph>
|
||||
{{ caseItem.content }}
|
||||
</a-typography-paragraph>
|
||||
<template #extra>
|
||||
<a-link>查看详情</a-link>
|
||||
</template>
|
||||
</a-card>
|
||||
</a-carousel-item>
|
||||
</a-carousel>
|
||||
</div>
|
||||
</section> -->
|
||||
|
||||
<!-- 行动号召部分 -->
|
||||
<section class="section cta-section">
|
||||
<a-result status="success" title="准备好提升您的研发效能了吗?" sub-title="我们的研发交付平台将帮助您的团队实现高效、稳定的软件交付">
|
||||
<template #extra>
|
||||
<a-space :size="24">
|
||||
<a-button type="primary" size="large" shape="round">免费试用</a-button>
|
||||
<a-button size="large" shape="round">预约演示</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
IconSync,
|
||||
IconBranch,
|
||||
IconUserGroup,
|
||||
IconCheck
|
||||
} from '@arco-design/web-vue/es/icon';
|
||||
|
||||
const values = [
|
||||
{
|
||||
icon: IconSync,
|
||||
title: "持续集成",
|
||||
description: "自动化构建、测试流程,快速反馈代码质量,提升开发效率"
|
||||
},
|
||||
{
|
||||
icon: IconBranch,
|
||||
title: "持续交付",
|
||||
description: "一键部署,快速迭代,确保软件随时可发布"
|
||||
},
|
||||
{
|
||||
icon: IconUserGroup,
|
||||
title: "高效协作",
|
||||
description: "跨团队无缝协作,可视化流程,透明化管理"
|
||||
}
|
||||
];
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: '自动化构建与测试',
|
||||
description: '支持多种语言和框架的自动化构建,集成单元测试、集成测试等质量门禁'
|
||||
},
|
||||
{
|
||||
title: '多环境部署',
|
||||
description: '支持开发、测试、预发、生产等多环境一键部署,配置灵活可定制'
|
||||
},
|
||||
{
|
||||
title: '可视化流水线',
|
||||
description: '直观展示构建、测试、部署全流程,实时监控各环节状态'
|
||||
},
|
||||
{
|
||||
title: '质量门禁',
|
||||
description: '代码规范检查、测试覆盖率要求、安全扫描等多维度质量管控'
|
||||
}
|
||||
];
|
||||
|
||||
// const cases = [
|
||||
// {
|
||||
// id: 1,
|
||||
// company: "某大型互联网公司",
|
||||
// content: "使用研发交付平台后,我们的部署频率提升了300%,故障率降低了60%,研发团队可以更专注于业务创新。",
|
||||
// image: 'https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/cd7a1aaea8e1c5e3d26fe2591e561798.png~tplv-uwbnlip3yd-webp.webp',
|
||||
// },
|
||||
// {
|
||||
// id: 2,
|
||||
// company: "某金融机构",
|
||||
// content: "平台的多环境管理和质量门禁功能帮助我们实现了合规要求,同时大幅提升了交付效率。",
|
||||
|
||||
// },
|
||||
// {
|
||||
// id: 3,
|
||||
// company: "某电商企业",
|
||||
// content: "可视化流水线和团队协作功能让我们的跨部门协作更加顺畅,版本发布时间从原来的2周缩短到2天。"
|
||||
// }
|
||||
// ];
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.product-page {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.driver {
|
||||
margin: 80px 0px;
|
||||
}
|
||||
|
||||
/* 核心价值卡片 */
|
||||
.value-cards {
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
.value-card {
|
||||
height: 200px;
|
||||
text-align: center;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.value-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 24px 0 16px;
|
||||
color: rgb(var(--primary-6));
|
||||
}
|
||||
|
||||
.card-meta {
|
||||
padding: 0 16px 24px;
|
||||
}
|
||||
|
||||
/* 功能特性列表 */
|
||||
.feature-list {
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
height: 100%;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
/* 客户案例 */
|
||||
.case-container {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.case-carousel {
|
||||
margin: 32px auto;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.case-card {
|
||||
padding: 24px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
/* 行动号召部分 */
|
||||
.cta-section {
|
||||
background: var(--color-fill-2);
|
||||
padding: 48px 24px;
|
||||
border-radius: 8px;
|
||||
margin-top: 64px;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.value-card,
|
||||
.feature-card {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
margin-top: 48px;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,11 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 登录表单 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped></style>
|
||||
@ -1,35 +1,74 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
import BackendLayout from '@/layout/BackendLayout.vue'
|
||||
import MenuLayout from '@/layout/MenuLayout.vue'
|
||||
import DashboardLayout from '@/layout/DashboardLayout.vue'
|
||||
import FrontendLayout from '@/layout/FrontendLayout.vue'
|
||||
import { beforeEach } from './interceptor'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'HomePage',
|
||||
component: BackendLayout,
|
||||
name: 'Frontend',
|
||||
redirect: { name: 'ProductPage' },
|
||||
component: () => FrontendLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'product',
|
||||
name: 'ProductPage',
|
||||
component: () => import('@/pages/frontend/ProductPage.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'LoginPage',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (About.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import('../pages/LoginPage.vue'),
|
||||
component: () => import('@/pages/LoginPage.vue'),
|
||||
},
|
||||
{
|
||||
path: '/cmdb',
|
||||
component: () => import('@/layout/BackendLayout.vue'),
|
||||
path: '/person',
|
||||
name: 'DashBoard',
|
||||
redirect: { name: 'DashboardPage' },
|
||||
component: DashboardLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'secret',
|
||||
name: 'SecretPage',
|
||||
component: () => import('@/pages/cmdb/SecretPage.vue'),
|
||||
path: 'dashboard',
|
||||
name: 'DashboardPage',
|
||||
component: () => import('@/pages/dashboard/DashboardPage.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/project',
|
||||
name: 'ProjectSystem',
|
||||
redirect: { name: 'AppPage' },
|
||||
component: MenuLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'app',
|
||||
name: 'AppPage',
|
||||
component: () => import('@/pages/project/AppPage.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/develop',
|
||||
name: 'DevelopSystem',
|
||||
redirect: { name: 'SprintPage' },
|
||||
component: MenuLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'sprint',
|
||||
name: 'SprintPage',
|
||||
component: () => import('@/pages/develop/SprintPage.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
// 导航守卫
|
||||
router.beforeEach(beforeEach)
|
||||
|
||||
export default router
|
||||
|
||||
20
devcloud/web/src/router/interceptor.js
Normal file
20
devcloud/web/src/router/interceptor.js
Normal file
@ -0,0 +1,20 @@
|
||||
import token from '@/storage/token'
|
||||
|
||||
var witheList = ['ProductPage', 'LoginPage']
|
||||
|
||||
export var beforeEach = async (to) => {
|
||||
// 白名单
|
||||
for (var name of witheList) {
|
||||
if (name === to.name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 检查用户是否已登录
|
||||
if (token.value.access_token) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 将用户重定向到登录页面
|
||||
return { name: 'LoginPage' }
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user