Conflicts:
	backend/src/main/resources/application.yaml
	frontend/vite.config.js
This commit is contained in:
zhengziru
2025-05-06 20:31:52 +08:00
31 changed files with 573 additions and 338 deletions
+11
View File
@@ -127,6 +127,17 @@
"selectedColor": "#f45a5a"
}
},
{
"pagePath": "pages/activity/rank/index",
"text": "排行榜",
"iconfont": {
"text": "\ue8b3",
"selectedText": "\ue8b3",
"fontSize": "17px",
"color": "#333",
"selectedColor": "#f45a5a"
}
},
{
"pagePath": "pages/profile/index",
"text": "我的",
+9 -13
View File
@@ -5,7 +5,8 @@
<view style="flex: 1;overflow-y: auto">
<!-- 活动图片 -->
<view class="event-header">
<image :src="detailData.cover" mode="aspectFill" class="event-cover"/>
<t-image :src=config.fileDomain+detailData.cover shape="round"
class="event-cover" fit="fill"/>
</view>
<!-- 活动标题与标签 -->
@@ -76,11 +77,13 @@
<!-- 项目图片区域 -->
<!-- v-if="item.img"-->
<!-- :src="item.img"-->
<image
<!-- <image
src="https://api.zzzmh.cn/v2/bz/v3/getUrl/c9d3deb2880411ebb6edd017c2d2eca220"
mode="aspectFill"
class="event-item-image"
/>
/>-->
<t-image :src=config.fileDomain+item.img shape="round"
class="event-item-image" fit="fill"/>
<!-- 项目信息区域 -->
<view class="event-item-info">
@@ -170,6 +173,7 @@ import {ref, onMounted, computed} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {Toast} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
import {config} from "@/utils/config";
const request = useRequest();
const actId = ref(null);
@@ -229,23 +233,15 @@ const navigateToSignUp = () => {
// 获取活动详情
const getActivityDetail = async (id) => {
try {
const loading = Toast({
message: '加载中...',
theme: 'loading',
duration: 0
});
const {code, data} = await request.get(`/client/activity/get/${id}`);
if (code === 0 && data) {
detailData.value = data;
} else {
Toast({message: '获取活动详情失败', theme: 'error'});
// Toast({message: '获取活动详情失败', theme: 'error'});
}
loading.clear();
} catch (error) {
console.error('获取活动详情错误', error);
Toast({message: '获取活动详情失败', theme: 'error'});
// Toast({message: '获取活动详情失败', theme: 'error'});
}
};
+8 -2
View File
@@ -8,6 +8,9 @@
shape="round"
class="search-input"
@onSearch="handleSearch"
@onSubmit="handleSearch"
@submit="handleSearch"
@search="handleSearch"
/>
</view>
</view>
@@ -31,6 +34,7 @@
<view>
<view style="display: flex;align-items: center;margin-bottom: 10px">
<image
src="https://assets.cumen.fun/upload/017cdaf1-c5fa-788e-eea2-d0ee249f902b/0187376a-3cfa-07b3-3cd6-7479bf463865?x-oss-process=style/720-jpg"
style="width: 30px;height: 30px;flex-shrink: 0"
@@ -41,7 +45,8 @@
</view>
</view>
<view style="display: flex;">
<image :src="item.cover" mode="aspectFill" style="flex-shrink:0;width: 120px;height: 90px;border-radius: 5px;"></image>
<t-image :src=config.fileDomain+item.cover shape="round"
style="flex-shrink:0;width: 120px;height: 90px;border-radius: 5px;" fit="fill"/>
<view
style="margin-left:10px;flex:1;overflow:hidden;display: flex;flex-direction: column;justify-content:space-around;color: #534d4d;">
<view
@@ -61,7 +66,7 @@
</view>
<view style="border-top: 1px solid #f0f0f0;margin-top: 10px;padding-top: 12px;display: flex;align-items: center;justify-content: space-between">
<view>
100人已报名
<!-- 100人已报名-->
</view>
<t-button theme="primary" shape="round" size="small">立即报名</t-button>
</view>
@@ -89,6 +94,7 @@ import {ref, reactive, onMounted} from 'vue';
import {Toast} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
import moment from "moment";
import {config} from "@/utils/config";
// 活动状态筛选选项
const statusOptions = [
+38 -6
View File
@@ -1,5 +1,10 @@
<template>
<view class="page-container">
<t-dropdown-menu class="top-dropdown">
<t-dropdown-item :options="actOptions" :value="activityId"/>
</t-dropdown-menu>
<view class="rank-header">
<view class="title-container">
<view class="title">活动排行榜</view>
@@ -75,17 +80,27 @@
</template>
<script setup>
import { ref, onMounted } from 'vue';
import {ref, onMounted, computed} from 'vue';
import { Toast } from 'tdesign-mobile-vue';
import { useRequest } from "@/utils/request";
import {onLoad} from "@dcloudio/uni-app";
// 排行榜数据
const actList = ref([])
const rankList = ref([]);
const activityId = ref([]);
const activityId = ref(null);
const loading = ref(false);
const refresherTriggered = ref(false);
const actOptions = computed(() => {
return actList.value.map(item => {
return {
value: item.id,
label: item.title
}
})
})
// 获取排行榜数据
const getRankData = async () => {
loading.value = true;
@@ -110,6 +125,22 @@ const getRankData = async () => {
}
};
async function listMineAct() {
try {
const {data, code, msg} = await useRequest().get('/client/activity/list-mine-act')
if (code === 0) {
actList.value = data
if (actList.value.length && activityId.value == null) {
activityId.value = actList.value[0].id
}
} else {
Toast.error(msg)
}
} catch (e) {
Toast.error(e.message)
}
}
// 刷新
const handleRefresh = () => {
if (loading.value) {
@@ -122,12 +153,13 @@ const handleRefresh = () => {
};
onLoad((option) => {
if (option.id) {
activityId.value=option.id
activityId.value = Number(option.id)
}
});
// 页面加载时获取数据
onMounted(() => {
getRankData();
onMounted(async () => {
await listMineAct()
await getRankData()
});
</script>
@@ -441,7 +473,7 @@ onMounted(() => {
.refresh-btn {
position: fixed;
right: 30rpx;
bottom: 40rpx;
bottom: 140rpx;
width: 90rpx;
height: 90rpx;
border-radius: 50%;
@@ -1,14 +1,5 @@
<template>
<view class="page-container">
<!-- <view class="act-info">-->
<!-- <view class="act-title">奋进新征程 运动促健康 2024晴江第六届"牧城之夜"荧光跑</view>-->
<!-- <view class="act-meta">-->
<!-- <view class="act-meta-item">22222</view>-->
<!-- <view class="act-meta-item">22222222222222222222</view>-->
<!-- </view>-->
<!-- </view>-->
<view class="form-area">
<t-form
ref="formRef"
@@ -17,23 +8,23 @@
show-error-message
label-align="left"
scroll-to-first-error="auto"
labelWidth="90px"
>
<view class="form-block">
<view class="form-block-title">
<t-icon name="institution"></t-icon>
单位信息
</view>
<t-form-item label="参加单位" name="unitName" :rules="[{required:true,message: '请输入单位名称'}]">
<!-- <t-input v-model="formData.unitName" borderless placeholder="请输入参加单位名称"></t-input>-->
<search-picker v-model="formData.unitName" :options="unitOptions" placeholder="请选择单位"></search-picker>
</t-form-item>
<t-form-item label="联系人" name="contactName" :rules="[{required:true,message: '请输入联系人姓名'}]">
<t-input v-model="formData.contactName" borderless placeholder="请输入联系人姓名"></t-input>
</t-form-item>
<t-form-item label="联系方式" name="contactPhone" :rules="[{required:true,message: '请输入联系电话'}]">
<t-input v-model="formData.contactPhone" borderless placeholder="请输入联系电话"></t-input>
</t-form-item>
</view>
<!-- <view class="form-block">-->
<!-- <view class="form-block-title">-->
<!-- <t-icon name="institution"></t-icon>-->
<!-- 单位信息-->
<!-- </view>-->
<!-- <t-form-item label="参加单位" name="unitName" :rules="[{required:true,message: '请输入单位名称'}]">-->
<!-- <t-input v-model="formData.unitName" borderless readonly placeholder="请输入参加单位名称"></t-input>-->
<!-- </t-form-item>-->
<!-- <t-form-item label="联系人" name="contactName" :rules="[{required:true,message: '请输入联系人姓名'}]">-->
<!-- <t-input v-model="formData.contactName" borderless placeholder="请输入联系人姓名"></t-input>-->
<!-- </t-form-item>-->
<!-- <t-form-item label="联系方式" name="contactPhone" :rules="[{required:true,message: '请输入联系电话'}]">-->
<!-- <t-input v-model="formData.contactPhone" borderless placeholder="请输入联系电话"></t-input>-->
<!-- </t-form-item>-->
<!-- </view>-->
<view class="form-block">
<view class="form-block-title">
@@ -79,14 +70,23 @@
<t-radio :block="false" name="radio" :value="false" label="否"/>
</t-radio-group>
</t-form-item>
<t-form-item label="是否联系人" :name="`userList[${index}].isContacts`"
:rules="[{required:true,message: '请选择是否为联系人'}]">
<t-radio-group v-model="user.isContacts" class="radio-box">
<t-radio :block="false" name="radio" :value="true" label="是"/>
<t-radio :block="false" name="radio" :value="false" label="否"/>
</t-radio-group>
</t-form-item>
</view>
</view>
</t-form>
<view class="add-user" @click="formData.userList.push({})">
<t-icon name="plus" size="24px"></t-icon>
<text>添加人员</text>
</view>
</view>
<!-- 悬浮添加按钮 -->
<view class="float-add-btn" @click="formData.userList.push({})">
<t-icon name="plus" size="24px" color="#fff"></t-icon>
</view>
<view class="footer">
<view class="agreement-checkbox">
<t-checkbox label="多选" icon="rectangle" v-model:checked="agreementChecked" default-checked>
@@ -105,6 +105,14 @@
{{ isSignUp ? '重新提交报名信息' : '提交报名'}}
</t-button>
</view>
<!-- 引导 -->
<t-guide
v-model="guideCurrent"
:steps="guideSteps"
>
</t-guide>
</view>
</template>
@@ -113,9 +121,32 @@ import {ref} from 'vue'
import {useRequest} from '@/utils/request';
import {Dialog, Toast} from "tdesign-mobile-vue";
import {onLoad} from "@dcloudio/uni-app";
import SearchPicker from "@/components/SearchPicker.vue";
import {useUserStore} from "@/store";
const request = useRequest()
const userStore= useUserStore()
// 引导部分
const guideCurrent = ref(-1)
const guideSteps = [
{
element:()=> document.querySelector('.float-add-btn'),
title: '添加活动报名人员',
placement: 'top'
},
{
element:()=> document.querySelector('.t-form'),
title: '填写报名信息',
placement: 'top'
},
{
element:()=> document.querySelector('.footer'),
title: '提交报名信息',
placement: 'top'
}
]
// 报名信息
const actId = ref(null)
const isSignUp = ref(null)
const agreementChecked = ref(false)
@@ -124,18 +155,17 @@ const formData = ref({
userList: [{}]
})
const unitOptions = ref([
{value:'单位1',label:'单位1'},
{value:'单位2',label:'单位2'},
{value:'单位3',label:'单位3'},
])
onLoad((option) => {
if (option.id) {
actId.value = option.id
formData.value.activityId = option.id
listSignUpUser()
getIsSignUp()
// 没报名进行引导
if(formData.value.userList.length === 0){
guideCurrent.value = 0
//formData.value.unitName = userStore.userInfo?.unit.name
}
} else {
Toast({message: 'ID不存在', theme: 'error'});
}
@@ -226,6 +256,7 @@ async function submitForm() {
title: '提交中...',
mask: true,
})
console.log(formData.value)
try {
const {code, data, msg} = await request.post('/client/activity/sign-up', formData.value)
if (code === 0) {
@@ -308,7 +339,6 @@ async function removeUser(index) {
.form-area {
flex: 1;
overflow-y: auto;
//margin-top: 100px;
margin-bottom: 130px;
}
@@ -379,16 +409,23 @@ async function removeUser(index) {
}
}
.add-user {
padding: 15px;
text-align: center;
color: #e74c3c;
font-weight: 500;
cursor: pointer;
.float-add-btn {
position: fixed;
right: 32rpx;
bottom: 160rpx;
width: 110rpx;
height: 110rpx;
border-radius: 50%;
background-color: #e74c3c;
display: flex;
justify-content: center;
align-items: center;
background: #ffffff;
justify-content: center;
z-index: 100;
transition: transform 0.2s;
}
.float-add-btn:active {
transform: scale(0.95);
}
.radio-box {
@@ -14,25 +14,25 @@
</view>
<!-- 小提示 -->
<view class="success-tips">
<t-icon name="info-circle-filled" size="16px" />
<text>活动开始前我们将通过短信通知您</text>
</view>
<!-- <view class="success-tips">-->
<!-- <t-icon name="info-circle-filled" size="16px" />-->
<!-- <text>活动开始前我们将通过短信通知您</text>-->
<!-- </view>-->
<!-- 底部按钮 -->
<view class="action-buttons">
<t-button theme="default" shape="round" @click="navigateToHome">返回首页</t-button>
<t-button theme="primary" shape="round" @click="viewRegistrationDetails">查看报名详情</t-button>
<t-button theme="primary" shape="round" @click="navigateToEdit">修改报名信息</t-button>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { Toast } from 'tdesign-mobile-vue';
import { useRequest } from '@/utils/request';
import {ref} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {Toast} from 'tdesign-mobile-vue';
import {useRequest} from '@/utils/request';
const request = useRequest();
const actId = ref(null);
@@ -69,9 +69,9 @@ const navigateToHome = () => {
};
// 查看报名详情
const viewRegistrationDetails = () => {
uni.navigateTo({
url: '/pages/activity/registration-details/index?id=' + actId.value
const navigateToEdit = () => {
uni.redirectTo({
url: '/pages/activity/registration-form/index?id=' + actId.value
});
};
</script>
@@ -106,7 +106,7 @@ const viewRegistrationDetails = () => {
image {
width: 100%;
height: 100%;
animation: float 3s ease-in-out infinite;
//animation: float 3s ease-in-out infinite;
}
}
+42 -51
View File
@@ -69,32 +69,10 @@
</view>
</view>
</view>
<div class="bg-decoration">
<svg class="wave" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
<path fill="#f45a5a" fill-opacity="0.1" d="M0,160L48,154.7C96,149,192,139,288,144C384,149,480,171,576,176C672,181,768,171,864,154.7C960,139,1056,117,1152,128C1248,139,1344,181,1392,202.7L1440,224L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z"></path>
</svg>
<svg class="dots" width="100" height="100" viewBox="0 0 100 100">
<circle fill="#f45a5a" fill-opacity="0.2" cx="10" cy="10" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="30" cy="10" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="50" cy="10" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="70" cy="10" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="90" cy="10" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="10" cy="30" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="30" cy="30" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="50" cy="30" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="70" cy="30" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="90" cy="30" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="10" cy="50" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="30" cy="50" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="50" cy="50" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="70" cy="50" r="3" />
<circle fill="#f45a5a" fill-opacity="0.2" cx="90" cy="50" r="3" />
</svg>
</div>
</template>
<view style="flex: 1;display: flex;justify-content: center;align-items: center">
<t-empty v-if="actOptions && actOptions.length === 0"
<view v-if="actOptions && actOptions.length === 0" style="flex: 1;display: flex;justify-content: center;align-items: center">
<t-empty
image="/static/img/empty.png"
description="报名活动后才能扫码签到"
style="flex: 1">
@@ -114,12 +92,14 @@
<script setup>
import {ref, computed, onMounted} from 'vue';
import {onBackPress, onLoad} from "@dcloudio/uni-app";
import {useRequest} from '@/utils/request';
import {useUserStore} from "@/store";
import {Dialog, Toast} from 'tdesign-mobile-vue';
import moment from 'moment';
import ScanQRCode from '@/components/scanQrCode.vue'
const request = useRequest()
const userStore = useUserStore()
const list = ref([])
const signInfo = ref({})
@@ -138,6 +118,20 @@ onMounted(() => {
listMineAct();
});
onLoad((options)=>{
if(options.id){
selectActId.value = Number(options.id)
signIn()
}
})
// 返回按钮
onBackPress(()=>{
uni.redirectTo({
url: '/pages/index/index'
})
})
// 获取活动列表
async function listMineAct() {
try {
@@ -175,6 +169,10 @@ async function getSignInfo(val) {
// 扫码签到
function handleScan() {
if(signInfo.value.isSignIn){
Toast('您已签到')
return
}
qrScannerRef.value.startScan()
}
@@ -185,6 +183,20 @@ function handleScanSuccess(val) {
Toast.error("请扫描合法的二维码")
return
}
if(val.indexOf('http')!== -1){
const url = new URL(val)
const id = url.searchParams.get('id')
if(id){
val = id
signIn()
return;
}else{
Toast.error("请扫描合法的二维码")
return
}
}
if (String(val) !== String(selectActId.value)) {
Toast.error('请扫码签到当前活动,扫描成功,活动ID为' + val + ",当前选择活动ID为" + selectActId.value)
return
@@ -195,6 +207,11 @@ function handleScanSuccess(val) {
// 签到
async function signIn() {
try {
if(signInfo.value.isSignIn){
Toast.error('您已签到')
return
}
await uni.showLoading({
title: '签到中...',
mask: true,
@@ -538,32 +555,6 @@ function navigateToSignUp(){
color: #999999;
}
.bg-decoration {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
overflow: hidden;
}
.wave {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 200px;
}
.dots {
position: absolute;
bottom: 5%;
right: 5%;
animation: float 4s ease-in-out infinite;
}
/* 导入Animate.css的部分动画 */
.animate__animated {
animation-duration: 1s;
+18 -18
View File
@@ -1,9 +1,10 @@
<template>
<view class="page-container">
<template v-if="actOptions && actOptions.length>0">
<t-dropdown-menu class="top-dropdown">
<t-dropdown-item :options="actOptions" :value="selectActId"/>
</t-dropdown-menu>
<t-dropdown-menu class="top-dropdown">
<t-dropdown-item :options="actOptions" :value="selectActId"/>
</t-dropdown-menu>
<template v-if="actOptions && actOptions.length > 0 && teamMembers.length > 0">
<view class="content-container">
<!-- <view class="page-header">-->
<!-- <text class="page-title">团队报名</text>-->
@@ -25,8 +26,11 @@
<view class="project-card__title">{{ project.name }}</view>
<div style="display: flex;justify-content: flex-end;margin-top: 10px;">
<t-button size="small" class="project-card__button" @click="openDesc(project)">项目介绍</t-button>
<t-button size="small" @click="selectProject(project)" class="project-card__button"> 活动报名 {{
<t-button theme="primary" size="small" class="project-card__button" @click="openDesc(project)">
项目介绍
</t-button>
<t-button theme="primary" size="small" @click="selectProject(project)" class="project-card__button">
活动报名 {{
registrationStatus.get(project.id)?.length ? registrationStatus.get(project.id)?.length : 0
}}人
</t-button>
@@ -41,10 +45,10 @@
</view>
</template>
<view v-else style="flex: 1;display: flex;justify-content: center;align-items: center">
<t-empty v-if="actOptions && actOptions.length === 0"
image="/static/img/empty.png"
description="您没有任何团队信息,无法进行项目报名"
style="flex: 1">
<t-empty
image="/static/img/empty.png"
description="您没有任何团队信息,无法进行项目报名"
style="flex: 1">
</t-empty>
</view>
@@ -205,9 +209,9 @@ async function listActs() {
label: item.title
}
})
// if(selectActId.value === null && data.length){
// selectActId.value = data[0].id
// }
if (selectActId.value === null && data.length) {
selectActId.value = data[0].id
}
} else {
Toast.error(msg)
}
@@ -240,7 +244,6 @@ async function listTeamUser() {
teamMembers.value = data;
isSelfLeader.value = data.find(t => t.userId === userStore?.userInfo.id)?.isLeader
} else {
Toast.error(msg)
}
} catch (e) {
Toast.error(e)
@@ -285,7 +288,6 @@ const getApplyUsers = async () => {
if (code === 0) {
selectedMemberList.value = data
} else {
Toast.error(msg)
}
} catch (e) {
Toast.error(e.message)
@@ -297,7 +299,7 @@ const cancelRegistration = async () => {
let data = {}
data.activityId = selectActId.value
data.itemId = selectedItemId.value
const {code, msg} = await useRequest().post('/client/item/cancel-team-sign-up/' ,{...data})
const {code, msg} = await useRequest().post('/client/item/cancel-team-sign-up/', {...data})
if (code === 0) {
Toast.success('取消报名成功')
showMemberPicker.value = false;
@@ -461,8 +463,6 @@ const submitRegistration = async () => {
.project-card__button {
margin-left: 10px;
background-color: #0052D9;
color: #ffffff;
}
+8 -8
View File
@@ -1,11 +1,11 @@
<template>
<view class="page-container">
<t-sticky>
<t-dropdown-menu class="top-dropdown-menu">
<t-dropdown-item :options="actOptions" :value="pageForm.actId" @change="actChange"/>
</t-dropdown-menu>
</t-sticky>
<view v-if="team">
<t-sticky>
<t-dropdown-menu class="top-dropdown-menu">
<t-dropdown-item :options="actOptions" :value="pageForm.actId" @change="actChange"/>
</t-dropdown-menu>
</t-sticky>
<!-- 团队卡片 -->
<div class="content-area">
<div class="team-card">
@@ -118,8 +118,7 @@
</div>
</view>
<view v-else style="flex: 1;display: flex;justify-content: center;align-items: center">
<t-empty v-if="actOptions && actOptions.length === 0"
image="/static/img/empty.png"
<t-empty image="/static/img/empty.png"
description="您没有任何团队信息"
style="flex: 1">
</t-empty>
@@ -265,7 +264,8 @@ const getListAct = async () => {
// 点击查看成绩排名
const queryRank = () => {
uni.navigateTo({url: '/pages/activity/rank/index?id=' + pageForm.actId})
// uni.navigateTo({url: '/pages/activity/rank/index?id=' + pageForm.actId})
uni.switchTab({url: '/pages/activity/rank/index?id=' + pageForm.actId})
}
// 初始化
+8 -8
View File
@@ -22,7 +22,7 @@
<view class="menu-item" @click="navigateTo('/pages/activity/team/index')">
<view class="menu-icon-wrapper yellow-bg">
<view class="menu-icon team-icon"></view>
<view class="badge">3</view>
<!-- <view class="badge">3</view>-->
</view>
<view class="menu-text">我的团队</view>
</view>
@@ -55,9 +55,8 @@
<t-swiper-item v-for="(item, index) in actList" :key="index" style="height: 192px">
<view class="activity-list" @click="navigateTo('/pages/activity/detail/index?id=' + item.id)">
<view class="activity-item">
<image class="activity-image"
:src="item.cover"
mode="widthFix"></image>
<t-image :src=config.fileDomain+item.cover shape="round"
class="activity-image" fit="fill"/>
<view class="activity-info">
<view class="activity-title">{{ item.title }}</view>
<view class="activity-meta">
@@ -71,7 +70,7 @@
</t-swiper>
<t-empty
v-if="actList.length === 0"
description="暂无精彩瞬间"
description="暂无近期活动"
/>
</view>
@@ -84,9 +83,8 @@
<view class="activity-list" v-if="wonderfulMomentList.length > 0">
<view class="moment-item" v-for="item in wonderfulMomentList" :key="item.id"
@click="handleWonderfulMoment(item)">
<image class="moment-image"
:src="item.cover"
mode="widthFix"></image>
<t-image :src=config.fileDomain+item.cover shape="round"
class="moment-image" fit="fill"/>
<view class="moment-content">
<view class="moment-title">{{ item.title }}</view>
<view class="moment-sub-content">
@@ -111,6 +109,7 @@ import {onMounted, ref} from "vue";
import {Toast} from "tdesign-mobile-vue";
import {useRequest} from "@/utils/request";
import moment from "moment";
import {config} from "@/utils/config";
const request = useRequest()
@@ -332,6 +331,7 @@ uni-page-body {
}
.activity-image {
height: 200px;
width: 100%;
}
+18 -31
View File
@@ -136,6 +136,7 @@ import {ref, reactive, computed, onMounted, onBeforeUnmount} from 'vue'
import {Toast} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
import {useUserStore} from "@/store";
import {onLoad} from "@dcloudio/uni-app";
const request = useRequest()
const userStore = useUserStore()
@@ -287,44 +288,30 @@ async function handleLogin() {
userStore.smsLogin(loginForm).then(() => {
userStore.getUserInfo().then(() => {
Toast({message: '登录成功', theme: 'success'})
uni.switchTab({
url: '/pages/index/index'
})
const redirect = uni.getStorageSync("REDIRECT_URL")
if(redirect){
if(redirect.startsWith('/pages/index/index') || redirect.startsWith('/pages/profile/index')){
uni.switchTab({
url: redirect
})
}else{
uni.navigateTo({
url: redirect
})
}
}else{
uni.switchTab({
url: '/pages/index/index'
})
}
}).catch(e => {
console.log(e)
Toast({message: e.message, theme: 'error'})
})
}).catch(e => {
console.log(e)
Toast({message: e.message, theme: 'error'})
})
// const {data, code, msg} = await request.post('/client/auth/sms-login', loginForm)
// if (code === 0) {
// Toast({message: '登录成功', theme: 'success'})
// // 保存token
// uni.setStorageSync('token', data)
//
// // 获取之前保存的重定向URL
// const redirectUrl = uni.getStorageSync('REDIRECT_URL') || '/pages/index/index'
// uni.removeStorageSync('REDIRECT_URL') // 使用后删除
//
// // 延迟跳转,让用户看到成功提示
// setTimeout(() => {
// if (redirectUrl.startsWith('/pages/index/') || redirectUrl.startsWith('/pages/profile/')) {
// // 主页和个人中心,使用switchTab
// uni.switchTab({
// url: redirectUrl
// })
// } else {
// // 其他页面使用reLaunch
// uni.reLaunch({
// url: redirectUrl
// })
// }
// }, 1000)
// } else {
// Toast({message: msg, theme: 'error'})
// }
} catch (e) {
console.error(e)
Toast({message: e.message, theme: 'error'})
+87 -85
View File
@@ -68,7 +68,7 @@
<input
class="input"
type="text"
v-model="formData.name"
v-model="formData.userName"
placeholder="请输入姓名"
maxlength="20"
/>
@@ -113,7 +113,7 @@
<input
class="input"
type="number"
v-model="formData.verificationCode"
v-model="formData.smsCode"
placeholder="请输入验证码"
maxlength="6"
/>
@@ -145,37 +145,37 @@
<!-- 搜索框 -->
<view class="search-box">
<t-icon name="search" size="22px" class="search-icon" />
<t-icon name="search" size="22px" class="search-icon"/>
<input
class="search-input"
type="text"
v-model="searchKeyword"
placeholder="搜索单位"
@input="handleSearch"
class="search-input"
type="text"
v-model="searchKeyword"
placeholder="搜索单位"
@input="handleSearch"
/>
<t-icon
v-if="searchKeyword"
name="close-circle-filled"
size="22px"
class="clear-icon"
@click="clearSearch"
v-if="searchKeyword"
name="close-circle-filled"
size="22px"
class="clear-icon"
@click="clearSearch"
/>
</view>
<!-- 单位列表 -->
<scroll-view
scroll-y
class="units-scroll-view"
scroll-with-animation
:scroll-into-view="scrollViewId"
scroll-y
class="units-scroll-view"
scroll-with-animation
:scroll-into-view="scrollViewId"
>
<view
v-for="unit in filteredUnits"
:key="unit.id"
:id="'unit-' + unit.id"
class="unit-item"
:class="{'unit-selected': selectedUnitId === unit.id}"
@click="selectUnit(unit)"
v-for="unit in filteredUnits"
:key="unit.id"
:id="'unit-' + unit.id"
class="unit-item"
:class="{'unit-selected': selectedUnitId === unit.id}"
@click="selectUnit(unit)"
>
<text class="unit-name">{{ unit.name }}</text>
<t-icon v-if="selectedUnitId === unit.id" name="check" size="22px" class="check-icon"/>
@@ -191,18 +191,21 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted, watch, nextTick, getCurrentInstance } from 'vue';
import { Toast } from 'tdesign-mobile-vue';
import { useRequest } from "@/utils/request";
import {ref, reactive, computed, onMounted, watch, nextTick, getCurrentInstance} from 'vue';
import {Dialog, Toast} from 'tdesign-mobile-vue';
import {useRequest} from "@/utils/request";
import {useUserStore} from "@/store";
const userStore = useUserStore();
// 表单数据
const formData = reactive({
name: '',
userName: '',
phone: '',
unitId: '',
unitName: '',
idCard: '',
verificationCode: ''
smsCode: ''
});
// 验证码倒计时
@@ -223,21 +226,10 @@ const filteredUnits = computed(() => {
return unitList.value;
}
return unitList.value.filter(unit =>
unit.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
unit.name.toLowerCase().includes(searchKeyword.value.toLowerCase())
);
});
// 表单验证
const isFormValid = computed(() => {
return formData.name &&
formData.phone &&
formData.phone.length === 11 &&
formData.unitId &&
formData.idCard &&
formData.idCard.length === 18 &&
formData.verificationCode;
});
// 当选择器打开时,预设选中当前已选单位
watch(showUnitPicker, (newVal) => {
if (newVal && formData.unitId) {
@@ -270,7 +262,7 @@ const selectUnit = (unit) => {
// 确认选择
const confirmUnitSelection = () => {
if (!selectedUnitId.value) {
Toast({ message: '请选择单位', theme: 'warning' });
Toast({message: '请选择单位', theme: 'warning'});
return;
}
@@ -285,22 +277,22 @@ const confirmUnitSelection = () => {
// 获取单位列表
const getUnitList = async () => {
try {
const { code, data, msg } = await useRequest().get('/open/common/units');
const {code, data, msg} = await useRequest().get('/open/common/units');
if (code === 0) {
unitList.value = data || [];
} else {
Toast({ message: msg || '获取单位列表失败', theme: 'error' });
Toast({message: msg || '获取单位列表失败', theme: 'error'});
}
} catch (error) {
console.error('获取单位列表失败', error);
Toast({ message: '获取单位列表失败', theme: 'error' });
Toast({message: '获取单位列表失败', theme: 'error'});
}
};
// 发送验证码
const sendVerificationCode = async () => {
if (!formData.phone || formData.phone.length !== 11) {
Toast({ message: '请输入正确的手机号', theme: 'error' });
Toast({message: '请输入正确的手机号', theme: 'error'});
return;
}
@@ -308,20 +300,17 @@ const sendVerificationCode = async () => {
sendingCode.value = true;
try {
const { code, msg } = await useRequest().post('/client/auth/send-verification-code', {
phone: formData.phone,
type: 'register'
});
const {code, msg} = await useRequest().post('/client/auth//send-reg-code/' + formData.phone);
if (code === 0) {
Toast({ message: '验证码已发送' });
Toast({message: '验证码已发送'});
startCountdown();
} else {
Toast({ message: msg || '发送验证码失败', theme: 'error' });
Toast({message: msg || '发送验证码失败', theme: 'error'});
}
} catch (error) {
console.error('发送验证码失败', error);
Toast({ message: '发送验证码失败', theme: 'error' });
Toast({message: '发送验证码失败', theme: 'error'});
} finally {
sendingCode.value = false;
}
@@ -340,54 +329,67 @@ const startCountdown = () => {
// 注册
const handleRegister = async () => {
if (!isFormValid.value) {
Toast({ message: '请完善注册信息', theme: 'error' });
return;
if (!formData.userName) {
Toast('请输入姓名');
return
}
if (!formData.phone) {
Toast('请输入手机号码');
return
}
if (!formData.phone.match(/^1[3456789]\d{9}$/)) {
Toast('请输入正确的手机号码');
return
}
if (!formData.idCard) {
Toast('请输入身份证号');
return
}
if (!formData.unitId) {
Toast('请选择单位');
return
}
if (!formData.idCard.match(/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/)) {
Toast('请输入正确的身份证号');
return
}
if (!formData.smsCode) {
Toast('请输入验证码');
return
}
registering.value = true;
try {
const { code, msg } = await useRequest().post('/client/auth/register', {
name: formData.name,
phone: formData.phone,
unitId: formData.unitId,
idCard: formData.idCard,
verificationCode: formData.verificationCode
});
const {code, msg, data} = await useRequest().post('/client/auth/register', formData);
if (code === 0) {
Toast({ message: '注册成功' });
// 注册成功后跳转到登录页
setTimeout(() => {
uni.navigateTo({
url: '/pages/login/index'
});
}, 1500);
Dialog.confirm({
title: '注册成功',
content: '是否立即进入系统?',
confirmBtn: '确定',
cancelBtn: '取消',
onConfirm: () => {
userStore.setToken(data)
userStore.getUserInfo().then(() => {
uni.switchTab({
url: '/pages/index/index'
})
}).catch(e => {
Toast({message: e.message, theme: 'error'})
})
}
})
} else {
Toast({ message: msg || '注册失败', theme: 'error' });
Toast({message: msg || '注册失败', theme: 'error'});
}
} catch (error) {
console.error('注册失败', error);
Toast({ message: '注册失败', theme: 'error' });
Toast({message: '注册失败', theme: 'error'});
} finally {
registering.value = false;
}
};
// 显示协议
const showAgreement = () => {
uni.navigateTo({
url: '/pages/agreement/index'
});
};
// 显示隐私政策
const showPrivacy = () => {
uni.navigateTo({
url: '/pages/privacy/index'
});
};
// 跳转登录页面
const goToLogin = () => {
uni.navigateTo({
Binary file not shown.
+2 -5
View File
@@ -19,6 +19,7 @@ export const checkAuth = async () => {
if (token) {
await useUserStore().getUserInfo()
} else {
uni.setStorageSync('REDIRECT_URL', window.location.pathname + window.location.search)
await uni.redirectTo({
url: '/pages/login/index'
})
@@ -88,12 +89,8 @@ async function routeInterceptor(args, whiteList) {
return args
} else {
console.log('拦截导航,跳转到登录页')
// 保存用户尝试访问的页面,以便登录后跳回
try {
uni.setStorageSync('REDIRECT_URL', targetUrl)
} catch (e) {
}
uni.setStorageSync('REDIRECT_URL', targetUrl + args.query)
Dialog.confirm({
title: '提示',