Explorar o código

初始别人的微信登录

xiaocao hai 7 meses
pai
achega
9801a9fb7f
Modificáronse 1 ficheiros con 924 adicións e 0 borrados
  1. 924 0
      src/pages/system/login/login.vue

+ 924 - 0
src/pages/system/login/login.vue

@@ -0,0 +1,924 @@
+<template>
+	<view class="login-page">
+		<!-- 状态栏占位 -->
+		<view class="status-bar"></view>
+
+		<!-- 水墨背景 -->
+		<view class="ink-bg">
+			<view class="ink-wash ink-1"></view>
+			<view class="ink-wash ink-2"></view>
+			<view class="ink-wash ink-3"></view>
+		</view>
+
+		<!-- 返回按钮 -->
+		<view class="nav-back pad-t-20" @click="goBack">
+			<view class="back-btn">
+				<u-icon name="arrow-left" size="30"></u-icon>
+			</view>
+		</view>
+
+		<!-- Logo和标题 -->
+		<view class="header">
+			<view class="logo-container">
+				<view class="logo-circle">
+					<image class="logo" mode="aspectFit"
+						src="https://ndtk.tos-cn-guangzhou.volces.com/uploads/156adea827104e38ae0e25ec4701ecfe.jpg">
+					</image>
+				</view>
+				<view class="logo-shadow"></view>
+			</view>
+			<view class="title-section">
+				<text class="title">宏匠</text>
+				<view class="title-brush"></view>
+			</view>
+			<text class="subtitle">唐卡世界,一键开启</text>
+		</view>
+
+		<!-- 登录按钮 -->
+		<view class="login-section">
+			<view class="btn-container">
+				<button :class="{ 'btn-loading': isLoading }" class="wechat-btn" @tap="handleWxLogin">
+					<view class="btn-ink"></view>
+					<text class="font32">一键快捷登录</text>
+				</button>
+			</view>
+
+			<view class="zen-divider">
+				<view class="zen-dot"></view>
+				<view class="zen-line"></view>
+				<view class="zen-dot"></view>
+			</view>
+
+			<view class="btn-container">
+				<button class="browse-btn" @tap="handleBrowse">
+					<text>暂不登录,先去逛逛</text>
+				</button>
+			</view>
+
+			<view class="agreement">
+				<view class="checkbox-wrapper" @tap="toggleAgreement">
+					<view :class="{ 'checked': isAgree }" class="zen-checkbox">
+						<view v-if="isAgree" class="zen-circle-inner"></view>
+					</view>
+				</view>
+				<text class="agreement-text">
+					登录即代表您已同意
+					<text class="link" @tap="goToPrivacy">《隐私政策》</text>
+					和
+					<text class="link" @tap="goToService">《服务协议》</text>
+				</text>
+			</view>
+		</view>
+
+		<!-- 底部装饰 -->
+		<view class="bottom-decoration">
+			<view class="zen-mountain">
+				<view class="mountain-peak"></view>
+				<view class="mountain-base"></view>
+			</view>
+		</view>
+
+		<!-- 首次登录设置弹框 -->
+		<u-popup :show="showProfileModal">
+			<view class="profile-modal">
+				<view class="modal-header">
+					<text class="modal-title">完善个人信息</text>
+					<text class="modal-subtitle">请设置您的账号和头像</text>
+				</view>
+
+				<view class="modal-content">
+					<!-- 头像选择 -->
+					<view class="avatar-section">
+						<text class="section-label">头像</text>
+						<button :disabled="isUploadingAvatar" class="avatar-button" open-type="chooseAvatar"
+							@chooseavatar="onChooseAvatar">
+							<view class="avatar-container">
+								<image :src="tempAvatar || '/static/images/avatar.png'" class="avatar-preview"
+									mode="aspectFill"></image>
+								<view class="avatar-overlay">
+									<u-icon v-if="!isUploadingAvatar" color="#fff" name="camera" size="24"></u-icon>
+									<u-loading-icon v-else color="#fff" mode="spinner" size="24"></u-loading-icon>
+								</view>
+							</view>
+						</button>
+					</view>
+
+					<!-- 账号输入 -->
+					<view class="account-section">
+						<text class="section-label">用户名</text>
+						<u-input v-model="tempAccount" :clearable="true" :maxlength="20" border="surround"
+							placeholder="请输入用户名"></u-input>
+					</view>
+				</view>
+
+				<view class="modal-actions">
+					<button :disabled="!tempAccount.trim() || isUploadingAvatar" class="confirm-btn"
+						@tap="confirmProfile">
+						<text v-if="!isUploadingAvatar">确认</text>
+						<text v-else>头像上传中...</text>
+					</button>
+				</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+
+// import {
+// 	wxLogin,
+// 	updateProfile
+// } from '@/config/api.js';
+// import {
+// 	UPLOAD_URL
+// } from '@/common/config.js';
+
+export default {
+	data() {
+		return {
+			isAgree: false,
+			isLoading: false,
+			redirectUrl: '', // 登录成功后的重定向地址
+			showProfileModal: false, // 显示个人信息设置弹框
+			tempAccount: '', // 临时账号
+			tempAvatar: '', // 临时头像
+			loginData: null, // 临时保存登录数据
+			isUploadingAvatar: false // 头像上传状态
+		}
+	},
+	onLoad(options) {
+		// 获取重定向地址
+		if (options.redirect) {
+			this.redirectUrl = decodeURIComponent(options.redirect);
+		}
+	},
+	methods: {
+		// 处理微信登录
+		async handleWxLogin() {
+			if (!this.isAgree) {
+				let that = this
+				// 弹出确认弹框询问用户是否同意协议
+				uni.showModal({
+					title: '提示',
+					content: '登录需要您同意《隐私政策》和《服务协议》,是否同意?',
+					success: function (res) {
+						if (res.confirm) {
+							// 用户点击同意,自动勾选协议并执行登录
+							that.isAgree = true;
+							that.executeLogin();
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+
+				return;
+			}
+
+			this.executeLogin();
+		},
+
+		// 执行登录逻辑
+		async executeLogin() {
+			uni.showLoading({
+				mask: true
+			})
+			this.isLoading = true;
+
+			uni.getUserProfile({
+				desc: '用于完善会员资料',
+				success: (profileRes) => {
+					uni.login({
+						provider: 'weixin',
+						success: async (loginRes) => {
+							try {
+								const loginData = await wxLogin({
+									code: loginRes.code,
+									account: profileRes.userInfo.nickName,
+									avatarUrl: profileRes.userInfo.avatarUrl,
+									grant_type: 'wechat'
+								});
+
+								if (loginData.code === 200) {
+									// 检查是否为首次登录
+									if (loginData.isFirstLogin) {
+										// 首次登录,显示个人信息设置弹框
+										this.loginData = loginData;
+										this.tempAccount = loginData.account || '';
+										this.tempAvatar = loginData.avatar || '';
+										this.showProfileModal = true;
+										this.isLoading = false;
+										uni.setStorageSync('access_token', loginData
+											.access_token);
+										uni.setStorageSync('refresh_token', loginData
+											.refresh_token);
+										uni.setStorageSync('user', loginData);
+
+										this.$store.commit('isLogin', true);
+										this.$store.commit('refresh_token', loginData
+											.refresh_token);
+										uni.hideLoading();
+									} else {
+										// 非首次登录,直接保存并跳转
+										this.saveLoginDataAndRedirect(loginData);
+									}
+								} else {
+									uni.showToast({
+										title: loginData.msg || '登录失败',
+										icon: 'none'
+									});
+								}
+							} catch (e) {
+								console.error('登录错误:', e);
+								uni.showToast({
+									title: '登录失败,请重试',
+									icon: 'none'
+								});
+							} finally {
+								this.isLoading = false;
+							}
+						},
+						fail: () => {
+							this.isLoading = false;
+						}
+					});
+				},
+				fail: (err) => {
+					this.isLoading = false;
+					if (err.errMsg.includes('cancel')) {
+						return; // 用户取消,不显示提示
+					}
+					uni.showToast({
+						title: '获取用户信息失败',
+						icon: 'none'
+					});
+				}
+			});
+		},
+
+		// 切换协议同意状态
+		toggleAgreement() {
+			this.isAgree = !this.isAgree;
+		},
+
+		// 返回上一页
+		goBack() {
+			uni.navigateBack();
+		},
+
+		// 跳转到隐私政策
+		goToPrivacy() {
+			uni.navigateTo({
+				url: '/packageUser/pages/richtext/index'
+			});
+		},
+
+		// 跳转到服务协议
+		goToService() {
+			uni.navigateTo({
+				url: '/packageUser/pages/richtext/index'
+			});
+		},
+
+		handleBrowse() {
+			// 直接返回首页
+			uni.switchTab({
+				url: '/pages/index/indexNew'
+			});
+		},
+
+		// 保存登录数据并跳转
+		saveLoginDataAndRedirect(loginData) {
+			uni.setStorageSync('access_token', loginData.access_token);
+			uni.setStorageSync('refresh_token', loginData.refresh_token);
+			uni.setStorageSync('user', loginData);
+
+			this.$store.commit('isLogin', true);
+			this.$store.commit('refresh_token', loginData.refresh_token);
+
+			uni.showToast({
+				title: '登录成功',
+				icon: 'success',
+				duration: 1000
+			});
+
+			// 登录成功后的跳转
+			setTimeout(() => {
+				if (this.redirectUrl) {
+					uni.redirectTo({
+						url: this.redirectUrl
+					});
+				} else {
+					// 检查是否有待处理的邀请,如果有则跳转到首页触发邀请弹框
+					const pendingInvitation = uni.getStorageSync('pendingInvitation');
+					if (pendingInvitation && pendingInvitation.inviterId) {
+						console.log('登录成功,发现待处理邀请,跳转到首页');
+						// 使用 reLaunch 确保首页能重新加载并检查邀请
+						uni.reLaunch({
+							url: '/pages/index/indexNew'
+						});
+					} else {
+						uni.switchTab({
+							url: '/pages/index/indexNew'
+						});
+					}
+				}
+			}, 1000);
+		},
+
+		// 微信小程序头像选择回调
+		async onChooseAvatar(e) {
+			console.log('选择头像:', e.detail.avatarUrl);
+
+			// 显示上传状态
+			this.isUploadingAvatar = true;
+			uni.showLoading({
+				title: '上传头像中...',
+				mask: true
+			});
+
+			try {
+				// 上传头像到服务器
+				const uploadResult = await this.uploadFilePromise(e.detail.avatarUrl);
+				if (uploadResult && uploadResult.data && uploadResult.data.link) {
+					this.tempAvatar = uploadResult.data.link;
+					uni.showToast({
+						title: '头像上传成功',
+						icon: 'success'
+					});
+				} else {
+					throw new Error('上传失败');
+				}
+			} catch (error) {
+				console.error('头像上传失败:', error);
+				uni.showToast({
+					title: '头像上传失败,请重试',
+					icon: 'none'
+				});
+			} finally {
+				this.isUploadingAvatar = false;
+				uni.hideLoading();
+			}
+		},
+
+		// 上传文件到服务器   一般用于上传头像
+		uploadFilePromise(filePath) {
+			return new Promise((resolve, reject) => {
+				// 优先使用登录数据中的token,如果没有则使用本地存储的token
+				const token = (this.loginData && this.loginData.access_token) || uni.getStorageSync(
+					'access_token');
+
+				if (!token) {
+					reject(new Error('未找到访问令牌'));
+					return;
+				}
+
+				uni.uploadFile({
+					url: UPLOAD_URL,
+					filePath: filePath,
+					header: {
+						"Blade-Auth": token
+					},
+					name: 'file',
+					formData: {
+						user: 'avatar'
+					},
+					success: (res) => {
+						try {
+							const result = JSON.parse(res.data);
+							if (result.success || result.code === 200) {
+								resolve(result);
+							} else {
+								reject(new Error(result.msg || '上传失败'));
+							}
+						} catch (e) {
+							reject(new Error('解析响应失败'));
+						}
+					},
+					fail: (error) => {
+						reject(error);
+					}
+				});
+			});
+		},
+
+		// 确认个人信息设置
+		async confirmProfile() {
+			if (!this.tempAccount.trim()) {
+				uni.showToast({
+					title: '请输入账号',
+					icon: 'none'
+				});
+				return;
+			}
+
+			uni.showLoading({
+				title: '保存中...',
+				mask: true
+			});
+
+			try {
+				// 调用更新用户信息接口
+				const updateRes = await updateProfile({
+					account: this.tempAccount.trim(),
+					avatar: this.tempAvatar
+				});
+
+				if (updateRes.code == 200) {
+					// 更新登录数据中的用户信息
+					this.loginData.account = this.tempAccount.trim();
+					if (this.tempAvatar) {
+						this.loginData.avatar = this.tempAvatar;
+						this.loginData.avatarUrl = this.tempAvatar; // 同时更新 avatarUrl 字段
+					}
+
+					// 更新本地存储的用户信息
+					uni.setStorageSync('user', this.loginData);
+
+					// 保存登录数据并跳转
+					this.saveLoginDataAndRedirect(this.loginData);
+					this.showProfileModal = false;
+				} else {
+					uni.showToast({
+						title: updateRes.msg || '保存失败,请重试',
+						icon: 'none'
+					});
+				}
+			} catch (error) {
+				console.error('更新用户信息错误:', error);
+				uni.showToast({
+					title: '保存失败,请重试',
+					icon: 'none'
+				});
+			} finally {
+				uni.hideLoading();
+			}
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.login-page {
+	min-height: 100vh;
+	background: linear-gradient(135deg, #f8f6f0 0%, #e8e4d8 50%, #d4cfc0 100%);
+	position: relative;
+	overflow: hidden;
+}
+
+.status-bar {
+	height: var(--status-bar-height);
+	width: 100%;
+}
+
+// 水墨背景
+.ink-bg {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	pointer-events: none;
+	z-index: 1;
+
+	.ink-wash {
+		position: absolute;
+		border-radius: 50%;
+		background: radial-gradient(circle, rgba(0, 0, 0, 0.03) 0%, transparent 70%);
+
+		&.ink-1 {
+			width: 400rpx;
+			height: 400rpx;
+			top: 10%;
+			right: -100rpx;
+			transform: rotate(15deg);
+		}
+
+		&.ink-2 {
+			width: 300rpx;
+			height: 300rpx;
+			top: 40%;
+			left: -50rpx;
+			transform: rotate(-20deg);
+		}
+
+		&.ink-3 {
+			width: 200rpx;
+			height: 200rpx;
+			bottom: 20%;
+			right: 20%;
+			transform: rotate(45deg);
+		}
+	}
+}
+
+.nav-back {
+	position: fixed;
+	left: 30rpx;
+	top: calc(var(--status-bar-height) + 20rpx);
+	z-index: 100;
+
+	.back-btn {
+		width: 80rpx;
+		height: 80rpx;
+		background: rgba(255, 255, 255, 0.8);
+		border: 1rpx solid rgba(0, 0, 0, 0.1);
+		border-radius: 50%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		transition: all 0.3s ease;
+		box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
+
+		&:active {
+			transform: scale(0.95);
+			box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
+		}
+	}
+
+	.back-icon {
+		font-family: "iconfont";
+		font-size: 36rpx;
+		color: #333;
+	}
+}
+
+.header {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	padding-top: 120rpx;
+	position: relative;
+	z-index: 2;
+
+	.logo-container {
+		position: relative;
+		margin-bottom: 60rpx;
+
+		.logo-circle {
+			width: 200rpx;
+			height: 200rpx;
+			border-radius: 50%;
+			background: linear-gradient(135deg, #fff 0%, #f5f5f5 100%);
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
+			border: 2rpx solid rgba(0, 0, 0, 0.05);
+
+			.logo {
+				width: 160rpx;
+				height: 160rpx;
+				border-radius: 50%;
+			}
+		}
+
+		.logo-shadow {
+			position: absolute;
+			bottom: -20rpx;
+			left: 50%;
+			transform: translateX(-50%);
+			width: 160rpx;
+			height: 20rpx;
+			background: radial-gradient(ellipse, rgba(0, 0, 0, 0.1) 0%, transparent 70%);
+			border-radius: 50%;
+		}
+	}
+
+	.title-section {
+		text-align: center;
+		margin-bottom: 20rpx;
+		position: relative;
+
+		.title {
+			font-size: 64rpx;
+			font-weight: normal;
+			color: #2c3e50;
+			letter-spacing: 12rpx;
+			font-family: 'KaiTi', '楷体', serif;
+			text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
+		}
+
+		.title-brush {
+			position: absolute;
+			bottom: -10rpx;
+			left: 50%;
+			transform: translateX(-50%);
+			width: 120rpx;
+			height: 4rpx;
+			background: linear-gradient(90deg, transparent, #2c3e50, transparent);
+			border-radius: 2rpx;
+		}
+	}
+
+	.subtitle {
+		font-size: 28rpx;
+		color: rgba(44, 62, 80, 0.7);
+		font-weight: 300;
+		letter-spacing: 4rpx;
+		font-style: italic;
+		font-family: inherit;
+	}
+}
+
+.login-section {
+	padding: 80rpx 60rpx;
+	position: relative;
+	z-index: 2;
+
+	.btn-container {
+		margin-bottom: 40rpx;
+	}
+
+	.wechat-btn {
+		position: relative;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		width: 100%;
+		height: 96rpx;
+		background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%);
+		border: 2rpx solid rgba(0, 0, 0, 0.08);
+		border-radius: 48rpx;
+		color: #2c3e50;
+		font-size: 32rpx;
+		font-weight: normal;
+		transition: all 0.3s ease;
+		box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
+		overflow: hidden;
+
+		&:active {
+			transform: translateY(2rpx);
+			box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12);
+		}
+
+		&.btn-loading {
+			background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
+			color: rgba(44, 62, 80, 0.5);
+		}
+
+		.btn-ink {
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			height: 100%;
+			background: linear-gradient(135deg, rgba(0, 0, 0, 0.02) 0%, transparent 50%);
+			pointer-events: none;
+		}
+
+		.wechat-icon {
+			width: 44rpx;
+			height: 44rpx;
+			margin-right: 16rpx;
+		}
+	}
+
+	.zen-divider {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin: 50rpx 0;
+
+		.zen-dot {
+			width: 8rpx;
+			height: 8rpx;
+			border-radius: 50%;
+			background: rgba(44, 62, 80, 0.3);
+		}
+
+		.zen-line {
+			width: 100rpx;
+			height: 1rpx;
+			background: linear-gradient(90deg, transparent, rgba(44, 62, 80, 0.2), transparent);
+			margin: 0 20rpx;
+		}
+	}
+
+	.browse-btn {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		width: 100%;
+		height: 88rpx;
+		background: transparent;
+		border: 1rpx solid rgba(44, 62, 80, 0.15);
+		border-radius: 44rpx;
+		color: rgba(44, 62, 80, 0.7);
+		font-size: 28rpx;
+		transition: all 0.3s ease;
+
+		&:active {
+			background: rgba(44, 62, 80, 0.05);
+			transform: translateY(1rpx);
+		}
+	}
+
+	.agreement {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-top: 60rpx;
+
+		.checkbox-wrapper {
+			margin-right: 16rpx;
+		}
+
+		.zen-checkbox {
+			width: 36rpx;
+			height: 36rpx;
+			border: 2rpx solid rgba(44, 62, 80, 0.3);
+			border-radius: 50%;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			transition: all 0.3s ease;
+			background: rgba(255, 255, 255, 0.8);
+
+			&.checked {
+				background: #2c3e50;
+				border-color: #2c3e50;
+			}
+
+			.zen-circle-inner {
+				width: 16rpx;
+				height: 16rpx;
+				border-radius: 50%;
+				background: #fff;
+			}
+		}
+
+		.agreement-text {
+			font-size: 24rpx;
+			color: rgba(44, 62, 80, 0.6);
+			line-height: 1.5;
+
+			.link {
+				color: #2c3e50;
+				text-decoration: underline;
+			}
+		}
+	}
+}
+
+// 底部装饰
+.bottom-decoration {
+	position: absolute;
+	bottom: 40rpx;
+	left: 50%;
+	transform: translateX(-50%);
+	z-index: 1;
+
+	.zen-mountain {
+		position: relative;
+		width: 200rpx;
+		height: 120rpx;
+
+		.mountain-peak {
+			position: absolute;
+			top: 0;
+			left: 50%;
+			transform: translateX(-50%);
+			width: 0;
+			height: 0;
+			border-left: 60rpx solid transparent;
+			border-right: 60rpx solid transparent;
+			border-bottom: 80rpx solid rgba(44, 62, 80, 0.1);
+		}
+
+		.mountain-base {
+			position: absolute;
+			bottom: 0;
+			left: 0;
+			width: 100%;
+			height: 40rpx;
+			background: linear-gradient(90deg, transparent, rgba(44, 62, 80, 0.05), transparent);
+			border-radius: 50%;
+		}
+	}
+}
+
+// 个人信息设置弹框样式
+.profile-modal {
+	width: 750rpx;
+	background: #fff;
+	border-radius: 20rpx;
+	overflow: hidden;
+
+	.modal-header {
+		text-align: center;
+		padding: 60rpx 40rpx 40rpx;
+		background: linear-gradient(135deg, #f8f6f0 0%, #e8e4d8 100%);
+
+		.modal-title {
+			display: block;
+			font-size: 36rpx;
+			font-weight: 600;
+			color: #2c3e50;
+			margin-bottom: 12rpx;
+		}
+
+		.modal-subtitle {
+			font-size: 26rpx;
+			color: rgba(44, 62, 80, 0.7);
+		}
+	}
+
+	.modal-content {
+		padding: 40rpx;
+
+		.avatar-section {
+			margin-bottom: 40rpx;
+			text-align: center;
+
+			.section-label {
+				display: block;
+				font-size: 28rpx;
+				color: #2c3e50;
+				margin-bottom: 20rpx;
+				font-weight: 500;
+			}
+
+			.avatar-button {
+				background: none;
+				border: none;
+				padding: 0;
+				margin: 0 auto;
+				display: block;
+				width: 160rpx;
+				height: 160rpx;
+
+				&::after {
+					border: none;
+				}
+
+				&:disabled {
+					opacity: 0.7;
+				}
+			}
+
+			.avatar-container {
+				position: relative;
+				width: 160rpx;
+				height: 160rpx;
+				border-radius: 50%;
+				overflow: hidden;
+				border: 3rpx solid #e8e4d8;
+
+				.avatar-preview {
+					width: 100%;
+					height: 100%;
+				}
+
+				.avatar-overlay {
+					position: absolute;
+					bottom: 0;
+					left: 0;
+					right: 0;
+					height: 50rpx;
+					background: rgba(0, 0, 0, 0.5);
+					display: flex;
+					align-items: center;
+					justify-content: center;
+				}
+			}
+		}
+
+		.account-section {
+			.section-label {
+				display: block;
+				font-size: 28rpx;
+				color: #2c3e50;
+				margin-bottom: 20rpx;
+				font-weight: 500;
+			}
+		}
+	}
+
+	.modal-actions {
+		padding: 0 40rpx 40rpx;
+
+		.confirm-btn {
+			width: 100%;
+			height: 80rpx;
+			background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
+			color: #fff;
+			border-radius: 40rpx;
+			font-size: 32rpx;
+			font-weight: 500;
+			border: none;
+			transition: all 0.3s ease;
+
+			&:disabled {
+				background: #bdc3c7;
+				color: rgba(255, 255, 255, 0.7);
+			}
+
+			&:not(:disabled):active {
+				transform: translateY(2rpx);
+				box-shadow: 0 2rpx 8rpx rgba(44, 62, 80, 0.3);
+			}
+		}
+	}
+}
+</style>