feta
This commit is contained in:
BIN
src/assets/images/home1.jpg
Normal file
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
BIN
src/assets/images/home2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 KiB |
@@ -81,7 +81,7 @@
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</li>
|
||||
<li>
|
||||
<!-- <li>
|
||||
<a-tooltip :content="t('settings.title')">
|
||||
<a-button class="nav-btn" type="outline" :shape="'circle'" @click="setVisible">
|
||||
<template #icon>
|
||||
@@ -89,13 +89,25 @@
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</li>
|
||||
</li> -->
|
||||
<li>
|
||||
<a-dropdown trigger="click">
|
||||
<a-avatar :size="32" :style="{ marginRight: '8px' }">
|
||||
<img alt="avatar" :src="avatar" />
|
||||
</a-avatar>
|
||||
<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-space @click="handleLogout">
|
||||
<icon-export />
|
||||
@@ -108,6 +120,8 @@
|
||||
</a-dropdown>
|
||||
</li>
|
||||
</ul>
|
||||
<PasswordChange v-model:open="passwordChangeVisible" />
|
||||
<ProfileChange v-model:open="profileChangeVisible" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -121,6 +135,8 @@ import { useDark, useFullscreen, useToggle } from '@vueuse/core'
|
||||
import { computed, inject, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import MessageBox from '../message-box/index.vue'
|
||||
import PasswordChange from '../password-change/index.vue'
|
||||
import ProfileChange from '../profile-change/index.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -157,6 +173,8 @@ const setVisible = () => {
|
||||
}
|
||||
const refBtn = ref()
|
||||
const triggerBtn = ref()
|
||||
const passwordChangeVisible = ref(false)
|
||||
const profileChangeVisible = ref(false)
|
||||
const setPopoverVisible = () => {
|
||||
const event = new MouseEvent('click', {
|
||||
view: window,
|
||||
@@ -168,6 +186,12 @@ const setPopoverVisible = () => {
|
||||
const handleLogout = () => {
|
||||
logout()
|
||||
}
|
||||
const handleOpenPasswordChange = () => {
|
||||
passwordChangeVisible.value = true
|
||||
}
|
||||
const handleOpenProfileChange = () => {
|
||||
profileChangeVisible.value = true
|
||||
}
|
||||
const setDropDownVisible = () => {
|
||||
const event = new MouseEvent('click', {
|
||||
view: window,
|
||||
|
||||
117
src/components/password-change/index.vue
Normal file
117
src/components/password-change/index.vue
Normal 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>
|
||||
121
src/components/profile-change/index.vue
Normal file
121
src/components/profile-change/index.vue
Normal 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>
|
||||
@@ -4,6 +4,9 @@ import type { LocationQueryRaw, Router } from 'vue-router'
|
||||
import { useUserStore } from '@/store'
|
||||
import { isLogin } from '@/utils/auth'
|
||||
|
||||
// 不需要登录验证的路由名称
|
||||
const whiteList = ['login', 'home']
|
||||
|
||||
export default function setupUserLoginInfoGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.start()
|
||||
@@ -27,7 +30,8 @@ export default function setupUserLoginInfoGuard(router: Router) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (to.name === 'login') {
|
||||
// 在白名单中的路由不需要登录验证
|
||||
if (whiteList.includes(to.name as string)) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ const useUserStore = defineStore('user', {
|
||||
SafeStorage.set(AppStorageKey.USER_INFO, details)
|
||||
this.userInfo = details
|
||||
} else {
|
||||
throw new Error('登录失败:未获取到 token')
|
||||
throw new Error('登录失败')
|
||||
}
|
||||
} catch (err) {
|
||||
clearToken()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<header class="navbar">
|
||||
<div class="navbar-content">
|
||||
<div class="logo" @click="goToHome">
|
||||
<img src="@/assets/logo.svg" alt="Logo" />
|
||||
<img src="@/assets/logo.png" alt="Logo" />
|
||||
<span class="logo-text">智能运维管理系统</span>
|
||||
</div>
|
||||
<nav class="nav-links">
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
<!-- 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">
|
||||
<h1>帮助中心</h1>
|
||||
<p>查找您需要的答案,快速解决问题</p>
|
||||
@@ -29,7 +31,7 @@
|
||||
@press-enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
<IconSearch />
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
@@ -43,31 +45,31 @@
|
||||
<aside class="help-sidebar">
|
||||
<a-menu :default-selected-keys="[activeCategory]" @menu-item-click="handleCategoryClick">
|
||||
<a-menu-item key="all">
|
||||
<template #icon><icon-list /></template>
|
||||
<template #icon><IconList /></template>
|
||||
全部文档
|
||||
</a-menu-item>
|
||||
<a-menu-item key="quickstart">
|
||||
<template #icon><icon-rocket /></template>
|
||||
<template #icon><IconRocket /></template>
|
||||
快速入门
|
||||
</a-menu-item>
|
||||
<a-menu-item key="dashboard">
|
||||
<template #icon><icon-dashboard /></template>
|
||||
<template #icon><IconDashboard /></template>
|
||||
仪表盘
|
||||
</a-menu-item>
|
||||
<a-menu-item key="alert">
|
||||
<template #icon><icon-alert /></template>
|
||||
<template #icon><IconAlertCircle /></template>
|
||||
告警管理
|
||||
</a-menu-item>
|
||||
<a-menu-item key="datacenter">
|
||||
<template #icon><icon-storage /></template>
|
||||
<template #icon><IconDatabase /></template>
|
||||
数据中心
|
||||
</a-menu-item>
|
||||
<a-menu-item key="report">
|
||||
<template #icon><icon-chart-line /></template>
|
||||
<template #icon><IconChartLine /></template>
|
||||
报表分析
|
||||
</a-menu-item>
|
||||
<a-menu-item key="system">
|
||||
<template #icon><icon-settings /></template>
|
||||
<template #icon><IconSettings /></template>
|
||||
系统设置
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
@@ -78,7 +80,7 @@
|
||||
<!-- FAQ 常见问题 -->
|
||||
<section id="faq" class="help-section">
|
||||
<h2 class="section-title">
|
||||
<icon-question-circle />
|
||||
<IconHelp />
|
||||
常见问题
|
||||
</h2>
|
||||
|
||||
@@ -100,7 +102,7 @@
|
||||
<!-- 使用指南 -->
|
||||
<section id="guides" class="help-section">
|
||||
<h2 class="section-title">
|
||||
<icon-book />
|
||||
<IconBook />
|
||||
使用指南
|
||||
</h2>
|
||||
|
||||
@@ -127,7 +129,7 @@
|
||||
<!-- 操作手册 -->
|
||||
<section class="help-section">
|
||||
<h2 class="section-title">
|
||||
<icon-file />
|
||||
<IconFile />
|
||||
操作手册
|
||||
</h2>
|
||||
|
||||
@@ -138,7 +140,7 @@
|
||||
class="manual-item"
|
||||
>
|
||||
<div class="manual-icon">
|
||||
<icon-download />
|
||||
<IconDownload />
|
||||
</div>
|
||||
<div class="manual-info">
|
||||
<h3>{{ manual.title }}</h3>
|
||||
@@ -159,38 +161,45 @@
|
||||
<!-- 联系我们 -->
|
||||
<section class="help-section contact-section">
|
||||
<h2 class="section-title">
|
||||
<icon-customer-service />
|
||||
<IconHeadset />
|
||||
需要更多帮助?
|
||||
</h2>
|
||||
<p class="section-desc">我们提供多种联系方式,随时为您解答疑问</p>
|
||||
|
||||
<div class="contact-cards">
|
||||
<div class="contact-card">
|
||||
<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>
|
||||
<h3>电话联系</h3>
|
||||
<p>工作日 9:00-18:00</p>
|
||||
<p class="contact-value">400-XXX-XXXX</p>
|
||||
</div>
|
||||
|
||||
<div class="contact-card">
|
||||
<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>
|
||||
<h3>邮件咨询</h3>
|
||||
<p>我们会在 24 小时内回复</p>
|
||||
<p class="contact-value">support@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="contact-card">
|
||||
<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>
|
||||
<h3>在线客服</h3>
|
||||
<p>实时在线解答</p>
|
||||
<a-button type="primary" @click="openChat">
|
||||
开始聊天
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -210,6 +219,24 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
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 searchQuery = ref('')
|
||||
@@ -265,7 +292,7 @@ const guides = ref([
|
||||
id: 1,
|
||||
title: '新手入门指南',
|
||||
description: '帮助您快速了解系统的基本功能和操作流程',
|
||||
icon: 'icon-rocket',
|
||||
icon: IconRocket,
|
||||
category: 'quickstart',
|
||||
categoryName: '快速入门',
|
||||
updatedAt: '2026-03-15'
|
||||
@@ -274,7 +301,7 @@ const guides = ref([
|
||||
id: 2,
|
||||
title: '仪表盘使用详解',
|
||||
description: '详细介绍仪表盘的各个功能模块和使用技巧',
|
||||
icon: 'icon-dashboard',
|
||||
icon: IconDashboard,
|
||||
category: 'dashboard',
|
||||
categoryName: '仪表盘',
|
||||
updatedAt: '2026-03-18'
|
||||
@@ -283,7 +310,7 @@ const guides = ref([
|
||||
id: 3,
|
||||
title: '告警配置完整教程',
|
||||
description: '从基础到高级的告警策略配置方法',
|
||||
icon: 'icon-alert',
|
||||
icon: IconAlertCircle,
|
||||
category: 'alert',
|
||||
categoryName: '告警管理',
|
||||
updatedAt: '2026-03-20'
|
||||
@@ -292,7 +319,7 @@ const guides = ref([
|
||||
id: 4,
|
||||
title: '机房设备管理手册',
|
||||
description: '机房、机柜、设备等资源的规范管理方法',
|
||||
icon: 'icon-storage',
|
||||
icon: IconDatabase,
|
||||
category: 'datacenter',
|
||||
categoryName: '数据中心',
|
||||
updatedAt: '2026-03-19'
|
||||
@@ -301,7 +328,7 @@ const guides = ref([
|
||||
id: 5,
|
||||
title: '数据报表生成指南',
|
||||
description: '学习如何生成和导出各类业务报表',
|
||||
icon: 'icon-chart-line',
|
||||
icon: IconChartLine,
|
||||
category: 'report',
|
||||
categoryName: '报表分析',
|
||||
updatedAt: '2026-03-17'
|
||||
@@ -310,7 +337,7 @@ const guides = ref([
|
||||
id: 6,
|
||||
title: '系统权限管理说明',
|
||||
description: '用户角色和权限配置的详细讲解',
|
||||
icon: 'icon-settings',
|
||||
icon: IconSettings,
|
||||
category: 'system',
|
||||
categoryName: '系统设置',
|
||||
updatedAt: '2026-03-16'
|
||||
@@ -467,25 +494,49 @@ const openChat = () => {
|
||||
|
||||
// Banner 区域
|
||||
.help-banner {
|
||||
position: relative;
|
||||
padding: 140px 24px 80px;
|
||||
background: linear-gradient(135deg, rgb(var(--primary-6)), rgb(var(--primary-4)));
|
||||
text-align: center;
|
||||
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 {
|
||||
position: relative;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
z-index: 2;
|
||||
|
||||
h1 {
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 16px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 18px;
|
||||
margin-bottom: 40px;
|
||||
opacity: 0.9;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.search-box {
|
||||
@@ -704,54 +755,88 @@ const openChat = () => {
|
||||
|
||||
// 联系我们
|
||||
.contact-section {
|
||||
text-align: center;
|
||||
|
||||
.section-desc {
|
||||
font-size: 16px;
|
||||
color: var(--color-text-2);
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.contact-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
|
||||
.contact-card {
|
||||
background: #fff;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
padding: 32px 24px;
|
||||
text-align: center;
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
transition: all 0.3s;
|
||||
flex: 1 1 300px;
|
||||
max-width: 360px;
|
||||
min-width: 280px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
|
||||
&:hover {
|
||||
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 {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgb(var(--primary-1));
|
||||
border-radius: 50%;
|
||||
color: rgb(var(--primary-6));
|
||||
font-size: 32px;
|
||||
margin: 0 auto 16px;
|
||||
background: linear-gradient(135deg, rgb(var(--primary-6)) 0%, rgb(var(--primary-5)) 100%);
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.3s;
|
||||
|
||||
svg {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-1);
|
||||
margin-bottom: 8px;
|
||||
&:hover .contact-icon {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: var(--color-text-2);
|
||||
margin-bottom: 8px;
|
||||
.contact-content {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
|
||||
&.contact-value {
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
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));
|
||||
}
|
||||
|
||||
.arco-btn {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -831,7 +916,19 @@ const openChat = () => {
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,17 +17,14 @@
|
||||
|
||||
<!-- 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">
|
||||
<h1 class="banner-title">智能运维管理系统</h1>
|
||||
<p class="banner-subtitle">
|
||||
提供全方位的 IT 运维管理解决方案,让系统运行更稳定、更高效
|
||||
</p>
|
||||
</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>
|
||||
|
||||
<!-- 功能特性区域 -->
|
||||
@@ -207,28 +204,46 @@ const scrollToSection = (id: string) => {
|
||||
justify-content: center;
|
||||
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 {
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
|
||||
.banner-title {
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
color: var(--color-text-1);
|
||||
color: #fff;
|
||||
margin-bottom: 16px;
|
||||
background: linear-gradient(135deg, rgb(var(--primary-6)), rgb(var(--primary-4)));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.banner-subtitle {
|
||||
font-size: 20px;
|
||||
color: var(--color-text-2);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-bottom: 40px;
|
||||
max-width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.banner-actions {
|
||||
@@ -237,46 +252,6 @@ const scrollToSection = (id: string) => {
|
||||
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%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 功能特性区域
|
||||
|
||||
@@ -22,20 +22,20 @@ import bannerImage from '@/assets/images/login-banner.png'
|
||||
const { t } = useI18n()
|
||||
const carouselItem = computed(() => [
|
||||
{
|
||||
slogan: t('login.banner.slogan1'),
|
||||
subSlogan: t('login.banner.subSlogan1'),
|
||||
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,
|
||||
// slogan: t('login.banner.slogan1'),
|
||||
// subSlogan: t('login.banner.subSlogan1'),
|
||||
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,
|
||||
// },
|
||||
])
|
||||
</script>
|
||||
|
||||
|
||||
@@ -22,26 +22,18 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import LoginBanner from './components/banner.vue'
|
||||
import LoginForm from './components/login-form.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LoginBanner,
|
||||
LoginForm,
|
||||
},
|
||||
setup() {
|
||||
const showQr = ref(false)
|
||||
const router = useRouter()
|
||||
const showQr = ref(false)
|
||||
|
||||
const goToHome = () => {
|
||||
window.location.href = '/#/home'
|
||||
}
|
||||
|
||||
return { showQr, goToHome }
|
||||
},
|
||||
})
|
||||
const goToHome = () => {
|
||||
router.push('/home')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
Reference in New Issue
Block a user