..
This commit is contained in:
Generated
+10
@@ -24,6 +24,7 @@
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-4050720250324001",
|
||||
"@dcloudio/uni-mp-xhs": "3.0.0-4050720250324001",
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4050720250324001",
|
||||
"moment": "^2.30.1",
|
||||
"tdesign-mobile-vue": "^1.8.3",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.1.9"
|
||||
@@ -8773,6 +8774,15 @@
|
||||
"integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-4050720250324001",
|
||||
"@dcloudio/uni-mp-xhs": "3.0.0-4050720250324001",
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4050720250324001",
|
||||
"moment": "^2.30.1",
|
||||
"tdesign-mobile-vue": "^1.8.3",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.1.9"
|
||||
|
||||
@@ -8,6 +8,11 @@ import 'tdesign-mobile-vue/es/style/index.css';
|
||||
// 自定义组件库样式
|
||||
import './theme.css'
|
||||
|
||||
// 引入moment
|
||||
import moment from 'moment';
|
||||
import 'moment/dist/locale/zh-cn'
|
||||
moment.locale('zh-cn')
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App);
|
||||
app.use(TDesign); // 使用TDesign组件库
|
||||
|
||||
+39
-8
@@ -6,10 +6,20 @@
|
||||
"navigationBarTitleText": "uni-app"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/list/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "赛事活动"
|
||||
"navigationBarTitleText": "赛事活动",
|
||||
"enablePullDownRefresh": true,
|
||||
"onReachBottomDistance": 100
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -29,31 +39,52 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的团队"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/profile/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "#FFFFFF",
|
||||
"navigationBarTitleText": "uni-app",
|
||||
"navigationBarBackgroundColor": "#ff5050",
|
||||
"navigationBarBackgroundColor": "#f45a5a",
|
||||
"backgroundColor": "#F8F8F8"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#000",
|
||||
"selectedColor": "#ff5050",
|
||||
"borderStyle": "black",
|
||||
"selectedColor": "#f45a5a",
|
||||
"borderStyle": "white",
|
||||
"backgroundColor": "#ffffff",
|
||||
"height": "60px",
|
||||
"fontSize": "12px",
|
||||
"fontSize": "14px",
|
||||
"iconWidth": "24px",
|
||||
"spacing": "3px",
|
||||
"iconfontSrc": "static/fonts/iconfont.ttf",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "组件"
|
||||
"text": "组件",
|
||||
"iconfont": {
|
||||
"text": "\ue64d",
|
||||
"selectedText": "\ue64d",
|
||||
"fontSize": "17px",
|
||||
"color": "#333",
|
||||
"selectedColor": "#f45a5a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/my/index",
|
||||
"text": "接口"
|
||||
"pagePath": "pages/profile/index",
|
||||
"text": "我的",
|
||||
"iconfont": {
|
||||
"text": "\ue600",
|
||||
"selectedText": "\ue600",
|
||||
"fontSize": "17px",
|
||||
"color": "#333",
|
||||
"selectedColor": "#f45a5a"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<!-- 顶部搜索区域 -->
|
||||
<view class="header">
|
||||
<view class="search-block">
|
||||
<t-search
|
||||
v-model="pageForm.searchKeyword"
|
||||
placeholder="搜索活动"
|
||||
shape="round"
|
||||
class="search"
|
||||
>
|
||||
<template #action>
|
||||
<t-button variant="text" @click="filterVisible = true">筛选</t-button>
|
||||
@@ -57,7 +55,7 @@
|
||||
@click="navigateToDetail(item.id)"
|
||||
>
|
||||
<view class="card-head">
|
||||
<image :src="item.image" mode="aspectFill" class="card-img"/>
|
||||
<image :src="item.cover" mode="aspectFill" class="card-img"/>
|
||||
<text class="card-tag" :class="getStatusClass(item.status)">
|
||||
{{ getStatusText(item.status) }}
|
||||
</text>
|
||||
@@ -68,33 +66,27 @@
|
||||
|
||||
<view class="card-info">
|
||||
<view class="info-item">
|
||||
<t-icon name="alarm" size="12px"/>
|
||||
<text>{{ item.date }}</text>
|
||||
报名时间
|
||||
{{moment(item.signStartTime).format('YYYY-MM-DD HH:mm:ss')}}
|
||||
{{moment(item.signEndTime).format('YYYY-MM-DD HH:mm:ss')}}
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<t-icon name="location" size="12px"/>
|
||||
<text>{{ item.location }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<t-icon name="user" size="12px"/>
|
||||
<text>{{ item.participants }}人已报名</text>
|
||||
活动时间
|
||||
{{moment(item.startTime).format('YYYY-MM-DD HH:mm:ss')}}
|
||||
{{moment(item.endTime).format('YYYY-MM-DD HH:mm:ss')}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-footer">
|
||||
<view class="price">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-value">{{ item.price }}</text>
|
||||
</view>
|
||||
<t-button
|
||||
size="small"
|
||||
shape="round"
|
||||
:theme="item.status === 2 ? 'default' : 'primary'"
|
||||
:disabled="item.status === 2"
|
||||
>
|
||||
{{ item.status === 1 ? '立即报名' : item.status === 3 ? '预约提醒' : '已结束' }}
|
||||
</t-button>
|
||||
</view>
|
||||
<!-- <view class="card-footer">-->
|
||||
<!-- <t-button-->
|
||||
<!-- size="small"-->
|
||||
<!-- shape="round"-->
|
||||
<!-- :theme="item.status === 2 ? 'default' : 'primary'"-->
|
||||
<!-- :disabled="item.status === 2"-->
|
||||
<!-- >-->
|
||||
<!-- {{ item.status === 1 ? '立即报名' : item.status === 3 ? '预约提醒' : '已结束' }}-->
|
||||
<!-- </t-button>-->
|
||||
<!-- </view>-->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -117,6 +109,7 @@
|
||||
import {ref, reactive, onMounted} from 'vue';
|
||||
import {Toast} from 'tdesign-mobile-vue';
|
||||
import {useRequest} from "@/util/request";
|
||||
import moment from "moment";
|
||||
|
||||
// 筛选选项
|
||||
const statusOptions = [
|
||||
@@ -141,12 +134,6 @@ const hasMore = ref(true);
|
||||
const refreshIng = ref(false);
|
||||
const filterVisible = ref(false);
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
keyword: '',
|
||||
status: ''
|
||||
});
|
||||
|
||||
// 获取状态样式类
|
||||
const getStatusClass = (status) => {
|
||||
return {
|
||||
@@ -187,6 +174,7 @@ const getListData = async (isRefresh = false) => {
|
||||
} else {
|
||||
dataList.value = [...dataList.value, ...data.list];
|
||||
}
|
||||
pageForm.totalCount = data.totalCount;
|
||||
hasMore.value = dataList.value.length < data.totalCount;
|
||||
pageForm.pageNumber++;
|
||||
console.log(hasMore.value)
|
||||
@@ -237,19 +225,12 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
.search-block {
|
||||
background-color: #fff;
|
||||
padding: 10px 16px;
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.search {
|
||||
--td-search-bg-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.filter {
|
||||
background-color: #fff;
|
||||
border-radius: 0 0 16px 16px;
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
<div class="member-name">
|
||||
{{ member.name }}
|
||||
<span class="member-role" v-if="member.role === 'leader'">队长</span>
|
||||
<span class="member-role" v-else-if="member.role === 'admin'" style="background-color: #ff9a5a;">管理员</span>
|
||||
</div>
|
||||
<div class="member-detail">
|
||||
<div class="member-detail-item">
|
||||
@@ -80,17 +79,18 @@
|
||||
|
||||
<!-- 当前角色与申请队长 -->
|
||||
<div v-if="currentUserRole !== 'leader'">
|
||||
<div class="role-notice" v-if="currentUserRole === 'admin'">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
您当前是管理员身份,拥有团队管理权限
|
||||
</div>
|
||||
<div class="role-notice" v-else>
|
||||
<div class="role-notice">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
您当前是普通成员身份
|
||||
</div>
|
||||
<button class="apply-leader-btn" @click="showLeaderConfirm">
|
||||
<t-button block
|
||||
theme="primary"
|
||||
variant="outline"
|
||||
size="large"
|
||||
shape="round"
|
||||
@click="leaderConfirmVisible = true">
|
||||
<i class="bi bi-award"></i>申请成为队长
|
||||
</button>
|
||||
</t-button>
|
||||
</div>
|
||||
<div class="role-notice" v-else>
|
||||
<i class="bi bi-check-circle"></i>
|
||||
@@ -100,15 +100,32 @@
|
||||
|
||||
<!-- 项目报名按钮 -->
|
||||
<div class="team-section">
|
||||
<button class="register-btn" @click="goToEventRegistration">
|
||||
<t-button block
|
||||
theme="primary"
|
||||
size="large"
|
||||
shape="round" @click="goToEventRegistration">
|
||||
<i class="bi bi-calendar-check"></i>
|
||||
项目报名
|
||||
</button>
|
||||
</t-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</view>
|
||||
|
||||
<t-dialog
|
||||
v-model:visible="leaderConfirmVisible"
|
||||
title="申请成为队长"
|
||||
cancel-btn="取消"
|
||||
confirm-btn="确认"
|
||||
@confirm="onLeaderConfirm"
|
||||
@cancel="leaderConfirmVisible = false"
|
||||
>
|
||||
<template #content>
|
||||
<p>申请成为队长前,请确认您已与团队其他成员商议并达成一致意见。</p>
|
||||
<p>一个团队仅能有一名队长,作为队长,您可以帮助队员一键报名项目。</p>
|
||||
</template>
|
||||
</t-dialog>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -124,7 +141,7 @@ const teamId = ''
|
||||
|
||||
const team = reactive({
|
||||
id: teamId,
|
||||
name: '江苏健康跑团',
|
||||
name: 'xx队',
|
||||
logoUrl: null,
|
||||
createdDate: '2023-10-15',
|
||||
eventId: '',
|
||||
@@ -132,7 +149,7 @@ const team = reactive({
|
||||
memberCount: 12,
|
||||
rank: 5,
|
||||
score: 256,
|
||||
description: '我们是一支来自江苏省的健康跑团队,致力于推广健康跑步文化,鼓励大家积极参与体育活动,共同展现江苏的活力风采。',
|
||||
description: 'xxxxxxxxxxxxxxxxxxx',
|
||||
|
||||
// 团队成员
|
||||
members: [
|
||||
@@ -179,6 +196,9 @@ const team = reactive({
|
||||
]
|
||||
});
|
||||
|
||||
const currentUserRole = ref('')
|
||||
// 申请成为队长弹框提示
|
||||
const leaderConfirmVisible = ref(false)
|
||||
|
||||
function handleMenuOpened(){
|
||||
|
||||
@@ -190,6 +210,12 @@ function actChange(val){
|
||||
console.log(val)
|
||||
}
|
||||
|
||||
// 申请成为队长
|
||||
function onLeaderConfirm(){
|
||||
|
||||
}
|
||||
|
||||
function goToEventRegistration(){}
|
||||
|
||||
|
||||
</script>
|
||||
@@ -310,4 +336,127 @@ function actChange(val){
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
/* 团队成员列表 */
|
||||
.team-section {
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-title i {
|
||||
margin-right: 8px;
|
||||
color: #e74c3c;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.member-list {
|
||||
background: white;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.member-item {
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.member-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.member-avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background-color: #f0f0f0;
|
||||
margin-right: 15px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.member-avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.member-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.member-role {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.member-detail {
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.member-detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.member-detail-item i {
|
||||
margin-right: 4px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.member-actions {
|
||||
color: #999;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.role-notice {
|
||||
background-color: #f8f9fa;
|
||||
padding: 12px 15px;
|
||||
border-radius: 8px;
|
||||
margin-top: 15px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.role-notice i {
|
||||
color: #ffa63f;
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<view class="login-container">
|
||||
<image
|
||||
class="banner-image"
|
||||
src="https://res.firstui.cn/template/loginB/login/img_banner_3x.png"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
|
||||
<view class="login-form">
|
||||
<text class="title">登录</text>
|
||||
|
||||
<t-input
|
||||
class="input-item"
|
||||
v-model="loginForm.phone"
|
||||
placeholder="请输入手机号码"
|
||||
>
|
||||
</t-input>
|
||||
|
||||
<t-input
|
||||
class="input-item"
|
||||
v-model="loginForm.smsCode"
|
||||
placeholder="请输入验证码"
|
||||
>
|
||||
<template #suffix>
|
||||
<t-button variant="text" class="verify-btn" @click="getSmsCode" :disabled="isCountingDown">
|
||||
{{ countDownText }}
|
||||
</t-button>
|
||||
</template>
|
||||
</t-input>
|
||||
|
||||
<!-- <view class="login-options">-->
|
||||
<!-- <text>账号登录</text>-->
|
||||
<!-- <text>注册</text>-->
|
||||
<!-- </view>-->
|
||||
|
||||
<t-button block theme="primary" size="large" class="login-btn" @click="handleLogin">
|
||||
登录
|
||||
</t-button>
|
||||
|
||||
<!-- <view class="other-login">-->
|
||||
<!-- <text class="other-text">其他登录方式</text>-->
|
||||
<!-- <view class="login-methods">-->
|
||||
<!-- <t-button shape="circle" variant="outline" icon="qq" class="method-btn" />-->
|
||||
<!-- <t-button shape="circle" variant="outline" icon="wechat" class="method-btn" />-->
|
||||
<!-- <t-button shape="circle" variant="outline" icon="image" class="method-btn" />-->
|
||||
<!-- </view>-->
|
||||
<!-- </view>-->
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive, computed, onMounted} from 'vue'
|
||||
import {Toast} from 'tdesign-mobile-vue';
|
||||
import {useRequest} from "@/util/request";
|
||||
|
||||
const request = useRequest()
|
||||
|
||||
const loginForm = reactive({
|
||||
phone: '',
|
||||
smsCode: ''
|
||||
})
|
||||
|
||||
const countdown = ref(0)
|
||||
const timer = ref(null)
|
||||
// 是否在倒计时中
|
||||
const isCountingDown = computed(() => countdown.value > 0)
|
||||
// 验证码按钮文字
|
||||
const countDownText = computed(() => {
|
||||
return isCountingDown.value ? `${countdown.value}秒后重试` : '获取验证码'
|
||||
})
|
||||
|
||||
// 开始倒计时
|
||||
const startCountDown = () => {
|
||||
countdown.value = 60
|
||||
timer.value = setInterval(() => {
|
||||
countdown.value--
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer.value)
|
||||
timer.value = null
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
// 获取验证码
|
||||
async function getSmsCode() {
|
||||
if (!loginForm.phone) {
|
||||
Toast.error('请输入手机号码');
|
||||
return
|
||||
}
|
||||
|
||||
if (isCountingDown.value) return
|
||||
try {
|
||||
const {code, data, msg} = await request.post('/client/auth/send-sms-code/' + loginForm.phone)
|
||||
if (code === 0) {
|
||||
Toast.success('验证码发送成功')
|
||||
// 开始倒计时
|
||||
startCountDown()
|
||||
} else {
|
||||
Toast.error(msg)
|
||||
}
|
||||
} catch (e) {
|
||||
Toast.error(e.message)
|
||||
} finally {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogin() {
|
||||
if (!loginForm.phone) {
|
||||
Toast.error('请输入手机号码');
|
||||
return
|
||||
}
|
||||
if (!loginForm.smsCode) {
|
||||
Toast.error('请输入验证码');
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const {data, code, msg} = await useRequest().post('/client/auth/sms-login', loginForm)
|
||||
if (code === 0) {
|
||||
Toast.success('登录成功')
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
} else {
|
||||
Toast.error(msg)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
Toast.error(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (timer.value) {
|
||||
clearInterval(timer.value)
|
||||
timer.value = null
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.login-container {
|
||||
min-height: 100vh;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
width: 100%;
|
||||
height: 260px;
|
||||
background-color: #4080ff;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
padding: 20px;
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin: 20px 0;
|
||||
display: block;
|
||||
color: var(--td-brand-color-5);
|
||||
}
|
||||
|
||||
.input-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.country-code {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.verify-btn {
|
||||
color: var(--td-brand-color-5);
|
||||
}
|
||||
|
||||
.login-options {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.other-login {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
|
||||
.other-text {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 0 10px;
|
||||
background: #fff;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 60px;
|
||||
height: 1px;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
&::before {
|
||||
left: -70px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: -70px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-methods {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
margin-top: 20px;
|
||||
|
||||
.method-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +0,0 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
我的页面
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,611 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<!-- 头部个人信息 -->
|
||||
<div class="profile-header">
|
||||
<div class="back-button" @click="goBack">
|
||||
<i class="bi bi-chevron-left"></i>
|
||||
</div>
|
||||
|
||||
<div class="profile-avatar">
|
||||
<img src="https://erp-bucket.pek3b.qingstor.com/assistant/avatar.jpg" alt="用户头像"
|
||||
onerror="this.src='data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMjAwIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0iI2U5ZWNlZiIvPjxwYXRoIGQ9Ik0xMDAgNTVhNDAgNDAgMCAxIDAgMCA4MCw0MCA0MCAwIDEgMCAwLTgwem0wIDE5MGM0MiAwIDgwLTM0IDgwLTc1IDAtMTcuNS05LjMtMzMuMi0yNC0zOS44YTYwIDYwIDAgMCAxLTExMiAwQzI5LjMgMTM3IDIwIDE1Mi41IDIwIDE3MGMwIDQxIDM4IDc1IDgwIDc1eiIgZmlsbD0iI2I5YmVjNyIvPjwvc3ZnPg=='">
|
||||
</div>
|
||||
|
||||
<div class="profile-name">{{ userData.name }}</div>
|
||||
<div class="profile-bio">{{ userData.bio }}</div>
|
||||
|
||||
<div class="profile-stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ userData.events }}</div>
|
||||
<div class="stat-label">参加活动</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ userData.points }}</div>
|
||||
<div class="stat-label">活力值</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ userData.teams }}</div>
|
||||
<div class="stat-label">加入团队</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 基本功能区 -->
|
||||
<div class="profile-functions">
|
||||
<div class="function-section">
|
||||
<div class="section-header">
|
||||
<div class="section-title">
|
||||
<i class="bi bi-gear-fill"></i>
|
||||
<span>基本功能</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="function-item" @click="navigateTo('personal-info')">
|
||||
<div class="item-icon">
|
||||
<i class="bi bi-person-badge"></i>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-title">个人资料</div>
|
||||
<div class="item-description">查看和编辑个人信息</div>
|
||||
</div>
|
||||
<div class="item-arrow">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="function-item" @click="navigateTo('certification')">
|
||||
<div class="item-icon" style="background-color: #f5f0ff;">
|
||||
<i class="bi bi-shield-check" style="color: #7e57c2;"></i>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-title">实名认证</div>
|
||||
<div class="item-description">已认证</div>
|
||||
</div>
|
||||
<div class="item-arrow">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 我参加的活动 -->
|
||||
<div class="function-section">
|
||||
<div class="function-item" @click="navigateTo('all-events')">
|
||||
<div class="item-icon" style="background-color: #fff4e5;">
|
||||
<i class="bi bi-calendar-event" style="color: #ff9800;"></i>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-title">我参加的活动</div>
|
||||
<div class="item-description">已参加 {{ userData.events }} 个活动</div>
|
||||
</div>
|
||||
<div class="item-arrow">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 我的团队 -->
|
||||
<div class="function-section">
|
||||
<div class="function-item" @click="navigateTo('all-teams')">
|
||||
<div class="item-icon" style="background-color: #e8f5e9;">
|
||||
<i class="bi bi-people-fill" style="color: #4caf50;"></i>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-title">我的团队</div>
|
||||
<div class="item-description">已加入 {{ userData.teams }} 个团队</div>
|
||||
</div>
|
||||
<div class="item-arrow">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部填充 -->
|
||||
<div class="bottom-padding"></div>
|
||||
|
||||
<!-- 底部导航栏 -->
|
||||
<div class="footer-nav">
|
||||
<div class="footer-item" @click="navigateTo('home')">
|
||||
<div class="footer-icon-container">
|
||||
<div class="footer-icon">
|
||||
<i class="bi bi-house-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-text">首页</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-item" @click="navigateTo('sports')">
|
||||
<div class="footer-icon-container">
|
||||
<div class="footer-icon">
|
||||
<i class="bi bi-lightning-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-text">运动</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-item" @click="navigateTo('community')">
|
||||
<div class="footer-icon-container">
|
||||
<div class="footer-icon">
|
||||
<i class="bi bi-people-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-text">社区</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-item active" @click="navigateTo('profile')">
|
||||
<div class="footer-icon-container">
|
||||
<div class="footer-icon">
|
||||
<i class="bi bi-person-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-text">我的</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive} from "vue";
|
||||
|
||||
// 用户数据
|
||||
const userData = reactive({
|
||||
name: '张三',
|
||||
bio: '热爱运动,热爱生活',
|
||||
events: 8,
|
||||
points: 1650,
|
||||
teams: 2
|
||||
});
|
||||
|
||||
// 参加的活动数据
|
||||
const events = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: '2023年江苏省青少年体育锦标赛',
|
||||
image: 'https://erp-bucket.pek3b.qingstor.com/assistant/premium-event-1.jpg',
|
||||
date: '2023.12.15-12.20',
|
||||
location: '南京奥体中心',
|
||||
status: 'completed'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '2023年冬季室内马拉松挑战赛',
|
||||
image: 'https://erp-bucket.pek3b.qingstor.com/assistant/premium-event-2.jpg',
|
||||
date: '2024.01.10-01.15',
|
||||
location: '苏州国际博览中心',
|
||||
status: 'upcoming'
|
||||
}
|
||||
]);
|
||||
|
||||
// 团队数据
|
||||
const teams = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '南京跑团',
|
||||
image: 'https://erp-bucket.pek3b.qingstor.com/assistant/team-1.jpg',
|
||||
members: 128,
|
||||
level: 5,
|
||||
role: '普通成员',
|
||||
currentEvent: { // 当前参与的活动
|
||||
id: 2,
|
||||
title: '2023年冬季室内马拉松挑战赛'
|
||||
},
|
||||
events: [1, 2] // 参与过的所有活动ID
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '江苏健走协会',
|
||||
image: 'https://erp-bucket.pek3b.qingstor.com/assistant/team-2.jpg',
|
||||
members: 256,
|
||||
level: 8,
|
||||
role: '团队管理员',
|
||||
currentEvent: {
|
||||
id: 1,
|
||||
title: '2023年江苏省青少年体育锦标赛'
|
||||
},
|
||||
events: [1]
|
||||
}
|
||||
]);
|
||||
|
||||
// 活动状态文本
|
||||
const getStatusText = (status) => {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return '已完成';
|
||||
case 'upcoming':
|
||||
return '即将开始';
|
||||
case 'in-progress':
|
||||
return '进行中';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 头部区域 */
|
||||
.profile-header {
|
||||
background: linear-gradient(135deg, #ff5050 0%, #ff9a5a 100%);
|
||||
padding: 30px 20px 20px;
|
||||
color: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin: 0 auto 15px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.profile-avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.profile-name {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.profile-bio {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.profile-stats {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-top: 15px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 功能区域 */
|
||||
.profile-functions {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.function-section {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.section-title i {
|
||||
font-size: 18px;
|
||||
color: #ff5050;
|
||||
}
|
||||
|
||||
.view-all {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.function-item {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
}
|
||||
|
||||
.function-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: #f5f8ff;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.item-icon i {
|
||||
font-size: 20px;
|
||||
color: #3f7eef;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.item-description {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.item-arrow {
|
||||
color: #ccc;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 我的活动 */
|
||||
.events-list, .teams-list {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.event-card, .team-card {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.event-image, .team-image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.event-image img, .team-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.event-info, .team-info {
|
||||
flex: 1;
|
||||
padding: 10px 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.event-title, .team-title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 5px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.event-meta, .team-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.meta-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.meta-info i {
|
||||
font-size: 14px;
|
||||
margin-right: 3px;
|
||||
color: #ff5050;
|
||||
}
|
||||
|
||||
.event-status, .team-status {
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.status-completed {
|
||||
background-color: #8e8e8e;
|
||||
}
|
||||
|
||||
.status-upcoming {
|
||||
background-color: #4caf50;
|
||||
}
|
||||
|
||||
.status-in-progress {
|
||||
background-color: #ff9800;
|
||||
}
|
||||
|
||||
/* 团队活动标签 */
|
||||
.team-event-tag {
|
||||
background-color: #e3f2fd;
|
||||
color: #0277bd;
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.team-event-tag i {
|
||||
font-size: 12px;
|
||||
margin-right: 4px;
|
||||
color: #0277bd;
|
||||
}
|
||||
|
||||
/* 底部导航栏 */
|
||||
.footer-nav {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 55px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
border-top: 1px solid #eee;
|
||||
z-index: 100;
|
||||
padding: 5px 0 5px;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.footer-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footer-icon-container {
|
||||
position: relative;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: transform 0.3s ease;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.footer-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.active .footer-icon-container {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
|
||||
.active .footer-icon-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background-color: #ff5050;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 6px rgba(255, 80, 80, 0.3);
|
||||
z-index: 1;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 12px;
|
||||
margin-top: 1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.active .footer-text {
|
||||
color: #ff5050;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.active i {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 底部填充 */
|
||||
.bottom-padding {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
transition: opacity 0.5s, visibility 0.5s;
|
||||
}
|
||||
|
||||
.loading-overlay.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid #f3f3f3;
|
||||
border-top: 5px solid #ff5050;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user