hookehuyr

feat(登录): 添加登录提示组件并优化登录流程

引入登录提示弹窗组件,替换原有的直接错误提示,提升用户体验
在公共混入中添加登录状态检查和确保登录的方法
修改登录成功后跳转逻辑,支持重定向到原页面
更新产品详情页的下载和发送邮件功能,使用新的登录提示
调整环境配置中的代理目标地址
......@@ -2,7 +2,7 @@
VITE_PORT = 8106
# 反向代理服务器地址
VITE_PROXY_TARGET = https://www.hager-electric.com/
VITE_PROXY_TARGET = https://www.hager.cn/
# VITE_PROXY_TARGET = https://oa.onwall.cn
# API请求前缀
......
......@@ -33,6 +33,7 @@ declare module 'vue' {
HagerMenu: typeof import('./src/components/hagerMenu.vue')['default']
HagerMore: typeof import('./src/components/hagerMore.vue')['default']
HagerService: typeof import('./src/components/common/hagerService.vue')['default']
LoginPrompt: typeof import('./src/components/common/loginPrompt.vue')['default']
Navbar: typeof import('./src/components/navbar.vue')['default']
Privacy: typeof import('./src/components/privacy.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
......
......@@ -10,6 +10,7 @@
<hager-header @onShowMenu="onShowMenu"></hager-header>
<router-view :class="[!isLoginPage ? 'wrapper' : '', is_xs ? 'xs' : '']"></router-view>
<hager-footer v-if="!isLoginPage" @send-openid="sendOpenid"></hager-footer>
<login-prompt></login-prompt>
<div v-if="showMask" class="mask">
</div>
</div>
......@@ -18,6 +19,7 @@
<script>
import hagerHeader from '@/components/common/hagerHeader.vue';
import hagerFooter from '@/components/common/hagerFooter.vue';
import loginPrompt from '@/components/common/loginPrompt.vue';
import mixin from '@/common/mixin';
import { wxInfo } from '@/utils/tools';
......@@ -30,7 +32,7 @@ export default {
{ name: 'description', content: '海格电气 构建未来电气世界 让人们的生活更安全、更清洁、更愉悦。' },
]
},
components: { hagerHeader, hagerFooter },
components: { hagerHeader, hagerFooter, loginPrompt },
mixins: [mixin.init],
data () {
return {
......
......@@ -9,6 +9,8 @@
import $ from 'jquery';
import { throttle } from 'lodash-es';
import axios from '@/utils/axios';
import { getUserInfoAPI } from '@/api/hager';
import { openLoginPrompt } from '@/utils/loginPrompt';
export default {
// 初始化设置
......@@ -44,6 +46,41 @@ export default {
this.top_img_height = $(window).width()*0.35 + 'px';
}
},
async getLoginStatus () {
const res = await getUserInfoAPI();
return Boolean(res && res.code && res.data);
},
goToLoginPage (redirect = this.$route.fullPath) {
const query = redirect ? { redirect } : {};
this.$router.push({
path: '/user/login',
query,
});
},
async ensureLogin (options = {}) {
const isLogin = await this.getLoginStatus();
if (typeof this.is_login === 'boolean') {
this.is_login = isLogin;
}
if (isLogin) {
return true;
}
const shouldLogin = await openLoginPrompt({
title: options.title || '登录后可下载资料',
message: options.message || '当前下载操作需要先登录,是否现在前往登录页面?',
confirmText: options.confirmText || '去登录',
cancelText: options.cancelText || '稍后再说',
});
if (shouldLogin) {
this.goToLoginPage(options.redirect || this.$route.fullPath);
}
return false;
},
maEvent: throttle(function () {
var p = Array.prototype.shift.call(arguments);
return axios.post('/srv/?a=log&p=' + p)
......
<template>
<div>
<el-dialog
v-if="!is_xs"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:show-close="false"
width="28rem"
custom-class="login-confirm-dialog"
@close="handleCancel">
<div class="login-confirm-content">
<div class="title">{{ prompt.title }}</div>
<div class="message">{{ prompt.message }}</div>
<div class="action-row">
<div class="action-btn cancel" @click="handleCancel">{{ prompt.cancelText }}</div>
<div class="action-btn confirm" @click="handleConfirm">{{ prompt.confirmText }}</div>
</div>
</div>
</el-dialog>
<transition v-else name="login-mobile-fade">
<div
v-if="drawerVisible"
class="login-confirm-mobile-mask"
@click.self="handleCancel">
<div class="login-confirm-mobile-panel">
<div class="login-confirm-content xs">
<div class="title">{{ prompt.title }}</div>
<div class="message">{{ prompt.message }}</div>
<div class="action-column">
<div class="action-btn confirm" @click="handleConfirm">{{ prompt.confirmText }}</div>
<div class="action-btn cancel" @click="handleCancel">{{ prompt.cancelText }}</div>
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
<script>
import mixin from '@/common/mixin';
import { closeLoginPrompt, loginPromptState } from '@/utils/loginPrompt';
export default {
name: 'LoginPrompt',
mixins: [mixin.init],
computed: {
prompt () {
return loginPromptState;
},
dialogVisible: {
get () {
return this.prompt.visible && !this.is_xs;
},
set (visible) {
if (!visible) {
this.handleCancel();
}
}
},
drawerVisible: {
get () {
return this.prompt.visible && this.is_xs;
},
set (visible) {
if (!visible) {
this.handleCancel();
}
}
}
},
methods: {
handleConfirm () {
closeLoginPrompt(true);
},
handleCancel () {
closeLoginPrompt(false);
}
}
}
</script>
<style lang="less">
.login-confirm-dialog {
border-radius: 0.75rem;
}
.login-confirm-content {
padding: 0.5rem 0;
text-align: center;
&.xs {
padding: 1.5rem 1rem 1rem;
}
.title {
font-size: 1.25rem;
font-weight: bold;
color: @primary-color;
}
.message {
margin-top: 1rem;
color: @text-color;
line-height: 1.6;
}
.action-row,
.action-column {
margin-top: 1.5rem;
display: flex;
gap: 0.75rem;
}
.action-column {
flex-direction: column;
}
.action-btn {
flex: 1;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
border: 1px solid @primary-color;
color: @primary-color;
box-sizing: border-box;
&:hover {
cursor: pointer;
}
&.confirm {
background-color: @primary-color;
color: #fff;
}
}
}
.login-confirm-mobile-mask {
position: fixed;
inset: 0;
z-index: 2008;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem 0.75rem;
background: rgba(0, 0, 0, 0.45);
box-sizing: border-box;
}
.login-confirm-mobile-panel {
width: 100%;
max-width: 22rem;
background: #fff;
border-radius: 1rem;
box-shadow: 0 0.75rem 2rem rgba(0, 0, 0, 0.16);
}
.login-mobile-fade-enter-active,
.login-mobile-fade-leave-active {
transition: opacity 0.24s ease;
}
.login-mobile-fade-enter-active .login-confirm-mobile-panel,
.login-mobile-fade-leave-active .login-confirm-mobile-panel {
transition: transform 0.24s ease;
}
.login-mobile-fade-enter,
.login-mobile-fade-leave-to {
opacity: 0;
}
.login-mobile-fade-enter .login-confirm-mobile-panel,
.login-mobile-fade-leave-to .login-confirm-mobile-panel {
transform: translateY(1rem) scale(0.96);
}
</style>
import Vue from 'vue';
const defaultOptions = {
title: '登录提示',
message: '当前操作需要先登录,是否前往登录页面?',
confirmText: '去登录',
cancelText: '暂不登录',
};
export const loginPromptState = Vue.observable({
visible: false,
title: defaultOptions.title,
message: defaultOptions.message,
confirmText: defaultOptions.confirmText,
cancelText: defaultOptions.cancelText,
});
let resolver = null;
const resetState = () => {
loginPromptState.visible = false;
loginPromptState.title = defaultOptions.title;
loginPromptState.message = defaultOptions.message;
loginPromptState.confirmText = defaultOptions.confirmText;
loginPromptState.cancelText = defaultOptions.cancelText;
resolver = null;
};
export const openLoginPrompt = (options = {}) => {
if (resolver) {
resolver(false);
resetState();
}
loginPromptState.title = options.title || defaultOptions.title;
loginPromptState.message = options.message || defaultOptions.message;
loginPromptState.confirmText = options.confirmText || defaultOptions.confirmText;
loginPromptState.cancelText = options.cancelText || defaultOptions.cancelText;
loginPromptState.visible = true;
return new Promise(resolve => {
resolver = resolve;
});
};
export const closeLoginPrompt = (confirmed = false) => {
if (resolver) {
resolver(confirmed);
}
resetState();
};
......@@ -144,8 +144,8 @@ import mixin from 'common/mixin';
import hagerBox from '@/components/common/hagerBox';
import hagerCarousel from '@/components/hagerCarousel';
import hagerH1 from '@/components/common/hagerH1.vue';
import { MessageBox, Message, Loading } from 'element-ui';
import { getProductInfoAPI, getUserInfoAPI, downEmailAPI, downZipAPI } from "@/api/hager.js";
import { Message, Loading } from 'element-ui';
import { getProductInfoAPI, downEmailAPI, downZipAPI } from "@/api/hager.js";
export default {
// TAG:配置页面meta和标题信息
......@@ -304,21 +304,11 @@ export default {
});
},
async getUserInfo () {
const { code, data } = await getUserInfoAPI();
if (code) {
if (data) {
this.is_login = true; // 已登录
} else {
this.is_login = false; // 未登录
}
}
this.is_login = await this.getLoginStatus();
},
async goToDownload () {
if (!this.is_login) {
Message({
type: 'error',
message: '请先登录'
});
const canDownload = await this.ensureLogin();
if (!canDownload) {
return;
}
if (this.is_download_checked) {
......@@ -354,11 +344,8 @@ export default {
item.checked_sum = item.list.filter(item => item.checked).length;
},
async sendEmail (item) {
if (!this.is_login) {
Message({
type: 'error',
message: '请先登录'
});
const canDownload = await this.ensureLogin();
if (!canDownload) {
return;
}
let loadingInstance = Loading.service({ fullscreen: true, background: 'rgba(0, 0, 0, 0.3)' });
......
......@@ -144,8 +144,8 @@ import mixin from 'common/mixin';
import hagerBox from '@/components/common/hagerBox';
import hagerCarousel from '@/components/hagerCarousel';
import hagerH1 from '@/components/common/hagerH1.vue';
import { MessageBox, Message, Loading } from 'element-ui';
import { getProductInfoAPI, getUserInfoAPI, downEmailAPI, downZipAPI } from "@/api/hager.js";
import { Message, Loading } from 'element-ui';
import { getProductInfoAPI, downEmailAPI, downZipAPI } from "@/api/hager.js";
export default {
// TAG:配置页面meta和标题信息
......@@ -304,21 +304,11 @@ export default {
});
},
async getUserInfo () {
const { code, data } = await getUserInfoAPI();
if (code) {
if (data) {
this.is_login = true; // 已登录
} else {
this.is_login = false; // 未登录
}
}
this.is_login = await this.getLoginStatus();
},
async goToDownload () {
if (!this.is_login) {
Message({
type: 'error',
message: '请先登录'
});
const canDownload = await this.ensureLogin();
if (!canDownload) {
return;
}
if (this.is_download_checked) {
......@@ -354,11 +344,8 @@ export default {
item.checked_sum = item.list.filter(item => item.checked).length;
},
async sendEmail (item) {
if (!this.is_login) {
Message({
type: 'error',
message: '请先登录'
});
const canDownload = await this.ensureLogin();
if (!canDownload) {
return;
}
let loadingInstance = Loading.service({ fullscreen: true, background: 'rgba(0, 0, 0, 0.3)' });
......
......@@ -31,7 +31,7 @@ import mixin from 'common/mixin';
import hagerInput from '@/components/common/hagerInput.vue';
import $ from 'jquery';
import { loginAPI } from '@/api/hager';
import { MessageBox, Message } from 'element-ui';
import { Message } from 'element-ui';
export default {
mixins: [mixin.init],
......@@ -77,7 +77,7 @@ export default {
type: 'success',
message: msg
});
this.$router.push('/');
this.$router.push(this.$route.query.redirect || '/');
}
},
goToRegister () {
......