feat: H5 WeChat one-tap authorize login/register and UI polish

Replace QR login with OAuth button; auto-authorize register/bind; follow OA in settings; advisor text styling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-07-30 15:56:46 +08:00
parent bd97ed76fd
commit 37d534edc3
12 changed files with 331 additions and 95 deletions

View File

@ -69,6 +69,12 @@ export async function wxLogin(loginUuid: string): Promise<WxLoginResult> {
return unwrapOrSelf<WxLoginResult>(res.data);
}
/** 微信网页授权登录(H5 点按钮,无需扫码) */
export async function wxOAuthLogin(code: string): Promise<WxLoginResult> {
const res = await axiosInstance.post('/rbac/auth/wx-oauth-login', { code });
return unwrapOrSelf<WxLoginResult>(res.data);
}
/** OAuth code 换取微信用户信息 */
export async function getOAuthUserInfo(code: string): Promise<WeChatOAuthUserInfo> {
const res = await axiosInstance.post(omsUrl('/wechat/oauth/user-info'), { code });

View File

@ -955,7 +955,7 @@
.ai-msg-row--assistant .ai-msg-bubble {
background: #fff;
color: var(--kc-primary, #1b2a6b);
color: #1d2129;
border: 1px solid rgba(27, 42, 107, 0.08);
box-shadow: 0 2px 8px rgba(27, 42, 107, 0.04);
border-bottom-left-radius: 6px;
@ -1055,7 +1055,7 @@
padding: 11px 4px 11px 12px;
border: none;
background: transparent;
color: var(--kc-primary, #1b2a6b);
color: #1d2129;
/* ≥16px:避免 iOS/微信聚焦输入框时整页缩放 */
font-size: 16px;
line-height: 1.45;
@ -1069,7 +1069,7 @@
}
.ai-composer-input::placeholder {
color: rgba(106, 117, 184, 0.7);
color: #c9cdd4;
}
.ai-composer-send {
@ -1121,8 +1121,9 @@
.ai-composer-hint {
margin: 0;
font-size: 0.72rem;
color: var(--kc-secondary, #6a75b8);
opacity: 0.85;
color: #86909c;
opacity: 0.72;
font-weight: 400;
}
.ai-composer-human {

View File

@ -6,7 +6,7 @@ import {
stashInviteRef,
} from '@/utils/inviteRefStorage';
export type WeChatH5Page = 'register' | 'bind';
export type WeChatH5Page = 'register' | 'bind' | 'login';
const DEFAULT_MP_APP_ID = 'wxd5141ba46c856e1f';
@ -59,14 +59,37 @@ export function resolveWeChatInviteRef(query: Record<string, unknown>): string {
return readStashedInviteRef();
}
/** 跳转微信 OAuth;邀请码写入 state + 7 天暂存,回调后带回 */
export function redirectToWeChatOAuth(page: WeChatH5Page, inviteCode?: string | null): void {
const appId = resolveWeChatMpAppId();
const code = String(inviteCode ?? '').trim() || readStashedInviteRef();
if (code) stashInviteRef(code);
function resolveOAuthRedirectPath(page: WeChatH5Page): string {
if (page === 'login') {
return `${window.location.origin}/h5/login`;
}
return `${window.location.origin}/wechat/${page}`;
}
const redirectUri = encodeURIComponent(`${window.location.origin}/wechat/${page}`);
const state = code ? `${page}:${encodeURIComponent(code)}` : page;
/**
* 跳转微信 OAuth。
* - register/bind:邀请码写入 state + 7 天暂存
* - login:可选把登录后回跳路径写入 state(login:/h5/orders)
*/
export function redirectToWeChatOAuth(
page: WeChatH5Page,
inviteCode?: string | null,
options?: { afterLoginPath?: string | null },
): void {
const appId = resolveWeChatMpAppId();
const redirectUri = encodeURIComponent(resolveOAuthRedirectPath(page));
let state = page;
if (page === 'login') {
const after = String(options?.afterLoginPath ?? '').trim();
if (after.startsWith('/h5')) {
state = `login:${encodeURIComponent(after)}`;
}
} else {
const code = String(inviteCode ?? '').trim() || readStashedInviteRef();
if (code) stashInviteRef(code);
state = code ? `${page}:${encodeURIComponent(code)}` : page;
}
window.location.replace(
`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}` +
@ -74,3 +97,17 @@ export function redirectToWeChatOAuth(page: WeChatH5Page, inviteCode?: string |
`&response_type=code&scope=snsapi_userinfo&state=${state}#wechat_redirect`,
);
}
/** 从 OAuth state 解析登录后回跳路径 */
export function parseAfterLoginPathFromOAuthState(state: unknown): string {
const raw = String(state ?? '').trim();
const match = raw.match(/^login:(.+)$/i);
if (!match?.[1]) return '';
try {
const path = decodeURIComponent(match[1]).trim();
return path.startsWith('/h5') ? path : '';
} catch {
const path = match[1].trim();
return path.startsWith('/h5') ? path : '';
}
}

View File

@ -310,6 +310,16 @@ function clearPriceContext() {
:deep(.ai-composer-hint) {
font-size: 11px;
color: #86909c;
opacity: 0.72;
}
:deep(.ai-msg-row--assistant .ai-msg-bubble) {
color: #1d2129;
}
:deep(.ai-composer-input) {
color: #1d2129;
}
:deep(.ai-msg-bubble) {

View File

@ -0,0 +1,86 @@
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { IconWechat } from '@arco-design/web-vue/es/icon';
import { isWeChatBrowser } from '@/utils/device';
import { redirectToWeChatOAuth } from '@/utils/wechatOAuth';
defineOptions({ name: 'WechatOAuthLoginButton' });
const props = withDefaults(
defineProps<{
afterLoginPath?: string;
long?: boolean;
size?: 'mini' | 'small' | 'medium' | 'large';
}>(),
{
afterLoginPath: '/h5/orders',
long: true,
size: 'large',
},
);
const { t } = useI18n();
const jumping = ref(false);
const inWeChat = computed(() => isWeChatBrowser());
function handleClick() {
if (jumping.value) return;
if (!inWeChat.value) return;
jumping.value = true;
redirectToWeChatOAuth('login', null, { afterLoginPath: props.afterLoginPath });
}
</script>
<template>
<div class="wx-oauth-login">
<a-button
v-if="inWeChat"
type="primary"
class="wx-oauth-login__btn"
:long="long"
:size="size"
:loading="jumping"
@click="handleClick"
>
<template #icon><icon-wechat /></template>
{{ jumping ? t('h5.login.oauthRedirecting') : t('h5.login.oauthLogin') }}
</a-button>
<template v-else>
<p class="wx-oauth-login__tip">{{ t('h5.login.openInWechat') }}</p>
<a-button type="primary" class="wx-oauth-login__btn" :long="long" :size="size" disabled>
<template #icon><icon-wechat /></template>
{{ t('h5.login.oauthLogin') }}
</a-button>
</template>
</div>
</template>
<style lang="less" scoped>
.wx-oauth-login {
width: 100%;
padding: 8px 0 4px;
}
.wx-oauth-login__btn {
height: 44px;
border-radius: 10px;
font-weight: 600;
background: #07c160 !important;
border-color: #07c160 !important;
&:hover,
&:active {
background: #06ad56 !important;
border-color: #06ad56 !important;
}
}
.wx-oauth-login__tip {
margin: 0 0 12px;
text-align: center;
font-size: 13px;
line-height: 1.5;
color: #86909c;
}
</style>

View File

@ -144,12 +144,12 @@ export default {
'h5.me.guestTitle': 'Welcome to Customer Center',
'h5.me.guestDesc':
'Long-press the QR code in WeChat to sign in. New users can register or bind first.',
'Tap authorize in WeChat to sign in. New users can register or bind first.',
'h5.me.register': 'WeChat register',
'h5.me.bind': 'Bind existing account',
'h5.me.passwordLogin': 'Password login',
'h5.me.needLogin': 'Please register or sign in first',
'h5.me.scanLogin': 'WeChat QR sign-in',
'h5.me.scanLogin': 'WeChat authorize sign-in',
'h5.wxQr.loading': 'Loading QR code…',
'h5.wxQr.loadFailed': 'Failed to load QR code. Please refresh.',
@ -164,10 +164,15 @@ export default {
'h5.login.title': 'Sign in',
'h5.login.brand': 'Customer Center',
'h5.login.tab.wechat': 'WeChat QR',
'h5.login.tab.wechat': 'WeChat',
'h5.login.tab.password': 'Account',
'h5.login.switchPassword': 'Sign in with password',
'h5.login.switchWechat': 'WeChat QR sign-in',
'h5.login.switchWechat': 'WeChat authorize sign-in',
'h5.login.oauthLogin': 'Sign in with WeChat',
'h5.login.oauthRedirecting': 'Opening WeChat authorization…',
'h5.login.oauthLoggingIn': 'Authorized. Signing you in…',
'h5.login.oauthFailed': 'WeChat sign-in failed. Please retry.',
'h5.login.openInWechat': 'Open this page in WeChat, then tap the button below to authorize.',
'h5.login.accountPlaceholder': 'Phone / email / customer code',
'h5.login.passwordPlaceholder': 'Enter password',
'h5.login.accountRequired': 'Please enter account',
@ -181,9 +186,9 @@ export default {
'h5.login.submitting': 'Signing in...',
'h5.login.success': 'Signed in',
'h5.login.failed': 'Sign-in failed. Check your account and password.',
'h5.login.newUserHint': 'New here? Register or bind an account first',
'h5.login.goRegister': 'Register',
'h5.login.goBind': 'Bind account',
'h5.login.newUserHint': 'No account? Authorize in WeChat to register or bind',
'h5.login.goRegister': 'Authorize & register',
'h5.login.goBind': 'Authorize & bind',
'h5.recharge.title': 'Recharge',
'h5.recharge.amount': 'Amount',
@ -537,7 +542,9 @@ export default {
'h5.me.editProfile': 'Edit profile',
'h5.me.addressManage': 'Addresses',
'h5.me.resetPassword': 'Change password',
'h5.me.language': 'Language',
'h5.me.language': 'Switch language',
'h5.me.followOfficial': 'Follow official account',
'h5.me.followOfficialMissing': 'Official account link is not configured. Please contact support.',
'h5.me.logout': 'Sign out',
'h5.me.logoutConfirm': 'Sign out now?',
'h5.me.customerCode': 'Code',

View File

@ -143,12 +143,12 @@ export default {
'h5.orders.detail.predeclareFailed': '预报失败',
'h5.me.guestTitle': '欢迎使用客户中心',
'h5.me.guestDesc': '微信内长按二维码识别即可登录;新用户可先注册或绑定',
'h5.me.guestDesc': '微信内点击授权即可登录;新用户请先注册或绑定',
'h5.me.register': '微信注册',
'h5.me.bind': '绑定已有账号',
'h5.me.passwordLogin': '账号密码登录',
'h5.me.needLogin': '请先注册或登录后使用',
'h5.me.scanLogin': '微信扫码登录',
'h5.me.scanLogin': '微信授权登录',
'h5.wxQr.loading': '二维码加载中…',
'h5.wxQr.loadFailed': '二维码加载失败,请刷新重试',
@ -163,10 +163,15 @@ export default {
'h5.login.title': '登录',
'h5.login.brand': '客户中心',
'h5.login.tab.wechat': '微信扫码',
'h5.login.tab.wechat': '微信登录',
'h5.login.tab.password': '账号登录',
'h5.login.switchPassword': '账号密码登录',
'h5.login.switchWechat': '微信扫码登录',
'h5.login.switchWechat': '微信授权登录',
'h5.login.oauthLogin': '微信授权登录',
'h5.login.oauthRedirecting': '正在打开微信授权…',
'h5.login.oauthLoggingIn': '授权成功,正在登录…',
'h5.login.oauthFailed': '微信授权登录失败,请重试',
'h5.login.openInWechat': '请在微信内打开本页,点击下方按钮一键授权登录',
'h5.login.accountPlaceholder': '手机号 / 邮箱 / 客户编号',
'h5.login.passwordPlaceholder': '请输入密码',
'h5.login.accountRequired': '请输入账号',
@ -180,9 +185,9 @@ export default {
'h5.login.submitting': '登录中...',
'h5.login.success': '登录成功',
'h5.login.failed': '登录失败,请检查账号密码',
'h5.login.newUserHint': '还没有账号?新客户请先注册或绑定',
'h5.login.goRegister': '注册新账号',
'h5.login.goBind': '绑定已有账号',
'h5.login.newUserHint': '还没有账号?微信内可一键授权注册或绑定',
'h5.login.goRegister': '微信授权注册',
'h5.login.goBind': '微信授权绑定',
'h5.recharge.title': '账户充值',
'h5.recharge.amount': '充值金额',
@ -535,7 +540,9 @@ export default {
'h5.me.editProfile': '编辑资料',
'h5.me.addressManage': '地址管理',
'h5.me.resetPassword': '修改密码',
'h5.me.language': '语言',
'h5.me.language': '切换语言',
'h5.me.followOfficial': '关注公众号',
'h5.me.followOfficialMissing': '暂未配置公众号跳转链接,请联系客服',
'h5.me.logout': '退出登录',
'h5.me.logoutConfirm': '确定退出登录?',
'h5.me.customerCode': '编号',

View File

@ -44,11 +44,9 @@
:class="{ 'is-active': activeTab === 'wechat' }"
:aria-hidden="activeTab !== 'wechat'"
>
<WechatQrPanel
compact
:active="activeTab === 'wechat'"
@success="onWxLoginSuccess"
/>
<WechatOAuthLoginButton :after-login-path="resolveRedirectPath()" />
<p v-if="oauthLoading" class="h5-login-oauth-status">{{ t('h5.login.oauthLoggingIn') }}</p>
<p v-else-if="errorBox && activeTab === 'wechat'" class="h5-login-error">{{ errorBox }}</p>
</div>
<form
@ -137,15 +135,20 @@ import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { Message } from '@arco-design/web-vue';
import { IconLeft, IconUserAdd, IconLink } from '@arco-design/web-vue/es/icon';
import { OmsLoginType, type LoginResultDto } from '@/api/rbac';
import { OmsLoginType } from '@/api/rbac';
import { isLogin, setToken } from '@/utils/auth';
import { applyRbacUserToUserStore } from '@/utils/enter-system';
import { ensureFullLocaleMessages } from '@/locale';
import { h5Navigate } from '@/utils/device';
import { h5Navigate, isWeChatBrowser } from '@/utils/device';
import { useRbacUserStore } from '@/store/modules/rbac-user';
import logoImg from '@/assets/logo2.png';
import LoginLangToggle from '@/views/login/components/LoginLangToggle.vue';
import WechatQrPanel from '../components/WechatQrPanel.vue';
import WechatOAuthLoginButton from '../components/WechatOAuthLoginButton.vue';
import { wxOAuthLogin } from '@/api/wechat';
import {
parseAfterLoginPathFromOAuthState,
redirectToWeChatOAuth,
} from '@/utils/wechatOAuth';
defineOptions({ name: 'H5Login' });
@ -158,6 +161,7 @@ const activeTab = ref<'wechat' | 'password'>('wechat');
const form = reactive({ account: '', password: '' });
const agreementChecked = ref(false);
const loading = ref(false);
const oauthLoading = ref(false);
const errorBox = ref('');
const agreementVisible = ref(false);
const agreementTitle = ref('');
@ -196,10 +200,18 @@ function goBack() {
}
function goRegister() {
if (isWeChatBrowser()) {
redirectToWeChatOAuth('register');
return;
}
h5Navigate(router, '/wechat/register');
}
function goBind() {
if (isWeChatBrowser()) {
redirectToWeChatOAuth('bind');
return;
}
h5Navigate(router, '/wechat/bind');
}
@ -215,6 +227,8 @@ function openAgreement(type: 'privacy' | 'terms') {
}
function resolveRedirectPath() {
const fromState = parseAfterLoginPathFromOAuthState(route.query.state);
if (fromState) return fromState;
const redirect = route.query.redirect;
if (typeof redirect === 'string' && redirect.startsWith('/h5')) {
return redirect;
@ -222,6 +236,11 @@ function resolveRedirectPath() {
return '/h5/orders';
}
function stripOAuthQuery() {
const { code: _c, state: _s, ...rest } = route.query;
return rest;
}
async function finishLogin() {
if (!rbacUserStore.userInfo) return;
if (rbacUserStore.token) {
@ -232,21 +251,35 @@ async function finishLogin() {
await nextTick();
const target = resolveRedirectPath();
try {
await router.replace(target);
await router.replace({ path: target, query: {} });
} catch (navErr) {
console.error('H5 登录跳转失败', navErr);
await router.replace('/h5/orders').catch(() => undefined);
}
}
async function onWxLoginSuccess(result: LoginResultDto) {
const ok = await rbacUserStore.loginByWxResult(result);
if (!ok) {
Message.error(t('h5.login.failed'));
return;
async function consumeOAuthCode(code: string) {
if (oauthLoading.value) return;
oauthLoading.value = true;
activeTab.value = 'wechat';
try {
const result = await wxOAuthLogin(code);
const ok = await rbacUserStore.loginByWxResult(result);
if (!ok) {
Message.error(t('h5.login.failed'));
await router.replace({ path: route.path, query: stripOAuthQuery() });
return;
}
Message.success(t('h5.login.success'));
await finishLogin();
} catch (e: any) {
const msg = e?.message || t('h5.login.oauthFailed');
Message.error(msg);
errorBox.value = msg;
await router.replace({ path: route.path, query: stripOAuthQuery() });
} finally {
oauthLoading.value = false;
}
Message.success(t('h5.wxQr.success'));
await finishLogin();
}
async function handleSubmit() {
@ -292,7 +325,6 @@ async function handleSubmit() {
watch(
() => route.query.tab,
(tab) => {
// 仅显式 ?tab=password 时进账号登录,其余一律默认微信扫码
if (tab === 'password') {
activeTab.value = 'password';
errorBox.value = '';
@ -304,9 +336,21 @@ watch(
{ immediate: true },
);
onMounted(() => {
onMounted(async () => {
if (isLogin()) {
router.replace(resolveRedirectPath());
return;
}
const code = typeof route.query.code === 'string' ? route.query.code.trim() : '';
if (code) {
await consumeOAuthCode(code);
return;
}
// 微信内且未带 code:可一键拉起授权(用户点按钮亦可)
if (isWeChatBrowser() && route.query.auto === '1') {
redirectToWeChatOAuth('login', null, { afterLoginPath: resolveRedirectPath() });
}
});
</script>
@ -531,6 +575,13 @@ onMounted(() => {
animation: h5-login-rise 0.28s var(--login-ease) both;
}
.h5-login-oauth-status {
margin: 12px 0 0;
text-align: center;
font-size: 13px;
color: #86909c;
}
.h5-login-agree {
margin: 4px 0 0;
align-items: flex-start;

View File

@ -1,6 +1,6 @@
<template>
<div class="h5-me-page" :class="{ 'h5-me-page--guest': !loggedIn }">
<!-- 访客:与 /h5/login 同款扫码入口,避免返回看到旧版多按钮登录 -->
<!-- 访客:微信授权登录(无二维码);新用户走注册/绑定 -->
<template v-if="!loggedIn">
<section class="h5-me-guest h5-enter">
<div class="h5-me-guest__brand">
@ -28,7 +28,7 @@
{{ t('h5.login.tab.password') }}
</button>
</div>
<WechatQrPanel compact :active="true" @success="onWxLoginSuccess" />
<WechatOAuthLoginButton :after-login-path="resolvePostLoginPath()" />
<p v-if="route.query.redirect" class="h5-me-tip">{{ t('h5.me.needLogin') }}</p>
</div>
@ -184,6 +184,18 @@
<icon-right />
</span>
</button>
<button
type="button"
class="h5-me-list__item"
:disabled="followJumping"
@click="openOfficialAccount"
>
<span class="h5-me-list__left">
<icon-wechat class="h5-me-list__icon" />
{{ t('h5.me.followOfficial') }}
</span>
<icon-right />
</button>
<button
type="button"
class="h5-me-list__item h5-me-list__item--danger"
@ -228,18 +240,18 @@ import {
IconUserAdd,
IconLink,
IconLanguage,
IconWechat,
} from '@arco-design/web-vue/es/icon';
import type { CustomerDto } from '@/api/customer';
import { resolveCurrentCustomer } from '@/api/customer/resolve';
import { postOrderList } from '@/api/order';
import { getMyCoupons, getMemberOverview } from '@/api/marketing/portal';
import { type LoginResultDto } from '@/api/rbac';
import { isLogin, setToken } from '@/utils/auth';
import { isLogin } from '@/utils/auth';
import { resolveAvatarUrl } from '@/utils/avatar';
import { getMapIdFromAccessToken } from '@/utils/jwt';
import { applyRbacUserToUserStore } from '@/utils/enter-system';
import { ensureFullLocaleMessages } from '@/locale';
import { h5Navigate } from '@/utils/device';
import { h5Navigate, isWeChatBrowser } from '@/utils/device';
import { redirectToWeChatOAuth } from '@/utils/wechatOAuth';
import { jumpToWechatOfficialAccount } from '@/utils/wechatFollowJump';
import { useUserStore } from '@/store';
import { useRbacUserStore } from '@/store/modules/rbac-user';
import useLocale from '@/hooks/locale';
@ -248,7 +260,7 @@ import iconServiceCoupon from '@/assets/icons/h5/service-coupon.svg?url';
import iconServiceInvite from '@/assets/icons/h5/service-invite.svg?url';
import iconServiceCommission from '@/assets/icons/h5/service-commission.svg?url';
import iconOrderPickup from '@/assets/icons/h5/order-pickup.svg?url';
import WechatQrPanel from '../components/WechatQrPanel.vue';
import WechatOAuthLoginButton from '../components/WechatOAuthLoginButton.vue';
import H5JourneyGuide from '../components/H5JourneyGuide.vue';
import H5CreateOrderFlow from '../components/H5CreateOrderFlow.vue';
import H5RechargeSheet from '../components/H5RechargeSheet.vue';
@ -284,6 +296,7 @@ const commissionVisible = ref(false);
const editVisible = ref(false);
const pwdVisible = ref(false);
const addressVisible = ref(false);
const followJumping = ref(false);
const pickupVisible = ref(false);
const pickupCount = ref(0);
const memberOverview = ref<Record<string, unknown> | null>(null);
@ -434,38 +447,19 @@ function resolvePostLoginPath() {
return '/h5/orders';
}
async function onWxLoginSuccess(result: LoginResultDto) {
const ok = await rbacUserStore.loginByWxResult(result);
if (!ok || !rbacUserStore.userInfo) {
Message.error(t('h5.login.failed'));
function goWechatRegister() {
if (isWeChatBrowser()) {
redirectToWeChatOAuth('register');
return;
}
if (rbacUserStore.token) {
setToken(rbacUserStore.token);
}
applyRbacUserToUserStore(rbacUserStore.userInfo);
await ensureFullLocaleMessages();
Message.success(t('h5.wxQr.success'));
const target = resolvePostLoginPath();
try {
await router.replace(target);
} catch (navErr) {
// 导航被中断时仍切到已登录态,避免卡在扫码页
console.error('H5 扫码登录跳转失败', navErr);
}
loggedIn.value = true;
if (route.path.startsWith('/h5/me')) {
await loadProfile();
}
}
function goWechatRegister() {
h5Navigate(router, '/wechat/register');
}
function goWechatBind() {
if (isWeChatBrowser()) {
redirectToWeChatOAuth('bind');
return;
}
h5Navigate(router, '/wechat/bind');
}
@ -494,6 +488,19 @@ function toggleLocale() {
void changeLocale(next, { silent: true });
}
async function openOfficialAccount() {
if (followJumping.value) return;
followJumping.value = true;
try {
const ok = await jumpToWechatOfficialAccount();
if (!ok) {
Message.warning(t('h5.me.followOfficialMissing'));
}
} finally {
followJumping.value = false;
}
}
function handleLogout() {
Modal.confirm({
title: t('h5.me.logout'),

View File

@ -180,7 +180,7 @@ import { handleApiError } from '@/utils/rbac-error-handler';
import { redirectToWeChatOAuth } from '@/utils/wechatOAuth';
import { loginCustomerAfterWechatAuth } from '@/utils/wechatAuthLogin';
import { isTimeoutError, withTimeout } from '@/utils/withTimeout';
import { hideWeChatBrowserNav } from '@/utils/device';
import { hideWeChatBrowserNav, isWeChatBrowser } from '@/utils/device';
import { jumpToWechatOfficialAccount } from '@/utils/wechatFollowJump';
import LoginLangToggle from '@/views/login/components/LoginLangToggle.vue';
@ -200,6 +200,10 @@ function goCustomerCenter() {
}
function goLogin() {
if (isWeChatBrowser()) {
redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' });
return;
}
router.replace('/h5/login');
}
@ -225,6 +229,10 @@ async function openOfficialAccount() {
}
function goRegister() {
if (isWeChatBrowser()) {
redirectToWeChatOAuth('register');
return;
}
router.replace('/wechat/register');
}
@ -354,8 +362,12 @@ onMounted(() => {
loadingHint.value = t('wechat.common.loading');
const code = route.query.code;
if (typeof code !== 'string' || !code) {
if (!isWeChatBrowser()) {
statusCode.value = 'need-auth';
return;
}
statusCode.value = 'need-auth';
authModalVisible.value = true;
startOAuth();
return;
}
void loadOAuthUserInfo(code);

View File

@ -10,10 +10,10 @@ export default {
'注册需获取微信昵称与头像,用于创建客户账号。请点击「立即授权」完成微信授权后继续填写资料。',
'wechat.common.authOk': '立即授权',
'wechat.common.authLater': '稍后再说',
'wechat.common.authGateTitle': '开始微信注册',
'wechat.common.authGateTip': '授权后即可填写资料并完成注册',
'wechat.common.authBindGateTitle': '开始绑定微信',
'wechat.common.authBindGateTip': '授权后即可验证系统账号并绑定',
'wechat.common.authGateTitle': '微信授权注册',
'wechat.common.authGateTip': '正在打开微信授权,授权后即可填写资料完成注册',
'wechat.common.authBindGateTitle': '微信授权绑定',
'wechat.common.authBindGateTip': '正在打开微信授权,授权后即可验证账号并绑定',
'wechat.follow.title': '请关注公众号',
'wechat.follow.redirecting': '正在打开公众号,关注后可随时从菜单进入客户中心',
@ -25,11 +25,11 @@ export default {
'wechat.register.title': '确认注册',
'wechat.register.subtitle': '已带入微信昵称与初始密码,确认后即可完成',
'wechat.register.success': '注册成功',
'wechat.register.successDesc': '账号已创建并绑定当前微信;登录可用客户编号或微信扫码',
'wechat.register.successDesc': '账号已创建并绑定当前微信;登录可用客户编号或微信授权',
'wechat.register.submit': '确认注册',
'wechat.register.submitting': '提交中…',
'wechat.register.alreadyBound': '该微信已绑定账号,请直接登录',
'wechat.register.alreadyBoundTip': '请使用微信扫码或账号密码登录',
'wechat.register.alreadyBoundTip': '请使用微信授权或账号密码登录',
'wechat.register.alreadyBoundInviteTip':
'您已是老用户,邀请礼仅限新注册客户;本次不会改绑引荐关系,请直接登录',
'wechat.register.oauthError': '微信授权失败,请重试',
@ -47,7 +47,7 @@ export default {
'wechat.register.passwordLetterAndDigit': '密码须同时包含字母和数字',
'wechat.register.passwordTipPrefix': '初始密码 ',
'wechat.register.passwordTipSuffix': ',可直接用,也可改成自己的',
'wechat.register.hint': '注册后可用微信扫码或客户编号+密码登录',
'wechat.register.hint': '注册后可用微信授权或客户编号+密码登录',
'wechat.register.goCenter': '进入客户中心',
'wechat.register.goLogin': '去登录',
'wechat.register.hasAccount': '已有系统账号?',
@ -63,7 +63,7 @@ export default {
'wechat.register.copyInviteFailed': '复制失败,请手动复制地址栏链接',
'wechat.bind.title': '绑定已有账号',
'wechat.bind.subtitle': '验证账号后,即可用当前微信扫码登录',
'wechat.bind.subtitle': '验证账号后,即可用当前微信授权登录',
'wechat.bind.success': '绑定成功',
'wechat.bind.successDesc': '当前微信已关联系统账号,可直接进入客户中心',
'wechat.bind.submit': '验证并绑定',
@ -74,7 +74,7 @@ export default {
'wechat.bind.password': '登录密码',
'wechat.bind.accountPlaceholder': '手机号 / 邮箱 / 客户编号',
'wechat.bind.passwordPlaceholder': '请输入密码',
'wechat.bind.hint': '绑定后可用微信扫码登录。解绑或重置密码请联系客服。',
'wechat.bind.hint': '绑定后可用微信授权登录。解绑或重置密码请联系客服。',
'wechat.bind.goCenter': '进入客户中心',
'wechat.bind.goLogin': '去登录',
'wechat.bind.noAccount': '还没有账号?',

View File

@ -75,7 +75,7 @@
"
/>
<div class="wechat-h5-state-actions">
<a-button type="primary" long @click="goLogin">{{ t('wechat.register.goLogin') }}</a-button>
<a-button type="primary" long @click="goOAuthLogin">{{ t('wechat.register.goLogin') }}</a-button>
<a-button long @click="goCustomerCenter">{{ t('wechat.register.goCenter') }}</a-button>
</div>
</div>
@ -270,9 +270,17 @@ function goCustomerCenter() {
}
function goLogin() {
if (isWeChatBrowser()) {
redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' });
return;
}
router.replace('/h5/login');
}
function goOAuthLogin() {
goLogin();
}
function finishAfterFollow() {
if (loggedInAfterRegister.value) {
goCustomerCenter();
@ -295,6 +303,10 @@ async function openOfficialAccount() {
}
function goBind() {
if (isWeChatBrowser()) {
redirectToWeChatOAuth('bind');
return;
}
router.replace('/wechat/bind');
}
@ -499,9 +511,9 @@ onMounted(() => {
statusCode.value = 'open-in-wechat';
return;
}
// 进入注册页主动弹窗,由用户确认后再跳转 OAuth,避免微信底部「完整服务」被动提示
// 微信内直接拉起授权,无需扫码 / 二次弹窗
statusCode.value = 'need-auth';
authModalVisible.value = true;
startOAuth();
return;
}
void loadOAuthUserInfo(code);