..
This commit is contained in:
+1
-1
@@ -85,7 +85,7 @@ public class ClientAuthController {
|
|||||||
@ApiOperation(value = "短信登录", nickname = "/sms-login", httpMethod = "POST")
|
@ApiOperation(value = "短信登录", nickname = "/sms-login", httpMethod = "POST")
|
||||||
public Object doSmsLogin(ClientLoginBody loginBody, HttpServletRequest req) {
|
public Object doSmsLogin(ClientLoginBody loginBody, HttpServletRequest req) {
|
||||||
try {
|
try {
|
||||||
String token = clientAuthService.doSmsLogin(loginBody);
|
String token = clientAuthService.doLogin(loginBody);
|
||||||
return Result.success().addData(token);
|
return Result.success().addData(token);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
|
|||||||
@@ -48,6 +48,16 @@ public class Client_user extends BaseModel implements Serializable {
|
|||||||
@Comment("单位ID")
|
@Comment("单位ID")
|
||||||
private String unitId;
|
private String unitId;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||||
|
@Comment("密码")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||||
|
@Comment("盐")
|
||||||
|
private String salt;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
@ColDefine(type = ColType.VARCHAR, width = 255)
|
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||||
@Comment("openId")
|
@Comment("openId")
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public interface ClientAuthService {
|
|||||||
* @param loginBody 登录信息
|
* @param loginBody 登录信息
|
||||||
* @return token
|
* @return token
|
||||||
*/
|
*/
|
||||||
String doSmsLogin(ClientLoginBody loginBody);
|
String doLogin(ClientLoginBody loginBody);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册
|
* 注册
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.budwk.app.client.auth.service;
|
package com.budwk.app.client.auth.service;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.budwk.app.base.exception.BaseException;
|
import com.budwk.app.base.exception.BaseException;
|
||||||
|
import com.budwk.app.base.utils.PwdUtil;
|
||||||
import com.budwk.app.client.auth.vo.ClientLoginBody;
|
import com.budwk.app.client.auth.vo.ClientLoginBody;
|
||||||
import com.budwk.app.client.auth.models.Client_user;
|
import com.budwk.app.client.auth.models.Client_user;
|
||||||
import com.budwk.app.client.auth.vo.ClientRegisterBody;
|
import com.budwk.app.client.auth.vo.ClientRegisterBody;
|
||||||
@@ -11,6 +13,9 @@ import org.nutz.dao.Cnd;
|
|||||||
import org.nutz.dao.Dao;
|
import org.nutz.dao.Dao;
|
||||||
import org.nutz.ioc.loader.annotation.Inject;
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
import org.nutz.lang.Strings;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@IocBean
|
@IocBean
|
||||||
public class ClientAuthServiceImpl implements ClientAuthService {
|
public class ClientAuthServiceImpl implements ClientAuthService {
|
||||||
@@ -21,23 +26,34 @@ public class ClientAuthServiceImpl implements ClientAuthService {
|
|||||||
private ValidateService validateService;
|
private ValidateService validateService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String doSmsLogin(ClientLoginBody loginBody) {
|
public String doLogin(ClientLoginBody loginBody) {
|
||||||
Client_user user = null;
|
Client_user user = null;
|
||||||
int count = dao.count(Client_user.class, Cnd.where(Client_user::getPhone, "=", loginBody.getPhone()));
|
int count = dao.count(Client_user.class, Cnd.where(Client_user::getPhone, "=", loginBody.getPhone()));
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
// // 自动注册
|
|
||||||
// user = new Client_user();
|
|
||||||
// user.setPhone(loginBody.getPhone());
|
|
||||||
// user.setUserName("用户" + loginBody.getPhone());
|
|
||||||
// dao.insert(user);
|
|
||||||
throw new BaseException("手机号码不存在");
|
throw new BaseException("手机号码不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
validateService.checkSMSCode(loginBody.getPhone(), loginBody.getSmsCode());
|
|
||||||
user = dao.fetch(Client_user.class, Cnd.where(Client_user::getPhone, "=", loginBody.getPhone()));
|
user = dao.fetch(Client_user.class, Cnd.where(Client_user::getPhone, "=", loginBody.getPhone()));
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new BaseException("用户登录失败");
|
throw new BaseException("用户不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StrUtil.isBlank(loginBody.getLoginMethod()) || !List.of("sms", "password").contains(loginBody.getLoginMethod())) {
|
||||||
|
throw new BaseException("登录方式不正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginBody.getLoginMethod().equals("sms")) {
|
||||||
|
validateService.checkSMSCode(loginBody.getPhone(), loginBody.getSmsCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginBody.getLoginMethod().equals("password")) {
|
||||||
|
String hashedPassword = PwdUtil.getPassword(loginBody.getPassword(), user.getSalt());
|
||||||
|
if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) {
|
||||||
|
throw new BaseException("密码不正确");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StpClientUtil.STP.login(user.getId());
|
StpClientUtil.STP.login(user.getId());
|
||||||
StpClientUtil.STP.checkLogin();
|
StpClientUtil.STP.checkLogin();
|
||||||
return StpClientUtil.STP.getTokenValue();
|
return StpClientUtil.STP.getTokenValue();
|
||||||
|
|||||||
@@ -9,4 +9,8 @@ public class ClientLoginBody {
|
|||||||
|
|
||||||
private String smsCode;
|
private String smsCode;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private String loginMethod;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,16 +62,16 @@
|
|||||||
<view class="login-card">
|
<view class="login-card">
|
||||||
<view class="login-tabs">
|
<view class="login-tabs">
|
||||||
<view
|
<view
|
||||||
:class="['tab', activeMethod === 'phone' ? 'active' : '']"
|
:class="['tab', loginForm.loginMethod === 'sms' ? 'active' : '']"
|
||||||
@click="setActiveMethod('phone')"
|
@click="loginForm.loginMethod = 'sms'"
|
||||||
>
|
>
|
||||||
手机快速登录
|
验证码登录
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
:class="['tab', activeMethod === 'password' ? 'active' : '']"
|
:class="['tab', loginForm.loginMethod === 'password' ? 'active' : '']"
|
||||||
@click="setActiveMethod('password')"
|
@click="loginForm.loginMethod = 'password'"
|
||||||
>
|
>
|
||||||
账号密码登录
|
密码登录
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 验证码输入框 -->
|
<!-- 验证码输入框 -->
|
||||||
<view class="input-box">
|
<view class="input-box" v-if="loginForm.loginMethod==='sms'">
|
||||||
<t-icon name="shield-error" size="20px" class="input-icon"/>
|
<t-icon name="shield-error" size="20px" class="input-icon"/>
|
||||||
<input
|
<input
|
||||||
class="input"
|
class="input"
|
||||||
@@ -104,6 +104,18 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 密码输入框 -->
|
||||||
|
<view class="input-box" v-else>
|
||||||
|
<t-icon name="user-password" size="20px" class="input-icon"/>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
type="password"
|
||||||
|
v-model="loginForm.password"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
maxlength="20"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 登录按钮 -->
|
<!-- 登录按钮 -->
|
||||||
<view class="submit-btn" @click="handleLogin">
|
<view class="submit-btn" @click="handleLogin">
|
||||||
立即登录
|
立即登录
|
||||||
@@ -127,17 +139,14 @@ import {useUserStore} from "@/store";
|
|||||||
|
|
||||||
const request = useRequest()
|
const request = useRequest()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const activeMethod = ref('phone')
|
|
||||||
|
|
||||||
const loginForm = reactive({
|
const loginForm = reactive({
|
||||||
phone: '',
|
phone: '',
|
||||||
smsCode: ''
|
smsCode: '',
|
||||||
|
password: '',
|
||||||
|
loginMethod: 'sms'
|
||||||
})
|
})
|
||||||
|
|
||||||
const setActiveMethod = (method) => {
|
|
||||||
activeMethod.value = method
|
|
||||||
}
|
|
||||||
|
|
||||||
// 倒计时相关
|
// 倒计时相关
|
||||||
const countdown = ref(0)
|
const countdown = ref(0)
|
||||||
const timer = ref(null)
|
const timer = ref(null)
|
||||||
@@ -265,10 +274,14 @@ async function handleLogin() {
|
|||||||
Toast('请输入手机号码');
|
Toast('请输入手机号码');
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!loginForm.smsCode) {
|
if (!loginForm.smsCode && loginForm.loginMethod === 'sms') {
|
||||||
Toast('请输入验证码');
|
Toast('请输入验证码');
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!loginForm.password && loginForm.loginMethod === 'password') {
|
||||||
|
Toast('请输入密码');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
userStore.smsLogin(loginForm).then(() => {
|
userStore.smsLogin(loginForm).then(() => {
|
||||||
@@ -318,7 +331,7 @@ async function handleLogin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateToRegister(){
|
function navigateToRegister() {
|
||||||
uni.redirectTo({
|
uni.redirectTo({
|
||||||
url: '/pages/login/register'
|
url: '/pages/login/register'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="register-container">
|
<view class="register-container">
|
||||||
<view class="title-area">
|
<!-- 上半部分:图片区域 -->
|
||||||
<image class="bg-image" src="/static/login-bg.jpg" mode="aspectFill"></image>
|
<view class="image-area">
|
||||||
<view class="overlay"></view>
|
<div class="tech-animation">
|
||||||
|
<svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 300 100">
|
||||||
|
<pattern id="pattern-hex" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse"
|
||||||
|
patternTransform="scale(0.8)">
|
||||||
|
<rect x="0" y="0" width="10" height="10" fill="none"/>
|
||||||
|
<path d="M0,5 L2.5,0 L7.5,0 L10,5 L7.5,10 L2.5,10 Z" stroke-width="0.5" stroke="rgba(244, 90, 90, 0.2)"
|
||||||
|
fill="rgba(244, 90, 90, 0.1)">
|
||||||
|
<animate attributeName="stroke-opacity" values="0.1;0.3;0.1" dur="3s" repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
</pattern>
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-hex)"/>
|
||||||
|
|
||||||
|
<g fill="none" stroke-width="0.5">
|
||||||
|
<path d="M20,50 Q60,20 100,50 T180,50 T260,50" stroke="rgba(244, 90, 90, 0.6)">
|
||||||
|
<animate attributeName="d"
|
||||||
|
values="M20,50 Q60,20 100,50 T180,50 T260,50;
|
||||||
|
M20,50 Q60,80 100,50 T180,50 T260,50;
|
||||||
|
M20,50 Q60,20 100,50 T180,50 T260,50"
|
||||||
|
dur="10s" repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<path d="M20,70 Q60,40 100,70 T180,70 T260,70" stroke="rgba(244, 90, 90, 0.4)">
|
||||||
|
<animate attributeName="d"
|
||||||
|
values="M20,70 Q60,40 100,70 T180,70 T260,70;
|
||||||
|
M20,70 Q60,100 100,70 T180,70 T260,70;
|
||||||
|
M20,70 Q60,40 100,70 T180,70 T260,70"
|
||||||
|
dur="12s" repeatCount="indefinite"/>
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<circle cx="50" cy="50" r="1" fill="rgba(244, 90, 90, 0.8)">
|
||||||
|
<animate attributeName="cx" values="50;250;50" dur="20s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="cy" values="50;30;80;50" dur="16s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="r" values="1;1.5;1" dur="5s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
|
||||||
|
<circle cx="150" cy="30" r="1" fill="rgba(244, 90, 90, 0.8)">
|
||||||
|
<animate attributeName="cx" values="150;100;200;150" dur="25s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="cy" values="30;60;20;30" dur="18s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="r" values="1;1.8;1" dur="6s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
|
||||||
|
<circle cx="250" cy="70" r="1" fill="rgba(244, 90, 90, 0.8)">
|
||||||
|
<animate attributeName="cx" values="250;150;250" dur="22s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="cy" values="70;40;70" dur="15s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="r" values="1;1.2;1" dur="4s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
<view class="title-content">
|
<view class="title-content">
|
||||||
<text class="main-title">JOIN US</text>
|
<text class="main-title">JOIN US</text>
|
||||||
<view class="sub-title-container">
|
<view class="sub-title-container">
|
||||||
@@ -11,91 +60,77 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="form-wrapper">
|
<!-- 下半部分:表单区域 -->
|
||||||
<!-- 注册表单区域 -->
|
<view class="form-area">
|
||||||
<view class="form-container">
|
<!-- 姓名输入框 -->
|
||||||
<view class="form-title">填写信息</view>
|
<view class="input-box">
|
||||||
|
<t-icon name="user" size="20px" class="input-icon"/>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
v-model="formData.name"
|
||||||
|
placeholder="请输入姓名"
|
||||||
|
maxlength="20"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 姓名输入框 -->
|
<!-- 手机号码输入框 -->
|
||||||
<view class="input-box">
|
<view class="input-box">
|
||||||
<view class="input-icon-container">
|
<t-icon name="mobile" size="20px" class="input-icon"/>
|
||||||
<t-icon name="user" size="22px" class="input-icon"/>
|
<input
|
||||||
</view>
|
class="input"
|
||||||
<input
|
type="tel"
|
||||||
class="input"
|
v-model="formData.phone"
|
||||||
type="text"
|
placeholder="请输入手机号"
|
||||||
v-model="formData.name"
|
maxlength="11"
|
||||||
placeholder="请输入姓名"
|
/>
|
||||||
maxlength="20"
|
</view>
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 手机号码输入框 -->
|
<!-- 单位输入框 -->
|
||||||
<view class="input-box">
|
<view class="input-box" @click="showUnitPicker = true">
|
||||||
<view class="input-icon-container">
|
<t-icon name="home" size="20px" class="input-icon"/>
|
||||||
<t-icon name="mobile" size="22px" class="input-icon"/>
|
<view class="unit-selector" :class="{'has-value': formData.unitName}">
|
||||||
</view>
|
{{ formData.unitName || '请选择单位' }}
|
||||||
<input
|
|
||||||
class="input"
|
|
||||||
type="tel"
|
|
||||||
v-model="formData.phone"
|
|
||||||
placeholder="请输入手机号"
|
|
||||||
maxlength="11"
|
|
||||||
/>
|
|
||||||
</view>
|
</view>
|
||||||
|
<t-icon name="chevron-right" size="20px" class="arrow-icon"/>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 单位输入框 -->
|
<!-- 身份证输入框 -->
|
||||||
<view class="input-box" @click="showUnitPicker = true">
|
<view class="input-box">
|
||||||
<view class="input-icon-container">
|
<t-icon name="creditcard" size="20px" class="input-icon"/>
|
||||||
<t-icon name="home" size="22px" class="input-icon"/>
|
<input
|
||||||
</view>
|
class="input"
|
||||||
<view class="input placeholder" :class="{'has-value': formData.unitName}">
|
type="text"
|
||||||
{{ formData.unitName || '请选择单位' }}
|
v-model="formData.idCard"
|
||||||
</view>
|
placeholder="请输入身份证号码"
|
||||||
<t-icon name="chevron-right" size="20px" class="arrow-icon"/>
|
maxlength="18"
|
||||||
</view>
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 身份证输入框 -->
|
<!-- 验证码输入框 -->
|
||||||
<view class="input-box">
|
<view class="input-box">
|
||||||
<view class="input-icon-container">
|
<t-icon name="shield-error" size="20px" class="input-icon"/>
|
||||||
<t-icon name="id-card" size="22px" class="input-icon"/>
|
<input
|
||||||
</view>
|
class="input"
|
||||||
<input
|
type="number"
|
||||||
class="input"
|
v-model="formData.verificationCode"
|
||||||
type="text"
|
placeholder="请输入验证码"
|
||||||
v-model="formData.idCard"
|
maxlength="6"
|
||||||
placeholder="请输入身份证号码"
|
/>
|
||||||
maxlength="18"
|
<view class="verify-code-btn" @click="sendVerificationCode" :class="{ disabled: countdown > 0 }">
|
||||||
/>
|
{{ countdown > 0 ? `${countdown}秒` : '获取验证码' }}
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 验证码输入框 -->
|
<!-- 注册按钮 -->
|
||||||
<view class="input-box">
|
<view class="submit-btn" @click="handleRegister">
|
||||||
<view class="input-icon-container">
|
{{ registering ? '注册中...' : '立即注册' }}
|
||||||
<t-icon name="shield-check" size="22px" class="input-icon"/>
|
</view>
|
||||||
</view>
|
|
||||||
<input
|
|
||||||
class="input"
|
|
||||||
type="number"
|
|
||||||
v-model="formData.verificationCode"
|
|
||||||
placeholder="请输入验证码"
|
|
||||||
maxlength="6"
|
|
||||||
/>
|
|
||||||
<view class="verify-code-btn" @click="sendVerificationCode" :class="{ disabled: countdown > 0 }">
|
|
||||||
{{ countdown > 0 ? `${countdown}秒` : '获取验证码' }}
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 注册按钮 -->
|
<!-- 登录链接 -->
|
||||||
<view class="submit-btn" :class="{ disabled: !isFormValid }" @click="handleRegister">
|
<view class="login-link-box">
|
||||||
{{ registering ? '注册中...' : '立即注册' }}
|
<text>已有账号?</text>
|
||||||
</view>
|
<text class="link-text" @click="goToLogin">立即登录</text>
|
||||||
|
|
||||||
<!-- 登录链接 -->
|
|
||||||
<view class="login-link-box">
|
|
||||||
<text>已有账号?</text>
|
|
||||||
<text class="link-text" @click="goToLogin">立即登录</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -368,184 +403,173 @@ onMounted(() => {
|
|||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.register-container {
|
.register-container {
|
||||||
|
height: 100vh;
|
||||||
|
background: #f4f2f2;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-area {
|
.image-area {
|
||||||
|
padding: 0;
|
||||||
|
height: 30vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 380rpx;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: linear-gradient(135deg, #7a1818 0%, #d03e3e 50%, #f45a5a 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-animation {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-animation svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-image {
|
.bg-image {
|
||||||
position: absolute;
|
display: none; /* 隐藏背景图片,使用动画背景 */
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
position: absolute;
|
display: none; /* 不需要叠加层 */
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background: linear-gradient(to bottom, rgba(244, 90, 90, 0.8), rgba(244, 90, 90, 0.5));
|
|
||||||
z-index: 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-content {
|
.title-content {
|
||||||
|
padding: 0 30px;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 3;
|
z-index: 1;
|
||||||
padding: 80rpx 40rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-title {
|
.main-title {
|
||||||
font-size: 68rpx;
|
font-size: 36px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #fff;
|
margin-bottom: 8px;
|
||||||
margin-bottom: 20rpx;
|
display: block;
|
||||||
letter-spacing: 2rpx;
|
position: relative;
|
||||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
z-index: 1;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-title-container {
|
.sub-title-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-title {
|
.sub-title {
|
||||||
font-size: 38rpx;
|
font-size: 18px;
|
||||||
color: #fff;
|
color: #ffffff;
|
||||||
font-weight: 500;
|
|
||||||
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-wrapper {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-area {
|
||||||
|
flex: 1;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
padding: 0 40rpx;
|
border-radius: 30px 30px 0 0;
|
||||||
margin-top: -60rpx;
|
margin-top: -30px;
|
||||||
z-index: 10;
|
padding: 30px 30px 0;
|
||||||
border-radius: 50rpx 50rpx 0 0;
|
position: relative;
|
||||||
}
|
z-index: 2;
|
||||||
|
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.05);
|
||||||
.form-container {
|
|
||||||
padding: 60rpx 0 50rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 40rpx;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-box {
|
.input-box {
|
||||||
|
height: 50px;
|
||||||
|
background-color: #eceaea;
|
||||||
|
border-radius: 25px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 20rpx 0;
|
padding: 0 15px;
|
||||||
margin-bottom: 30rpx;
|
margin-bottom: 16px;
|
||||||
position: relative;
|
border-bottom: none;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-box:focus-within {
|
|
||||||
border-bottom-color: #f45a5a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-icon-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 70rpx;
|
|
||||||
height: 70rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(244, 90, 90, 0.1);
|
|
||||||
margin-right: 20rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-icon {
|
.input-icon {
|
||||||
color: #f45a5a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow-icon {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 28rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
line-height: 40rpx;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
color: #999;
|
color: #999;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-icon-container {
|
||||||
|
display: none; /* 不使用图标容器 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.input, .unit-selector {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
font-size: 15px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-selector {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-value {
|
.has-value {
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.verify-code-btn {
|
.arrow-icon {
|
||||||
padding: 10rpx 20rpx;
|
color: #999;
|
||||||
background-color: #f45a5a;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 26rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
text-align: center;
|
|
||||||
min-width: 180rpx;
|
|
||||||
margin-left: 10rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled {
|
.verify-code-btn {
|
||||||
background-color: #f8a8a8;
|
color: #f45a5a;
|
||||||
color: #fff;
|
font-size: 14px;
|
||||||
pointer-events: none;
|
white-space: nowrap;
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
min-width: auto;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
color: #999;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
background: linear-gradient(135deg, #f45a5a, #f78484);
|
margin-top: 32px;
|
||||||
height: 90rpx;
|
height: 50px;
|
||||||
border-radius: 45rpx;
|
background-color: #f45a5a;
|
||||||
color: #fff;
|
border-radius: 25px;
|
||||||
font-size: 32rpx;
|
color: white;
|
||||||
|
font-size: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 50rpx 0 30rpx;
|
|
||||||
transition: transform 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-btn:active {
|
&.disabled {
|
||||||
transform: scale(0.98);
|
opacity: 0.6;
|
||||||
}
|
background-color: #f45a5a;
|
||||||
|
}
|
||||||
.submit-btn.disabled {
|
|
||||||
opacity: 0.7;
|
|
||||||
background: #ccc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-link-box {
|
.login-link-box {
|
||||||
|
margin-top: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #666;
|
color: #666;
|
||||||
margin: 20rpx 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-text {
|
.link-text {
|
||||||
color: #f45a5a;
|
color: #f45a5a;
|
||||||
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-picker {
|
.custom-picker {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default defineConfig({
|
|||||||
},*/
|
},*/
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://192.168.41.126:9090',
|
target: 'http://localhost:9090',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/api/, '')
|
rewrite: (path) => path.replace(/^\/api/, '')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user