This commit is contained in:
zpc 2025-09-11 05:30:19 +08:00
parent 429192cdd2
commit 00f0187a82
9 changed files with 451 additions and 516 deletions

View File

@ -4,7 +4,7 @@
"id": 7,
"reservation_id": 3,
"user_id": 3796,
"role": 1,
"role": 1, // 1 0
"join_time": "2025/09/03 04:05:40",
"status": 0,
"userName": "闪光大帕鲁",
@ -13,11 +13,11 @@
}
],
"id": 3,
"title": "测试1",
"title": "测试1", //
"room_id": 3,
"room_name": "306小包间",
"start_time": "2025/09/13 21:09:43",
"end_time": "2025/09/13 21:34:43",
"room_name": "306小包间",//
"start_time": "2025/09/13 21:09:43", //
"end_time": "2025/09/13 21:34:43",//
"duration_minutes": 25,
"player_count": 4,
"game_type": "扑克",

View File

@ -74,28 +74,7 @@ const shouldUseCache = () => {
export const getReservation = async (index = 1, size = 20) => {
const res = await getReservationList(index, size);
if (res != null && res.length > 0) {
// {
// id: '',
// status: '',
// description: '',
// time: '',
// room: '',
// requirements: '',
// personCount: 4, //总人数
// joinPerson: [{
// id: 1,
// name: '张三',
// avatar: '',
// phone: '',
// }], //已加入人
// }
//[{"participants":[{"id":7,"reservation_id":3,"user_id":3796,"role":1,"join_time":"2025/09/03 04:05:40",
// "status":0,"userName":"闪光大帕鲁","avatarImage":"https://admin-1308826010.cos.ap-shanghai.myqcloud.com/
// users20250908/20250908152502_6366.png","userBlackStatus":0}],
// "id":3,"title":"测试1","room_id":3,"room_name":"306小包间",
// "start_time":"2025/09/13 21:09:43","end_time":"2025/09/13 21:34:43",
// "duration_minutes":25,"player_count":4,"game_type":"扑克","game_rule":"跑得快",
// "extra_info":"11","is_smoking":1,"gender_limit":2,"credit_limit":0.0,"min_age":0,"max_age":0,"deposit_fee":0.00,"status":0,"created_at":"2025/09/03 00:21:16","updated_at":"2025/09/03 21:10:21","remarks":""}]
console.log("记录", res);
var list = res.map(item => {
let start_time = parseTimeString(item.start_time);
@ -127,6 +106,7 @@ export const getReservation = async (index = 1, size = 20) => {
let requirements = requirementsList.join(",");
return {
id: item.id,
data: item,
status: item.status,
description: item.title,
dateStr: formatTime(start_time, "yyyy-MM-dd"),
@ -150,6 +130,7 @@ export const getReservation = async (index = 1, size = 20) => {
});
return list;
// return [];
}
return [];
}

View File

@ -9,7 +9,7 @@ import request from '@/common/system/request';
export const getReservationList = async (index = 1, size = 20) => {
console.log('getReservationList', index, size);
const res = await request.get("sq/GetReservationList", { pageIndex: index, pageSize: size });
const res = await request.getOrCache("sq/GetReservationList", { pageIndex: index, pageSize: size }, 5);
if (res.code == 0) {
return res.data;
}

View File

@ -60,6 +60,33 @@ export function showModal(options = {}) {
});
}
/**
*
* @param {String} title
* @param {String} content
* @returns
*/
export function showModalConfirm(title, content) {
return new Promise((resolve) => {
uni.showModal({
title: title || '提示',
content: content || '',
confirmText: '确定',
cancelText: '取消',
showCancelButton: true,
showConfirmButton: true,
confirmColor: '#1989FA',
cancelColor: '#9F9F9F',
success: function (res) {
if (res.confirm) {
resolve(true);
} else if (res.cancel) {
resolve(false);
}
}
});
});
}
/**
* 显示提示信息
* @param {*} title 提示信息

2
components.d.ts vendored
View File

@ -11,6 +11,8 @@ declare module 'vue' {
Container: typeof import('./components/com/page/container.vue')['default']
MahjongCard: typeof import('./components/index/MahjongCard.vue')['default']
NoData: typeof import('./components/com/page/no-data.vue')['default']
NoEmpty: typeof import('./components/com/index/NoEmpty.vue')['default']
ReservationPopup: typeof import('./components/com/index/ReservationPopup.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
UniNavBar: typeof import('./components/uni-nav-bar/uni-nav-bar.vue')['default']

View File

@ -0,0 +1,53 @@
<template>
<view class="page">
<view class="image-container">
<image
class="empty-image"
src="@@:app/static/index/no.png"
mode="widthFix"
/>
</view>
<view class="text-container">
<text class="empty-text">
现在还没人预约~
<br />
休息一会吧
</text>
</view>
</view>
</template>
<script setup>
</script>
<style scoped lang="scss">
.page {
.image-container {
display: flex;
justify-content: center;
}
.empty-image {
width: 184rpx;
height: 184rpx;
}
.text-container {
display: flex;
justify-content: center;
margin-top: 30rpx;
}
.empty-text {
width: 210rpx;
height: 80rpx;
font-family: PingFang SC, PingFang SC;
font-weight: 500;
font-size: 28rpx;
color: #6F6F6F;
text-align: center;
font-style: normal;
text-transform: none;
}
}
</style>

View File

@ -0,0 +1,259 @@
<template>
<uni-popup ref="popup" type="center">
<view class="column center"
style="width: 680rpx; background-color: white; border-radius: 10rpx; padding: 20rpx;">
<text style="">预约信息</text>
<view class="column"
style="width: 100%; height: 180rpx; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx;">
<view class="row" style="margin-top: 20rpx; align-items: center; margin-left: 20rpx;">
<text style="font-size: 22rpx;">发起者</text>
<image :src="initiatorInfo.avatarImage || ''"
style="width: 50rpx; height: 50rpx; background-color: antiquewhite; border-radius: 50%; margin-left: 20rpx;"
mode=""></image>
<text style="font-size: 20rpx; margin-left: 15rpx;">{{ initiatorInfo.userName || '' }}</text>
</view>
<view class="row" style="margin-top: 30rpx; align-items: center; margin-left: 20rpx;">
<text style="font-size: 22rpx;">参与者</text>
<view class="row" v-for="(item, index) in participantList" :key="index"
style="align-items: center;">
<view class="" style="position: relative; width: 50rpx; height: 50rpx; display: flex;">
<image :src="item.avatarImage || ''" @click="openUserPop(item)"
style="width: 50rpx; height: 50rpx; background-color: antiquewhite; border-radius: 50%; margin-left: 20rpx; position: absolute;"
mode=""></image>
<view v-if="item.userBlackStatus === 1" class="center"
style="width: 50rpx; height: 20rpx; background-color: #FFB7B7; position: absolute; left: 40%; top: -10rpx;">
<text style="font-size: 10rpx;">黑名单</text>
</view>
</view>
<text
style="font-size: 20rpx; margin-left: 30rpx; width: 100rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{
item.userName || '' }}</text>
</view>
</view>
</view>
<view class="column"
style="width: 100%; height: 180rpx; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx;font-size: 22rpx;">
<view class="row" style="justify-content: space-between; margin: 20rpx;">
<text>开始时间</text>
<text>{{ formatTime(reservationData.start_time) }}</text>
</view>
<view class="row" style="justify-content: space-between; margin: 0 20rpx;">
<text>结束时间</text>
<text>{{ formatTime(reservationData.end_time) }}</text>
</view>
<text style="margin: 30rpx 20rpx 0;">合计{{ formatDuration(reservationData.duration_minutes) }}</text>
</view>
<view class="column"
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">房间号{{ reservationData.room_name || ''
}}</text>
<text style="margin: 10rpx 20rpx;">人数{{ reservationData.player_count || 0 }}</text>
<text style="margin: 10rpx 20rpx;">玩法类型{{ reservationData.game_type || '' }}</text>
<text style="margin: 10rpx 20rpx;">具体规则{{ reservationData.game_rule || '' }}</text>
<text style="margin: 10rpx 20rpx 20rpx;">补充信息{{ reservationData.extra_info || '无' }}</text>
</view>
<view class="column"
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">是否禁烟{{
getSmokingText(reservationData.is_smoking) }}</text>
<text style="margin: 10rpx 20rpx;">性别{{ getGenderText(reservationData.gender_limit) }}</text>
<text style="margin: 10rpx 20rpx 20rpx;">信誉{{ reservationData.credit_limit || 0 }}</text>
</view>
<view class="column"
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">鸽子费{{ reservationData.deposit_fee || 0
}}</text>
<text
style="margin: 10rpx 20rpx 20rpx; color: #9F9F9F;">组局成功后若有牌友未赴约其鸽子费平均分给其他牌友组局成功或失败后鸽子费将全额返还</text>
</view>
<view class="row" v-if="isAddhandleJoin == 0" style="width: 100%; margin-top: 30rpx;">
<view class="center" @click="close"
style="height: 80rpx; flex: 1; background-color: #9F9F9F; border-radius: 10rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">关闭</text>
</view>
<view class="center" @click="handleJoin"
style="height: 80rpx; flex: 3; background-color: #1989FA; border-radius: 10rpx; margin-left: 20rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">参与组局</text>
</view>
</view>
<view class="row" v-if="isAddhandleJoin == 1" style="width: 100%; margin-top: 30rpx;">
<view class="center" @click="close"
style="height: 80rpx; flex: 1; background-color: #9F9F9F; border-radius: 10rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">关闭</text>
</view>
<view class="center" @click="exitJoin"
style="height: 80rpx; flex: 3; background-color: #1989FA; border-radius: 10rpx; margin-left: 20rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">退出组局</text>
</view>
</view>
<view class="row" v-if="isAddhandleJoin == 2" style="width: 100%; margin-top: 30rpx;">
<view class="center" @click="close"
style="height: 80rpx; flex: 1; background-color: #9F9F9F; border-radius: 10rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">关闭</text>
</view>
<view class="center" @click="cancelJoin"
style="height: 80rpx; flex: 3; background-color: #1989FA; border-radius: 10rpx; margin-left: 20rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">取消组局</text>
</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, computed } from 'vue'
import { userInfo } from '@/common/server/user'
import { showModalConfirm } from '@/common/utils'
//
const popup = ref(null)
//
const reservationData = ref({})
//
const initiatorInfo = computed(() => {
if (reservationData.value.participants && reservationData.value.participants.length > 0) {
return reservationData.value.participants.find(p => p.role === 1) || {}
}
return {}
})
//
const participantList = computed(() => {
if (reservationData.value.participants && reservationData.value.participants.length > 0) {
return reservationData.value.participants.filter(p => p.role === 0) || []
}
return []
});
var isAddhandleJoin = ref(0);
//
const show = (item) => {
reservationData.value = item || {}
if (userInfo.value != null) {
var _initiatorInfo = item.participants.find(p => p.role === 1);
if (_initiatorInfo != null) {
if (userInfo.value.id == _initiatorInfo.user_id) {
isAddhandleJoin.value = 2;
}
}
}
popup.value.open()
}
//
const close = () => {
popup.value.close()
}
//
const formatTime = (timeStr) => {
if (!timeStr) return ''
// "2025/09/13 21:09:43" "2025/09/13 2109"
const date = new Date(timeStr)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
}
//
const formatDuration = (minutes) => {
if (!minutes) return '0分钟'
const hours = Math.floor(minutes / 60)
const mins = minutes % 60
if (hours > 0) {
return mins > 0 ? `${hours}小时${mins}分钟` : `${hours}小时`
}
return `${mins}分钟`
}
//
const getSmokingText = (isSmoking) => {
return isSmoking === 1 ? '禁烟' : '不禁烟'
}
//
const getGenderText = (genderLimit) => {
switch (genderLimit) {
case 0:
return '不限'
case 1:
return '男'
case 2:
return '女'
default:
return '不限'
}
}
//
const handleJoin = () => {
//
emit('join', reservationData.value)
}
const cancelJoin = async () => {
var res = await showModalConfirm('提示', '确定要取消组局吗?')
if (res) {
emit('cancelJoin', reservationData.value)
}
}
const exitJoin = async () => {
var res = await showModalConfirm('提示', '确定要退出组局吗?')
if (res) {
emit('exitJoin', reservationData.value)
}
}
//
const openUserPop = (user) => {
//
emit('openUserPop', user)
}
//
const emit = defineEmits(['join', 'openUserPop', 'cancelJoin', 'exitJoin'])
//
defineExpose({
show,
close
})
</script>
<style lang="scss" scoped>
.column {
display: flex;
flex-direction: column;
}
.row {
display: flex;
flex-direction: row;
}
.center {
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@ -11,6 +11,9 @@
</view>
</view>
</template>
<template #empty>
<com-index-NoEmpty></com-index-NoEmpty>
</template>
<view style="z-index: 100;margin-top:10rpx;height:532rpx;">
<view style="width: 100%;width:100%;display:flex;justify-content: center;"
@ -34,102 +37,12 @@
</z-paging>
<uni-popup ref="cardInfo" type="center">
<view class="column center"
style="width: 680rpx; background-color: white; border-radius: 10rpx; padding: 20rpx;">
<text style="">预约信息</text>
<view class="column"
style="width: 100%; height: 180rpx; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx;">
<view class="row" style="margin-top: 20rpx; align-items: center; margin-left: 20rpx;">
<text style="font-size: 22rpx;">发起者</text>
<image src=""
style="width: 50rpx; height: 50rpx; background-color: antiquewhite; border-radius: 50%; margin-left: 20rpx;"
mode=""></image>
<text style="font-size: 20rpx; margin-left: 15rpx;">苏家辉</text>
</view>
<view class="row" style="margin-top: 30rpx; align-items: center; margin-left: 20rpx;">
<text style="font-size: 22rpx;">参与者</text>
<view class="row" v-for="(item, index) in 3" :key="index" style="align-items: center;">
<view class="" style="position: relative; width: 50rpx; height: 50rpx; display: flex;">
<image src="" @click="openUserPop()"
style="width: 50rpx; height: 50rpx; background-color: antiquewhite; border-radius: 50%; margin-left: 20rpx; position: absolute;"
mode=""></image>
<view class="center"
style="width: 50rpx; height: 20rpx; background-color: #FFB7B7; position: absolute; left: 40%; top: -10rpx;">
<text style="font-size: 10rpx;">黑名单</text>
</view>
</view>
<text
style="font-size: 20rpx; margin-left: 30rpx; width: 100rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">树下的胖子</text>
</view>
</view>
</view>
<view class="column"
style="width: 100%; height: 180rpx; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx;font-size: 22rpx;">
<view class="row" style="justify-content: space-between; margin: 20rpx;">
<text>开始时间</text>
<text>2025/08/27 1530</text>
</view>
<view class="row" style="justify-content: space-between; margin: 0 20rpx;">
<text>结束时间</text>
<text>2025/08/27 1730</text>
</view>
<text style="margin: 30rpx 20rpx 0;">合计2小时</text>
</view>
<view class="column"
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">房间号304-大包30/小时</text>
<text style="margin: 10rpx 20rpx;">人数3</text>
<text style="margin: 10rpx 20rpx;">玩法类型扑克</text>
<text style="margin: 10rpx 20rpx;">具体规则斗地主</text>
<text style="margin: 10rpx 20rpx 20rpx;">补充信息</text>
</view>
<view class="column"
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">是否禁烟禁烟</text>
<text style="margin: 10rpx 20rpx;">性别不限</text>
<text style="margin: 10rpx 20rpx 20rpx;">信誉4.0</text>
</view>
<view class="column"
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">鸽子费0</text>
<text
style="margin: 10rpx 20rpx 20rpx; color: #9F9F9F;">组局成功后若有牌友未赴约其鸽子费平均分给其他牌友组局成功或失败后鸽子费将全额返还</text>
</view>
<view class="row" style="width: 100%; margin-top: 30rpx;">
<view class="center" @click="clasePop()"
style="height: 80rpx; flex: 1; background-color: #9F9F9F; border-radius: 10rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">关闭</text>
</view>
<view class="center"
style="height: 80rpx; flex: 3; background-color: #1989FA; border-radius: 10rpx; margin-left: 20rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">参与组局</text>
</view>
</view>
</view>
</uni-popup>
<!-- 预约信息弹窗组件 -->
<ReservationPopup ref="reservationPopup" @join="handleJoin" @openUserPop="openUserPop" />
<uni-popup ref="userInfo" type="center">
<uni-popup ref="userInfo_popup" type="center">
<view class="column" style="width: 500rpx; background-color: white; padding: 20rpx;">
@ -185,6 +98,7 @@ import {
} from 'vue'
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import MahjongCard from '@/components/index/MahjongCard.vue'
import ReservationPopup from '@/components/com/index/ReservationPopup.vue'
import {
homeData,
preloadHomeData,
@ -195,6 +109,7 @@ import {
getConfigData,
preloadConfigData
} from '@/common/server/config'
import { userInfo, loadUserInfo } from '@/common/server/user'
const statusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight);
const getBackgroundImg = () => {
return {
@ -217,8 +132,8 @@ const mockData = ref([]) // 模拟数据源
//
const pagePaging = ref(null)
const cardInfo = ref(null)
const userInfo = ref(null)
const reservationPopup = ref(null)
const userInfo_popup = ref(null)
//
const initMockData = () => {
@ -277,20 +192,16 @@ const clearData = () => {
}
//
const openPop = () => {
cardInfo.value.open()
}
const clasePop = () => {
cardInfo.value.close()
const openPop = (item) => {
reservationPopup.value.show(item.data)
}
const openUserPop = () => {
userInfo.value.open()
userInfo_popup.value.open()
}
const closeUserPop = () => {
userInfo.value.close()
userInfo_popup.value.close()
}
const onChange = (e) => {
@ -306,6 +217,8 @@ const handleJoin = (item) => {
icon: 'success',
duration: 1500
})
//
reservationPopup.value.close()
}
//
@ -317,6 +230,7 @@ onMounted(() => {
onLoad(async () => {
if (!homeData.value) preloadHomeData();
if (!configData.value) preloadConfigData();
await loadUserInfo();
// initMockData();
// initMockData();
});

View File

@ -1,58 +1,34 @@
<template>
<view class="login-container">
<!-- 背景装饰 -->
<view class="bg-decoration">
<!-- 云朵装饰 -->
<view class="cloud cloud-1"></view>
<view class="cloud cloud-2"></view>
<view class="cloud cloud-3"></view>
<!-- 小鸟装饰 -->
<view class="bird bird-1"></view>
<view class="bird bird-2"></view>
</view>
<!-- 头部区域 -->
<view class="header">
<image src="@@:app/static/Logo.jpg" class="logo" mode="aspectFit"></image>
<text class="welcome-text">欢迎登录</text>
</view>
<!-- 登录表单 -->
<view></view>
<view class="login-form">
<!-- 一键登录按钮 -->
<view class="one-click-login">
<button v-if="agreedToTerms && isMobile" class="quick-login-btn" :class="{ disabled: loading }"
open-type="getPhoneNumber" @getphonenumber="mobileClickLogin">
<text>{{ loading ? '登录中...' : '一键登录' }}</text>
</button>
<button v-if="!agreedToTerms" class="quick-login-btn" :class="{ disabled: loading }"
@click="agreementClickLogin">
<text>{{ loading ? '登录中...' : '一键登录' }}</text>
</button>
<button v-if="agreedToTerms && !isMobile" class="quick-login-btn" :class="{ disabled: loading }"
@click="anonymousLoginClickLogin">
<text>{{ loading ? '登录中...' : '一键登录' }}</text>
</button>
</view>
<image src="@@:app/static/login/logo_5.png" style="width: 500rpx;height:500rpx;" mode="widthFix"></image>
</view>
<view class="login-button-container">
<!-- 用户协议同意 -->
<view class="agreement-section">
<view class="agreement-checkbox" @click="toggleAgreement">
<checkbox :value="agreedToTerms" :checked="agreedToTerms"
style="transform:scale(0.5);margin-top: -8rpx;" />
<text class="agreement-text">
我已阅读并同意
<text class="agreement-link" @click.stop="showUserAgreement">用户协议</text>
<text class="agreement-link" @click.stop="showPrivacyPolicy">隐私政策</text>
</text>
</view>
</view>
<!-- 暂不登录按钮 -->
<view class="skip-login-btn" @click="skipLogin">
<text>暂不登录</text>
<button v-if="agreedToTerms && isMobile" class="login-button" :class="{ disabled: loading }"
open-type="getPhoneNumber" @getphonenumber="mobileClickLogin">
<view class="login-button-text">{{ (loading ? '登录中...' : '一键登录') }}</view>
</button>
<button v-if="!agreedToTerms" class="login-button" :class="{ disabled: loading }"
@click="agreementClickLogin">
<view class="login-button-text">{{ (loading ? '登录中...' : '一键登录') }}</view>
</button>
<button v-if="agreedToTerms && !isMobile" class="login-button" :class="{ disabled: loading }"
@click="anonymousLoginClickLogin">
<view class="login-button-text">{{ (loading ? '登录中...' : '一键登录') }}</view>
</button>
</view>
<view class="agreement-container">
<view class="agreement-checkbox" @click="toggleAgreement">
<checkbox :value="agreedToTerms" :checked="agreedToTerms"
style="transform:scale(0.5);margin-top: -8rpx;position: relative;left: 8px;" />
<text class="agreement-text">
我已阅读并同意
<text class="agreement-link" @click.stop="showUserAgreement">用户协议</text>
<text class="agreement-link" @click.stop="showPrivacyPolicy">隐私政策</text>
</text>
</view>
</view>
</view>
@ -127,7 +103,6 @@ const mobileClickLogin = async (e) => {
}
const anonymousLoginClickLogin = async (e) => {
console.log('aaa');
loading.value = true
try {
// API
@ -198,354 +173,78 @@ onLoad(() => {
<style lang="scss" scoped>
.login-container {
min-height: 100vh;
background: linear-gradient(180deg, #87CEEB 0%, #98FB98 50%, #F0E68C 100%);
position: relative;
overflow: hidden;
background: #F7F7F7;
width: 100%;
height: 100%;
}
.login-form {
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
align-items: center;
padding-top: 400rpx;
}
.login-button {
width: 642rpx;
height: 80rpx;
background: #00AC4E;
box-shadow: 0rpx 0rpx 10rpx 2rpx rgba(0, 0, 0, 0.25);
border-radius: 16rpx 16rpx 16rpx 16rpx;
display: flex;
align-content: center;
justify-content: center;
align-items: center;
}
.bg-decoration {
.login-button-text {
font-family: PingFang SC, PingFang SC;
font-weight: 500;
font-size: 30rpx;
color: #FFFFFF;
text-align: left;
font-style: normal;
text-transform: none;
}
.login-button-container {
display: flex;
justify-content: center;
position: absolute;
top: 0;
left: 0;
bottom: 280rpx;
width: 100%;
height: 100%;
pointer-events: none;
&.disabled {
background: linear-gradient(135deg, #D3D3D3 0%, #A9A9A9 100%);
color: #666;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.1);
}
}
//
.cloud {
.agreement-container {
display: flex;
justify-content: center;
position: absolute;
background: rgba(255, 255, 255, 0.8);
border-radius: 50rpx;
&::before,
&::after {
content: '';
position: absolute;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
}
&.cloud-1 {
width: 120rpx;
height: 40rpx;
top: 15%;
left: 10%;
&::before {
width: 50rpx;
height: 50rpx;
top: -25rpx;
left: 10rpx;
}
&::after {
width: 60rpx;
height: 60rpx;
top: -30rpx;
right: 10rpx;
}
}
&.cloud-2 {
width: 100rpx;
height: 35rpx;
top: 25%;
right: 15%;
&::before {
width: 40rpx;
height: 40rpx;
top: -20rpx;
left: 8rpx;
}
&::after {
width: 50rpx;
height: 50rpx;
top: -25rpx;
right: 8rpx;
}
}
&.cloud-3 {
width: 80rpx;
height: 30rpx;
top: 70%;
left: 20%;
&::before {
width: 35rpx;
height: 35rpx;
top: -17rpx;
left: 6rpx;
}
&::after {
width: 40rpx;
height: 40rpx;
top: -20rpx;
right: 6rpx;
}
}
}
//
.bird {
position: absolute;
width: 20rpx;
height: 15rpx;
background: #8B4513;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
&::before {
content: '';
position: absolute;
width: 8rpx;
height: 6rpx;
background: #8B4513;
border-radius: 50%;
top: 2rpx;
left: -5rpx;
transform: rotate(-20deg);
}
&.bird-1 {
top: 20%;
right: 25%;
animation: fly 3s ease-in-out infinite;
}
&.bird-2 {
top: 60%;
left: 15%;
animation: fly 4s ease-in-out infinite reverse;
}
}
//
.flower {
position: absolute;
width: 30rpx;
height: 30rpx;
&::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
background: radial-gradient(circle, #FFB6C1 30%, #FF69B4 70%);
border-radius: 50%;
}
&::after {
content: '';
position: absolute;
width: 6rpx;
height: 20rpx;
background: #228B22;
border-radius: 3rpx;
bottom: -20rpx;
left: 50%;
transform: translateX(-50%);
}
&.flower-1 {
top: 40%;
left: 8%;
animation: sway 2s ease-in-out infinite;
}
&.flower-2 {
top: 80%;
right: 10%;
animation: sway 2.5s ease-in-out infinite reverse;
}
&.flower-3 {
top: 50%;
right: 30%;
animation: sway 3s ease-in-out infinite;
}
}
//
@keyframes fly {
0%,
100% {
transform: translateX(0) translateY(0);
}
25% {
transform: translateX(10rpx) translateY(-5rpx);
}
50% {
transform: translateX(20rpx) translateY(0);
}
75% {
transform: translateX(10rpx) translateY(5rpx);
}
}
@keyframes sway {
0%,
100% {
transform: rotate(0deg);
}
25% {
transform: rotate(2deg);
}
50% {
transform: rotate(0deg);
}
75% {
transform: rotate(-2deg);
}
}
@keyframes shine {
0% {
transform: translateX(-100%) translateY(-100%) rotate(45deg);
}
100% {
transform: translateX(100%) translateY(100%) rotate(45deg);
}
}
.header {
text-align: center;
margin-bottom: 80rpx;
.logo {
width: 200rpx;
height: 200rpx;
border-radius: 50%;
margin-bottom: 30rpx;
border: 8rpx solid #FFE4B5;
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
}
.app-name {
display: block;
font-size: 48rpx;
font-weight: bold;
color: #8B4513;
margin-bottom: 20rpx;
text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.1);
}
.welcome-text {
display: block;
font-size: 28rpx;
color: #228B22;
font-weight: 500;
}
}
.login-form {
padding: 0 60rpx;
bottom: 180rpx;
width: 100%;
max-width: 600rpx;
}
.one-click-login {
margin: 40rpx 0;
.agreement-text {
.quick-login-btn {
background: linear-gradient(135deg, #F36903 0%, #E55A00 100%);
color: white;
height: 100rpx;
border-radius: 50rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: bold;
box-shadow: 0 8rpx 20rpx rgba(243, 105, 3, 0.3);
border: 4rpx solid #FFE4B5;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transform: rotate(45deg);
transition: all 0.5s;
}
&:active::before {
animation: shine 0.6s ease-in-out;
}
&.disabled {
background: linear-gradient(135deg, #D3D3D3 0%, #A9A9A9 100%);
color: #666;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.1);
}
.quick-login-icon {
width: 40rpx;
height: 40rpx;
margin-right: 15rpx;
}
}
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #515151;
text-align: left;
font-style: normal;
text-transform: none;
}
.agreement-section {
margin: 30rpx 0;
.agreement-checkbox {
display: flex;
align-items: flex-start;
.checkbox {
width: 30rpx;
height: 30rpx;
margin-right: 15rpx;
margin-top: 5rpx;
flex-shrink: 0;
}
.agreement-text {
font-size: 24rpx;
color: #8B4513;
line-height: 1.5;
.agreement-link {
color: blue;
text-decoration: underline;
font-weight: 500;
}
}
}
}
.skip-login-btn {
text-align: center;
margin: 40rpx 0;
text {
font-size: 28rpx;
color: #8B4513;
text-decoration: underline;
font-weight: 500;
}
.agreement-link {
color: #00AC4E;
}
</style>