feat:更新系统ui样式
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<link rel="icon" href="/logo.png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>xSynergy远程协作系统</title>
|
||||
</head>
|
||||
|
||||
195
modify.md
Normal file
195
modify.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# XSynergy Web 管理平台 UI 设计说明
|
||||
|
||||
## 一、核心约定与不可修改内容(总览)
|
||||
|
||||
### 1. 核心功能不可修改
|
||||
|
||||
- **登录逻辑不可修改**:用户认证、密码验证及登录流程保持现有逻辑。
|
||||
- **系统功能保留**:
|
||||
- 用户管理、角色与权限管理
|
||||
- 全局消息发送
|
||||
- 远程协作记录管理
|
||||
- **功能操作与交互**:
|
||||
- 所有原有操作按钮(如头像、通知、快速操作)必须保留。
|
||||
- 功能的基本可访问性和权限控制保持不变。
|
||||
|
||||
### 2. 主题色与按钮颜色
|
||||
|
||||
- **系统主色**:`#409EFF`
|
||||
应用范围:
|
||||
- 登录按钮、输入框 focus 状态
|
||||
- 导航栏背景、选中菜单、高亮条
|
||||
- 重要交互元素及图标高亮
|
||||
- **登录按钮**:
|
||||
- 宽度占满卡片,视觉突出
|
||||
- Hover 与点击状态明显
|
||||
- **输入框**:
|
||||
- 高度统一,圆角设计
|
||||
- Focus 状态明显
|
||||
|
||||
### 3. 整体风格与UI约定
|
||||
|
||||
- **风格统一**:
|
||||
- 科技感强、现代化、企业级视觉
|
||||
- 扁平化、简洁、清晰
|
||||
- **字体层级**:
|
||||
- 系统名称:最大字体
|
||||
- 模块标题:中等字体
|
||||
- 描述文本:常规字体
|
||||
- 辅助说明:较小字体
|
||||
- **卡片设计**:
|
||||
- 圆角设计、轻微阴影、背景浅色或半透明
|
||||
- **背景**:
|
||||
- 深色科技风(深蓝、深灰、深色渐变)
|
||||
- 可包含科技网格线、动态粒子或线条动画
|
||||
|
||||
### 4. 交互与响应式约定
|
||||
|
||||
- 登录表单输入顺畅,错误提示清晰,按钮反馈明显
|
||||
- 页面适配不同屏幕尺寸:
|
||||
- 大屏:左右布局同时显示
|
||||
- 中屏:保持布局但缩小内容
|
||||
- 移动端:左侧品牌区可隐藏,仅显示登录区域
|
||||
- 导航栏与菜单:
|
||||
- 左侧导航栏可固定或折叠
|
||||
- 支持二级/三级菜单,子菜单层级清晰
|
||||
- 动画过渡流畅,鼠标或触摸操作反馈明确
|
||||
- 整体视觉与交互保持一致性,主题色统一
|
||||
|
||||
------
|
||||
|
||||
## 二、登录页面优化
|
||||
|
||||
### 1. 整体设计目标
|
||||
|
||||
- 现代化企业级设计风格
|
||||
- 明显的科技感视觉效果
|
||||
- 清晰的品牌展示
|
||||
- 登录区域突出且易于操作
|
||||
- 与系统主题颜色保持统一
|
||||
- **不修改任何登录逻辑**
|
||||
|
||||
### 2. 页面整体布局结构
|
||||
|
||||
- 登录页采用 **左右双区域布局**:
|
||||
|
||||
```
|
||||
页面
|
||||
├── 背景层(科技网格/动态效果)
|
||||
└── 内容容器
|
||||
├── 左侧品牌展示区(约 55%)
|
||||
└── 右侧登录表单区(约 45%)
|
||||
```
|
||||
|
||||
- 品牌介绍与登录卡片在同一容器,页面整体居中显示
|
||||
|
||||
### 3. 背景设计(科技风格)
|
||||
|
||||
- 取消传统背景图片
|
||||
- 可采用科技网格线、动态粒子、科技线条动画或渐变色背景
|
||||
- 颜色偏深:深蓝、深灰、深色渐变
|
||||
- 避免背景过亮,保证登录卡片突出
|
||||
|
||||
### 4. 左侧区域(品牌与产品介绍)
|
||||
|
||||
#### 品牌 Logo 与系统名称
|
||||
|
||||
- **XSynergy 管理平台**
|
||||
- 企业级 AR 远程协作管理系统
|
||||
- 系统名称字体较大,可搭配科技感字体和 Logo
|
||||
|
||||
#### 产品简介
|
||||
|
||||
- XSynergy 是企业 AR 远程协作平台
|
||||
- 实现远程指导与问题解决
|
||||
- 降低差旅成本、提升效率、促进知识沉淀
|
||||
|
||||
#### 核心能力展示(图标 + 简短说明)
|
||||
|
||||
- AR远程协作:现场人员与远程专家实时连接
|
||||
- 实时音视频通信:保证远程协作顺畅
|
||||
- AI智能辅助:识别现场问题,提高效率
|
||||
- 远程协作记录:会议记录便于复盘
|
||||
|
||||
#### Web管理平台功能
|
||||
|
||||
- 用户管理:企业用户及第三方系统对接
|
||||
- 角色与权限管理:精细化权限控制
|
||||
- 全局消息发送:系统公告与通知
|
||||
- 远程协作记录管理:统一管理历史数据
|
||||
|
||||
#### 产品优势
|
||||
|
||||
- 降低差旅成本
|
||||
- 提升问题解决效率
|
||||
- 突破地域限制
|
||||
- 促进知识沉淀
|
||||
|
||||
### 5. 右侧区域(登录表单区)
|
||||
|
||||
- 卡片式设计
|
||||
- 登录卡片包含:
|
||||
1. 登录标题(系统登录)
|
||||
2. 用户名输入框
|
||||
3. 密码输入框
|
||||
4. 可选功能
|
||||
5. 登录按钮(登录系统)
|
||||
6. 系统信息提示
|
||||
- 输入框统一高度,圆角设计,Focus 状态明显
|
||||
- 登录按钮宽度占满卡片,视觉突出,Hover 与点击状态明显
|
||||
|
||||
------
|
||||
|
||||
## 三、导航栏和头部页面优化
|
||||
|
||||
### 1. 头部导航栏
|
||||
|
||||
- 主题色:`#409EFF`
|
||||
- 增加现代化、扁平化设计风格
|
||||
- 元素间距适中,图标文字清晰
|
||||
- 鼠标悬停轻微动画或渐变效果
|
||||
- 保留系统常用操作按钮
|
||||
- 响应式设计,移动端可清晰显示
|
||||
- 点击或悬停有明显视觉反馈
|
||||
- 下拉菜单圆角、阴影、渐变效果
|
||||
|
||||
### 2. 左侧导航栏
|
||||
|
||||
- 支持固定或折叠
|
||||
- 分组清晰(模块分组、功能分类)
|
||||
- 主题色 `#409EFF` 应用于选中菜单、高亮条
|
||||
- 未选中菜单为淡灰色或半透明
|
||||
- 图标统一风格(线性或扁平)
|
||||
- 鼠标悬停颜色渐变或轻微背景动画
|
||||
- 选中状态突出显示
|
||||
- 可折叠菜单支持滑动动画
|
||||
- 二级/三级菜单层级清晰
|
||||
|
||||
### 3. 响应式与整体体验
|
||||
|
||||
- 大屏幕:完整导航显示
|
||||
- 小屏/移动端:自动折叠左侧导航栏,提供图标模式
|
||||
- 动画过渡适中,操作反馈明确
|
||||
- 头部与左侧导航颜色风格统一
|
||||
- 系统主题色 `#409EFF` 保持统一
|
||||
|
||||
------
|
||||
|
||||
## 四、UI设计规范
|
||||
|
||||
- **主主题色**:`#409EFF`
|
||||
- **卡片设计**:圆角、轻微阴影、浅色或半透明背景
|
||||
- **字体层级**:
|
||||
- 系统名称:最大
|
||||
- 模块标题:中等
|
||||
- 描述文本:常规
|
||||
- 辅助说明:较小
|
||||
- **页面交互体验**:
|
||||
- 登录表单顺畅,输入错误提示清晰
|
||||
- 登录按钮反馈明显
|
||||
- 页面加载快速
|
||||
- 不影响原有登录逻辑
|
||||
- **响应式适配**:
|
||||
- 大屏设备:左右布局显示
|
||||
- 中等屏幕:缩小内容
|
||||
- 移动端:左侧品牌区可隐藏,仅显示登录区域
|
||||
BIN
public/logo.png
Normal file
BIN
public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 301 KiB |
@@ -1,17 +1,12 @@
|
||||
<template>
|
||||
<div v-if="!item.hidden">
|
||||
<template
|
||||
v-if="
|
||||
hasOneShowingChild(item.children, item) &&
|
||||
(!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
|
||||
!item.alwaysShow
|
||||
"
|
||||
>
|
||||
<template v-if="
|
||||
hasOneShowingChild(item.children, item) &&
|
||||
(!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
|
||||
!item.alwaysShow
|
||||
">
|
||||
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
|
||||
<el-menu-item
|
||||
:index="resolvePath(onlyOneChild.path)"
|
||||
:class="{ 'submenu-title-noDropdown': !isNest }"
|
||||
>
|
||||
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
|
||||
<!-- <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" /> -->
|
||||
<!-- {{ onlyOneChild.meta.title }} -->
|
||||
<!-- <template #title> -->
|
||||
@@ -29,14 +24,8 @@
|
||||
<span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
|
||||
</template>
|
||||
|
||||
<sidebar-item
|
||||
v-for="child in item.children"
|
||||
:key="child.path"
|
||||
:is-nest="true"
|
||||
:item="child"
|
||||
:base-path="resolvePath(child.path)"
|
||||
class="nest-menu"
|
||||
/>
|
||||
<sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child"
|
||||
:base-path="resolvePath(child.path)" class="nest-menu" />
|
||||
</el-sub-menu>
|
||||
</div>
|
||||
</template>
|
||||
@@ -114,3 +103,284 @@ function hasTitle(title) {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 优化:侧边栏菜单项样式 */
|
||||
.sidebar-item-wrapper {
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 优化:菜单项基础样式 */
|
||||
:deep(.el-menu-item) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px !important;
|
||||
margin: 4px 8px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 优化:悬停效果 */
|
||||
:deep(.el-menu-item:hover) {
|
||||
background-color: var(--el-menu-hover-bg-color, rgba(64, 158, 255, 0.1)) !important;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* 优化:激活菜单项样式 */
|
||||
:deep(.el-menu-item.is-active) {
|
||||
background: var(--el-color-primary-light-9);
|
||||
/* background: linear-gradient(90deg, var(--el-color-primary-light-9) 0%, transparent 100%); */
|
||||
color: var(--el-color-primary) !important;
|
||||
font-weight: 500;
|
||||
border-right: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 优化:激活指示器 */
|
||||
.menu-active-indicator {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 3px;
|
||||
height: 20px;
|
||||
background: var(--el-color-primary);
|
||||
/* background: linear-gradient(180deg, var(--el-color-primary) 0%, var(--el-color-primary-light-3) 100%); */
|
||||
border-radius: 0 4px 4px 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item.is-active) .menu-active-indicator {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 优化:图标样式 */
|
||||
.menu-icon-wrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 12px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: currentColor;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item:hover) .menu-icon {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.menu-icon-placeholder {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
/* 优化:标题样式 */
|
||||
.menu-title {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: all 0.3s ease;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
/* 优化:子菜单样式 */
|
||||
:deep(.el-sub-menu) {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* 优化:子菜单标题 */
|
||||
:deep(.el-sub-menu__title) {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding: 0 20px !important;
|
||||
margin: 0 8px;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu__title:hover) {
|
||||
background-color: var(--el-menu-hover-bg-color, rgba(64, 158, 255, 0.1)) !important;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* 优化:子菜单标题包装器 */
|
||||
.submenu-title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 优化:子菜单箭头样式 */
|
||||
.submenu-arrow {
|
||||
margin-left: auto;
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu.is-opened) .submenu-arrow {
|
||||
transform: rotate(90deg);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
/* 优化:子菜单容器 */
|
||||
.submenu-children-wrapper {
|
||||
padding-left: 32px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 优化:子菜单项样式 */
|
||||
:deep(.nest-menu .el-menu-item) {
|
||||
padding-left: 44px !important;
|
||||
font-size: 13px;
|
||||
background: transparent;
|
||||
margin: 2px 8px;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:deep(.nest-menu .el-menu-item::before) {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 28px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--el-text-color-secondary);
|
||||
opacity: 0.5;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
:deep(.nest-menu .el-menu-item:hover::before) {
|
||||
background-color: var(--el-color-primary);
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) scale(1.5);
|
||||
}
|
||||
|
||||
:deep(.nest-menu .el-menu-item.is-active::before) {
|
||||
background-color: var(--el-color-primary);
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) scale(2);
|
||||
box-shadow: 0 0 8px var(--el-color-primary);
|
||||
}
|
||||
|
||||
/* 优化:popper菜单样式(针对水平折叠模式) */
|
||||
:deep(.el-popper.is-pure) {
|
||||
border: none !important;
|
||||
box-shadow: 0 6px 16px -8px rgba(0, 0, 0, 0.08), 0 9px 28px 0 rgba(0, 0, 0, 0.05), 0 12px 48px 16px rgba(0, 0, 0, 0.03) !important;
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden;
|
||||
padding: 4px 0 !important;
|
||||
}
|
||||
|
||||
:deep(.el-popper .el-menu--vertical) {
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
:deep(.el-popper .el-menu-item) {
|
||||
min-width: 160px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
margin: 2px 4px;
|
||||
padding: 0 12px !important;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* 优化:折叠时的特殊样式 */
|
||||
:deep(.el-menu--collapse) .menu-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.el-menu--collapse) .menu-icon-wrapper {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
:deep(.el-menu--collapse) .el-sub-menu__title {
|
||||
padding: 0 16px !important;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 优化:动画效果 */
|
||||
@keyframes menuItemFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-item-wrapper {
|
||||
animation: menuItemFadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
/* 优化:暗色主题适配 */
|
||||
:root[data-theme='dark'] {
|
||||
--el-menu-hover-bg-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* 优化:响应式调整 */
|
||||
@media screen and (max-width: 768px) {
|
||||
|
||||
:deep(.el-menu-item),
|
||||
:deep(.el-sub-menu__title) {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化:打印样式 */
|
||||
@media print {
|
||||
.sidebar-item-wrapper {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化:禁用状态样式 */
|
||||
:deep(.el-menu-item.is-disabled) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item.is-disabled:hover) {
|
||||
background: none !important;
|
||||
transform: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="wrapper-content">
|
||||
<div class="content-nav">
|
||||
<div class="nav-left">
|
||||
<div class="nav-left">
|
||||
<div>xSynergy远程协作后台管理系统</div>
|
||||
</div>
|
||||
<div class="nav-right">
|
||||
@@ -221,70 +221,68 @@ onMounted(async () => {})
|
||||
z-index: 1111;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
-webkit-box-shadow: 2px 0 9px rgba(0, 21, 41, 0.35);
|
||||
box-shadow: 2px 0 9px rgba(0, 21, 41, 0.35);
|
||||
padding: 3px 20px;
|
||||
padding: 0 20px;
|
||||
background-color: #409EFF;
|
||||
// background-color: #434343;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
transition: background 0.3s;
|
||||
|
||||
.nav-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
box-sizing: border-box;
|
||||
div {
|
||||
font-size: 19px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
|
||||
img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
.screenfull {
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
color: #ffe565;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
margin-right: 30px;
|
||||
img{
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
transition: all 0.3s;
|
||||
|
||||
img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(255, 255, 255, 0.4);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.username {
|
||||
padding: 0 13px;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
margin-left: 10px;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.nickName {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
|
||||
color: #fff;
|
||||
&:hover img {
|
||||
transform: scale(1.05);
|
||||
border-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
margin-left: 6px;
|
||||
font-size: 20px;
|
||||
color: #ffe565;
|
||||
.el-dropdown-menu {
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
|
||||
.el-dropdown-item {
|
||||
transition: background 0.3s, color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(64, 158, 255, 0.1);
|
||||
color: #409EFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,105 @@
|
||||
<template>
|
||||
<div class="loginView">
|
||||
<div class="wrapper-content" >
|
||||
<div class="login-form">
|
||||
<div class="selected-rectangle"></div>
|
||||
<el-form ref="loginRef" class="form-info" :model="loginForm" :rules="loginRules">
|
||||
<h2 class="title">欢迎登录</h2>
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="loginForm.username" auto-complete="off" placeholder="请输入您的账号" size="large"
|
||||
type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input v-model="loginForm.password" auto-complete="off" placeholder="请输入密码" size="large"
|
||||
type="password" @keyup.enter="handleLogin"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :loading="loading" size="large" class="button-login" type="primary"
|
||||
@click.prevent="handleLogin">
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登录中...</span>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<!-- <div class="el-login-footer">
|
||||
<span>{{ $t('Copyright') }}</span>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="loginView">
|
||||
|
||||
<!-- 背景 -->
|
||||
<div class="login-bg"></div>
|
||||
|
||||
<div class="login-wrapper">
|
||||
|
||||
<div class="login-card">
|
||||
|
||||
<!-- 左侧品牌 -->
|
||||
<div class="brand-panel">
|
||||
|
||||
<h1 class="brand-title">XSynergy</h1>
|
||||
|
||||
<h3 class="brand-sub">
|
||||
企业级 AR 远程协作管理平台
|
||||
</h3>
|
||||
|
||||
<p class="brand-desc">
|
||||
通过实时音视频、增强现实标注与人工智能技术,
|
||||
实现现场人员与远程专家沉浸式协作,
|
||||
打破地理限制,提高企业协作效率。
|
||||
</p>
|
||||
|
||||
<div class="feature-list">
|
||||
|
||||
<div class="feature-item">AR远程协作</div>
|
||||
<div class="feature-item">实时音视频</div>
|
||||
<div class="feature-item">AI智能辅助</div>
|
||||
<div class="feature-item">协作记录管理</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 右侧登录 -->
|
||||
<div class="login-panel">
|
||||
|
||||
<h2 class="login-title">系统登录</h2>
|
||||
|
||||
<el-form
|
||||
ref="loginRef"
|
||||
class="login-form"
|
||||
:model="loginForm"
|
||||
:rules="loginRules"
|
||||
>
|
||||
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
placeholder="请输入账号"
|
||||
size="large"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
size="large"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button
|
||||
:loading="loading"
|
||||
class="login-button"
|
||||
type="primary"
|
||||
size="large"
|
||||
@click.prevent="handleLogin"
|
||||
>
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登录中...</span>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useUserStore } from '@/stores/modules/user.js'
|
||||
import { watch, ref, getCurrentInstance, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElNotification,ElMessage } from 'element-plus'
|
||||
import { ElNotification, ElMessage } from 'element-plus'
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { useMeterStore } from '@/stores/modules/meter'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const meterStore = useMeterStore()
|
||||
|
||||
@@ -51,7 +109,7 @@ const loginView = ref(true)
|
||||
|
||||
// 监听路由变化,获取重定向参数
|
||||
watch(() => route, (newRoute) => {
|
||||
redirect.value = newRoute.query && newRoute.query.redirect;
|
||||
redirect.value = newRoute.query && newRoute.query.redirect;
|
||||
}, { immediate: true });
|
||||
|
||||
const loginForm = ref({
|
||||
@@ -67,11 +125,11 @@ const loginRules = {
|
||||
const loading = ref(false)
|
||||
|
||||
|
||||
function handleLogin() {
|
||||
function handleLogin() {
|
||||
proxy.$refs.loginRef.validate((valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
if(!localStorage?.getItem('UDID')){
|
||||
if (!localStorage?.getItem('UDID')) {
|
||||
ElMessage({
|
||||
message: '服务错误,请刷新页面',
|
||||
type: 'warning',
|
||||
@@ -89,8 +147,8 @@ const loading = ref(false)
|
||||
password: ciphertext,
|
||||
username: loginForm.value.username,
|
||||
})
|
||||
.then(async (res) => {
|
||||
const userInfo = JSON.parse(sessionStorage.getItem('userData'))
|
||||
.then(async (res) => {
|
||||
const userInfo = JSON.parse(sessionStorage.getItem('userData'))
|
||||
await handleLoginSuccess();
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -105,23 +163,23 @@ const loading = ref(false)
|
||||
/**
|
||||
* 处理登录成功后的跳转逻辑
|
||||
*/
|
||||
async function handleLoginSuccess() {
|
||||
try {
|
||||
async function handleLoginSuccess() {
|
||||
try {
|
||||
// 如果有重定向路径且不是登录页,则跳转到重定向页面
|
||||
if (redirect.value && redirect.value !== '/login') {
|
||||
|
||||
if (redirect.value && redirect.value !== '/login') {
|
||||
|
||||
// 确保路由存在,如果不存在则跳转到默认页面
|
||||
try {
|
||||
// 解析路径,检查是否是有效路由
|
||||
const resolved = router.resolve(redirect.value);
|
||||
if (resolved.matched.length > 0) {
|
||||
if (resolved.matched.length > 0) {
|
||||
await router.push(redirect.value);
|
||||
} else {
|
||||
console.warn('重定向路径无效,跳转到默认页面');
|
||||
console.warn('重定向路径无效,跳转到默认页面');
|
||||
await router.push('/userManagement');
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('重定向跳转失败,跳转到默认页面:', error);
|
||||
console.warn('重定向跳转失败,跳转到默认页面:', error);
|
||||
await router.push('/userManagement');
|
||||
}
|
||||
} else {
|
||||
@@ -167,347 +225,222 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.loginView {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wrapper-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-image: url('@/assets/images/loginBg.png');
|
||||
background-size: cover;
|
||||
padding: 0 50px;
|
||||
/* 背景 */
|
||||
|
||||
.content-nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1111;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
box-shadow: 0px 2px 5px 0px rgba(153, 153, 153, 0.2);
|
||||
padding: 0 35px;
|
||||
background-color: #fff;
|
||||
.login-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
.nav-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
box-sizing: border-box;
|
||||
background:
|
||||
radial-gradient(circle at 20% 20%, rgba(64,158,255,.15), transparent 40%),
|
||||
radial-gradient(circle at 80% 80%, rgba(64,158,255,.15), transparent 40%),
|
||||
linear-gradient(120deg,#f6f9fc,#eef3ff);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
/* 动态光效 */
|
||||
|
||||
div {
|
||||
padding-left: 12px;
|
||||
font-size: 22px;
|
||||
color: #051435;
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-bg::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
rgba(64,158,255,.25),
|
||||
transparent 70%
|
||||
);
|
||||
|
||||
filter: blur(80px);
|
||||
|
||||
animation: floatLight 12s infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes floatLight {
|
||||
|
||||
from {
|
||||
transform: translate(-200px,-100px);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate(300px,200px);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 居中容器 */
|
||||
|
||||
.login-wrapper {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 主卡片 */
|
||||
|
||||
.login-card {
|
||||
|
||||
width: 960px;
|
||||
height: 520px;
|
||||
|
||||
display: flex;
|
||||
|
||||
border-radius: 16px;
|
||||
|
||||
background: rgba(255,255,255,.9);
|
||||
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
box-shadow:
|
||||
0 20px 50px rgba(0,0,0,.08);
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 左侧品牌 */
|
||||
|
||||
.brand-panel {
|
||||
|
||||
flex: 1;
|
||||
|
||||
padding: 60px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
color: #303133;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.brand-sub {
|
||||
color: #409EFF;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.brand-desc {
|
||||
color: #606266;
|
||||
line-height: 1.7;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2,1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
|
||||
padding: 10px 14px;
|
||||
|
||||
border-radius: 6px;
|
||||
|
||||
background: #f5f7fa;
|
||||
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
transition: all .25s;
|
||||
}
|
||||
|
||||
.feature-item:hover {
|
||||
border-color: #409EFF;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
/* 右侧登录 */
|
||||
|
||||
.login-panel {
|
||||
|
||||
width: 380px;
|
||||
|
||||
padding: 60px 40px;
|
||||
|
||||
border-left: 1px solid #ebeef5;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
border-radius: 35px;
|
||||
background: #f5f7fa;
|
||||
font-size: 18px;
|
||||
border: 1px solid #c9d4e6;
|
||||
color: #051435;
|
||||
padding: 0 30px;
|
||||
|
||||
input {
|
||||
height: 50px;
|
||||
color: #051435;
|
||||
}
|
||||
border-radius: 6px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__error) {
|
||||
font-size: 15px;
|
||||
font-weight: 20px;
|
||||
/* 登录按钮 */
|
||||
|
||||
.login-button {
|
||||
|
||||
width: 100%;
|
||||
|
||||
height: 44px;
|
||||
|
||||
border-radius: 6px;
|
||||
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#409EFF,
|
||||
#66b1ff
|
||||
);
|
||||
|
||||
border: none;
|
||||
}
|
||||
|
||||
// ::v-deep .el-input__inner::placeholder {
|
||||
// color: #051435;
|
||||
// }
|
||||
.login-button:hover {
|
||||
|
||||
.login-form {
|
||||
border-radius: 20px;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
position: relative;
|
||||
transform: translateY(-1px);
|
||||
|
||||
.selected-rectangle {
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
border: 4px solid white;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: -11px;
|
||||
left: 290px;
|
||||
animation: floatRotate 2s ease-in-out infinite;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
@keyframes floatRotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.el-login-footer {
|
||||
position: absolute;
|
||||
bottom: -30px;
|
||||
left: -5%;
|
||||
width: 110%;
|
||||
text-align: center;
|
||||
color: #687898;
|
||||
font-family: Arial;
|
||||
font-size: 16px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.form-info {
|
||||
margin-right: 50px;
|
||||
width: 550px;
|
||||
height: 530px;
|
||||
border-radius: 25px;
|
||||
padding: 75px 50px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
margin: 0 auto;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #051435;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.button-login {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
margin-top: 35px;
|
||||
border-radius: 35px;
|
||||
font-size: 22px;
|
||||
background: #00C8C4;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.button-ok {
|
||||
width: 100%;
|
||||
margin-top: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 13px;
|
||||
font-size: 16px;
|
||||
color: #687898;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input) {
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
height: 40px;
|
||||
width: 32px;
|
||||
margin-left: 0px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
box-shadow: 0 6px 16px rgba(64,158,255,.25);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 765px) {
|
||||
.wrapper-content {
|
||||
padding: 0 20px;
|
||||
/* 移动端 */
|
||||
|
||||
.content-nav {
|
||||
height: 60px;
|
||||
padding: 0 15px;
|
||||
@media screen and (max-width: 900px) {
|
||||
|
||||
.nav-left {
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.login-card {
|
||||
|
||||
div {
|
||||
padding-left: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flex-direction: column;
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
padding: 0 30px;
|
||||
width: 90%;
|
||||
|
||||
input {
|
||||
height: 40px;
|
||||
color: #051435;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
height: auto;
|
||||
}
|
||||
|
||||
// ::v-deep .el-input__inner::placeholder {
|
||||
// color: #051435;
|
||||
// }
|
||||
.login-form {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
padding: 60px 30px;
|
||||
height: auto;
|
||||
.login-panel {
|
||||
|
||||
.selected-rectangle {
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
border: 4px solid white;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: -11px;
|
||||
left: 170px;
|
||||
animation: floatRotate 2s ease-in-out infinite;
|
||||
transform-origin: center center;
|
||||
}
|
||||
width: 100%;
|
||||
|
||||
.el-login-footer {
|
||||
font-size: 14px;
|
||||
}
|
||||
border-left: none;
|
||||
|
||||
.form-info {
|
||||
margin-right: 0;
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
background: #ffffff;
|
||||
padding: 25px;
|
||||
height: auto;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
margin: 0 auto;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #051435;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.button-login {
|
||||
height: 40px;
|
||||
margin-top: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.button-ok {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input) {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
height: 40px;
|
||||
width: 32px;
|
||||
margin-left: 0px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-link.modern {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.home-link.modern:hover {
|
||||
color: #409EFF;
|
||||
background-color: rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
|
||||
.home-link.modern i {
|
||||
font-size: 16px;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.home-link.modern:hover i {
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user