Merge branch 'develop' of http://25.13.9.101:9000/K17_AITS/tra-app into develop

This commit is contained in:
杨航
2025-11-12 17:36:50 +08:00
48 changed files with 2817 additions and 406 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ export const get_base_url = (url = '') => {
}
// #endif
// #ifdef APP-PLUS
return 'https://aitstest.jlbank.com.cn:7001' || ENV.VITE_APP_BASE_API_Url
return ENV.VITE_APP_BASE_API_Url
// #endif
} else { // 其他环境,包括生产环境,sit环境,uat环境
+9
View File
@@ -116,3 +116,12 @@ export const switchIcon = () : string => {
`.trim();
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgXml)}`;
};
// 左右切换箭头
export const arrowRightIcon = (color : string = '#F5212D') : string => {
const svgXml = `
<svg t="1762762748602" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7658" width="64" height="64" fill="${color}"><path d="M462.08 192a47.36 47.36 0 0 0 0 64l256 256-256 256a47.36 47.36 0 1 0 64 64l288.64-288.64A50.56 50.56 0 0 0 832 512a47.36 47.36 0 0 0-14.08-33.28L529.28 192a47.36 47.36 0 0 0-67.2 0z" p-id="7659"></path><path d="M206.08 192a47.36 47.36 0 0 0 0 64l256 256-256 256a47.36 47.36 0 1 0 64 64l291.84-286.08A50.56 50.56 0 0 0 576 512a47.36 47.36 0 0 0-14.08-33.28L273.28 192a47.36 47.36 0 0 0-67.2 0z" p-id="7660" fill="${color}"></path></svg>
`.trim();
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgXml)}`;
};
+1 -1
View File
@@ -110,7 +110,7 @@
<view class="reference_answer_title">参考答案:</view>
{{ subject_data.itemVos[0]?.itemAnswer ?? '' }}
</view>
<view class="analysis_note">试题解析:{{ subject_data?.analyContent }}</view>
<view class="analysis_note" v-if="props.mode !== 'pk_in'">试题解析:{{ subject_data?.analyContent }}</view>
</view>
</view>
</view>
+224 -9
View File
@@ -1,17 +1,232 @@
<template>
<view class="">
<uni-popup ref="popup" border-radius="10px 10px 0 0">
<view class="" style="background-color: aqua;">
<uni-popup ref="popup" background-color="rgba(10,10,10, 0.8)" >
<view class="popup-content" @touchmove.stop>
<view class="fl1"></view>
<view class="max-div">
<view class="popup_back_div" @click="close">
<image :src="optionErrorIcon('#C8BBB7')" class="img"></image>
</view>
<view class="title_div">
<image class="img" src="@/static/images/pointsAndRank/badge-wheat-left.png"></image>
<view class="title">恭喜你获得新徽章</view>
<image class="img" src="@/static/images/pointsAndRank/badge-wheat-right.png"></image>
</view>
<view class="points-div">
<view class="doct_icon" @click="doct_click(-1)">
<view class="doct_icon_div left">
<image :src="arrowRightIcon('#D88F50')" class="img" mode="widthFix"></image>
</view>
</view>
<swiper class="swiper">
<swiper-item class="swiper-item-div">
<image src="@/static/images/test/xz.png" class="img"></image>
</swiper-item>
<swiper-item class="swiper-item-div">
<image src="@/static/images/test/xz.png" class="img"></image>
<!-- // <image-preview :src="item.imgAddr" class="img" mode="widthFix"></image-preview> -->
</swiper-item>
</swiper>
<view class="doct_icon" @click="doct_click(1)">
<view class="doct_icon_div right">
<image :src="arrowRightIcon('#D88F50')" class="img" mode="widthFix"></image>
</view>
</view>
<!-- <view class="points-img-div">
<image-preview :src="nowImgSrc"></image-preview>
</view> -->
</view>
<view class="button-div">
<uv-button
class="button"
text="领取"
loadingText="签到中"
:custom-style="customStyle"
:color="buttonColor"
@click="checkIn"
></uv-button>
</view>
</view>
<view class="fl1"></view>
</view>
</uni-popup>
</view>
</template>
<script setup>
const popup = ref(null)
const open = () => {
}
defineExpose({open})
import { ref, computed } from 'vue';
import { optionErrorIcon, optionSuccessIcon } from '@/common/imgSvg';
import { addPointsByCheckIn, queryCheckIn } from '@/api/pointsAndRank.js';
import common from '@/common/common';
import { arrowRightIcon } from '@/common/imgSvg';
const popup = ref(null);
const nowImgSrc = ref('');
const badgesList = ref([]);
const pointsList = ref([]);
const buttonColor = 'linear-gradient( 270deg, #E0914A 0%, #EDBE7F 100%)';
const customStyle = {
borderRadius: '40rpx' //圆角
};
const emits = defineEmits(['checkInFun']);
// 退出签到
const close = () => {
popup.value.close();
};
const open = (points = [], badges = []) => {
if (!Array.isArray(badges) || badges.length === 0) {
console.error('badges 不是有效的数组或为空');
return;
}
badgesList.value = badges;
pointsList.value = points;
popup.value.open();
};
defineExpose({ open, close });
</script>
<style></style>
<style scoped lang="scss">
.points-div {
// width: 100%;
display: flex;
position: relative;
align-items: center;
justify-content: center;
.swiper {
height: 400rpx;
width: 520rpx;
.swiper-item-div {
/* 父容器只需居中对齐,无需设置背景图 */
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-image: url('@/static/images/pointsAndRank/fs.png');
background-size: 110% 110%; /* 背景图比img大10% */
background-position: center; /* 背景图居中 */
background-repeat: no-repeat; /* 禁止背景图重复 */
.img {
/* 1. 缩放img(根据需要调整宽高,这里缩到原来的80%左右示例) */
width: 300rpx;
height: 270rpx;
transform: scale(0.8);
}
}
}
.doct_icon {
padding: 0%;
display: flex;
.doct_icon_div {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.left {
transform: rotate(180deg);
}
width: 8vw;
display: flex;
align-items: center;
flex-direction: column;
}
}
.button-div {
height: 92rpx;
width: 442rpx;
margin: 34rpx auto 0 auto;
.button {
width: 100%;
height: 100%;
}
}
.continuous-check-in-div {
display: flex;
border-bottom: 2rpx solid #efefef;
margin-bottom: 16rpx;
padding-top: 8rpx;
height: 128rpx;
.img {
width: 112rpx;
height: 102rpx;
}
.text {
margin-left: 16rpx;
font-weight: 400;
font-size: 30rpx;
height: 82rpx;
color: #333333;
display: flex;
align-items: flex-end;
}
.number {
font-weight: 600;
color: #0066ff;
font-family: Arial Black;
}
}
.title_div {
display: flex;
align-items: center;
justify-content: center;
margin-top: 54rpx;
margin-bottom: 24rpx;
height: 68rpx;
.img {
width: 26rpx;
height: 44rpx;
}
.title {
font-family: AlimamaShuHeiTi, AlimamaShuHeiTi;
font-weight: bold;
font-size: 36rpx;
color: #d88f50;
margin: 0rpx 30rpx;
text-align: center;
}
}
.popup_back_div {
width: 60rpx;
height: 60rpx;
position: absolute;
top: 20rpx;
right: 20rpx;
.img {
margin: 10rpx;
width: 40rpx;
height: 40rpx;
}
}
.popup-content {
width: 100vw;
height: 100vh;
z-index: 99999;
display: flex;
flex-direction: column;
align-items: center;
// padding: 40rpx 0;
}
.max-div {
// max-width: 400rpx;
// width: 80vw;
background-color: #fefbfa;
border-radius: 38rpx;
position: relative;
// padding: 26rpx;
overflow: hidden;
margin-top: var(--status-bar-height);
margin-bottom: 120rpx;
}
</style>
+19 -21
View File
@@ -95,7 +95,7 @@
// 注意:如果天数从1开始(不是0),需要减1再计算
return Math.floor((currentDay - 1) / 7);
});
const emits = defineEmits(['checkInFun'])
const emits = defineEmits(['checkInFun']);
// 切换显示状态
const switchStatus = () => {
expand.value = !expand.value;
@@ -109,14 +109,24 @@
const checkIn = () => {
if (checkInLoading.value) return;
checkInLoading.value = true;
addPointsByCheckIn({days:6})
addPointsByCheckIn({ days: continuousDay.value })
.then((res) => {
console.log('签到结果', res);
checkInLoading.value = false;
isSigned.value = true;
continuousDay.value = continuousDay.value + 1;
common.msg('签到成功,请明日继续!')
emits('checkInFun', true)
// common.msg('签到成功,请明日继续!')
emits(
'checkInFun',
true,
[{ changePoints: 5 }],
[
{
pointsBadgeImg: '/traoss/tras3/show/S3F0250181220722025110713501700000661177',
pointsBadgeName: '黄金徽章'
}
]
);
})
.catch((err) => {
checkInLoading.value = false;
@@ -195,11 +205,11 @@
return groupedList;
}
const open = (checkInToday,days) => {
isSigned.value = checkInToday==='Y'?true:false// 今日签到状态
// isSigned.value = false// 今日签到状态
continuousDay.value = days // 连续签到
const open = (checkInToday, days) => {
isSigned.value = checkInToday === 'Y' ? true : false; // 今日签到状态
isSigned.value = false; // 今日签到状态
continuousDay.value = days; // 连续签到
// 示例:X=10时,以"10天前"为第1天,生成35天列表
try {
const X = continuousDay.value - (isSigned.value ? 1 : 0);
@@ -207,18 +217,6 @@
console.log(`${continuousDay.value}`);
console.log(`${X}天前为第1天的35天分组日期:`);
dateGroups.value = generateGroupedDateList(X);
// dateGroups.value.forEach((group) => {
// console.log(`\n第${group.groupIndex + 1}组:`);
// console.log(
// group.days.map((day) => ({
// 日期: day.date,
// 天数序号: day.index, // 显示当前是第几天
// text: day.text,
// 类型: day.type
// }))
// );
// });
} catch (err) {
console.error(err.message);
}
+178 -3
View File
@@ -1,8 +1,183 @@
<template>
<view class="">
<uni-popup ref="popup">
<view class="popup-content" @touchmove.stop>
<view class="fl1"></view>
<view class="max-div">
<view class="popup_back_div" @click="clickFun(false)">
<image :src="optionErrorIcon('#C8BBB7')" class="img"></image>
</view>
<view class="title_div">
<image class="img" src="@/static/images/pointsAndRank/badge-wheat-left.png"></image>
<view class="title">恭喜你获得积分</view>
<image class="img" src="@/static/images/pointsAndRank/badge-wheat-right.png"></image>
</view>
<view class="points-div">
<view class="points-img-div">
<image class="img_100" src="@/static/images/pointsAndRank/like.png" mode=""></image>
</view>
<view class="points-num-div">+{{showData.num}}</view>
<view class="points-msg-div" v-show="showData.tip"> {{showData.tip}}</view>
</view>
<view class="button-div">
<uv-button
class="button"
text="开心收下"
:custom-style="customStyle"
:color="buttonColor"
@click="clickFun(true)"
></uv-button>
</view>
</view>
<view class="fl1"></view>
</view>
</uni-popup>
</view>
</template>
<script>
<script setup>
import { ref, computed } from 'vue';
import { optionErrorIcon, optionSuccessIcon } from '@/common/imgSvg';
import { addPointsByCheckIn, queryCheckIn } from '@/api/pointsAndRank.js';
import common from '@/common/common';
const showData = ref({
num: 1,
tip:''
});
const badgesList =ref([]);
const pointsList = ref([]);
const popup = ref(null);
const buttonColor = 'linear-gradient( 270deg, #F81758 0%, #FFA062 100%)';
const customStyle = {
borderRadius: '40rpx' //圆角
};
const emits = defineEmits(['confirm']);
const open = (points, badges=[]) => {
if (!Array.isArray(points) || points.length === 0) {
console.error('points 不是有效的数组或为空');
// 积分为空直接调用完成
emits(
'confirm',
status,
points,
badges
);
return;
}
badgesList.value = badges
// 解构出第一项和剩余元素(原数组不变)
const [firstItem, ...remainingPoints] = points;
console.log('firstItem', firstItem);
showData.value['num'] = firstItem['changePoints'];
pointsList.value = remainingPoints;
popup.value.open();
};
const close = () => {
popup.value.close();
};
// 点击
const clickFun = (status = true) => {
emits(
'confirm',
status,
pointsList.value,
badgesList.value
);
};
defineExpose({ open, close });
</script>
<style>
</style>
<style scoped lang="scss">
.points-div {
display: flex;
flex-direction: column;
align-items: center;
padding: 10rpx 0 0 0;
.points-img-div {
width: 320rpx;
height: 278rpx;
}
.points-num-div {
font-family: Arial, Arial;
font-weight: normal;
font-size: 108rpx;
color: #333333;
line-height: 108rpx;
text-align: center;
font-weight: 600;
}
.points-msg-div {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #ae723b;
line-height: 40px;
text-align: center;
}
}
.button-div {
height: 92rpx;
width: 442rpx;
margin: 40rpx auto 20rpx auto;
.button {
width: 100%;
height: 100%;
}
}
.title_div {
display: flex;
align-items: center;
justify-content: center;
margin-top: 34rpx;
height: 68rpx;
.img {
width: 26rpx;
height: 44rpx;
}
.title {
font-family: AlimamaShuHeiTi, AlimamaShuHeiTi;
font-weight: bold;
font-size: 46rpx;
color: #d88f50;
margin: 0rpx 30rpx;
text-align: center;
}
}
.popup_back_div {
width: 60rpx;
height: 60rpx;
position: absolute;
top: 20rpx;
right: 20rpx;
.img {
margin: 10rpx;
width: 40rpx;
height: 40rpx;
}
}
.popup-content {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
}
.max-div {
width: 80vw;
background-color: #fefbfa;
border-radius: 38rpx;
position: relative;
padding: 26rpx;
overflow: hidden;
margin-top: var(--status-bar-height);
margin-bottom: 120rpx;
}
</style>
+2 -1
View File
@@ -47,4 +47,5 @@ export const styleButton = {
backgroundColor: '#E2EDFF',
borderRadius: '8rpx'
}
}
}
export const pop_up_time = 500;
+330 -320
View File
@@ -1,337 +1,347 @@
<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>
<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';
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';
import { nextTick } from 'process';
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 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 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();
// 生成匹配延迟时间
// 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();
}
};
// 匹配定时器
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;
pkData.userId = data_info.userId;
pkData.userName = data_info.userName;
data_info.orgIdName = pkData.orgIdName;
data_info.pkUserName = pkData.pkUserName;
data_info.pkOrgIdName = pkData.pkOrgIdName;
data_info.pkImgAddr = pkData.pkImgAddr;
data_info.pkIsAnony = pkData.pkIsAnony;
console.log('上一页数据', pkData);
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 * 100);
} else {
// 取消挑战
// resetChallengeState();
}
};
interface InstitutionItem {
orgName: string; // 机构名称,类型为字符串
orgId: string; // 机构ID,类型为字符串
parentId: string; // 父级ID,类型为字符串
children?: InstitutionItem[];
}
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;
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;
};
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);
// }
}
});
onLoad((e) => {
const lastData = common.getPageCache('competition_list');
console.log('lastData', lastData);
const userInfo = getUserInfo();
console.log('userInfo', userInfo);
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';
// 匹配成功样式动画
@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;
}
/* 容器样式 */
.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>
+2 -1
View File
@@ -138,7 +138,6 @@
// 计算对方分数
const opponentScore = computed(() => {
// 筛选出所有 asrTm <= 当前时长的项
const validItems = pkData.traCompetitionPkTimeRecordVos.filter((item) => item.asrTm <= practiceTime.value);
if (validItems.length > 0) {
@@ -413,6 +412,8 @@
// console.log('倒计时时间', practiceRemainingTime.value);
// Pk时间到
if (practiceRemainingTime.value <= 0) {
}
}, 1000);
};
+5 -4
View File
@@ -57,7 +57,7 @@
</view>
<view class="pk-msg-right-div">
<view class="avatar-div">
<image-preview v-if="pkData.isAnonyPk === 'N'" class="img" :src="pkData.imgPk"></image-preview>
<image-preview v-if="pkData.pkIsAnony === 'N'" class="img" :src="pkData.imgPk"></image-preview>
<image v-else class="img" :src="defaultAnonymousAvatar" mode="aspectFill"></image>
</view>
<view class="name-div">{{ pkData.userNamePk }}</view>
@@ -123,7 +123,7 @@
userIdPk: '', // 用户Id String
userNamePk: '', // 用户姓名 String
imgPk: '', // 对方头像 String
isAnonyPk: 'Y', // 匿名 String
pkIsAnony: 'Y', // 匿名 String
correctNubPk: 0, // 正确数 int
qnsNubPk: 0, // 总数 int
timeTakenPk: 0, // 用时 int
@@ -147,7 +147,8 @@
const pkDataInit = common.getPageCache('competition_pk_to_resulf');
Object.assign(pkData, {
userName: pkDataInit.userName,
isAnony: pkDataInit.isAnony
isAnony: pkDataInit.isAnony,
pkIsAnony: pkDataInit.pkIsAnony,
});
});
onMounted(() => {
@@ -168,7 +169,7 @@
userIdPk: userResult.userIdPk, // 用户Id String
userNamePk: userResult.userNamePk, // 用户姓名 String
imgPk: userResult.imgPk, // 用户姓名 String
pkIsAnony: userResult.pkIsAnony, // 匿名 String
pkIsAnony: userResult.isAnonyPk, // 匿名 String
correctNubPk: userResult.correctNubPk, // 正确数 int
qnsNubPk: userResult.qnsNubPk || 0, // 总数 int
timeTakenPk: userResult.timeTakenPk || 0, // 用时 int
@@ -1,19 +1,22 @@
<template>
<view class="item">
<view class="details_div">
<view class="img_div">
<image-preview class="img" :src="item.ossAddr"></image-preview>
</view>
<view class="right_div">
<view class="title ellipsis-text">
{{ item.examName }}
{{ item.comName }}
</view>
<view class="prompt_div">
<view class="prompt_div_left">
<view class="top">{{ item.flagQuery === 'Y' ? item.highestGrade : '--' || 0 }}</view>
<view class="top">{{ item.highestGrade }}</view>
<view class="bottom">历史最高分</view>
</view>
<view class="prompt_div_line"></view>
<view class="prompt_div_right" @click="show_list_click">
<view class="click_text">
{{ item.examCnt || 0 }}
{{ item.comNub || 0 }}
<uv-icon
class="click_text_icon"
:class="{ click_text_icon_action: show_list_state }"
@@ -23,7 +26,7 @@
:bold="true"
></uv-icon>
</view>
<view class="bottom">考试记录</view>
<view class="bottom">竞赛记录</view>
</view>
</view>
</view>
@@ -33,11 +36,11 @@
<view class="list_content">
<view class="list_small_item">
<image src="@/static/images/courseRecord/time.png" class="icon_img"></image>
<view class="prompt">考试时间{{ item_list.examStartTm }}</view>
<view class="prompt">竞赛时间{{ item_list.startTm }}</view>
</view>
<view class="list_small_item">
<image src="@/static/images/courseRecord/duration.png" class="icon_img"></image>
<view class="prompt">考试得分{{ item_list.flagQuery === 'Y' ? item_list.examGrade : '--' }}</view>
<view class="prompt">竞赛得分{{ item_list.comGrade ?? '--' }}</view>
</view>
</view>
<view class="fl1"></view>
@@ -66,16 +69,22 @@
}
});
const item = computed(() => props.item);
const fetchFunction = (params) => queryTraCompetitionRecordInfoPaging({ examId: item.value.examId, ...params });
const fetchFunction = (params) => queryTraCompetitionRecordInfoPaging({ comId: item.value.comId, ...params });
const { status, data_list, show_list_state, list_div_height, show_list_click, getData, clear } = useItemList(fetchFunction);
// 跳转到详情
const click_list_item = (list_item) => {
const papersName = item.value.examName;
const flagQueryDetail = list_item.flagQueryDetail === '' ? list_item.flagQuery : list_item.flagQueryDetail;
const pkData = {
userName: 'x',
isAnony: list_item.isAnony
}
common.setPageCache('competition_pk_to_resulf', pkData);
common.navigateTo(
`/pages/examination/result?execId=${list_item.execId}&papersName=${papersName}&mode=record&flagQuery=${list_item.flagQuery}&flagQueryDetail=${flagQueryDetail}`
`/pages/competition/result?&execId=${list_item.execId}&orgId=${list_item.orgId}&papersId=${list_item.papersId}`
);
};
defineExpose({clear})
</script>
-15
View File
@@ -28,21 +28,6 @@
<image-preview :src="item.imgAddr" class="img" mode="widthFix"></image-preview>
</view>
</swiper-item>
<!-- <swiper-item class="">
<view class="swiper-item-div">
<image src="@/static/images/mascot/ma_2.png" class="img" mode="widthFix"></image>
</view>
</swiper-item>
<swiper-item class="">
<view class="swiper-item-div">
<image src="@/static/images/mascot/ma_3.png" class="img" mode="widthFix"></image>
</view>
</swiper-item>
<swiper-item class="">
<view class="swiper-item-div">
<image src="@/static/images/mascot/ma_4.png" class="img" mode="widthFix"></image>
</view>
</swiper-item> -->
</swiper>
<view class="doct_icon right" @click="doct_click(1)">
<view class="doct_icon_div">
+33 -10
View File
@@ -48,8 +48,8 @@
<view class="work_number_div">工号{{ user_info.loginName }}</view>
<view class="fl1"></view>
</view>
<view class="sign-in-div" v-if="user_info.checkInToday==='N'" @click="checkInClick">签到</view>
<view class="already-sign-in-div" v-if="user_info.checkInToday==='Y'" @click="checkInClick">已签</view>
<view class="sign-in-div" v-if="user_info.checkInToday === 'N'" @click="checkInClick">签到</view>
<view class="already-sign-in-div" v-if="user_info.checkInToday === 'Y'" @click="checkInClick">已签</view>
</view>
<!-- 用户学习时间信息 -->
<view class="study_information">
@@ -64,7 +64,7 @@
></uv-count-to>
</view>
</view>
<view class="item-interval"></view>
<view class="item-interval" @click="test()"></view>
<view class="item" @click="common.navigateTo('/pages/pointsAndRank/badge')">
<view class="top">徽章墙</view>
<view class="bottom">
@@ -97,7 +97,7 @@
</view>
</view>
<view class="scroll-view-item">
<view class="item" @click="goTest()">
<view class="item" @click="goTest()">
<image class="icon" src="@/static/images/me/report.png"></image>
<view class="title">AI陪练记录</view>
</view>
@@ -159,6 +159,8 @@
@confirm="avatarOnConfirm"
></avatar-cropper>
<checkIn ref="checkInRef" @checkInFun="checkInFun"></checkIn>
<badge ref="badgeRef"></badge>
<points ref="pointsRef" @confirm="pointsConfirm"></points>
</view>
</template>
@@ -179,6 +181,9 @@
import useUpdateAvatar from '@/composables/useUpdateAvatar';
import { queryCurrentStatus, queryCheckIn } from '@/api/pointsAndRank.js';
import checkIn from '@/components/points-and-badge/check-in';
import badge from '@/components/points-and-badge/badge';
import points from '@/components/points-and-badge/points';
import {pop_up_time} from '@/enum';
const { avatarCropperUrl, click_update_avatar, avatarCropperStatus, avatarOnCancel, avatarOnConfirm } = useUpdateAvatar({
success: (userData) => {
user_info.imageAddr = userData.imageAddr;
@@ -220,8 +225,10 @@
const socketStore = useSocketStore();
const badgeNumRef = ref(null);
const checkInRef = ref(null);
const badgeRef = ref(null);
const pointsRef = ref(null);
const currentPointsRef = ref(null);
const user_info = reactive({
userName: '',
userId: '',
@@ -231,17 +238,33 @@
checkInDays: 0 // 连续签到天数
});
const goTest = () => {
console.log('goTest');
// common.navigateTo('/pages/test/index');
common.navigateTo('/pages/test/index');
};
const test = () => {
badgeRef.value.open();
};
// 签到打开
const checkInClick = () => {
checkInRef.value.open(user_info.checkInToday, user_info.checkInDays);
};
// 签到成功回调
const checkInFun = (status) => {
if(status) {
getCheckInInfo()
const checkInFun = (status, pointsList = [], badgesList = []) => {
if (status) {
getCheckInInfo();
}
pointsRef.value.open(pointsList, badgesList);
};
// 积分确认
const pointsConfirm = (status, pointsList = [], badgesList = []) => {
console.log('xxx', status, pointsList, badgesList);
pointsRef.value.close();
if (badgesList.length > 0) {
nextTick(() => {
setTimeout(() => {
badgeRef.value.open(pointsList, badgesList);
}, pop_up_time);
});
}
};
const click_loginout = () => {
+2
View File
@@ -165,6 +165,7 @@
display: flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
margin: 30rpx 0;
.title {
margin: 0 16rpx;
@@ -173,6 +174,7 @@
color: #ae723b;
text-align: center;
font-style: normal;
white-space: nowrap;
}
.img {
width: 242rpx;
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB