Files
his/openhis-ui-vue3/src/views/login.vue

648 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="login">
<!-- 顶部 -->
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<div class="el-login-top">
<el-image :src="logoNew"></el-image>
</div>
<h1 class="title">{{ currentTenantName || settings.systemName }}信息管理系统</h1>
<p class="login-subtitle">请使用您的账号密码安全登录系统</p>
<el-form-item prop="username">
<p class="label">用户名</p>
<el-input
v-model="loginForm.username"
type="text"
size="large"
auto-complete="off"
placeholder="账号"
@input="handleUsernameChange"
>
<template #prefix
><svg-icon icon-class="user" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<p class="label">密码</p>
<el-input
v-model="loginForm.password"
:type="passwordVisible ? 'text' : 'password'"
size="large"
auto-complete="off"
placeholder="密码"
@keyup.enter="handleLogin"
>
<template #prefix
><svg-icon icon-class="password" class="el-input__icon input-icon"
/></template>
<template #suffix>
<span
class="password-toggle"
@click="togglePasswordVisibility"
style="cursor: pointer; color: #606266; font-size: 14px; padding: 0 10px;"
>
{{ passwordVisible ? '隐藏' : '显示' }}
</span>
</template>
</el-input>
</el-form-item>
<el-form-item prop="tenantId">
<p class="label">医疗机构</p>
<el-select
v-model="loginForm.tenantId"
size="large"
placeholder="请选择医疗机构"
clearable
filterable
>
<el-option
v-for="item in tenantOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item class="login-options">
<el-checkbox v-model="loginForm.rememberMe" class="remember-me">记住我</el-checkbox>
<el-link type="primary" class="forgot-password" @click="handleForgotPassword">忘记密码</el-link>
</el-form-item>
<!-- <el-form-item prop="tenantId"> -->
<!-- <span class="descriptions-item-label" style="margin: 0 10px 0 0">连接医保</span> -->
<!-- <el-switch v-model="loginForm.invokeYb" @change="topNavChange" size="large"/> -->
<!-- </el-form-item> -->
<!--<el-form-item prop="code" v-if="captchaEnabled">
<el-input
v-model="loginForm.code"
size="large"
auto-complete="off"
placeholder="验证码"
style="width: 63%"
@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-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>-->
<el-form-item style="width: 100%">
<el-button
:loading="loading"
size="large"
type="primary"
style="width: 100%"
@click.prevent="handleLogin"
>
<span v-if="!loading"> </span>
<span v-else-if="!signIng"> 中...</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>
<div class="footer">
© 2025 {{ currentTenantName || settings.systemName }} | 版本 v2.5.1
<!-- 公司版权信息新增 -->
<div class="company-copyright">
技术支持上海经创贺联信息技术有限公司
</div>
</div>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<div class="el-login-footer-link">
<span><el-link :underline="false">his账号用户协议</el-link></span>
<span>|</span>
<span>
<el-link :underline="false">关于his账号与隐私的声明</el-link>
</span>
<span>|</span>
<span><el-link :underline="false">常见问题</el-link></span>
<span>|</span>
<span><el-link :underline="false">Cookies</el-link></span>
</div>
<span>
<el-link :underline="false">Copyright © 2018-2025 his.vip All Rights Reserved. </el-link>
</span>
</div>
</div>
</template>
<script setup>
import { getCurrentInstance, ref, reactive, computed, onMounted, watch } from 'vue';
import settings from '@/settings';
import { getCodeImg, sign, getUserBindTenantList } from '@/api/login';
import { invokeYbPlugin } from '@/api/public';
import Cookies from 'js-cookie';
import { encrypt, decrypt } from '@/utils/jsencrypt';
import useUserStore from '@/store/modules/user';
import { ElMessage } from 'element-plus';
import logoNew from '@/assets/logo/LOGO.jpg';
const userStore = useUserStore();
const route = useRoute();
const router = useRouter();
const { proxy } = getCurrentInstance();
const env = import.meta.env.MODE;
const loginForm = ref({
username: '',
password: '',
rememberMe: false,
code: '',
uuid: '',
tenantId: '',
});
const tenantOptions = ref([]);
const currentTenantName = ref('');
const loginRules = {
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
code: [{ required: true, trigger: 'change', message: '请输入验证码' }],
};
const codeUrl = ref('');
const loading = ref(false);
const signIng = ref(false);
// 验证码开关
const captchaEnabled = ref(true);
// 注册开关
const register = ref(false);
const redirect = ref(undefined);
const passwordVisible = ref(false);
function togglePasswordVisibility() {
passwordVisible.value = !passwordVisible.value;
}
// 处理忘记密码功能
function handleForgotPassword() {
// 这里可以添加忘记密码的逻辑,例如跳转到忘记密码页面或显示忘记密码弹窗
// 目前先显示一个提示
ElMessage({
message: '忘记密码功能正在开发中',
type: 'info'
});
}
watch(
route,
(newRoute) => {
redirect.value = newRoute.query && newRoute.query.redirect;
},
{ immediate: true }
);
// 页面加载时从 localStorage 获取 invokeYb 的值和从 Cookies 获取记住的登录信息
onMounted(() => {
const storedInvokeYb = localStorage.getItem('invokeYb');
if (storedInvokeYb !== null) {
loginForm.value.invokeYb = storedInvokeYb === 'true';
} else {
// 如果 localStorage 中没有值,则设置默认值并保存
localStorage.setItem('invokeYb', 'true');
}
// 从 Cookies 中恢复记住的登录信息
const rememberMe = Cookies.get('rememberMe');
if (rememberMe && rememberMe === 'true') {
const username = Cookies.get('username');
const password = Cookies.get('password');
if (username) {
loginForm.value.username = username;
loginForm.value.rememberMe = true;
// 解密密码
if (password) {
try {
loginForm.value.password = decrypt(password);
} catch (e) {
console.error('密码解密失败:', e);
}
}
}
}
// 获取医疗机构列表
getUserBindTenantList().then((res) => {
tenantOptions.value = res.data.map(item => ({
label: item.tenantName,
value: item.id
}));
// 如果只有一个医疗机构,自动选中
if (tenantOptions.value.length === 1) {
loginForm.value.tenantId = tenantOptions.value[0].value;
currentTenantName.value = tenantOptions.value[0].label;
}
});
});
function handleLogin() {
proxy.$refs.loginRef.validate((valid) => {
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');
}
// 调用action的登录方法
userStore
.login(loginForm.value)
.then(async () => {
const query = route.query;
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur];
}
return acc;
}, {});
if (true) {
router.push({ path: redirect.value || '/', query: otherQueryParams });
} else {
signIng.value = true;
userStore.getInfo();
GetMacString();
}
})
.catch(() => {
loading.value = false;
// 重新获取验证码
if (captchaEnabled.value) {
//getCode();
}
});
}
});
}
// 获取MAC 加密地址
async function GetMacString() {
// if (window.CefSharp === undefined) {
// } else {
try {
// 必须参数
const data = {
FunctionId: 5,
IP: 'ddjk.jlhs.gov.cn',
PORT: 20215,
TIMEOUT: 2000,
LOG_PATH: 'C:/neu_log/',
SFZ_DRIVER_TYPE: 0,
};
// 获取 mac 地址
// let result = await window.chrome.webview.hostObjects.CSharpAccessor.GetMacString(
// JSON.stringify(data)
// );
// 获取IP地址
// let ip = await window.chrome.webview.hostObjects.CSharpAccessor.GetIpString();
// 获取 mac 地址 兼容win7 start--------------------------------------
let result = undefined;
// await CefSharp.BindObjectAsync('boundAsync');
// console.log(boundAsync);
// await boundAsync.getMacString(JSON.stringify(data)).then((res) => {
// result = res
// });
await invokeYbPlugin(data).then((res) => {
result = res.data;
if (result === undefined || result === '' || result === ' ') {
throw new Error('获取 mac 地址失败');
}
});
// 获取ip地址
let ip = undefined;
// await boundAsync.getIpString().then((res) => {
// ip = res;
// });
await invokeYbPlugin({ FunctionId: 6 }).then((res) => {
ip = res.data;
if (ip === undefined || ip === '' || ip === ' ') {
throw new Error('获取 ip 地址失败');
}
});
// 医保签到
signIn(result, ip);
// end--------------------------------------
// 解析返回的结果
// let cardInfo = JSON.parse(jsonResult);
} catch (error) {
console.error('调用失败:', error);
}
// }
}
function getCode() {
getCodeImg().then((res) => {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (captchaEnabled.value) {
codeUrl.value = 'data:image/gif;base64,' + res.img;
loginForm.value.uuid = res.uuid;
}
});
}
// 监听租户选择变化
watch(() => loginForm.value.tenantId, (newTenantId) => {
const selectedTenant = tenantOptions.value.find(item => item.value === newTenantId);
if (selectedTenant) {
currentTenantName.value = selectedTenant.label;
}
});
// 切换医保连接开关时更新 localStorage
function topNavChange(value) {
localStorage.setItem('invokeYb', value.toString());
}
//账号变化时
function handleUsernameChange(newVal) {
getTenantList(newVal);
}
//查询租户列表
function getTenantList(username) {
if (!username) {
return;
}
getUserBindTenantList(username).then((res) => {
loginForm.value.tenantId = '';
if (res.code == 200) {
if (res.data.length > 0) {
tenantOptions.value = res.data.map((item) => ({
value: item.id,
label: item.tenantName,
}));
loginForm.value.tenantId = tenantOptions.value[0].value; //默认选中第一个
currentTenantName.value = tenantOptions.value[0].label;
}
}
});
}
/**
* description: 签到方法
* @param practitionerId 参与者 id
* @param mac 用户 mac 地址
* @param ip 签到的 IP 地址
*/
async function signIn(mac, ip) {
// 用户 id
const practitionerId = userStore.practitionerId;
console.log('userStore', userStore);
// 签到的 IP 地址
try {
const response = await sign(practitionerId, mac, ip);
if (response.code !== 200) {
throw new Error('签到失败,错误信息:' + response.msg);
}
signIng.value = false;
loading.value = false;
const query = route.query;
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur];
}
return acc;
}, {});
userStore.removeRoles();
router.push({ path: redirect.value || '/', query: otherQueryParams });
console.log('签到成功:', response);
} catch (error) {
userStore.logOut();
signIng.value = false;
loading.value = false;
proxy.$message.error('医保签到失败');
console.error('签到失败:', error);
}
}
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),
};
}
function handleUserName(value) {
let user = {
admin: {
username: 'admin',
password: 'admin123',
},
doctor: {
username: 'gjlin',
password: 'wi123456',
},
drug: {
username: 'mqyk',
password: 'mqyk123',
},
nurse: {
username: 'nmhs',
password: 'nmhs123',
},
charge: {
username: 'admin',
password: 'admin123',
},
};
loginForm.value.username = user[value].username;
loginForm.value.password = user[value].password;
handleLogin();
}
//getCode();
getCookie();
getTenantList(loginForm.value.username);
</script>
<style>
html, body {
height: auto;
min-height: 100vh;
overflow-y: auto;
margin: 0;
padding: 0;
}
</style>
<style lang="scss" scoped>
.login {
display: flex;
justify-content: center;
background: #f8f9fa;
min-height: 100vh;
padding-bottom: 100px; /* 为底部固定footer留出空间 */
//background-image: url("../assets/images/login-background.jpg");
background-size: cover;
}
.title {
margin: 10px auto 15px auto;
text-align: center;
color: #000;
font-family: 'Microsoft Yahei,STHeiti,Simsun,STSong,Helvetica Neue,Helvetica,Arial,sans-serif';
}
.label{
display: block;
margin-bottom: 6px;
font-size: 14px;
font-weight: 500;
color: var(--text);
}
.login-options {
display: flex;
justify-content: space-between;
align-items: center;
margin: 8px 0 10px 0;
width: 100%; /* 确保容器占满宽度 */
}
.remember-me {
margin: 0;
color: var(--text);
}
.forgot-password {
font-size: 14px;
color: var(--primary);
cursor: pointer;
text-align: right; /* 确保文本右对齐 */
margin-left: auto; /* 确保元素右对齐 */
}
.footer {
margin-top: 32px;
font-size: 12px;
color: var(--text-secondary);
}
.login-subtitle {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 20px;
}
.login-form {
border-radius: 16px;
box-shadow: 0 4px 12px var(--shadow);
border: 1px solid var(--border);
background: #ffffff;
width: 100%;
max-width: 400px;
padding: 40px;
padding: 25px 25px 5px 25px;
text-align: center;
.el-input {
height: 50px; // 修改输入框高度
input {
height: 50px; // 修改输入框高度
font-size: 18px; // 修改输入框内文字大小
}
}
.input-icon {
height: 49px; // 调整图标高度以适应输入框高度
width: 14px;
margin-left: 0px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 50px; // 调整验证码区域高度以适应输入框高度
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-top {
text-align: center;
margin: 0 auto 10px;
.el-image {
max-width: 120px;
max-height: 120px;
}
}
.el-login-footer {
height: 80px;
line-height: 30px;
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: 100%;
text-align: center;
color: #000;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
background-color: #f1f1f1;
z-index: 100; /* 确保footer在最上层 */
span {
margin: 0 10px;
}
}
.login-code-img {
height: 50px; // 调整验证码图片高度以适应输入框高度
padding-left: 12px;
}
:deep(.el-input__wrapper) {
background-color: #f5f7fa !important;
border-color: #e4e7ed !important;
border-radius: 10px !important;
font-size: 18px !important;
height: 50px !important; // 修改输入框高度
}
:deep(.el-button--large) {
font-size: 20px !important; // 增加按钮内文字大小
height: 50px !important; // 增加按钮高度
padding: 10px 20px !important; // 调整按钮内边距
border-radius: 10px !important;
}
:deep(.el-input__suffix-inner .el-input__icon) {
width: 24px !important; // 调整图标的宽度
height: 24px !important; // 调整图标的高度
font-size: 24px !important; // 调整图标的字体大小
color: #606266 !important; // 确保图标颜色可见
cursor: pointer !important; // 确保鼠标悬停时显示指针
}
:deep(.password-toggle) {
line-height: 50px !important; // 确保文字垂直居中
user-select: none; // 禁止选中文字
}
:deep(.el-select__wrapper) {
background-color: #f5f7fa !important;
border-color: #e4e7ed !important;
border-radius: 10px !important;
font-size: 18px !important;
height: 50px !important; // 修改输入框高度
}
:deep(.el-form-item) {
margin-bottom: 15px !important; // 减小输入框之间的距离
}
</style>