This commit is contained in:
ygx
2026-03-28 15:44:57 +08:00
parent 1020699b8c
commit 877e3115c4
11 changed files with 497 additions and 167 deletions

BIN
src/assets/images/home1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

BIN
src/assets/images/home2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

View File

@@ -81,7 +81,7 @@
</a-button> </a-button>
</a-tooltip> </a-tooltip>
</li> </li>
<li> <!-- <li>
<a-tooltip :content="t('settings.title')"> <a-tooltip :content="t('settings.title')">
<a-button class="nav-btn" type="outline" :shape="'circle'" @click="setVisible"> <a-button class="nav-btn" type="outline" :shape="'circle'" @click="setVisible">
<template #icon> <template #icon>
@@ -89,13 +89,25 @@
</template> </template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
</li> </li> -->
<li> <li>
<a-dropdown trigger="click"> <a-dropdown trigger="click">
<a-avatar :size="32" :style="{ marginRight: '8px' }"> <a-avatar :size="32" :style="{ marginRight: '8px' }">
<img alt="avatar" :src="avatar" /> <img alt="avatar" :src="avatar" />
</a-avatar> </a-avatar>
<template #content> <template #content>
<a-doption>
<a-space @click="handleOpenProfileChange">
<icon-user />
<span>个人资料</span>
</a-space>
</a-doption>
<a-doption>
<a-space @click="handleOpenPasswordChange">
<icon-lock />
<span>修改密码</span>
</a-space>
</a-doption>
<a-doption> <a-doption>
<a-space @click="handleLogout"> <a-space @click="handleLogout">
<icon-export /> <icon-export />
@@ -108,6 +120,8 @@
</a-dropdown> </a-dropdown>
</li> </li>
</ul> </ul>
<PasswordChange v-model:open="passwordChangeVisible" />
<ProfileChange v-model:open="profileChangeVisible" />
</div> </div>
</template> </template>
@@ -121,6 +135,8 @@ import { useDark, useFullscreen, useToggle } from '@vueuse/core'
import { computed, inject, ref } from 'vue' import { computed, inject, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import MessageBox from '../message-box/index.vue' import MessageBox from '../message-box/index.vue'
import PasswordChange from '../password-change/index.vue'
import ProfileChange from '../profile-change/index.vue'
const { t } = useI18n() const { t } = useI18n()
@@ -157,6 +173,8 @@ const setVisible = () => {
} }
const refBtn = ref() const refBtn = ref()
const triggerBtn = ref() const triggerBtn = ref()
const passwordChangeVisible = ref(false)
const profileChangeVisible = ref(false)
const setPopoverVisible = () => { const setPopoverVisible = () => {
const event = new MouseEvent('click', { const event = new MouseEvent('click', {
view: window, view: window,
@@ -168,6 +186,12 @@ const setPopoverVisible = () => {
const handleLogout = () => { const handleLogout = () => {
logout() logout()
} }
const handleOpenPasswordChange = () => {
passwordChangeVisible.value = true
}
const handleOpenProfileChange = () => {
profileChangeVisible.value = true
}
const setDropDownVisible = () => { const setDropDownVisible = () => {
const event = new MouseEvent('click', { const event = new MouseEvent('click', {
view: window, view: window,

View File

@@ -0,0 +1,117 @@
<template>
<a-modal
v-model:visible="visible"
:title="'修改密码'"
:width="400"
@cancel="handleClose"
@ok="handleSavePassword"
:ok-text="'保存'"
:cancel-text="'取消'"
:confirm-loading="loading"
>
<a-form :model="form" layout="vertical">
<a-form-item :label="'新密码'" field="newPassword" :required="true">
<a-input-password
v-model="form.newPassword"
:placeholder="'请输入新密码'"
allow-clear
/>
</a-form-item>
<a-form-item :label="'确认密码'" field="confirmPassword" :required="true">
<a-input-password
v-model="form.confirmPassword"
:placeholder="'请再次输入新密码'"
allow-clear
/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { Message } from '@arco-design/web-vue';
import { resetUserPassword } from '@/api/module/user';
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage';
interface Props {
open: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: 'update:open', value: boolean): void;
}>();
const loading = ref(false);
const visible = ref(props.open);
const form = ref({
newPassword: '',
confirmPassword: ''
});
// 监听 props.open 变化
watch(() => props.open, (val) => {
visible.value = val;
});
// 监听 visible 变化,同步到父组件
watch(visible, (val) => {
emit('update:open', val);
});
// 保存密码
const handleSavePassword = async () => {
// 表单验证
if (!form.value.newPassword || !form.value.confirmPassword) {
Message.error('请填写新密码和确认密码');
return;
}
if (form.value.newPassword.length < 6) {
Message.error('密码至少需要6个字符');
return;
}
if (form.value.newPassword !== form.value.confirmPassword) {
Message.error('两次输入的密码不一致');
return;
}
loading.value = true;
try {
// 从 SafeStorage 获取登录用户信息
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {};
const res = await resetUserPassword({
account: userInfo.account || '',
code: '123456', // 暂时没校验,随便传
password: form.value.newPassword,
phone: userInfo.phone || '13800138000' // 暂时没校验,随便传
});
if (res.code === 0) {
Message.success('密码修改成功');
handleClose();
} else {
Message.error(res.message || '密码修改失败');
}
} catch (error) {
console.error('修改密码失败:', error);
Message.error('密码修改失败,请稍后重试');
} finally {
loading.value = false;
}
};
// 关闭弹窗
const handleClose = () => {
form.value = {
newPassword: '',
confirmPassword: ''
};
visible.value = false;
};
</script>

View File

@@ -0,0 +1,121 @@
<template>
<a-modal
v-model:visible="visible"
:title="'个人资料'"
:width="400"
@cancel="handleClose"
@ok="handleSaveProfile"
:ok-text="'保存'"
:cancel-text="'取消'"
:confirm-loading="loading"
>
<a-form :model="form" layout="vertical">
<a-form-item :label="'账号'" field="account">
<a-input
v-model="form.account"
:placeholder="'账号'"
disabled
/>
</a-form-item>
<a-form-item :label="'姓名'" field="name">
<a-input
v-model="form.name"
:placeholder="'请输入姓名'"
allow-clear
/>
</a-form-item>
<a-form-item :label="'邮箱'" field="email">
<a-input
v-model="form.email"
:placeholder="'请输入邮箱'"
allow-clear
/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { Message } from '@arco-design/web-vue';
import { modifyUser } from '@/api/module/user';
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage';
interface Props {
open: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: 'update:open', value: boolean): void;
}>();
const loading = ref(false);
const visible = ref(props.open);
const form = ref({
account: '',
name: '',
email: ''
});
// 监听 props.open 变化
watch(() => props.open, (val) => {
visible.value = val;
if (val) {
// 打开时从存储中获取用户信息
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {};
form.value = {
account: userInfo.account || '',
name: userInfo.name || '',
email: userInfo.email || ''
};
}
});
// 监听 visible 变化,同步到父组件
watch(visible, (val) => {
emit('update:open', val);
});
// 保存个人资料
const handleSaveProfile = async () => {
loading.value = true;
try {
const userInfo = SafeStorage.get(AppStorageKey.USER_INFO) || {};
const updatedUserInfo = {
...userInfo,
...form.value,
id: userInfo.user_id
};
const res = await modifyUser(updatedUserInfo);
if (res.code === 0) {
Message.success('个人资料修改成功');
SafeStorage.set(AppStorageKey.USER_INFO, updatedUserInfo);
handleClose();
} else {
Message.error(res.message || '个人资料修改失败');
}
} catch (error) {
console.error('个人资料修改失败:', error);
Message.error('个人资料修改失败,请稍后重试');
} finally {
loading.value = false;
}
};
// 关闭弹窗
const handleClose = () => {
form.value = {
account: '',
name: '',
email: ''
};
visible.value = false;
};
</script>

View File

@@ -4,6 +4,9 @@ import type { LocationQueryRaw, Router } from 'vue-router'
import { useUserStore } from '@/store' import { useUserStore } from '@/store'
import { isLogin } from '@/utils/auth' import { isLogin } from '@/utils/auth'
// 不需要登录验证的路由名称
const whiteList = ['login', 'home']
export default function setupUserLoginInfoGuard(router: Router) { export default function setupUserLoginInfoGuard(router: Router) {
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
NProgress.start() NProgress.start()
@@ -27,7 +30,8 @@ export default function setupUserLoginInfoGuard(router: Router) {
} }
} }
} else { } else {
if (to.name === 'login') { // 在白名单中的路由不需要登录验证
if (whiteList.includes(to.name as string)) {
next() next()
return return
} }

View File

@@ -68,7 +68,7 @@ const useUserStore = defineStore('user', {
SafeStorage.set(AppStorageKey.USER_INFO, details) SafeStorage.set(AppStorageKey.USER_INFO, details)
this.userInfo = details this.userInfo = details
} else { } else {
throw new Error('登录失败:未获取到 token') throw new Error('登录失败')
} }
} catch (err) { } catch (err) {
clearToken() clearToken()

View File

@@ -4,7 +4,7 @@
<header class="navbar"> <header class="navbar">
<div class="navbar-content"> <div class="navbar-content">
<div class="logo" @click="goToHome"> <div class="logo" @click="goToHome">
<img src="@/assets/logo.svg" alt="Logo" /> <img src="@/assets/logo.png" alt="Logo" />
<span class="logo-text">智能运维管理系统</span> <span class="logo-text">智能运维管理系统</span>
</div> </div>
<nav class="nav-links"> <nav class="nav-links">
@@ -17,6 +17,8 @@
<!-- Banner 区域 --> <!-- Banner 区域 -->
<section class="help-banner"> <section class="help-banner">
<img src="@/assets/images/home1.jpg" alt="Banner" class="banner-image" />
<div class="banner-overlay"></div>
<div class="banner-content"> <div class="banner-content">
<h1>帮助中心</h1> <h1>帮助中心</h1>
<p>查找您需要的答案快速解决问题</p> <p>查找您需要的答案快速解决问题</p>
@@ -29,7 +31,7 @@
@press-enter="handleSearch" @press-enter="handleSearch"
> >
<template #prefix> <template #prefix>
<icon-search /> <IconSearch />
</template> </template>
</a-input> </a-input>
</div> </div>
@@ -43,31 +45,31 @@
<aside class="help-sidebar"> <aside class="help-sidebar">
<a-menu :default-selected-keys="[activeCategory]" @menu-item-click="handleCategoryClick"> <a-menu :default-selected-keys="[activeCategory]" @menu-item-click="handleCategoryClick">
<a-menu-item key="all"> <a-menu-item key="all">
<template #icon><icon-list /></template> <template #icon><IconList /></template>
全部文档 全部文档
</a-menu-item> </a-menu-item>
<a-menu-item key="quickstart"> <a-menu-item key="quickstart">
<template #icon><icon-rocket /></template> <template #icon><IconRocket /></template>
快速入门 快速入门
</a-menu-item> </a-menu-item>
<a-menu-item key="dashboard"> <a-menu-item key="dashboard">
<template #icon><icon-dashboard /></template> <template #icon><IconDashboard /></template>
仪表盘 仪表盘
</a-menu-item> </a-menu-item>
<a-menu-item key="alert"> <a-menu-item key="alert">
<template #icon><icon-alert /></template> <template #icon><IconAlertCircle /></template>
告警管理 告警管理
</a-menu-item> </a-menu-item>
<a-menu-item key="datacenter"> <a-menu-item key="datacenter">
<template #icon><icon-storage /></template> <template #icon><IconDatabase /></template>
数据中心 数据中心
</a-menu-item> </a-menu-item>
<a-menu-item key="report"> <a-menu-item key="report">
<template #icon><icon-chart-line /></template> <template #icon><IconChartLine /></template>
报表分析 报表分析
</a-menu-item> </a-menu-item>
<a-menu-item key="system"> <a-menu-item key="system">
<template #icon><icon-settings /></template> <template #icon><IconSettings /></template>
系统设置 系统设置
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
@@ -78,7 +80,7 @@
<!-- FAQ 常见问题 --> <!-- FAQ 常见问题 -->
<section id="faq" class="help-section"> <section id="faq" class="help-section">
<h2 class="section-title"> <h2 class="section-title">
<icon-question-circle /> <IconHelp />
常见问题 常见问题
</h2> </h2>
@@ -100,7 +102,7 @@
<!-- 使用指南 --> <!-- 使用指南 -->
<section id="guides" class="help-section"> <section id="guides" class="help-section">
<h2 class="section-title"> <h2 class="section-title">
<icon-book /> <IconBook />
使用指南 使用指南
</h2> </h2>
@@ -127,7 +129,7 @@
<!-- 操作手册 --> <!-- 操作手册 -->
<section class="help-section"> <section class="help-section">
<h2 class="section-title"> <h2 class="section-title">
<icon-file /> <IconFile />
操作手册 操作手册
</h2> </h2>
@@ -138,7 +140,7 @@
class="manual-item" class="manual-item"
> >
<div class="manual-icon"> <div class="manual-icon">
<icon-download /> <IconDownload />
</div> </div>
<div class="manual-info"> <div class="manual-info">
<h3>{{ manual.title }}</h3> <h3>{{ manual.title }}</h3>
@@ -159,38 +161,45 @@
<!-- 联系我们 --> <!-- 联系我们 -->
<section class="help-section contact-section"> <section class="help-section contact-section">
<h2 class="section-title"> <h2 class="section-title">
<icon-customer-service /> <IconHeadset />
需要更多帮助 需要更多帮助
</h2> </h2>
<p class="section-desc">我们提供多种联系方式随时为您解答疑问</p>
<div class="contact-cards"> <div class="contact-cards">
<div class="contact-card"> <div class="contact-card">
<div class="contact-icon"> <div class="contact-icon">
<icon-phone /> <IconPhone />
</div>
<div class="contact-content">
<h3>电话联系</h3>
<p class="contact-time">工作日 9:00-18:00</p>
<p class="contact-value">400-XXX-XXXX</p>
</div> </div>
<h3>电话联系</h3>
<p>工作日 9:00-18:00</p>
<p class="contact-value">400-XXX-XXXX</p>
</div> </div>
<div class="contact-card"> <div class="contact-card">
<div class="contact-icon"> <div class="contact-icon">
<icon-email /> <IconMail />
</div>
<div class="contact-content">
<h3>邮件咨询</h3>
<p class="contact-time">我们会在 24 小时内回复</p>
<p class="contact-value">support@example.com</p>
</div> </div>
<h3>邮件咨询</h3>
<p>我们会在 24 小时内回复</p>
<p class="contact-value">support@example.com</p>
</div> </div>
<div class="contact-card"> <div class="contact-card">
<div class="contact-icon"> <div class="contact-icon">
<icon-message /> <IconMessage />
</div>
<div class="contact-content">
<h3>在线客服</h3>
<p class="contact-time">实时在线解答</p>
<a-button type="primary" @click="openChat">
开始聊天
</a-button>
</div> </div>
<h3>在线客服</h3>
<p>实时在线解答</p>
<a-button type="primary" @click="openChat">
开始聊天
</a-button>
</div> </div>
</div> </div>
</section> </section>
@@ -210,6 +219,24 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import {
IconSearch,
IconList,
IconRocket,
IconDashboard,
IconAlertCircle,
IconDatabase,
IconChartLine,
IconSettings,
IconHelp,
IconBook,
IconFile,
IconDownload,
IconHeadset,
IconPhone,
IconMail,
IconMessage,
} from '@tabler/icons-vue'
const router = useRouter() const router = useRouter()
const searchQuery = ref('') const searchQuery = ref('')
@@ -265,7 +292,7 @@ const guides = ref([
id: 1, id: 1,
title: '新手入门指南', title: '新手入门指南',
description: '帮助您快速了解系统的基本功能和操作流程', description: '帮助您快速了解系统的基本功能和操作流程',
icon: 'icon-rocket', icon: IconRocket,
category: 'quickstart', category: 'quickstart',
categoryName: '快速入门', categoryName: '快速入门',
updatedAt: '2026-03-15' updatedAt: '2026-03-15'
@@ -274,7 +301,7 @@ const guides = ref([
id: 2, id: 2,
title: '仪表盘使用详解', title: '仪表盘使用详解',
description: '详细介绍仪表盘的各个功能模块和使用技巧', description: '详细介绍仪表盘的各个功能模块和使用技巧',
icon: 'icon-dashboard', icon: IconDashboard,
category: 'dashboard', category: 'dashboard',
categoryName: '仪表盘', categoryName: '仪表盘',
updatedAt: '2026-03-18' updatedAt: '2026-03-18'
@@ -283,7 +310,7 @@ const guides = ref([
id: 3, id: 3,
title: '告警配置完整教程', title: '告警配置完整教程',
description: '从基础到高级的告警策略配置方法', description: '从基础到高级的告警策略配置方法',
icon: 'icon-alert', icon: IconAlertCircle,
category: 'alert', category: 'alert',
categoryName: '告警管理', categoryName: '告警管理',
updatedAt: '2026-03-20' updatedAt: '2026-03-20'
@@ -292,7 +319,7 @@ const guides = ref([
id: 4, id: 4,
title: '机房设备管理手册', title: '机房设备管理手册',
description: '机房、机柜、设备等资源的规范管理方法', description: '机房、机柜、设备等资源的规范管理方法',
icon: 'icon-storage', icon: IconDatabase,
category: 'datacenter', category: 'datacenter',
categoryName: '数据中心', categoryName: '数据中心',
updatedAt: '2026-03-19' updatedAt: '2026-03-19'
@@ -301,7 +328,7 @@ const guides = ref([
id: 5, id: 5,
title: '数据报表生成指南', title: '数据报表生成指南',
description: '学习如何生成和导出各类业务报表', description: '学习如何生成和导出各类业务报表',
icon: 'icon-chart-line', icon: IconChartLine,
category: 'report', category: 'report',
categoryName: '报表分析', categoryName: '报表分析',
updatedAt: '2026-03-17' updatedAt: '2026-03-17'
@@ -310,7 +337,7 @@ const guides = ref([
id: 6, id: 6,
title: '系统权限管理说明', title: '系统权限管理说明',
description: '用户角色和权限配置的详细讲解', description: '用户角色和权限配置的详细讲解',
icon: 'icon-settings', icon: IconSettings,
category: 'system', category: 'system',
categoryName: '系统设置', categoryName: '系统设置',
updatedAt: '2026-03-16' updatedAt: '2026-03-16'
@@ -467,36 +494,60 @@ const openChat = () => {
// Banner 区域 // Banner 区域
.help-banner { .help-banner {
position: relative;
padding: 140px 24px 80px; padding: 140px 24px 80px;
background: linear-gradient(135deg, rgb(var(--primary-6)), rgb(var(--primary-4)));
text-align: center; text-align: center;
color: #fff; color: #fff;
overflow: hidden;
.banner-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 0;
}
.banner-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.3) 100%);
z-index: 1;
}
.banner-content { .banner-content {
position: relative;
max-width: 800px; max-width: 800px;
margin: 0 auto; margin: 0 auto;
z-index: 2;
h1 { h1 {
font-size: 48px; font-size: 48px;
font-weight: 700; font-weight: 700;
margin-bottom: 16px; margin-bottom: 16px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
} }
p { p {
font-size: 18px; font-size: 18px;
margin-bottom: 40px; margin-bottom: 40px;
opacity: 0.9; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
} }
.search-box { .search-box {
max-width: 600px; max-width: 600px;
margin: 0 auto; margin: 0 auto;
:deep(.arco-input) { :deep(.arco-input) {
background: #fff; background: #fff;
border: none; border: none;
font-size: 16px; font-size: 16px;
&::placeholder { &::placeholder {
color: var(--color-text-3); color: var(--color-text-3);
} }
@@ -704,54 +755,88 @@ const openChat = () => {
// 联系我们 // 联系我们
.contact-section { .contact-section {
text-align: center;
.section-desc {
font-size: 16px;
color: var(--color-text-2);
margin-bottom: 32px;
}
.contact-cards { .contact-cards {
display: grid; display: flex;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); flex-wrap: wrap;
justify-content: center;
gap: 24px; gap: 24px;
.contact-card { .contact-card {
background: #fff; background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border: 1px solid var(--color-border); border: 1px solid var(--color-border);
border-radius: 8px; border-radius: 16px;
padding: 32px 24px; padding: 24px;
text-align: center;
transition: all 0.3s; transition: all 0.3s;
flex: 1 1 300px;
max-width: 360px;
min-width: 280px;
display: flex;
align-items: center;
gap: 20px;
&:hover { &:hover {
border-color: rgb(var(--primary-6)); border-color: rgb(var(--primary-6));
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
transform: translateY(-4px);
} }
.contact-icon { .contact-icon {
width: 64px; width: 56px;
height: 64px; height: 56px;
display: flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: rgb(var(--primary-1)); background: linear-gradient(135deg, rgb(var(--primary-6)) 0%, rgb(var(--primary-5)) 100%);
border-radius: 50%; border-radius: 12px;
color: rgb(var(--primary-6)); color: #fff;
font-size: 32px; flex-shrink: 0;
margin: 0 auto 16px; transition: all 0.3s;
svg {
width: 28px;
height: 28px;
}
} }
h3 { &:hover .contact-icon {
font-size: 18px; transform: scale(1.05);
font-weight: 600; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
color: var(--color-text-1);
margin-bottom: 8px;
} }
p { .contact-content {
font-size: 14px; flex: 1;
color: var(--color-text-2); text-align: left;
margin-bottom: 8px;
h3 {
&.contact-value {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: var(--color-text-1);
margin-bottom: 8px;
}
.contact-time {
font-size: 13px;
color: var(--color-text-3);
margin-bottom: 6px;
}
.contact-value {
font-size: 15px;
font-weight: 600;
color: rgb(var(--primary-6)); color: rgb(var(--primary-6));
} }
.arco-btn {
margin-top: 8px;
}
} }
} }
} }
@@ -831,7 +916,19 @@ const openChat = () => {
} }
.contact-cards { .contact-cards {
grid-template-columns: 1fr; flex-direction: column;
align-items: center;
.contact-card {
max-width: 100%;
flex: 1 1 100%;
flex-direction: column;
text-align: center;
.contact-content {
text-align: center;
}
}
} }
} }
} }

View File

@@ -17,17 +17,14 @@
<!-- Banner 区域 --> <!-- Banner 区域 -->
<section id="home" class="banner"> <section id="home" class="banner">
<img src="@/assets/images/home1.jpg" alt="Banner" class="banner-image" />
<div class="banner-overlay"></div>
<div class="banner-content"> <div class="banner-content">
<h1 class="banner-title">智能运维管理系统</h1> <h1 class="banner-title">智能运维管理系统</h1>
<p class="banner-subtitle"> <p class="banner-subtitle">
提供全方位的 IT 运维管理解决方案让系统运行更稳定更高效 提供全方位的 IT 运维管理解决方案让系统运行更稳定更高效
</p> </p>
</div> </div>
<div class="banner-decoration">
<div class="circle circle-1"></div>
<div class="circle circle-2"></div>
<div class="circle circle-3"></div>
</div>
</section> </section>
<!-- 功能特性区域 --> <!-- 功能特性区域 -->
@@ -206,77 +203,55 @@ const scrollToSection = (id: string) => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
.banner-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 0;
}
.banner-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.3) 100%);
z-index: 1;
}
.banner-content { .banner-content {
text-align: center; text-align: center;
z-index: 1; z-index: 2;
.banner-title { .banner-title {
font-size: 48px; font-size: 48px;
font-weight: 700; font-weight: 700;
color: var(--color-text-1); color: #fff;
margin-bottom: 16px; margin-bottom: 16px;
background: linear-gradient(135deg, rgb(var(--primary-6)), rgb(var(--primary-4))); text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
} }
.banner-subtitle { .banner-subtitle {
font-size: 20px; font-size: 20px;
color: var(--color-text-2); color: rgba(255, 255, 255, 0.9);
margin-bottom: 40px; margin-bottom: 40px;
max-width: 600px; max-width: 600px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
} }
.banner-actions { .banner-actions {
display: flex; display: flex;
gap: 16px; gap: 16px;
justify-content: center; justify-content: center;
} }
} }
.banner-decoration {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
.circle {
position: absolute;
border-radius: 50%;
opacity: 0.1;
&.circle-1 {
width: 400px;
height: 400px;
background: rgb(var(--primary-6));
top: -200px;
right: -100px;
}
&.circle-2 {
width: 300px;
height: 300px;
background: rgb(var(--success-6));
bottom: -150px;
left: -100px;
}
&.circle-3 {
width: 200px;
height: 200px;
background: rgb(var(--warning-6));
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
} }
// 功能特性区域 // 功能特性区域

View File

@@ -22,20 +22,20 @@ import bannerImage from '@/assets/images/login-banner.png'
const { t } = useI18n() const { t } = useI18n()
const carouselItem = computed(() => [ const carouselItem = computed(() => [
{ {
slogan: t('login.banner.slogan1'), // slogan: t('login.banner.slogan1'),
subSlogan: t('login.banner.subSlogan1'), // subSlogan: t('login.banner.subSlogan1'),
image: bannerImage, image: 'https://ops.apinb.com/assets/login-image-CPDtwfmL.png',
},
{
slogan: t('login.banner.slogan2'),
subSlogan: t('login.banner.subSlogan2'),
image: bannerImage,
},
{
slogan: t('login.banner.slogan3'),
subSlogan: t('login.banner.subSlogan3'),
image: bannerImage,
}, },
// {
// slogan: t('login.banner.slogan2'),
// subSlogan: t('login.banner.subSlogan2'),
// image: bannerImage,
// },
// {
// slogan: t('login.banner.slogan3'),
// subSlogan: t('login.banner.subSlogan3'),
// image: bannerImage,
// },
]) ])
</script> </script>

View File

@@ -22,26 +22,18 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, ref } from 'vue' import { ref } from 'vue'
import { useRouter } from 'vue-router'
import LoginBanner from './components/banner.vue' import LoginBanner from './components/banner.vue'
import LoginForm from './components/login-form.vue' import LoginForm from './components/login-form.vue'
export default defineComponent({ const router = useRouter()
components: { const showQr = ref(false)
LoginBanner,
LoginForm, const goToHome = () => {
}, router.push('/home')
setup() { }
const showQr = ref(false)
const goToHome = () => {
window.location.href = '/#/home'
}
return { showQr, goToHome }
},
})
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>