Compare commits
4 Commits
dfb3d478f0
...
ccce5849f8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccce5849f8 | ||
|
|
782a3b3088 | ||
|
|
1c2b751896 | ||
|
|
24328dbb51 |
@ -1,12 +1,13 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 登录方法
|
// 登录方法
|
||||||
export function login(username, password, code, uuid) {
|
export function login(username, password, code, uuid,loginParam) {
|
||||||
const data = {
|
const data = {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
code,
|
code,
|
||||||
uuid
|
uuid,
|
||||||
|
loginParam
|
||||||
}
|
}
|
||||||
return request({
|
return request({
|
||||||
url: '/login',
|
url: '/login',
|
||||||
@ -28,6 +29,17 @@ export function loginAnonymous(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getLocalconfig(data) {
|
||||||
|
return request({
|
||||||
|
url: '/getLocalconfig',
|
||||||
|
headers: {
|
||||||
|
isToken: false
|
||||||
|
},
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
timeout: 20000
|
||||||
|
})
|
||||||
|
}
|
||||||
// 注册方法
|
// 注册方法
|
||||||
export function register(data) {
|
export function register(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|||||||
BIN
src/assets/images/logo_new.png
Normal file
BIN
src/assets/images/logo_new.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 6.7 KiB |
@ -1,27 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
<!-- 侧边栏折叠按钮(保留原有功能) -->
|
||||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
|
<hamburger
|
||||||
<top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
|
id="hamburger-container"
|
||||||
|
:is-active="appStore.sidebar.opened"
|
||||||
|
class="hamburger-container"
|
||||||
|
@toggleClick="toggleSideBar"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="right-menu">
|
<!-- 核心改造:替换面包屑/顶部导航,改为产品名称 + 登录信息布局 -->
|
||||||
|
<div class="custom-header-content">
|
||||||
|
<!-- 左侧:APP产品名称 -->
|
||||||
|
<div class="app-name">
|
||||||
|
仙桃市第一人民医院检验系统 <!-- 替换为实际产品名 -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:功能按钮 + 登录信息 + 原生设置菜单 -->
|
||||||
|
<div class="right-content">
|
||||||
|
<!-- 保留原有功能按钮(搜索、全屏、布局大小)
|
||||||
<template v-if="appStore.device !== 'mobile'">
|
<template v-if="appStore.device !== 'mobile'">
|
||||||
<header-search id="header-search" class="right-menu-item" />
|
<header-search id="header-search" class="right-menu-item" />
|
||||||
|
|
||||||
<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
|
|
||||||
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
|
|
||||||
</el-tooltip> -->
|
|
||||||
|
|
||||||
<!-- <el-tooltip content="文档地址" effect="dark" placement="bottom">
|
|
||||||
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
|
|
||||||
</el-tooltip> -->
|
|
||||||
|
|
||||||
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
||||||
|
|
||||||
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
-->
|
||||||
|
<!-- 登录人信息(姓名 + 角色) -->
|
||||||
|
<div class="user-info" v-if="userStore.nickName">
|
||||||
|
欢迎,{{ userStore.nickName }}({{ userStore.roleName }})
|
||||||
|
</div>
|
||||||
|
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
||||||
|
|
||||||
|
<!-- 原生个人信息/设置菜单(保留原有功能) -->
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
||||||
<div class="avatar-wrapper">
|
<div class="avatar-wrapper">
|
||||||
@ -45,10 +56,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ElMessageBox} from 'element-plus'
|
import {ElMessageBox} from 'element-plus'
|
||||||
|
// 引入原有组件
|
||||||
import Breadcrumb from '@/components/Breadcrumb'
|
import Breadcrumb from '@/components/Breadcrumb'
|
||||||
import TopNav from '@/components/TopNav'
|
import TopNav from '@/components/TopNav'
|
||||||
import Hamburger from '@/components/Hamburger'
|
import Hamburger from '@/components/Hamburger'
|
||||||
@ -57,14 +70,34 @@ import SizeSelect from '@/components/SizeSelect'
|
|||||||
import HeaderSearch from '@/components/HeaderSearch'
|
import HeaderSearch from '@/components/HeaderSearch'
|
||||||
import RuoYiGit from '@/components/RuoYi/Git'
|
import RuoYiGit from '@/components/RuoYi/Git'
|
||||||
import RuoYiDoc from '@/components/RuoYi/Doc'
|
import RuoYiDoc from '@/components/RuoYi/Doc'
|
||||||
|
// 引入Store
|
||||||
import useAppStore from '@/store/modules/app'
|
import useAppStore from '@/store/modules/app'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
|
// 引入图标和默认头像(可替换为你的默认头像)
|
||||||
|
import {CaretBottom} from '@element-plus/icons-vue'
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
|
// 处理角色名称(取第一个角色,多角色可自定义)
|
||||||
|
const roleName = computed(() => {
|
||||||
|
if (!userStore.roles || userStore.roles.length === 0) {
|
||||||
|
return '普通用户'
|
||||||
|
}
|
||||||
|
// 角色编码映射表:key 是你的 roles 编码,value 是要显示的中文
|
||||||
|
const roleMap = {
|
||||||
|
'ROLE_DEFAULT': '普通用户',
|
||||||
|
'admin': '管理员',
|
||||||
|
'ROLE_ADMIN': '超级管理员',
|
||||||
|
'ROLE_OPERATOR': '操作员' // 按你的实际角色编码添加
|
||||||
|
}
|
||||||
|
// 取第一个角色编码,映射为中文
|
||||||
|
return roleMap[userStore.roles[0]] || userStore.roles[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 原有方法保留
|
||||||
function toggleSideBar() {
|
function toggleSideBar() {
|
||||||
appStore.toggleSideBar()
|
appStore.toggleSideBar()
|
||||||
}
|
}
|
||||||
@ -91,10 +124,12 @@ function logout() {
|
|||||||
userStore.logOut().then(() => {
|
userStore.logOut().then(() => {
|
||||||
location.href = '/index';
|
location.href = '/index';
|
||||||
})
|
})
|
||||||
}).catch(() => { });
|
}).catch(() => {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits(['setLayout'])
|
const emits = defineEmits(['setLayout'])
|
||||||
|
|
||||||
function setLayout() {
|
function setLayout() {
|
||||||
emits('setLayout');
|
emits('setLayout');
|
||||||
}
|
}
|
||||||
@ -102,16 +137,20 @@ function setLayout() {
|
|||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
.navbar {
|
.navbar {
|
||||||
height: 50px;
|
height: 50px; /* 固定高度,与原有一致 */
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #fff;
|
//background: #fff;
|
||||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
background: #0d9488;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.15);
|
||||||
|
//box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||||
|
display: flex; /* 改为flex布局,确保子元素对齐 */
|
||||||
|
align-items: center; /* 垂直居中 */
|
||||||
|
|
||||||
.hamburger-container {
|
.hamburger-container {
|
||||||
line-height: 46px;
|
line-height: 50px; /* 与导航栏高度一致 */
|
||||||
height: 100%;
|
height: 100%;
|
||||||
float: left;
|
padding: 0 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.3s;
|
transition: background 0.3s;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
@ -121,32 +160,50 @@ function setLayout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-container {
|
/* 核心布局:产品名称 + 右侧内容容器 */
|
||||||
float: left;
|
.custom-header-content {
|
||||||
}
|
|
||||||
|
|
||||||
.topmenu-container {
|
|
||||||
position: absolute;
|
|
||||||
left: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.errLog-container {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-menu {
|
|
||||||
float: right;
|
|
||||||
height: 100%;
|
|
||||||
line-height: 50px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
&:focus {
|
justify-content: space-between;
|
||||||
outline: none;
|
flex: 1; /* 占满剩余宽度 */
|
||||||
|
padding: 0 20px;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 产品名称样式 */
|
||||||
|
.app-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff; /* 若依主题色,可替换为品牌色 */
|
||||||
|
white-space: nowrap; /* 防止换行 */
|
||||||
|
font-style: italic; /* 关键:字体倾斜 */
|
||||||
|
/* 可选优化:增加倾斜角度控制(默认 italic 约 14 度,可自定义) */
|
||||||
|
/* transform: skewX(-10deg); /* 向左倾斜 10 度,正负值控制倾斜方向 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧内容容器(功能按钮 + 用户信息 + 头像菜单) */
|
||||||
|
.right-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px; /* 元素间距,可调整 */
|
||||||
|
white-space: nowrap; /* 防止换行 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 用户信息样式 */
|
||||||
|
.user-info {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #e6f7f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 原有右侧菜单样式调整 */
|
||||||
.right-menu-item {
|
.right-menu-item {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@ -163,29 +220,34 @@ function setLayout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 头像容器样式调整 */
|
||||||
.avatar-container {
|
.avatar-container {
|
||||||
margin-right: 40px;
|
|
||||||
|
|
||||||
.avatar-wrapper {
|
.avatar-wrapper {
|
||||||
margin-top: 5px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px; /* 头像与下拉图标间距 */
|
||||||
|
|
||||||
.user-avatar {
|
.user-avatar {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 40px;
|
width: 36px; /* 调整头像大小 */
|
||||||
height: 40px;
|
height: 36px;
|
||||||
border-radius: 10px;
|
border-radius: 50%; /* 圆形头像,可改为10px保持原有风格 */
|
||||||
|
object-fit: cover; /* 防止头像变形 */
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
i {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
|
||||||
right: -20px;
|
|
||||||
top: 25px;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
color: #5a5e66;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 隐藏原有面包屑和顶部导航(已替换) */
|
||||||
|
.breadcrumb-container,
|
||||||
|
.topmenu-container {
|
||||||
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
191
src/layout/components/Navbarold.vue
Normal file
191
src/layout/components/Navbarold.vue
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
<template>
|
||||||
|
<div class="navbar">
|
||||||
|
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||||
|
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
|
||||||
|
<top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
|
||||||
|
|
||||||
|
<div class="right-menu">
|
||||||
|
<template v-if="appStore.device !== 'mobile'">
|
||||||
|
<header-search id="header-search" class="right-menu-item" />
|
||||||
|
|
||||||
|
<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
|
||||||
|
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
|
||||||
|
</el-tooltip> -->
|
||||||
|
|
||||||
|
<!-- <el-tooltip content="文档地址" effect="dark" placement="bottom">
|
||||||
|
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
|
||||||
|
</el-tooltip> -->
|
||||||
|
|
||||||
|
<screenfull id="screenfull" class="right-menu-item hover-effect" />
|
||||||
|
|
||||||
|
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||||
|
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<div class="avatar-container">
|
||||||
|
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
||||||
|
<div class="avatar-wrapper">
|
||||||
|
<img :src="userStore.avatar" class="user-avatar" />
|
||||||
|
<el-icon><caret-bottom /></el-icon>
|
||||||
|
</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<router-link to="/user/profile">
|
||||||
|
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||||
|
</router-link>
|
||||||
|
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
|
||||||
|
<span>布局设置</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided command="logout">
|
||||||
|
<span>退出登录</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
import Breadcrumb from '@/components/Breadcrumb'
|
||||||
|
import TopNav from '@/components/TopNav'
|
||||||
|
import Hamburger from '@/components/Hamburger'
|
||||||
|
import Screenfull from '@/components/Screenfull'
|
||||||
|
import SizeSelect from '@/components/SizeSelect'
|
||||||
|
import HeaderSearch from '@/components/HeaderSearch'
|
||||||
|
import RuoYiGit from '@/components/RuoYi/Git'
|
||||||
|
import RuoYiDoc from '@/components/RuoYi/Doc'
|
||||||
|
import useAppStore from '@/store/modules/app'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
|
function toggleSideBar() {
|
||||||
|
appStore.toggleSideBar()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCommand(command) {
|
||||||
|
switch (command) {
|
||||||
|
case "setLayout":
|
||||||
|
setLayout();
|
||||||
|
break;
|
||||||
|
case "logout":
|
||||||
|
logout();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
userStore.logOut().then(() => {
|
||||||
|
location.href = '/index';
|
||||||
|
})
|
||||||
|
}).catch(() => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
const emits = defineEmits(['setLayout'])
|
||||||
|
function setLayout() {
|
||||||
|
emits('setLayout');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.navbar {
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||||
|
|
||||||
|
.hamburger-container {
|
||||||
|
line-height: 46px;
|
||||||
|
height: 100%;
|
||||||
|
float: left;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.025);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-container {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topmenu-container {
|
||||||
|
position: absolute;
|
||||||
|
left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errLog-container {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-menu {
|
||||||
|
float: right;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 50px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-menu-item {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #5a5e66;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
|
||||||
|
&.hover-effect {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.025);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-container {
|
||||||
|
margin-right: 40px;
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
margin-top: 5px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: -20px;
|
||||||
|
top: 25px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -17,6 +17,8 @@ const useUserStore = defineStore(
|
|||||||
token: getToken(),
|
token: getToken(),
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
nickName: '',
|
||||||
|
roleName: '',
|
||||||
avatar: '',
|
avatar: '',
|
||||||
roles: [],
|
roles: [],
|
||||||
permissions: []
|
permissions: []
|
||||||
@ -46,8 +48,9 @@ const useUserStore = defineStore(
|
|||||||
const password = userInfo.password
|
const password = userInfo.password
|
||||||
const code = userInfo.code
|
const code = userInfo.code
|
||||||
const uuid = userInfo.uuid
|
const uuid = userInfo.uuid
|
||||||
|
const loginParam=userInfo.loginParam
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
login(username, password, code, uuid).then(res => {
|
login(username, password, code, uuid,loginParam).then(res => {
|
||||||
setToken(res.token)
|
setToken(res.token)
|
||||||
this.token = res.token
|
this.token = res.token
|
||||||
resolve()
|
resolve()
|
||||||
@ -61,6 +64,10 @@ const useUserStore = defineStore(
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getInfo().then(res => {
|
getInfo().then(res => {
|
||||||
const user = res.user
|
const user = res.user
|
||||||
|
const rolelist = user.roles
|
||||||
|
if (rolelist && rolelist.length > 0) {
|
||||||
|
this.roleName=rolelist[0].roleName
|
||||||
|
}
|
||||||
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
|
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
|
||||||
|
|
||||||
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||||
@ -71,6 +78,7 @@ const useUserStore = defineStore(
|
|||||||
}
|
}
|
||||||
this.id = user.userId
|
this.id = user.userId
|
||||||
this.name = user.userName
|
this.name = user.userName
|
||||||
|
this.nickName= user.nickName
|
||||||
this.avatar = avatar
|
this.avatar = avatar
|
||||||
resolve(res)
|
resolve(res)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
|||||||
@ -791,6 +791,7 @@ const shHandleCheck = () => {
|
|||||||
const unconfirmlogRef = ref();
|
const unconfirmlogRef = ref();
|
||||||
const unShHandle = () => {
|
const unShHandle = () => {
|
||||||
uncheck2({ ...tableKey.value, yhdh: props.labPat.yhdh }).then(res => {
|
uncheck2({ ...tableKey.value, yhdh: props.labPat.yhdh }).then(res => {
|
||||||
|
console.log('res',res);
|
||||||
if (res.code == "4") {
|
if (res.code == "4") {
|
||||||
tableKey.value.problemId = res.problemId;
|
tableKey.value.problemId = res.problemId;
|
||||||
reasonText.value = res.msg;
|
reasonText.value = res.msg;
|
||||||
|
|||||||
@ -1,36 +1,81 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login">
|
<div class="login">
|
||||||
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
<el-form ref="loginRef" class="login-form">
|
||||||
<h3 class="title">实验室管理系统</h3>
|
<div class="login-header">
|
||||||
<el-form-item prop="username">
|
<div class="login-logo">
|
||||||
|
<img src="@/assets/images/logo_new.png" alt="系统Logo" />
|
||||||
|
</div>
|
||||||
|
<div class="login-split"></div>
|
||||||
|
<div class="login-title">
|
||||||
|
<h1>{{ oem.appname}}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab切换容器 -->
|
||||||
|
<el-tabs v-model="activeName" type="card" @tab-click="handleClick" class="login-tabs">
|
||||||
|
<!-- 工号登录 Tab -->
|
||||||
|
<el-tab-pane label="账号密码登录" name="first">
|
||||||
|
<el-form-item label="医疗机构:" prop="loginYLJG"class="custom-select">
|
||||||
|
<el-select
|
||||||
|
v-model="loginForm.loginParam.loginYLJG"
|
||||||
|
placeholder="请选择医疗机构"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<svg-icon icon-class="international" class="el-input__icon input-icon" />
|
||||||
|
</template>
|
||||||
|
<el-option
|
||||||
|
v-for="item in selectOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="用户代号:" prop="username">
|
||||||
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
|
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
|
||||||
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
|
<template #prefix>
|
||||||
|
<svg-icon icon-class="user" class="el-input__icon input-icon" />
|
||||||
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="password">
|
|
||||||
|
<el-form-item label="登录密码:" prop="password">
|
||||||
<el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码"
|
<el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码"
|
||||||
@keyup.enter="handleLogin">
|
@keyup.enter="handleLogin">
|
||||||
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
|
<template #prefix>
|
||||||
|
<svg-icon icon-class="password" class="el-input__icon input-icon" />
|
||||||
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item prop="code" v-if="captchaEnabled">
|
|
||||||
|
<!-- 修复:移除空的el-form-item,验证码关闭时不渲染任何内容 -->
|
||||||
|
<el-form-item prop="code" v-if="!captchaEnabled" size="large">
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="code" label="验 证 码 :" v-if="captchaEnabled">
|
||||||
<el-input
|
<el-input
|
||||||
|
|
||||||
v-model="loginForm.code"
|
v-model="loginForm.code"
|
||||||
size="large"
|
size="large"
|
||||||
auto-complete="off"
|
auto-complete="off"
|
||||||
placeholder="验证码"
|
placeholder="验证码"
|
||||||
style="width: 63%"
|
style="width: 53%"
|
||||||
@keyup.enter="handleLogin"
|
@keyup.enter="handleLogin"
|
||||||
>
|
>
|
||||||
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
|
<template #prefix>
|
||||||
|
<svg-icon icon-class="validCode" class="el-input__icon input-icon" />
|
||||||
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<div class="login-code">
|
<div class="login-code">
|
||||||
<img :src="codeUrl" @click="getCode" class="login-code-img" />
|
<img :src="codeUrl" @click="getCode" class="login-code-img" />
|
||||||
</div>
|
</div>
|
||||||
</el-form-item> -->
|
</el-form-item>
|
||||||
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
|
|
||||||
<el-form-item style="width: 100%;">
|
<el-form-item style="width: 100%;">
|
||||||
<el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleLogin">
|
<el-button :loading="loading" size="large" type="primary" class="login-btn" style="width: 100%;" @click.prevent="handleLogin">
|
||||||
<span v-if="!loading">登 录</span>
|
<span v-if="!loading">登 录</span>
|
||||||
<span v-else>登 录 中...</span>
|
<span v-else>登 录 中...</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -38,66 +83,91 @@
|
|||||||
<router-link class="link-type" :to="'/register'">立即注册</router-link>
|
<router-link class="link-type" :to="'/register'">立即注册</router-link>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="扫码登录" name="second">
|
||||||
|
<div class="tab-placeholder">
|
||||||
|
<el-icon class="placeholder-icon"><Sms /></el-icon>
|
||||||
|
<p>正在加载二维码...</p>
|
||||||
|
<el-button type="success" icon="wechat" class="wechat-login-btn">刷新二维码</el-button>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="UKEY登录" name="third">
|
||||||
|
<div class="tab-placeholder">
|
||||||
|
<el-icon class="placeholder-icon"><Wechat /></el-icon>
|
||||||
|
<p>UKEY登录功能开发中...</p>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</el-form>
|
</el-form>
|
||||||
<!-- 底部 -->
|
|
||||||
<div class="el-login-footer">
|
<div class="el-login-footer">
|
||||||
<!-- <span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span> -->
|
<span>版权所有(R)2022-2026 {{ oem.compay}} 电话:{{oem.phone}} E-mail:{{oem.Email}} version:{{oem.version}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getCodeImg } from "@/api/login";
|
import { ref, watch, getCurrentInstance, onMounted } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { useFingerprint } from '@/utils/useFingerprint';
|
||||||
|
import { getCodeImg, getInfo, getLocalconfig } from "@/api/login";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { encrypt, decrypt } from "@/utils/jsencrypt";
|
import { encrypt, decrypt } from "@/utils/jsencrypt";
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
const userStore = useUserStore()
|
// 初始化响应式变量
|
||||||
|
const activeName = ref('first');
|
||||||
|
const userStore = useUserStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { proxy } = getCurrentInstance();
|
const oem = ref({
|
||||||
|
appname: "实验室信息管理系统",
|
||||||
|
phone: "",
|
||||||
|
compay: "",
|
||||||
|
version: "",
|
||||||
|
Email: ""
|
||||||
|
});
|
||||||
|
const loginRef = ref(null); // 修复:直接用ref获取表单引用
|
||||||
const loginForm = ref({
|
const loginForm = ref({
|
||||||
username: "admin",
|
username: "admin",
|
||||||
password: "", //admin123
|
password: "",
|
||||||
rememberMe: false,
|
rememberMe: false,
|
||||||
code: "",
|
code: "",
|
||||||
uuid: ""
|
uuid: "",
|
||||||
|
loginParam:{loginYLJG:"",localid:""}
|
||||||
});
|
});
|
||||||
|
const selectOptions = ref([{ id: '1', name: '总院' }]);
|
||||||
const loginRules = {
|
const loginRules = ref({ // 修复:改为ref响应式,方便动态修改
|
||||||
username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
|
username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
|
||||||
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
|
// password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
|
||||||
|
hospid: [{ required: true, message: '请选择医疗机构', trigger: 'change' }],
|
||||||
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
||||||
};
|
});
|
||||||
|
|
||||||
const codeUrl = ref("");
|
const codeUrl = ref("");
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
// 验证码开关
|
|
||||||
const captchaEnabled = ref(true);
|
const captchaEnabled = ref(true);
|
||||||
// 注册开关
|
|
||||||
const register = ref(false);
|
const register = ref(false);
|
||||||
const redirect = ref(undefined);
|
const redirect = ref(undefined);
|
||||||
|
|
||||||
watch(route, (newRoute) => {
|
// 路由监听(优化版)
|
||||||
redirect.value = newRoute.query && newRoute.query.redirect;
|
watch(
|
||||||
}, { immediate: true });
|
() => route.query.redirect,
|
||||||
|
(newRedirect) => {
|
||||||
function handleLogin() {
|
if (redirect.value !== newRedirect) {
|
||||||
proxy.$refs.loginRef.validate(valid => {
|
redirect.value = newRedirect || '';
|
||||||
if (valid) {
|
|
||||||
loading.value = true;
|
|
||||||
// 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
|
|
||||||
if (loginForm.value.rememberMe) {
|
|
||||||
Cookies.set("username", loginForm.value.username, { expires: 30 });
|
|
||||||
Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
|
|
||||||
Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
|
|
||||||
} else {
|
|
||||||
// 否则移除
|
|
||||||
Cookies.remove("username");
|
|
||||||
Cookies.remove("password");
|
|
||||||
Cookies.remove("rememberMe");
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 登录方法(修复proxy.$refs问题)
|
||||||
|
const handleLogin = async () => {
|
||||||
|
loginForm.value.loginParam.loginYLJG
|
||||||
|
loading.value = true;
|
||||||
|
Cookies.set("czlis_username", loginForm.value.username, { expires: 30 });
|
||||||
|
Cookies.set("czlis_loginYLJG", loginForm.value.loginParam.loginYLJG, {expires: 30});
|
||||||
// 调用action的登录方法
|
// 调用action的登录方法
|
||||||
userStore.login(loginForm.value).then(() => {
|
userStore.login(loginForm.value).then(() => {
|
||||||
const query = route.query;
|
const query = route.query;
|
||||||
@ -107,7 +177,10 @@ function handleLogin() {
|
|||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
router.push({ path: redirect.value || "/", query: otherQueryParams });
|
const targetPath = redirect.value || "/";
|
||||||
|
if (route.path !== targetPath) {
|
||||||
|
router.push({ path: targetPath, query: otherQueryParams });
|
||||||
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
// 重新获取验证码
|
// 重新获取验证码
|
||||||
@ -115,33 +188,90 @@ function handleLogin() {
|
|||||||
getCode();
|
getCode();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCode() {
|
// 获取验证码
|
||||||
|
const getCode = () => {
|
||||||
getCodeImg().then(res => {
|
getCodeImg().then(res => {
|
||||||
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
|
captchaEnabled.value = res.captchaEnabled ?? true;
|
||||||
if (captchaEnabled.value) {
|
if (captchaEnabled.value) {
|
||||||
codeUrl.value = "data:image/gif;base64," + res.img;
|
codeUrl.value = "data:image/gif;base64," + res.img;
|
||||||
loginForm.value.uuid = res.uuid;
|
loginForm.value.uuid = res.uuid;
|
||||||
}
|
}
|
||||||
|
// 验证码关闭时移除校验规则,避免报错
|
||||||
|
if (!captchaEnabled.value) {
|
||||||
|
loginRules.value.code = [];
|
||||||
|
} else {
|
||||||
|
loginRules.value.code = [{required: true, trigger: "change", message: "请输入验证码"}];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function getCookie() {
|
|
||||||
const username = Cookies.get("username");
|
|
||||||
const password = Cookies.get("password");
|
|
||||||
const rememberMe = Cookies.get("rememberMe");
|
|
||||||
loginForm.value = {
|
|
||||||
username: username === undefined ? loginForm.value.username : username,
|
|
||||||
password: password === undefined ? loginForm.value.password : decrypt(password),
|
|
||||||
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
getCode();
|
// 从Cookie获取账号密码
|
||||||
|
const getCookie = () => {
|
||||||
|
const cookieUsername = Cookies.get("czlis_username");
|
||||||
|
const cookieloginYLJG = Cookies.get("czlis_loginYLJG");
|
||||||
|
//console.log('cookieloginYLJG', cookieloginYLJG);
|
||||||
|
if (cookieUsername !== undefined && cookieUsername !== null&& cookieUsername !=="") {
|
||||||
|
loginForm.value.username = cookieUsername;
|
||||||
|
}
|
||||||
|
if (cookieloginYLJG !== undefined && cookieloginYLJG !== null&& cookieloginYLJG !=="") {
|
||||||
|
loginForm.value.loginParam.loginYLJG = cookieloginYLJG;
|
||||||
|
}
|
||||||
|
//console.log('loginForm', loginForm);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取本地配置
|
||||||
|
const Localconfig = (localid) => {
|
||||||
|
getLocalconfig({localid}).then(res => {
|
||||||
|
if (res) {
|
||||||
|
const {localconfig, hosplist, oem: oemData} = res;
|
||||||
|
// 修复:赋值oem数据,显示动态标题
|
||||||
|
if (oemData) oem.value = oemData;
|
||||||
|
// console.log('oemData', oemData);
|
||||||
|
// console.log('oem', oem.value);
|
||||||
|
// 修复:判重后更新下拉选项,避免递归
|
||||||
|
if (JSON.stringify(selectOptions.value) !== JSON.stringify(hosplist)) {
|
||||||
|
selectOptions.value = hosplist || [{id: '1', name: '总院'}];
|
||||||
|
// 判重后赋值,避免重复修改触发更新
|
||||||
|
if (!selectOptions.value.some(item => item.id === loginForm.value.hospid)) {
|
||||||
|
loginForm.value.loginParam.loginYLJG = selectOptions.value[0]?.id || '1';
|
||||||
|
}
|
||||||
getCookie();
|
getCookie();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error("获取本地配置失败:", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取指纹并加载配置
|
||||||
|
const {getFingerprint} = useFingerprint();
|
||||||
|
const refreshFingerprint = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getFingerprint();
|
||||||
|
loginForm.value.loginParam.localid=res.visitorId
|
||||||
|
Localconfig(res.visitorId);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("获取指纹失败:", err);
|
||||||
|
Localconfig("default"); // 兜底使用默认ID
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tab切换事件(移除activeName手动赋值,避免递归)
|
||||||
|
const handleClick = (tab) => {
|
||||||
|
console.log('当前Tab:', tab.name);
|
||||||
|
// 仅在切回账号密码登录时刷新验证码
|
||||||
|
if (tab.name === 'first' && captchaEnabled.value) {
|
||||||
|
getCode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 页面挂载时初始化
|
||||||
|
onMounted(() => {
|
||||||
|
refreshFingerprint();
|
||||||
|
getCode();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
@ -149,22 +279,20 @@ getCookie();
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
background-image: url("../assets/images/login-background.jpg");
|
background-image: url("../assets/images/login-background.jpg");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
|
||||||
margin: 0px auto 30px auto;
|
|
||||||
text-align: center;
|
|
||||||
color: #707070;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form {
|
.login-form {
|
||||||
border-radius: 6px;
|
background: rgba(255, 255, 255, 0.4);
|
||||||
background: #ffffff;
|
backdrop-filter: blur(10px);
|
||||||
width: 400px;
|
-webkit-backdrop-filter: blur(10px);
|
||||||
padding: 25px 25px 5px 25px;
|
border: 1px solid rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 480px;
|
||||||
|
padding: 35px 35px 5px 35px;
|
||||||
|
|
||||||
.el-input {
|
.el-input {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@ -181,12 +309,6 @@ getCookie();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-tip {
|
|
||||||
font-size: 13px;
|
|
||||||
text-align: center;
|
|
||||||
color: #bfbfbf;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-code {
|
.login-code {
|
||||||
width: 33%;
|
width: 33%;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@ -195,6 +317,9 @@ getCookie();
|
|||||||
img {
|
img {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,8 +336,87 @@ getCookie();
|
|||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-code-img {
|
/* Logo和标题样式 */
|
||||||
|
.login-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-logo img {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding-left: 12px;
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-split {
|
||||||
|
width: 1px;
|
||||||
|
height: 30px;
|
||||||
|
background-color: #ccc;
|
||||||
|
margin: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-title h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #1989fa;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab容器样式(修复冲突) */
|
||||||
|
.login-tabs {
|
||||||
|
width: 100%;
|
||||||
|
height: 350px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 8px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 1px 10px 10px;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
:deep(.el-tabs__header) {
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-prev),
|
||||||
|
:deep(.el-tabs__nav-next) {
|
||||||
|
display: none !important; // 隐藏Tab左右箭头
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-wrap) {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select {
|
||||||
|
margin-top: 20px; /* 可根据需求调整数值 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab占位样式 */
|
||||||
|
.tab-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: #666;
|
||||||
|
height: calc(100% - 40px); // 适配Tab容器高度
|
||||||
|
|
||||||
|
.placeholder-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wechat-login-btn {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 隐藏必填项星号 */
|
||||||
|
:deep(.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap .el-form-item__asterisk) {
|
||||||
|
display: none !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
448
src/views/logincopy.vue
Normal file
448
src/views/logincopy.vue
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
<template>
|
||||||
|
<div class="login">
|
||||||
|
<el-form ref="loginRef" class="login-form">
|
||||||
|
<div class="login-header">
|
||||||
|
<div class="login-logo">
|
||||||
|
<img src="@/assets/images/logo_new.png" alt="系统Logo" />
|
||||||
|
</div>
|
||||||
|
<div class="login-split"></div>
|
||||||
|
<div class="login-title">
|
||||||
|
<h1>{{ oem.appname}}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab切换容器 -->
|
||||||
|
<el-tabs v-model="activeName" type="card" @tab-click="handleClick" class="login-tabs">
|
||||||
|
<!-- 工号登录 Tab -->
|
||||||
|
<el-tab-pane label="账号密码登录" name="first">
|
||||||
|
<el-form-item label="医疗机构:" prop="hospid"class="custom-select">
|
||||||
|
<el-select
|
||||||
|
v-model="loginForm.hospid"
|
||||||
|
placeholder="请选择医疗机构"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<svg-icon icon-class="international" class="el-input__icon input-icon" />
|
||||||
|
</template>
|
||||||
|
<el-option
|
||||||
|
v-for="item in selectOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="用户代号:" prop="username">
|
||||||
|
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
|
||||||
|
<template #prefix>
|
||||||
|
<svg-icon icon-class="user" class="el-input__icon input-icon" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="登录密码:" prop="password">
|
||||||
|
<el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码"
|
||||||
|
@keyup.enter="handleLogin">
|
||||||
|
<template #prefix>
|
||||||
|
<svg-icon icon-class="password" class="el-input__icon input-icon" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 修复:移除空的el-form-item,验证码关闭时不渲染任何内容 -->
|
||||||
|
<el-form-item prop="code" v-if="!captchaEnabled" size="large">
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="code" label="验 证 码 :" v-if="captchaEnabled">
|
||||||
|
<el-input
|
||||||
|
|
||||||
|
v-model="loginForm.code"
|
||||||
|
size="large"
|
||||||
|
auto-complete="off"
|
||||||
|
placeholder="验证码"
|
||||||
|
style="width: 53%"
|
||||||
|
@keyup.enter="handleLogin"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<svg-icon icon-class="validCode" class="el-input__icon input-icon" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<div class="login-code">
|
||||||
|
<img :src="codeUrl" @click="getCode" class="login-code-img" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item style="width: 100%;">
|
||||||
|
<el-button :loading="loading" size="large" type="primary" class="login-btn" style="width: 100%;" @click.prevent="handleLogin">
|
||||||
|
<span v-if="!loading">登 录</span>
|
||||||
|
<span v-else>登 录 中...</span>
|
||||||
|
</el-button>
|
||||||
|
<div style="float: right;" v-if="register">
|
||||||
|
<router-link class="link-type" :to="'/register'">立即注册</router-link>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="扫码登录" name="second">
|
||||||
|
<div class="tab-placeholder">
|
||||||
|
<el-icon class="placeholder-icon"><Sms /></el-icon>
|
||||||
|
<p>正在加载二维码...</p>
|
||||||
|
<el-button type="success" icon="wechat" class="wechat-login-btn">刷新二维码</el-button>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="UKEY登录" name="third">
|
||||||
|
<div class="tab-placeholder">
|
||||||
|
<el-icon class="placeholder-icon"><Wechat /></el-icon>
|
||||||
|
<p>UKEY登录功能开发中...</p>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="el-login-footer">
|
||||||
|
<span>版权所有(R)2022-2026 {{ oem.compay}} 电话:{{oem.phone}} E-mail:{{oem.Email}} version:{{oem.version}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, getCurrentInstance, onMounted } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { useFingerprint } from '@/utils/useFingerprint';
|
||||||
|
import { getCodeImg, getInfo, getLocalconfig } from "@/api/login";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { encrypt, decrypt } from "@/utils/jsencrypt";
|
||||||
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
|
// 初始化响应式变量
|
||||||
|
const activeName = ref('first');
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const oem = ref({
|
||||||
|
appname: "实验室信息管理系统",
|
||||||
|
phone: "",
|
||||||
|
compay: "",
|
||||||
|
version: "",
|
||||||
|
Email: ""
|
||||||
|
});
|
||||||
|
const loginRef = ref(null); // 修复:直接用ref获取表单引用
|
||||||
|
const loginForm = ref({
|
||||||
|
username: "admin",
|
||||||
|
password: "",
|
||||||
|
rememberMe: false,
|
||||||
|
code: "",
|
||||||
|
uuid: "",
|
||||||
|
hospid: "1"
|
||||||
|
});
|
||||||
|
const selectOptions = ref([{ id: '1', name: '总院' }]);
|
||||||
|
const loginRules = ref({ // 修复:改为ref响应式,方便动态修改
|
||||||
|
username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
|
||||||
|
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
|
||||||
|
hospid: [{ required: true, message: '请选择医疗机构', trigger: 'change' }],
|
||||||
|
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
||||||
|
});
|
||||||
|
const codeUrl = ref("");
|
||||||
|
const loading = ref(false);
|
||||||
|
const captchaEnabled = ref(true);
|
||||||
|
const register = ref(false);
|
||||||
|
const redirect = ref(undefined);
|
||||||
|
|
||||||
|
// 路由监听(优化版)
|
||||||
|
watch(
|
||||||
|
() => route.query.redirect,
|
||||||
|
(newRedirect) => {
|
||||||
|
if (redirect.value !== newRedirect) {
|
||||||
|
redirect.value = newRedirect || '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 登录方法(修复proxy.$refs问题)
|
||||||
|
const handleLogin = async () => {
|
||||||
|
console.log("sss",loginForm.value)
|
||||||
|
try {
|
||||||
|
const valid = await loginRef.value.validate(); // 直接用ref.validate()
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true;
|
||||||
|
// 记住密码逻辑
|
||||||
|
if (loginForm.value.rememberMe) {
|
||||||
|
Cookies.set("username", loginForm.value.username, { expires: 30 });
|
||||||
|
Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
|
||||||
|
Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
|
||||||
|
} else {
|
||||||
|
Cookies.remove("username");
|
||||||
|
Cookies.remove("password");
|
||||||
|
Cookies.remove("rememberMe");
|
||||||
|
}
|
||||||
|
// 调用action的登录方法
|
||||||
|
userStore.login(loginForm.value).then(() => {
|
||||||
|
const query = route.query;
|
||||||
|
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
|
||||||
|
if (cur !== "redirect") {
|
||||||
|
acc[cur] = query[cur];
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
if (route.path !== targetPath) {
|
||||||
|
router.push({path: redirect.value || "/", query: otherQueryParams});
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
// 重新获取验证码
|
||||||
|
if (captchaEnabled.value) {
|
||||||
|
getCode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
// 调用登录接口
|
||||||
|
await userStore.login(loginForm.value);
|
||||||
|
// 路由跳转(防重复跳转)
|
||||||
|
const query = route.query;
|
||||||
|
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
|
||||||
|
if (cur !== "redirect") acc[cur] = query[cur];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
const targetPath = redirect.value || "/";
|
||||||
|
if (route.path !== targetPath) {
|
||||||
|
router.push({ path: targetPath, query: otherQueryParams });
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("登录验证失败:", err);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
if (captchaEnabled.value) getCode(); // 失败后刷新验证码
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取验证码
|
||||||
|
const getCode = () => {
|
||||||
|
getCodeImg().then(res => {
|
||||||
|
captchaEnabled.value = res.captchaEnabled ?? true;
|
||||||
|
if (captchaEnabled.value) {
|
||||||
|
codeUrl.value = "data:image/gif;base64," + res.img;
|
||||||
|
loginForm.value.uuid = res.uuid;
|
||||||
|
}
|
||||||
|
// 验证码关闭时移除校验规则,避免报错
|
||||||
|
if (!captchaEnabled.value) {
|
||||||
|
loginRules.value.code = [];
|
||||||
|
} else {
|
||||||
|
loginRules.value.code = [{ required: true, trigger: "change", message: "请输入验证码" }];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 从Cookie获取账号密码
|
||||||
|
const getCookie = () => {
|
||||||
|
const username = Cookies.get("username");
|
||||||
|
const password = Cookies.get("password");
|
||||||
|
const rememberMe = Cookies.get("rememberMe");
|
||||||
|
loginForm.value = {
|
||||||
|
...loginForm.value,
|
||||||
|
username: username ?? loginForm.value.username,
|
||||||
|
password: password ? decrypt(password) : loginForm.value.password,
|
||||||
|
rememberMe: rememberMe ? Boolean(rememberMe) : false
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取本地配置
|
||||||
|
const Localconfig = (localid) => {
|
||||||
|
getLocalconfig({ localid }).then(res => {
|
||||||
|
if (res) {
|
||||||
|
const { localconfig, hosplist, oem: oemData } = res;
|
||||||
|
console.log('本地配置:', localconfig, '医院列表:', hosplist);
|
||||||
|
// 修复:赋值oem数据,显示动态标题
|
||||||
|
if (oemData) oem.value = oemData;
|
||||||
|
// 修复:判重后更新下拉选项,避免递归
|
||||||
|
if (JSON.stringify(selectOptions.value) !== JSON.stringify(hosplist)) {
|
||||||
|
selectOptions.value = hosplist || [{ id: '1', name: '总院' }];
|
||||||
|
// 判重后赋值,避免重复修改触发更新
|
||||||
|
if (!selectOptions.value.some(item => item.id === loginForm.value.hospid)) {
|
||||||
|
loginForm.value.hospid = selectOptions.value[0]?.id || '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error("获取本地配置失败:", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取指纹并加载配置
|
||||||
|
const { getFingerprint } = useFingerprint();
|
||||||
|
const refreshFingerprint = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getFingerprint();
|
||||||
|
Localconfig(res.visitorId);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("获取指纹失败:", err);
|
||||||
|
Localconfig("default"); // 兜底使用默认ID
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tab切换事件(移除activeName手动赋值,避免递归)
|
||||||
|
const handleClick = (tab) => {
|
||||||
|
console.log('当前Tab:', tab.name);
|
||||||
|
// 仅在切回账号密码登录时刷新验证码
|
||||||
|
if (tab.name === 'first' && captchaEnabled.value) {
|
||||||
|
getCode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 页面挂载时初始化
|
||||||
|
onMounted(() => {
|
||||||
|
refreshFingerprint();
|
||||||
|
getCode();
|
||||||
|
getCookie();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.login {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
background-image: url("../assets/images/login-background.jpg");
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 480px;
|
||||||
|
padding: 35px 35px 5px 35px;
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
height: 40px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-icon {
|
||||||
|
height: 39px;
|
||||||
|
width: 14px;
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-code {
|
||||||
|
width: 33%;
|
||||||
|
height: 40px;
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
img {
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-login-footer {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial;
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logo和标题样式 */
|
||||||
|
.login-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-logo img {
|
||||||
|
height: 40px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-split {
|
||||||
|
width: 1px;
|
||||||
|
height: 30px;
|
||||||
|
background-color: #ccc;
|
||||||
|
margin: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-title h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #1989fa;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab容器样式(修复冲突) */
|
||||||
|
.login-tabs {
|
||||||
|
width: 100%;
|
||||||
|
height: 350px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 8px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 1px 10px 10px;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
:deep(.el-tabs__header) {
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-prev),
|
||||||
|
:deep(.el-tabs__nav-next) {
|
||||||
|
display: none !important; // 隐藏Tab左右箭头
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-wrap) {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.custom-select {
|
||||||
|
margin-top: 20px; /* 可根据需求调整数值 */
|
||||||
|
}
|
||||||
|
/* Tab占位样式 */
|
||||||
|
.tab-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: #666;
|
||||||
|
height: calc(100% - 40px); // 适配Tab容器高度
|
||||||
|
|
||||||
|
.placeholder-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wechat-login-btn {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 隐藏必填项星号 */
|
||||||
|
:deep(.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap .el-form-item__asterisk) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user