This commit is contained in:
owenchen committed 2026-10-17 13:22:10 +08:00
1 parent b5d2a2ede0
commit aff58692bb
17 files changed
+1851 -240

No files matched your search

@@ -0,0 +1,267 @@
<script setup lang="ts">
// 代理登录页:复刻旧系统 WebVue views/login/loginAgent.vue
// 已登录的管理员在「我的」页输入被代理号 + 对方手机号 + 密码,LoginAgent 成功后覆盖当前会话
// 加密与登录同算法:RsaUserPassword = RSA( guid + MD5(密码) )
// 视觉与登录页共用一套品牌语言:暖米白底 + 叶脉树 Logo + 黛翠绿渐变按钮 + 图标内嵌白底胶囊输入框
import { useLocale } from "uview-pro";
import { ref } from "vue";
import { brandGlyph, veinGlyph } from "@/common/brand";
import { backendLocale } from "@/common/http.interceptor";
import { encryptLoginPassword, makeGuid } from "@/common/pwd-crypto";
import { useAuthGuard } from "@/composables";
import { LoginData as LoginDataClass } from "@/data/auto/loginData";
import { pageUrl } from "@/router/routes";
import authService from "@/service/security/authService";
import { useLoginProfileStore } from "@/stores";
const { t } = useLocale();
useAuthGuard();
const loginProfileStore = useLoginProfileStore();
const agentUserName = ref("");
const mobile = ref("");
const password = ref("");
const showPwd = ref(false);
const loading = ref(false);
/** 返回「我的」:代理登录从「我的」跳入,优先回退;深链直达无栈时重进「我的」 */
function goBack(): void {
if (getCurrentPages().length > 1) {
uni.navigateBack();
}
else {
uni.reLaunch({ url: pageUrl("mineTab") });
}
}
async function handleSubmit() {
if (!agentUserName.value) {
uni.showToast({ title: t("LoginAgentPage.agentRequired"), icon: "none" });
return;
}
if (!mobile.value) {
uni.showToast({ title: t("LoginAgentPage.mobileRequired"), icon: "none" });
return;
}
if (!password.value) {
uni.showToast({ title: t("LoginAgentPage.passwordRequired"), icon: "none" });
return;
}
loading.value = true;
try {
const guid = makeGuid();
// 1. 取后端 RSA 公钥
const key = await authService.GetRsaPublicKey();
// 2. RSA( guid + MD5(密码) )
const rsaUserPassword = encryptLoginPassword(password.value, guid, String(key));
if (!rsaUserPassword) {
uni.showToast({ title: "密码加密失败,请重试", icon: "none" });
return;
}
// 3. 代理登录(成功后覆盖当前会话档案与令牌)
const data = new LoginDataClass();
data.AgentUserName = agentUserName.value;
data.UserMobile = mobile.value;
data.Guid = guid;
data.RsaUserPassword = rsaUserPassword;
data.Culture = backendLocale();
const profile = await authService.LoginAgent(data);
loginProfileStore.updateLoginProfile(profile);
uni.showToast({ title: t("LoginAgentPage.success"), icon: "none" });
// reLaunch 清空页面栈,避免返回键退回代理登录页
setTimeout(() => uni.reLaunch({ url: pageUrl("dashboard") }), 800);
}
catch {
// 错误提示已由 http.interceptor 统一处理
}
finally {
loading.value = false;
}
}
</script>
<template>
<view class="login-page">
<!-- 背景右上淡叶脉暗纹(纯装饰,低透明) -->
<image class="login-vein" :src="veinGlyph" mode="aspectFit" />
<!-- 品牌位:叶脉树 Logo + 名称 + 「代理登录」副标题 -->
<view class="login-brand">
<view class="login-brand__logo">
<image class="login-brand__glyph" :src="brandGlyph" mode="aspectFit" />
</view>
<view class="login-brand__name">
{{ t("LoginForm.brandName") }}
</view>
<view class="login-brand__sub">
{{ t("LoginAgentPage.title") }}
</view>
</view>
<!-- 表单:图标内嵌白底圆角胶囊 -->
<view class="login-form">
<view class="login-field">
<u-icon name="account" size="34" color="#9aa0a6" />
<input v-model="agentUserName" class="login-field__input" :placeholder="t('LoginAgentPage.agentLabel')"
placeholder-class="login-field__ph">
</view>
<view class="login-field">
<u-icon name="phone" size="34" color="#9aa0a6" />
<input v-model="mobile" class="login-field__input" type="number"
:placeholder="t('LoginAgentPage.mobileLabel')" placeholder-class="login-field__ph">
</view>
<view class="login-field">
<u-icon name="lock" size="34" color="#9aa0a6" />
<input v-model="password" class="login-field__input" :password="!showPwd"
:placeholder="t('LoginAgentPage.passwordLabel')" placeholder-class="login-field__ph"
@confirm="handleSubmit">
<u-icon :name="showPwd ? 'eye-fill' : 'eye-off'" size="34" color="#9aa0a6"
@click="showPwd = !showPwd" />
</view>
</view>
<!-- 按钮:代理登录黛翠绿渐变实心,返回白底绿描边 -->
<view class="login-btn login-btn--submit" @click="handleSubmit">
<u-loading v-if="loading" mode="circle" color="#ffffff" :size="34" />
<text v-else>{{ t("LoginAgentPage.submitBut") }}</text>
</view>
<view class="login-btn login-btn--ghost" @click="goBack">
<text>{{ t("LoginAgentPage.backBut") }}</text>
</view>
</view>
</template>
<style lang="scss" scoped>
/* 品牌配色:黛翠绿主色 + 琥珀金点缀 + 暖米白底,与登录页同一套变量 */
$brand-green: #2f7d5c;
$brand-green-dark: #1f5f47;
$brand-green-light: #47a678;
$brand-amber: #c8912f;
$brand-ink: #1f3d2f;
.login-page {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
min-height: 100vh;
padding: 120rpx 60rpx 60rpx;
overflow: hidden;
background: linear-gradient(165deg, #f7f0e3 0%, #f1e7d6 55%, #eaddc7 100%);
}
/* 右上淡叶脉暗纹:半隐若现,不拦截点击 */
.login-vein {
position: absolute;
top: -40rpx;
right: -60rpx;
width: 460rpx;
height: 460rpx;
opacity: 0.08;
pointer-events: none;
}
/* ===== 品牌位 ===== */
.login-brand {
display: flex;
z-index: 1;
flex-direction: column;
align-items: center;
margin-bottom: 70rpx;
}
.login-brand__logo {
display: flex;
align-items: center;
justify-content: center;
width: 120rpx;
height: 120rpx;
margin-bottom: 24rpx;
border-radius: 34rpx;
background: linear-gradient(150deg, $brand-green-light, $brand-green-dark);
box-shadow: 0 16rpx 36rpx rgba(31, 95, 71, 0.32);
}
.login-brand__glyph {
width: 70rpx;
height: 70rpx;
}
.login-brand__name {
font-size: 48rpx;
font-weight: 700;
letter-spacing: 4rpx;
color: $brand-ink;
}
.login-brand__sub {
margin-top: 10rpx;
font-size: 26rpx;
letter-spacing: 6rpx;
color: $brand-amber;
}
/* ===== 表单 ===== */
.login-form {
display: flex;
z-index: 1;
flex-direction: column;
gap: 28rpx;
width: 100%;
}
.login-field {
display: flex;
align-items: center;
gap: 16rpx;
padding: 0 32rpx;
border-radius: 20rpx;
background-color: #ffffff;
box-shadow: 0 6rpx 20rpx rgba(31, 95, 71, 0.06);
}
.login-field__input {
flex: 1;
height: 92rpx;
font-size: 30rpx;
color: $brand-ink;
}
.login-field__ph {
color: #b3b8bd;
}
/* ===== 按钮 ===== */
.login-btn {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 92rpx;
margin-top: 32rpx;
border-radius: 46rpx;
font-size: 32rpx;
font-weight: 600;
letter-spacing: 8rpx;
}
.login-btn--submit {
color: #ffffff;
background: linear-gradient(135deg, $brand-green-light, $brand-green-dark);
box-shadow: 0 16rpx 32rpx rgba(31, 95, 71, 0.3);
}
.login-btn--ghost {
margin-top: 24rpx;
color: $brand-green;
border: 2rpx solid $brand-green;
background-color: #ffffff;
}
</style>