..
This commit is contained in:
+1
-1
@@ -85,7 +85,7 @@ public class ClientAuthController {
|
||||
@ApiOperation(value = "短信登录", nickname = "/sms-login", httpMethod = "POST")
|
||||
public Object doSmsLogin(ClientLoginBody loginBody, HttpServletRequest req) {
|
||||
try {
|
||||
String token = clientAuthService.doSmsLogin(loginBody);
|
||||
String token = clientAuthService.doLogin(loginBody);
|
||||
return Result.success().addData(token);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
|
||||
@@ -48,6 +48,16 @@ public class Client_user extends BaseModel implements Serializable {
|
||||
@Comment("单位ID")
|
||||
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
|
||||
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||
@Comment("openId")
|
||||
|
||||
@@ -10,7 +10,7 @@ public interface ClientAuthService {
|
||||
* @param loginBody 登录信息
|
||||
* @return token
|
||||
*/
|
||||
String doSmsLogin(ClientLoginBody loginBody);
|
||||
String doLogin(ClientLoginBody loginBody);
|
||||
|
||||
/**
|
||||
* 注册
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.budwk.app.client.auth.service;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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.models.Client_user;
|
||||
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.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
public class ClientAuthServiceImpl implements ClientAuthService {
|
||||
@@ -21,23 +26,34 @@ public class ClientAuthServiceImpl implements ClientAuthService {
|
||||
private ValidateService validateService;
|
||||
|
||||
@Override
|
||||
public String doSmsLogin(ClientLoginBody loginBody) {
|
||||
public String doLogin(ClientLoginBody loginBody) {
|
||||
Client_user user = null;
|
||||
int count = dao.count(Client_user.class, Cnd.where(Client_user::getPhone, "=", loginBody.getPhone()));
|
||||
if (count == 0) {
|
||||
// // 自动注册
|
||||
// user = new Client_user();
|
||||
// user.setPhone(loginBody.getPhone());
|
||||
// user.setUserName("用户" + loginBody.getPhone());
|
||||
// dao.insert(user);
|
||||
throw new BaseException("手机号码不存在");
|
||||
}
|
||||
|
||||
validateService.checkSMSCode(loginBody.getPhone(), loginBody.getSmsCode());
|
||||
user = dao.fetch(Client_user.class, Cnd.where(Client_user::getPhone, "=", loginBody.getPhone()));
|
||||
|
||||
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.checkLogin();
|
||||
return StpClientUtil.STP.getTokenValue();
|
||||
|
||||
@@ -9,4 +9,8 @@ public class ClientLoginBody {
|
||||
|
||||
private String smsCode;
|
||||
|
||||
private String password;
|
||||
|
||||
private String loginMethod;
|
||||
|
||||
}
|
||||
|
||||
@@ -62,16 +62,16 @@
|
||||
<view class="login-card">
|
||||
<view class="login-tabs">
|
||||
<view
|
||||
:class="['tab', activeMethod === 'phone' ? 'active' : '']"
|
||||
@click="setActiveMethod('phone')"
|
||||
:class="['tab', loginForm.loginMethod === 'sms' ? 'active' : '']"
|
||||
@click="loginForm.loginMethod = 'sms'"
|
||||
>
|
||||
手机快速登录
|
||||
验证码登录
|
||||
</view>
|
||||
<view
|
||||
:class="['tab', activeMethod === 'password' ? 'active' : '']"
|
||||
@click="setActiveMethod('password')"
|
||||
:class="['tab', loginForm.loginMethod === 'password' ? 'active' : '']"
|
||||
@click="loginForm.loginMethod = 'password'"
|
||||
>
|
||||
账号密码登录
|
||||
密码登录
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 验证码输入框 -->
|
||||
<view class="input-box">
|
||||
<view class="input-box" v-if="loginForm.loginMethod==='sms'">
|
||||
<t-icon name="shield-error" size="20px" class="input-icon"/>
|
||||
<input
|
||||
class="input"
|
||||
@@ -104,6 +104,18 @@
|
||||
</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">
|
||||
立即登录
|
||||
@@ -127,17 +139,14 @@ import {useUserStore} from "@/store";
|
||||
|
||||
const request = useRequest()
|
||||
const userStore = useUserStore()
|
||||
const activeMethod = ref('phone')
|
||||
|
||||
const loginForm = reactive({
|
||||
phone: '',
|
||||
smsCode: ''
|
||||
smsCode: '',
|
||||
password: '',
|
||||
loginMethod: 'sms'
|
||||
})
|
||||
|
||||
const setActiveMethod = (method) => {
|
||||
activeMethod.value = method
|
||||
}
|
||||
|
||||
// 倒计时相关
|
||||
const countdown = ref(0)
|
||||
const timer = ref(null)
|
||||
@@ -265,10 +274,14 @@ async function handleLogin() {
|
||||
Toast('请输入手机号码');
|
||||
return
|
||||
}
|
||||
if (!loginForm.smsCode) {
|
||||
if (!loginForm.smsCode && loginForm.loginMethod === 'sms') {
|
||||
Toast('请输入验证码');
|
||||
return
|
||||
}
|
||||
if (!loginForm.password && loginForm.loginMethod === 'password') {
|
||||
Toast('请输入密码');
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
userStore.smsLogin(loginForm).then(() => {
|
||||
@@ -318,7 +331,7 @@ async function handleLogin() {
|
||||
}
|
||||
}
|
||||
|
||||
function navigateToRegister(){
|
||||
function navigateToRegister() {
|
||||
uni.redirectTo({
|
||||
url: '/pages/login/register'
|
||||
})
|
||||
|
||||
@@ -1,8 +1,57 @@
|
||||
<template>
|
||||
<view class="register-container">
|
||||
<view class="title-area">
|
||||
<image class="bg-image" src="/static/login-bg.jpg" mode="aspectFill"></image>
|
||||
<view class="overlay"></view>
|
||||
<!-- 上半部分:图片区域 -->
|
||||
<view class="image-area">
|
||||
<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">
|
||||
<text class="main-title">JOIN US</text>
|
||||
<view class="sub-title-container">
|
||||
@@ -11,91 +60,77 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-wrapper">
|
||||
<!-- 注册表单区域 -->
|
||||
<view class="form-container">
|
||||
<view class="form-title">填写信息</view>
|
||||
<!-- 下半部分:表单区域 -->
|
||||
<view class="form-area">
|
||||
<!-- 姓名输入框 -->
|
||||
<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-icon-container">
|
||||
<t-icon name="user" size="22px" class="input-icon"/>
|
||||
</view>
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
v-model="formData.name"
|
||||
placeholder="请输入姓名"
|
||||
maxlength="20"
|
||||
/>
|
||||
</view>
|
||||
<!-- 手机号码输入框 -->
|
||||
<view class="input-box">
|
||||
<t-icon name="mobile" size="20px" class="input-icon"/>
|
||||
<input
|
||||
class="input"
|
||||
type="tel"
|
||||
v-model="formData.phone"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 手机号码输入框 -->
|
||||
<view class="input-box">
|
||||
<view class="input-icon-container">
|
||||
<t-icon name="mobile" size="22px" class="input-icon"/>
|
||||
</view>
|
||||
<input
|
||||
class="input"
|
||||
type="tel"
|
||||
v-model="formData.phone"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
/>
|
||||
<!-- 单位输入框 -->
|
||||
<view class="input-box" @click="showUnitPicker = true">
|
||||
<t-icon name="home" size="20px" class="input-icon"/>
|
||||
<view class="unit-selector" :class="{'has-value': formData.unitName}">
|
||||
{{ formData.unitName || '请选择单位' }}
|
||||
</view>
|
||||
<t-icon name="chevron-right" size="20px" class="arrow-icon"/>
|
||||
</view>
|
||||
|
||||
<!-- 单位输入框 -->
|
||||
<view class="input-box" @click="showUnitPicker = true">
|
||||
<view class="input-icon-container">
|
||||
<t-icon name="home" size="22px" class="input-icon"/>
|
||||
</view>
|
||||
<view class="input placeholder" :class="{'has-value': formData.unitName}">
|
||||
{{ formData.unitName || '请选择单位' }}
|
||||
</view>
|
||||
<t-icon name="chevron-right" size="20px" class="arrow-icon"/>
|
||||
</view>
|
||||
<!-- 身份证输入框 -->
|
||||
<view class="input-box">
|
||||
<t-icon name="creditcard" size="20px" class="input-icon"/>
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
v-model="formData.idCard"
|
||||
placeholder="请输入身份证号码"
|
||||
maxlength="18"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 身份证输入框 -->
|
||||
<view class="input-box">
|
||||
<view class="input-icon-container">
|
||||
<t-icon name="id-card" size="22px" class="input-icon"/>
|
||||
</view>
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
v-model="formData.idCard"
|
||||
placeholder="请输入身份证号码"
|
||||
maxlength="18"
|
||||
/>
|
||||
<!-- 验证码输入框 -->
|
||||
<view class="input-box">
|
||||
<t-icon name="shield-error" size="20px" class="input-icon"/>
|
||||
<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="input-box">
|
||||
<view class="input-icon-container">
|
||||
<t-icon name="shield-check" size="22px" class="input-icon"/>
|
||||
</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" @click="handleRegister">
|
||||
{{ registering ? '注册中...' : '立即注册' }}
|
||||
</view>
|
||||
|
||||
<!-- 注册按钮 -->
|
||||
<view class="submit-btn" :class="{ disabled: !isFormValid }" @click="handleRegister">
|
||||
{{ registering ? '注册中...' : '立即注册' }}
|
||||
</view>
|
||||
|
||||
<!-- 登录链接 -->
|
||||
<view class="login-link-box">
|
||||
<text>已有账号?</text>
|
||||
<text class="link-text" @click="goToLogin">立即登录</text>
|
||||
</view>
|
||||
<!-- 登录链接 -->
|
||||
<view class="login-link-box">
|
||||
<text>已有账号?</text>
|
||||
<text class="link-text" @click="goToLogin">立即登录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -107,36 +142,36 @@
|
||||
<view class="picker-title">选择单位</view>
|
||||
<view class="confirm-btn" @click="confirmUnitSelection">确定</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-box">
|
||||
<t-icon name="search" size="22px" class="search-icon" />
|
||||
<input
|
||||
class="search-input"
|
||||
type="text"
|
||||
v-model="searchKeyword"
|
||||
<input
|
||||
class="search-input"
|
||||
type="text"
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索单位"
|
||||
@input="handleSearch"
|
||||
@input="handleSearch"
|
||||
/>
|
||||
<t-icon
|
||||
v-if="searchKeyword"
|
||||
name="close-circle-filled"
|
||||
size="22px"
|
||||
class="clear-icon"
|
||||
@click="clearSearch"
|
||||
<t-icon
|
||||
v-if="searchKeyword"
|
||||
name="close-circle-filled"
|
||||
size="22px"
|
||||
class="clear-icon"
|
||||
@click="clearSearch"
|
||||
/>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 单位列表 -->
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="units-scroll-view"
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="units-scroll-view"
|
||||
scroll-with-animation
|
||||
:scroll-into-view="scrollViewId"
|
||||
>
|
||||
<view
|
||||
v-for="unit in filteredUnits"
|
||||
:key="unit.id"
|
||||
<view
|
||||
v-for="unit in filteredUnits"
|
||||
:key="unit.id"
|
||||
:id="'unit-' + unit.id"
|
||||
class="unit-item"
|
||||
:class="{'unit-selected': selectedUnitId === unit.id}"
|
||||
@@ -187,7 +222,7 @@ const filteredUnits = computed(() => {
|
||||
if (!searchKeyword.value) {
|
||||
return unitList.value;
|
||||
}
|
||||
return unitList.value.filter(unit =>
|
||||
return unitList.value.filter(unit =>
|
||||
unit.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
);
|
||||
});
|
||||
@@ -238,7 +273,7 @@ const confirmUnitSelection = () => {
|
||||
Toast({ message: '请选择单位', theme: 'warning' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const selectedUnit = unitList.value.find(unit => unit.id === selectedUnitId.value);
|
||||
if (selectedUnit) {
|
||||
formData.unitId = selectedUnit.id;
|
||||
@@ -268,16 +303,16 @@ const sendVerificationCode = async () => {
|
||||
Toast({ message: '请输入正确的手机号', theme: 'error' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (countdown.value > 0) return;
|
||||
|
||||
|
||||
sendingCode.value = true;
|
||||
try {
|
||||
const { code, msg } = await useRequest().post('/client/auth/send-verification-code', {
|
||||
phone: formData.phone,
|
||||
type: 'register'
|
||||
});
|
||||
|
||||
|
||||
if (code === 0) {
|
||||
Toast({ message: '验证码已发送' });
|
||||
startCountdown();
|
||||
@@ -309,7 +344,7 @@ const handleRegister = async () => {
|
||||
Toast({ message: '请完善注册信息', theme: 'error' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
registering.value = true;
|
||||
try {
|
||||
const { code, msg } = await useRequest().post('/client/auth/register', {
|
||||
@@ -319,7 +354,7 @@ const handleRegister = async () => {
|
||||
idCard: formData.idCard,
|
||||
verificationCode: formData.verificationCode
|
||||
});
|
||||
|
||||
|
||||
if (code === 0) {
|
||||
Toast({ message: '注册成功' });
|
||||
// 注册成功后跳转到登录页
|
||||
@@ -368,184 +403,173 @@ onMounted(() => {
|
||||
|
||||
<style scoped lang="less">
|
||||
.register-container {
|
||||
height: 100vh;
|
||||
background: #f4f2f2;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.title-area {
|
||||
.image-area {
|
||||
padding: 0;
|
||||
height: 30vh;
|
||||
position: relative;
|
||||
height: 380rpx;
|
||||
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 {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
display: none; /* 隐藏背景图片,使用动画背景 */
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
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;
|
||||
display: none; /* 不需要叠加层 */
|
||||
}
|
||||
|
||||
.title-content {
|
||||
padding: 0 30px;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
padding: 80rpx 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 68rpx;
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
letter-spacing: 2rpx;
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: #ffffff;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.sub-title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 38rpx;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.form-wrapper {
|
||||
font-size: 18px;
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.form-area {
|
||||
flex: 1;
|
||||
background-color: #ffffff;
|
||||
padding: 0 40rpx;
|
||||
margin-top: -60rpx;
|
||||
z-index: 10;
|
||||
border-radius: 50rpx 50rpx 0 0;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
padding: 60rpx 0 50rpx;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 40rpx;
|
||||
text-align: center;
|
||||
border-radius: 30px 30px 0 0;
|
||||
margin-top: -30px;
|
||||
padding: 30px 30px 0;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.input-box {
|
||||
height: 50px;
|
||||
background-color: #eceaea;
|
||||
border-radius: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
margin-bottom: 30rpx;
|
||||
position: relative;
|
||||
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;
|
||||
padding: 0 15px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
color: #f45a5a;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
height: 40rpx;
|
||||
line-height: 40rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
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 {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.verify-code-btn {
|
||||
padding: 10rpx 20rpx;
|
||||
background-color: #f45a5a;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
border-radius: 8rpx;
|
||||
text-align: center;
|
||||
min-width: 180rpx;
|
||||
margin-left: 10rpx;
|
||||
.arrow-icon {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background-color: #f8a8a8;
|
||||
color: #fff;
|
||||
pointer-events: none;
|
||||
.verify-code-btn {
|
||||
color: #f45a5a;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
min-width: auto;
|
||||
|
||||
&.disabled {
|
||||
color: #999;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background: linear-gradient(135deg, #f45a5a, #f78484);
|
||||
height: 90rpx;
|
||||
border-radius: 45rpx;
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
margin-top: 32px;
|
||||
height: 50px;
|
||||
background-color: #f45a5a;
|
||||
border-radius: 25px;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 50rpx 0 30rpx;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.submit-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.submit-btn.disabled {
|
||||
opacity: 0.7;
|
||||
background: #ccc;
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
background-color: #f45a5a;
|
||||
}
|
||||
}
|
||||
|
||||
.login-link-box {
|
||||
margin-top: 16px;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.link-text {
|
||||
color: #f45a5a;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.custom-picker {
|
||||
|
||||
@@ -34,7 +34,7 @@ export default defineConfig({
|
||||
},*/
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://192.168.41.126:9090',
|
||||
target: 'http://localhost:9090',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user