338 lines
12 KiB
Vue
338 lines
12 KiB
Vue
<template>
|
||
<view class="main_div max_page">
|
||
<template v-if="!isMatchingSuccess">
|
||
<!-- 顶部一些按钮 -->
|
||
<view class="top-button-div">
|
||
<view class="back_div" @click="common.navigateBack()">
|
||
<view class="back-icon"></view>
|
||
</view>
|
||
<view class="fl1"></view>
|
||
<view class="top-icon-div" @click="click_top_icon(1)">
|
||
<image class="img1" :src="data_info.isAnony === 'N' ? pkAnonymousClose() : pkAnonymousOpen()"></image>
|
||
<view class="text">{{ data_info.isAnony === 'N' ? '不匿名' : '匿名' }}</view>
|
||
</view>
|
||
<view class="top-icon-div" @click="click_top_icon(2)">
|
||
<image class="img2" :src="pkRules()"></image>
|
||
<view class="text">规则说明</view>
|
||
</view>
|
||
<view class="top-icon-div" @click="click_top_icon(3)">
|
||
<image class="img3" :src="pkRankingList()"></image>
|
||
<view class="text">排行榜</view>
|
||
</view>
|
||
</view>
|
||
<view class="title-div" :class="{ hidden: challengeLoading }">
|
||
{{ data_info.comName }}
|
||
</view>
|
||
<view class="match-msg-div" :class="{ hidden: !challengeLoading }">匹配中...</view>
|
||
<view class="avatar-div">
|
||
<view class="avatar-surround-div">
|
||
<view class="avatar-img-div" :class="{ hidden: challengeLoading }">
|
||
<cached-avatar v-show="data_info.isAnony === 'N'" class="img"></cached-avatar>
|
||
<image v-show="data_info.isAnony === 'Y'" class="img" :src="defaultAnonymousAvatar"></image>
|
||
</view>
|
||
</view>
|
||
<view class="avatar-name-div">{{ data_info.isAnony === 'N' ? data_info.userName : '匿名' }}</view>
|
||
</view>
|
||
<view class="select-div" :class="{ hidden: challengeLoading }" @click="selectionInstitutionsRef.open(true)">
|
||
<view class="select-institution-div">
|
||
{{ data_info.orgName }}
|
||
<image class="img" :src="switchIcon()"></image>
|
||
</view>
|
||
<view class="select-institution-msg-div">*选择机构后,会根据机构进行人员随机匹配挑战</view>
|
||
</view>
|
||
<view class="fl1"></view>
|
||
<!-- 匹配时间 -->
|
||
<view class="match-time-div" :class="{ hidden: !challengeLoading }">
|
||
{{ formatTime(practiceTime) }}
|
||
</view>
|
||
<view class="button-div" v-show="challengeButtonStatus">
|
||
<view
|
||
:class="{ start_button: !challengeLoading, cancel_button: challengeLoading, 'button-transition': true }"
|
||
@click="start_challenge"
|
||
>
|
||
{{ challengeLoading ? '取消匹配' : '开始挑战' }}
|
||
</view>
|
||
</view>
|
||
<view class="fl1"></view>
|
||
<view class="fl1"></view>
|
||
<view class="fl1"></view>
|
||
</template>
|
||
<template v-else>
|
||
<view class="matching-success-msg">匹配成功</view>
|
||
<view class="matching-success-animation">
|
||
<view class="left animate-left">
|
||
<image class="img" src="@/static/images/competition/matching-success-left.png" mode="widthFix"></image>
|
||
<view class="information-div-left">
|
||
<view class="avatar-img-div">
|
||
<cached-avatar v-show="data_info.isAnony === 'N'" class="img"></cached-avatar>
|
||
<image v-show="data_info.isAnony === 'Y'" class="img" :src="defaultAnonymousAvatar"></image>
|
||
</view>
|
||
<view class="name">
|
||
{{ data_info.isAnony === 'N' ? data_info.userName : '匿名' }}
|
||
</view>
|
||
<view class="org">
|
||
{{ data_info.isAnony === 'N' ? data_info.orgIdName : '机构不显示' }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="right animate-right">
|
||
<image class="img" src="@/static/images/competition/matching-success-right.png" mode="widthFix"></image>
|
||
<view class="information-div-right">
|
||
<view class="avatar-img-div">
|
||
<image-preview
|
||
v-if="data_info.pkIsAnony === 'N'"
|
||
class="img"
|
||
:src="data_info.pkimgAddr"
|
||
></image-preview>
|
||
<image v-else class="img" :src="defaultAnonymousAvatar" mode="aspectFill"></image>
|
||
</view>
|
||
<view class="name">
|
||
{{ data_info.pkIsAnony === 'N' ? data_info.pkUserName : '匿名' }}
|
||
</view>
|
||
<view class="org">
|
||
{{ data_info.pkIsAnony === 'N' ? data_info.pkOrgIdName : '机构不显示' }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="matching-success-text">准备!</view>
|
||
</template>
|
||
<uni-popup ref="popupRef" class="popup">
|
||
<view class="sheet_popup_div">
|
||
<view class="title_div">双人对战规则说明</view>
|
||
<view class="note_div">
|
||
{{ data_info.ruleDesc }}
|
||
</view>
|
||
</view>
|
||
</uni-popup>
|
||
<selectionInstitutions ref="selectionInstitutionsRef" @submit="selectionInstitutionsSubmit"></selectionInstitutions>
|
||
<uv-picker ref="anonyPickerRef" :columns="[anonyOptions]" keyName="label" @confirm="anonyConfirm"></uv-picker>
|
||
</view>
|
||
|
||
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import {ref, onMounted, reactive} from 'vue';
|
||
import common, {getUserInfo} from '@/common/common';
|
||
import {MatchTimeGenerator, formatTime} from './js/matching';
|
||
import {queryTraCompetitionInfoByPkUser} from '@/api/competition';
|
||
import {onLoad} from '@dcloudio/uni-app';
|
||
import {defaultAnonymousAvatar} from '@/enum';
|
||
import {pkAnonymousClose, pkAnonymousOpen, pkRankingList, pkRules, switchIcon} from '@/common/imgSvg';
|
||
import {queryTraUserAnonyByUserId, updateTraUserAnony} from '@/api/user';
|
||
import selectionInstitutions from '@/components/selection-institutions/index.vue';
|
||
|
||
const data_info = reactive({
|
||
userId: '',
|
||
userName: '',
|
||
orgName: '全部', // 机构名称
|
||
orgId: 'A', // 机构的ID,全行是A
|
||
papersId: '', // 试卷id
|
||
isAnony: '', // 是否匿名 Y是N否
|
||
ruleDesc: '', // 规则说明
|
||
comName: '', // 标题
|
||
comId: '',
|
||
orgIdName: '',
|
||
pkIsAnony: '',
|
||
pkimgAddr: '',
|
||
pkUserName: '',
|
||
pkOrgIdName: ''
|
||
});
|
||
|
||
const popupRef = ref(null);
|
||
const anonyPickerRef = ref(null);
|
||
const selectionInstitutionsRef = ref(null);
|
||
const challengeLoading = ref(false);
|
||
const isMatchingSuccess = ref(false); // 匹配成功状态
|
||
const challengeButtonStatus = ref(true); // 匹配按钮显示
|
||
const practiceTime = ref(0); // 匹配时间
|
||
let practiceTimeTimer: number | null = null; // 匹配时间计时器
|
||
let matchTimer: number | null = null; // 匹配成功定时器
|
||
const anonyOptions = [
|
||
{
|
||
value: 'Y',
|
||
label: '匿名'
|
||
},
|
||
{
|
||
value: 'N',
|
||
label: '不匿名'
|
||
}
|
||
];
|
||
// 开始挑战
|
||
const start_challenge = () => {
|
||
if (!challengeLoading.value) {
|
||
challengeLoading.value = true;
|
||
// 清除旧计时器
|
||
if (practiceTimeTimer) {
|
||
clearInterval(practiceTimeTimer);
|
||
}
|
||
// 开始计时
|
||
const practiceStartTime = new Date().getTime();
|
||
practiceTimeTimer = setInterval(() => {
|
||
const currentTime = Date.now();
|
||
practiceTime.value = Math.floor((currentTime - practiceStartTime) / 1000);
|
||
}, 1000);
|
||
|
||
// 生成匹配延迟时间
|
||
// const delayTime = 0;
|
||
const delayTime = MatchTimeGenerator.generateInSeconds();
|
||
|
||
// 匹配定时器
|
||
matchTimer = setTimeout(async () => {
|
||
try {
|
||
challengeButtonStatus.value = false;
|
||
const orgId = data_info.orgId
|
||
const papersId = data_info.papersId
|
||
const comId = data_info.comId
|
||
const pkDataBody = await queryTraCompetitionInfoByPkUser({
|
||
// orgId:510121,
|
||
// papersId:'COMPETITION_PAPERS0250640321542025101615442400000003107',
|
||
// comId:'TRA_COMPETITION_INFO0250640321542025101615442400000003099'
|
||
orgId, papersId, comId
|
||
});
|
||
const pkData = pkDataBody.body;
|
||
// 下面两行是给后端擦屁股,后端传回的数值为空
|
||
pkData.orgId = orgId
|
||
pkData.papersId = papersId
|
||
common.setPageCache('competition_pk', pkData);
|
||
// 1. 显示匹配成功状态
|
||
isMatchingSuccess.value = true;
|
||
uni.vibrateLong({
|
||
success: function () {
|
||
}
|
||
});
|
||
// 2. 执行匹配成功动画(等待1.5秒)
|
||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||
// 3. 清除计时器
|
||
if (practiceTimeTimer) {
|
||
clearInterval(practiceTimeTimer);
|
||
practiceTimeTimer = null;
|
||
}
|
||
// 5. 跳转页面
|
||
common.navigateTo('/pages/competition/pk');
|
||
challengeLoading.value = true;
|
||
} catch (error) {
|
||
common.msg('队列繁忙,请稍后再试!');
|
||
// 出错处理
|
||
} finally {
|
||
resetChallengeState();
|
||
}
|
||
}, delayTime * 1000);
|
||
} else {
|
||
// 取消挑战
|
||
resetChallengeState();
|
||
}
|
||
};
|
||
|
||
interface InstitutionItem {
|
||
orgName: string; // 机构名称,类型为字符串
|
||
orgId: string; // 机构ID,类型为字符串
|
||
parentId: string; // 父级ID,类型为字符串
|
||
children?: InstitutionItem[];
|
||
}
|
||
|
||
const selectionInstitutionsSubmit = (options: [InstitutionItem, ...InstitutionItem[]]) => {
|
||
// 缓存起来
|
||
common.setValue('cache_selection_institutions' + data_info.userId, options);
|
||
const lastItem = options.at(-1);
|
||
data_info.orgName = lastItem.orgName;
|
||
data_info.orgId = lastItem.orgId;
|
||
|
||
selectionInstitutionsRef.value.close();
|
||
};
|
||
const anonyConfirm = (e) => {
|
||
const originalIsAnony = data_info.isAnony;
|
||
const isAnony = e.value[0].value;
|
||
if (isAnony === data_info.isAnony) return;
|
||
data_info.isAnony = isAnony;
|
||
common.loading('修改中');
|
||
updateTraUserAnony({isAnony})
|
||
.then(() => {
|
||
setTimeout(() => common.msg('切换成功'), 400);
|
||
})
|
||
.catch(() => {
|
||
// 4. 请求失败:用初始值回滚
|
||
data_info.isAnony = originalIsAnony;
|
||
common.msg('切换失败,请重试'); // 可选:提示失败
|
||
})
|
||
.finally(() => {
|
||
common.hideLoading();
|
||
});
|
||
};
|
||
const click_top_icon = (type: number) => {
|
||
// 匿名切换
|
||
if (type === 1) {
|
||
anonyPickerRef.value.setIndexs([data_info.isAnony === 'Y' ? 0 : 1], true);
|
||
anonyPickerRef.value.open();
|
||
} else if (type === 2) {
|
||
// 打开规则说明
|
||
popupRef.value.open('bottom');
|
||
} else if (type === 3) {
|
||
// 去排行榜
|
||
common.navigateTo(`/pages/competition/ranking?papersId=${data_info.papersId}&orgId=${data_info.orgId}`)
|
||
}
|
||
};
|
||
// 统一重置状态的函数
|
||
const resetChallengeState = () => {
|
||
if (matchTimer) {
|
||
clearTimeout(matchTimer);
|
||
matchTimer = null;
|
||
}
|
||
if (practiceTimeTimer) {
|
||
clearInterval(practiceTimeTimer);
|
||
practiceTimeTimer = null;
|
||
}
|
||
challengeButtonStatus.value = true;
|
||
challengeLoading.value = false;
|
||
isMatchingSuccess.value = false;
|
||
practiceTime.value = 0;
|
||
};
|
||
|
||
onLoad((e) => {
|
||
const lastData = common.getPageCache('competition_list');
|
||
console.log('lastData', lastData);
|
||
const userInfo = getUserInfo();
|
||
Object.assign(data_info, {
|
||
...lastData,
|
||
userId: userInfo.userId,
|
||
userName: userInfo.userName,
|
||
gender: userInfo.gender,
|
||
papersId: e.papersId,
|
||
comId: e.comId
|
||
});
|
||
const selectOptions = common.getValue('cache_selection_institutions' + data_info.userId);
|
||
if (Array.isArray(selectOptions) && selectOptions.length > 0) {
|
||
const lastItem = selectOptions.at(-1);
|
||
data_info.orgName = lastItem.orgName;
|
||
data_info.orgId = lastItem.orgId;
|
||
// const orgTreeInit = common.getValue('orgTree');
|
||
// if (Array.isArray(orgTreeInit) && orgTreeInit.length > 0) {
|
||
// console.log('selectOptions', selectOptions);
|
||
// }
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
// 匹配成功样式动画
|
||
@import './style/matching-success.scss';
|
||
@import './style/matching-loading.scss';
|
||
@import './style/matching-prelude.scss';
|
||
|
||
/* 容器样式 */
|
||
.max_page {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background-image: url('@/static/images/competition/bg.png');
|
||
background-color: #000; /* 图片高度不足时的背景色 */
|
||
background-size: 100% auto; /* 宽度填满,高度自适应 */
|
||
background-position: center top; /* 图片顶部居中显示 */
|
||
background-repeat: no-repeat; /* 不重复平铺 */
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
</style>
|