429 lines
13 KiB
Vue
429 lines
13 KiB
Vue
<template>
|
||
<div class="login">
|
||
<!-- <FloatingLines :enabled-waves="['top', 'middle', 'bottom']" :line-count="[10, 15, 20]" :line-distance="[8, 6, 4]"
|
||
:bend-radius="5.0" :bend-strength="-0.5" :interactive="true" :parallax="true" /> -->
|
||
<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="loginYLJG" class="custom-select">
|
||
<el-select v-model="loginForm.loginParam.loginYLJG" placeholder="请选择医疗机构" style="width: 100%"
|
||
@change="handleChange">
|
||
<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.toString()"></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';
|
||
|
||
import { useCommonStore } from '@/store/modules/commonStore'
|
||
const mbStore = useCommonStore();
|
||
// 初始化响应式变量
|
||
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: "",
|
||
loginParam: { loginYLJG: undefined, localid: undefined, loginYLJGName: undefined }
|
||
});
|
||
const selectOptions = ref([]);
|
||
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 }
|
||
);
|
||
|
||
const handleChange = (value) => {
|
||
const item = selectOptions.value.find(item => item.id == value);
|
||
loginForm.value.loginParam.loginYLJG = value;
|
||
loginForm.value.loginParam.loginYLJGName = item?.name || '';
|
||
}
|
||
|
||
// 登录方法(修复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 });
|
||
Cookies.set("czlis_loginYLJGName", loginForm.value.loginParam.loginYLJGName, { expires: 30 });
|
||
// 调用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;
|
||
}, {});
|
||
const targetPath = redirect.value || "/";
|
||
if (route.path !== targetPath) {
|
||
router.push({ path: targetPath, query: otherQueryParams });
|
||
}
|
||
}).catch(() => {
|
||
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 cookieUsername = Cookies.get("czlis_username");
|
||
const cookieloginYLJG = Cookies.get("czlis_loginYLJG");
|
||
if (cookieUsername !== undefined && cookieUsername !== null && cookieUsername !== "") {
|
||
loginForm.value.username = cookieUsername;
|
||
}
|
||
if (cookieloginYLJG !== undefined && cookieloginYLJG !== null && cookieloginYLJG !== "") {
|
||
loginForm.value.loginParam.loginYLJG = cookieloginYLJG;
|
||
}
|
||
const cookieloginYLJGName = Cookies.get("czlis_loginYLJGName");
|
||
if (cookieloginYLJGName !== undefined && cookieloginYLJGName !== null && cookieloginYLJGName !== "") {
|
||
loginForm.value.loginParam.loginYLJGName = cookieloginYLJGName;
|
||
}
|
||
};
|
||
|
||
// 获取本地配置
|
||
const Localconfig = (localid) => {
|
||
getLocalconfig({ localid }).then(res => {
|
||
if (res) {
|
||
const { localconfig, hosplist, oem: oemData } = res;
|
||
mbStore.setDefaultConfig(localconfig)
|
||
if (oemData) oem.value = oemData;
|
||
|
||
if (JSON.stringify(selectOptions.value) !== JSON.stringify(hosplist)) {
|
||
selectOptions.value = hosplist || [];
|
||
// 判重后赋值,避免重复修改触发更新
|
||
if (!selectOptions.value.some(item => item.id === loginForm.value.hospid)) {
|
||
loginForm.value.loginParam.loginYLJG = selectOptions.value[0]?.id || 1;
|
||
loginForm.value.loginParam.loginYLJGName = selectOptions.value[0]?.name || '';
|
||
}
|
||
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>
|
||
|
||
<style lang='scss' scoped>
|
||
.login {
|
||
position: relative;
|
||
height: 100vh;
|
||
background-image: url("../assets/images/login-background.jpg");
|
||
background-size: cover;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.login-form {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
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> |