三阶段签到页面
@@ -3,7 +3,7 @@ ENV = 'development'
|
||||
# 'development'
|
||||
|
||||
# VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001'
|
||||
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
|
||||
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
|
||||
# VITE_APP_BASE_API_Url = 'http://192.168.247.200'
|
||||
# VITE_APP_BASE_API_Url = 'http://aitscdn.jlbank.com.cn:7001'
|
||||
# VITE_APP_BASE_API_Url = 'http://192.168.108.129'
|
||||
@@ -27,7 +27,7 @@ VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7002'
|
||||
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
|
||||
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.91:9786'
|
||||
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.78:9786'
|
||||
# VITE_APP_BASE_H5_API_Url_TRAAPP = 'http://192.168.247.200'
|
||||
#VITE_APP_BASE_H5_API_Url_TRAAPP = 'http://192.168.247.200'
|
||||
# VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.32.154:9602'
|
||||
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://192.168.247.200'
|
||||
#VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://192.168.247.200'
|
||||
# VITE_APP_BASE_H5_API_Url_TRAASK = 'http://25.64.32.154:9605'
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
ENV = 'development.rd'
|
||||
# 'development.rd'
|
||||
|
||||
VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
|
||||
#VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
|
||||
VITE_APP_BASE_API_Url = 'http://25.18.122.91:9786'
|
||||
|
||||
VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.32.154:9604'
|
||||
|
||||
@@ -4,6 +4,10 @@ import {
|
||||
queryCurrentValidMascotInfo
|
||||
} from "@/api/mascot.js"
|
||||
|
||||
import {
|
||||
queryCurrentStatus
|
||||
} from '@/api/pointsAndRank.js';
|
||||
|
||||
import {
|
||||
setToken,
|
||||
setUserInfo,
|
||||
@@ -24,15 +28,26 @@ export const useAccountLoginApp = (data) => request({
|
||||
}) => {
|
||||
if (rtnCode === '0000') {
|
||||
setToken(body)
|
||||
const res = await Promise.all([queryCurrentUser(), queryCurrentValidMascotInfo()])
|
||||
const res = await Promise.all([queryCurrentUser(), queryCurrentValidMascotInfo()
|
||||
// queryCurrentStatus()
|
||||
])
|
||||
const userInfo = getUserInfo()
|
||||
const mascotInfo = res[1]
|
||||
setUserInfo({
|
||||
...userInfo,
|
||||
'mascotInfo': mascotInfo
|
||||
})
|
||||
// 积分状态
|
||||
// const pointsStatus = res[2].body
|
||||
|
||||
common.setValue('points_data', {
|
||||
badgeNum: pointsStatus['badgeNum'] ?? 0, // 徽章数
|
||||
currentPoints: pointsStatus['currentPoints'] ?? 0, // 当前积分
|
||||
currentRankAddr: pointsStatus['ossAddr'] ?? '', // 当前段位图片
|
||||
currentRankName: pointsStatus['currentRankName']?? '未知', // 当前段位名称
|
||||
})
|
||||
return {
|
||||
mascotState:!mascotInfo.mascotId
|
||||
mascotState: !mascotInfo.mascotId
|
||||
}
|
||||
}
|
||||
return Promise.reject({
|
||||
@@ -40,22 +55,6 @@ export const useAccountLoginApp = (data) => request({
|
||||
body
|
||||
})
|
||||
}).catch(res => {
|
||||
/**
|
||||
error: "Service Unavailable"
|
||||
path: "/traapp/access/userLogin"
|
||||
requestId: "74d60800-104"
|
||||
status: 503
|
||||
timestamp: "2025-06-24T00:53:35.293+00:00"
|
||||
*/
|
||||
/**
|
||||
body: null
|
||||
channelNo: "FF6944B69A99745C009F4D0B3C3B22B8E81B22F587D68455E354BABBABCCB6246C54ABD28F3C55AAD34224A16CE7E24E"
|
||||
head: null
|
||||
message: "系统异常"
|
||||
rtnCode: "9999"
|
||||
rtnTime: "2025-06-24 08:54:52"
|
||||
*/
|
||||
|
||||
return Promise.reject(res)
|
||||
})
|
||||
|
||||
@@ -72,7 +71,7 @@ export const queryCurrentUser = (data) => request({
|
||||
method: 'post',
|
||||
data
|
||||
}).then(res => {
|
||||
|
||||
|
||||
// deptId: ""
|
||||
// deptName: null
|
||||
// loginName: "ADMIN" // todo id
|
||||
@@ -92,107 +91,109 @@ export const queryCurrentUser = (data) => request({
|
||||
// #ifdef APP-PLUS
|
||||
// 更新用户信息中的头像路径
|
||||
const updateUserAvatar = (imagePath) => {
|
||||
const _userInfo = getUserInfo() ?? {}
|
||||
setUserInfo({
|
||||
..._userInfo,
|
||||
...res.body,
|
||||
'imagePath': imagePath
|
||||
})
|
||||
const _userInfo = getUserInfo() ?? {}
|
||||
setUserInfo({
|
||||
..._userInfo,
|
||||
...res.body,
|
||||
'imagePath': imagePath
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 确保目录存在
|
||||
const ensureDirectoryExists = (dirPath) => {
|
||||
return new Promise((resolve) => {
|
||||
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, (fs) => {
|
||||
fs.root.getDirectory(dirPath, { create: true }, resolve, resolve)
|
||||
})
|
||||
})
|
||||
return new Promise((resolve) => {
|
||||
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, (fs) => {
|
||||
fs.root.getDirectory(dirPath, {
|
||||
create: true
|
||||
}, resolve, resolve)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 生成唯一文件名
|
||||
const generateUniqueFilename = (pathName) => {
|
||||
if (!pathName || typeof pathName !== 'string') {
|
||||
throw new Error('无效的路径');
|
||||
}
|
||||
|
||||
// 直接使用原始文件名(带扩展名)
|
||||
const filename = pathName.split('/').pop();
|
||||
|
||||
if (!filename) {
|
||||
throw new Error('无法从路径中提取文件名');
|
||||
}
|
||||
|
||||
// 确保文件扩展名为.png (可选,根据实际需求)
|
||||
const ext = filename.split('.').pop().toLowerCase();
|
||||
if (ext === 'png' || ext === 'jpg' || ext === 'jpeg' || ext === 'webp') {
|
||||
return filename;
|
||||
}
|
||||
|
||||
// 如果没有有效扩展名,添加.png
|
||||
return `${filename}.png`;
|
||||
if (!pathName || typeof pathName !== 'string') {
|
||||
throw new Error('无效的路径');
|
||||
}
|
||||
|
||||
// 直接使用原始文件名(带扩展名)
|
||||
const filename = pathName.split('/').pop();
|
||||
|
||||
if (!filename) {
|
||||
throw new Error('无法从路径中提取文件名');
|
||||
}
|
||||
|
||||
// 确保文件扩展名为.png (可选,根据实际需求)
|
||||
const ext = filename.split('.').pop().toLowerCase();
|
||||
if (ext === 'png' || ext === 'jpg' || ext === 'jpeg' || ext === 'webp') {
|
||||
return filename;
|
||||
}
|
||||
|
||||
// 如果没有有效扩展名,添加.png
|
||||
return `${filename}.png`;
|
||||
};
|
||||
|
||||
|
||||
// 下载并保存头像
|
||||
const downloadAndSaveAvatar = async (fileUrl, localFilePath) => {
|
||||
try {
|
||||
await ensureDirectoryExists('avatar')
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log('开始下载头像:', fileUrl)
|
||||
const dtask = plus.downloader.createDownload(fileUrl, {
|
||||
filename: localFilePath
|
||||
}, (d, status) => {
|
||||
if (status === 200) {
|
||||
console.log("保存路径:", d.filename)
|
||||
resolve(d.filename)
|
||||
} else {
|
||||
console.error("文件下载失败:", status)
|
||||
reject(new Error(`下载失败,状态码: ${status}`))
|
||||
}
|
||||
})
|
||||
|
||||
dtask.start()
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('创建目录失败:', error)
|
||||
throw error
|
||||
}
|
||||
try {
|
||||
await ensureDirectoryExists('avatar')
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log('开始下载头像:', fileUrl)
|
||||
const dtask = plus.downloader.createDownload(fileUrl, {
|
||||
filename: localFilePath
|
||||
}, (d, status) => {
|
||||
if (status === 200) {
|
||||
console.log("保存路径:", d.filename)
|
||||
resolve(d.filename)
|
||||
} else {
|
||||
console.error("文件下载失败:", status)
|
||||
reject(new Error(`下载失败,状态码: ${status}`))
|
||||
}
|
||||
})
|
||||
|
||||
dtask.start()
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('创建目录失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查文件是否存在
|
||||
const checkFileExists = (filePath) => {
|
||||
return new Promise((resolve) => {
|
||||
uni.getFileInfo({
|
||||
filePath,
|
||||
success: () => resolve(true),
|
||||
fail: () => resolve(false)
|
||||
})
|
||||
})
|
||||
return new Promise((resolve) => {
|
||||
uni.getFileInfo({
|
||||
filePath,
|
||||
success: () => resolve(true),
|
||||
fail: () => resolve(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 主逻辑
|
||||
(async () => {
|
||||
try {
|
||||
const pathName = res.body.imageAddr
|
||||
const fileUrl = base_url + pathName
|
||||
|
||||
// 生成保存路径
|
||||
const filename = generateUniqueFilename(pathName)
|
||||
const localFilePath = '_doc/avatar/' + filename
|
||||
|
||||
// 检查文件是否存在
|
||||
const exists = await checkFileExists(localFilePath)
|
||||
|
||||
if (exists) {
|
||||
// console.log('头像已存在,无需下载')
|
||||
updateUserAvatar(localFilePath)
|
||||
} else {
|
||||
const savedPath = await downloadAndSaveAvatar(fileUrl, localFilePath)
|
||||
updateUserAvatar(savedPath)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('头像预加载过程发生错误:', error)
|
||||
}
|
||||
try {
|
||||
const pathName = res.body.imageAddr
|
||||
const fileUrl = base_url + pathName
|
||||
|
||||
// 生成保存路径
|
||||
const filename = generateUniqueFilename(pathName)
|
||||
const localFilePath = '_doc/avatar/' + filename
|
||||
|
||||
// 检查文件是否存在
|
||||
const exists = await checkFileExists(localFilePath)
|
||||
|
||||
if (exists) {
|
||||
// console.log('头像已存在,无需下载')
|
||||
updateUserAvatar(localFilePath)
|
||||
} else {
|
||||
const savedPath = await downloadAndSaveAvatar(fileUrl, localFilePath)
|
||||
updateUserAvatar(savedPath)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('头像预加载过程发生错误:', error)
|
||||
}
|
||||
})()
|
||||
// #endif
|
||||
|
||||
@@ -230,4 +231,5 @@ export const loginOut = (data) => request({
|
||||
setUserInfo(null)
|
||||
common.setValue('me_statistics_data', null) // 我的页统计数据缓存
|
||||
common.setValue('index_statistics_data', null) // 首页统计数据缓存
|
||||
common.setValue('points_data', null) // 首页统计数据缓存
|
||||
})
|
||||
@@ -108,4 +108,16 @@ export const queryCurrentStatus = (data) => {
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 徽章墙
|
||||
*/
|
||||
export const queryRankWall = (data) => {
|
||||
return request({
|
||||
url: base_url + '/traPointsInfo/queryRankWall',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
@@ -233,8 +233,8 @@ function setValue(key, value) {
|
||||
uni.setStorageSync(key, value);
|
||||
}
|
||||
|
||||
function getValue(key) {
|
||||
return uni.getStorageSync(key)
|
||||
function getValue(key, defaultValue=null) {
|
||||
return uni.getStorageSync(key) ?? defaultValue
|
||||
}
|
||||
|
||||
function isLogin() {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, nextTick, computed, toRaw, watch } from 'vue';
|
||||
import { ref, reactive, onMounted, nextTick, computed, watch } from 'vue';
|
||||
import { optionErrorIcon, optionSuccessIcon } from '@/common/imgSvg';
|
||||
|
||||
const emit = defineEmits(['click_option']);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<view class="">
|
||||
<uni-popup ref="popup" border-radius="10px 10px 0 0">
|
||||
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const popup = ref(null)
|
||||
const open = () => {
|
||||
|
||||
}
|
||||
defineExpose({open})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,417 @@
|
||||
<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="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="continuous-check-in-div">
|
||||
<image class="img" src="@/static/images/pointsAndRank/badge-icon.png"></image>
|
||||
<text class="text">
|
||||
您已连续签到
|
||||
<text class="number">27</text>
|
||||
天
|
||||
</text>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="scroll-Y" :show-scrollbar="false">
|
||||
<view class="calendar-div">
|
||||
<view class="calendar-line" v-for="(line, lineIndex) in dateGroups">
|
||||
<view
|
||||
class="calendar-day"
|
||||
v-for="(day, dayIndex) in line.days"
|
||||
:class="{ hide: day.index > 30, expand: expand }"
|
||||
>
|
||||
<view class="calendar-day-bg" :class="[day.type, isSigned ? 'signed' : 'no-signed']">
|
||||
<image
|
||||
v-if="day.type === 'past' || (day.type === 'today' && isSigned)"
|
||||
:src="optionSuccessIcon('#FFFFFF')"
|
||||
class="img"
|
||||
></image>
|
||||
<text v-else>+{{ day.text }}</text>
|
||||
<view class="check-in-tip">签</view>
|
||||
</view>
|
||||
<view class="calendar-day-title">
|
||||
{{ day.date }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="switch-div" @click="switchStatus()">
|
||||
<view class="back_div">
|
||||
<view class="back-icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="button-div">
|
||||
<uv-button
|
||||
class="button"
|
||||
text="签到"
|
||||
:custom-style="customStyle"
|
||||
:color="buttonColor"
|
||||
@click="expand = !expand"
|
||||
></uv-button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fl1"></view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { optionErrorIcon, optionSuccessIcon } from '@/common/imgSvg';
|
||||
|
||||
const popup = ref(null);
|
||||
const buttonColor = 'linear-gradient( 270deg, #E0914A 0%, #EDBE7F 100%)';
|
||||
const customStyle = {
|
||||
borderRadius: '40rpx' //圆角
|
||||
};
|
||||
const dateGroups = ref([]);
|
||||
const expand = ref(true); // 展开状态
|
||||
const isSigned = ref(false); // 今日签到状态
|
||||
|
||||
// 切换显示状态
|
||||
const switchStatus = () => {
|
||||
|
||||
}
|
||||
// 退出签到
|
||||
const close = () => {
|
||||
popup.value.close();
|
||||
}
|
||||
/**
|
||||
* 生成35天日期列表(分5组,每组7天),包含类型标记和text字段
|
||||
* 以"前X天"为第一天,第1天显示1,每7天显示5,第30天显示30
|
||||
* @param {number} X - 前X天的天数(0-35之间的整数,总天数=35)
|
||||
* @returns {Array<{
|
||||
* groupIndex: number, // 组索引(0-4)
|
||||
* days: Array<{
|
||||
* date: string, // mm-dd格式日期
|
||||
* type: 'past' | 'today' | 'future', // 日期类型
|
||||
* index: number, // 相对今天的索引(负数为过去,正数为未来)
|
||||
* text: string // 显示文本(1/5/30)
|
||||
* }>
|
||||
* }>} 分组后的日期列表
|
||||
*/
|
||||
function generateGroupedDateList(X) {
|
||||
if (X < 0 || X > 35 || !Number.isInteger(X)) {
|
||||
throw new Error('X必须是0到35之间的整数');
|
||||
}
|
||||
|
||||
const allDays = [];
|
||||
const today = new Date();
|
||||
const todayDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
|
||||
// 生成前X天到后(35-X)天的所有日期(共35天)
|
||||
for (let i = -X; i <= 35 - X - 1; i++) {
|
||||
const currentDate = new Date(today);
|
||||
currentDate.setDate(today.getDate() + i);
|
||||
const pureDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
|
||||
|
||||
// 确定日期类型
|
||||
let type;
|
||||
if (pureDate < todayDate) type = 'past';
|
||||
else if (pureDate > todayDate) type = 'future';
|
||||
else type = 'today';
|
||||
|
||||
// 格式化日期
|
||||
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(currentDate.getDate()).padStart(2, '0');
|
||||
|
||||
// 计算当前是第几天(以"前X天"为第1天)
|
||||
const dayNumber = i - -X + 1; // 核心计算:转换为从1开始的序列
|
||||
|
||||
// 计算text字段
|
||||
let text = '1'; // 默认显示1
|
||||
if (dayNumber === 30) {
|
||||
text = '30'; // 第30天显示30
|
||||
} else if ((dayNumber % 30) % 7 === 0) {
|
||||
text = '5'; // 每7天(第7、14、21、28、35天)显示5
|
||||
}
|
||||
|
||||
allDays.push({
|
||||
date: `${month}.${day}`,
|
||||
type,
|
||||
index: i - -X + 1,
|
||||
text,
|
||||
i
|
||||
});
|
||||
}
|
||||
|
||||
// 每7天分为一组,共5组
|
||||
const groupedList = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const start = i * 7;
|
||||
const end = start + 7;
|
||||
groupedList.push({
|
||||
groupIndex: i,
|
||||
days: allDays.slice(start, end)
|
||||
});
|
||||
}
|
||||
|
||||
return groupedList;
|
||||
}
|
||||
|
||||
// 示例:X=10时,以"10天前"为第1天,生成35天列表
|
||||
try {
|
||||
const X = 10;
|
||||
dateGroups.value = generateGroupedDateList(X);
|
||||
console.log(`以${X}天前为第1天的35天分组日期:`);
|
||||
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);
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
console.log('111111111');
|
||||
popup.value.open();
|
||||
};
|
||||
defineExpose({ open, close });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 日期
|
||||
.calendar-day {
|
||||
text-align: center;
|
||||
// 隐藏展开时超过30天的样式
|
||||
&.hide.expand {
|
||||
.calendar-day-title {
|
||||
width: 70rpx;
|
||||
color: #fff;
|
||||
}
|
||||
.calendar-day-bg {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.calendar-day-bg {
|
||||
width: 70rpx;
|
||||
height: 84rpx;
|
||||
border-radius: 8rpx;
|
||||
// 前面日期,代表已签到
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
font-family: Arial, Arial;
|
||||
font-size: 32rpx;
|
||||
|
||||
.img {
|
||||
width: 32rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
&.past {
|
||||
background-color: #ffd07c;
|
||||
}
|
||||
|
||||
// 未来日期,代表未签到
|
||||
&.future {
|
||||
color: #d88f50;
|
||||
font-weight: bold;
|
||||
background: #ffe6b8;
|
||||
border: 4rpx solid #ffd58a;
|
||||
}
|
||||
// 当前已签到和未签到
|
||||
&.signed.today {
|
||||
background-color: #ffd07c;
|
||||
}
|
||||
&.no-signed.today {
|
||||
color: #d88f50;
|
||||
font-weight: bold;
|
||||
background: #ffe6b8;
|
||||
border: 4rpx solid #ffd58a;
|
||||
}
|
||||
// 当天的签到小图标
|
||||
.check-in-tip {
|
||||
display: none;
|
||||
}
|
||||
&.today > .check-in-tip {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
font-size: 20rpx;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #fff;
|
||||
box-shadow: 0 0 4rpx #fef5e6;
|
||||
color: #fff;
|
||||
font-weight: 400;
|
||||
background-color: #f8831c;
|
||||
|
||||
display: flex;
|
||||
justify-content: center; /* 水平居中 */
|
||||
align-items: center; /* 垂直居中 */
|
||||
/* 2. 解决文字被边框挤压:清除默认内边距,重置行高 */
|
||||
padding: 0; /* 清除默认内边距(部分元素默认有padding) */
|
||||
line-height: 1; /* 行高与字体大小一致,避免垂直方向多余空间 */
|
||||
/* 3. 旋转30度(transform 旋转属性) */
|
||||
transform: translate(30%, -40%) rotate(-30deg);
|
||||
}
|
||||
}
|
||||
.calendar-day-title {
|
||||
margin: 8rpx 0;
|
||||
font-family: ArialMT;
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
line-height: 28rpx;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-Y {
|
||||
/**
|
||||
* 26rpx 主盒子内间距
|
||||
* 68rpx 标题高度
|
||||
* 128rpx 日历高度
|
||||
* 16rpx 日历下外间距
|
||||
* 92rpx 下方按钮盒子高度
|
||||
* 80rpx 切换盒子高度
|
||||
*/
|
||||
height: calc(100% - 26rpx - 68rpx - 128rpx - 16rpx - 92rpx - 80rpx - 8rpx );
|
||||
}
|
||||
|
||||
.switch-div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 60rpx;
|
||||
margin: 10rpx 30%;
|
||||
.back_div {
|
||||
.back-icon {
|
||||
position: relative;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
.back-icon::before,
|
||||
.back-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.back-icon::after {
|
||||
top: 25%;
|
||||
left: 25%;
|
||||
width: 40%;
|
||||
height: 40%;
|
||||
border-top: 2rpx solid #bfbfbf;
|
||||
border-left: 2rpx solid #bfbfbf;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
.calendar-div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.calendar-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.button-div {
|
||||
height: 92rpx;
|
||||
width: 442rpx;
|
||||
margin: 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: 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>
|
||||
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -33,7 +33,7 @@ export const SUBJECT_TYPE = [{
|
||||
}
|
||||
]
|
||||
export const defaultAvatar = '/static/images/me/default_user_avatar.png'
|
||||
export const defaultAnonymousAvatar = '/static/images/me/default_user_avatar.png'
|
||||
export const defaultAnonymousAvatar = '/static/images/competition/competition_default_avatar.png'
|
||||
export const styleButton = {
|
||||
style_button_1: {
|
||||
color: '#FFFFFF',
|
||||
|
||||
@@ -375,6 +375,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/pointsAndRank/badge",
|
||||
"style": {
|
||||
"navigationBarTitleText": "徽章列表",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/course/index",
|
||||
"style": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="item" :class="getStatusClass(item.isFinish)" @click="click_item(item)">
|
||||
<view class="item" :class="getStatusClass(item)" @click="click_item(item)">
|
||||
<view class="img_div">
|
||||
<image-preview class="img" :src="item.ossAddr"></image-preview>
|
||||
<view class="subscript" v-if="item.isFinish === '02'">已完成</view>
|
||||
@@ -10,15 +10,19 @@
|
||||
{{ item.taskName }}
|
||||
</view>
|
||||
<view class="time_div" v-if="item.limitTyp === '02'">
|
||||
起止时间:<text class="blod_color">{{ item.startTm?.substr(0, 10) }}至{{ item.endTm?.substr(0, 10) }}</text>
|
||||
起止时间:
|
||||
<text class="blod_color">{{ item.startTm?.substr(0, 10) }}至{{ item.endTm?.substr(0, 10) }}</text>
|
||||
</view>
|
||||
<view class="people_progress_div">
|
||||
<view class="mr-10">
|
||||
完成人数:<text class="">{{ item.taskFinishSums ?? 0 }}/{{ item.taskSums ?? 0 }}</text>
|
||||
完成人数:
|
||||
<text class="">{{ item.taskFinishSums ?? 0 }}/{{ item.taskSums ?? 0 }}</text>
|
||||
</view>
|
||||
<view>
|
||||
进度:
|
||||
<text class="">{{ item.finishCrs ?? item.learnedNumber ?? 0 }}/{{ item.sumCrs ?? item.taskNumber ?? 0 }}门课程</text>
|
||||
<text class="">
|
||||
{{ item.finishCrs ?? item.learnedNumber ?? 0 }}/{{ item.sumCrs ?? item.taskNumber ?? 0 }}门课程
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -37,26 +41,27 @@
|
||||
});
|
||||
const emit = defineEmits(['click_item']);
|
||||
const item = computed(() => props.item);
|
||||
const getStatusClass = (isFinish) => {
|
||||
const getStatusClass = (item) => {
|
||||
// 01进行中、02已完成、03已超时
|
||||
// if (isFinish === '00') return 'in_progress';
|
||||
// if (isFinish === '01') return 'completed';
|
||||
// return 'expired';
|
||||
if (isFinish === '02') return 'completed';
|
||||
if (item.isFinish === '02') return 'completed';
|
||||
if (item.isTimeOut === '02') return 'expired';
|
||||
return 'expired';
|
||||
};
|
||||
// 跳转到详情
|
||||
const click_item = (item) => {
|
||||
if(item.limitTyp === '02') {
|
||||
if(item.isTimeOut === '01') {
|
||||
if (item.limitTyp === '02') {
|
||||
if (item.isTimeOut === '01') {
|
||||
return common.msg('任务未开始');
|
||||
} else if(item.isTimeOut === '02') {
|
||||
} else if (item.isTimeOut === '02') {
|
||||
return common.msg('任务已结束');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setPageCache('learning_tasks_details', item);
|
||||
common.navigateTo('/pages/learningTasks/details?taskId='+item.taskId);
|
||||
common.navigateTo('/pages/learningTasks/details?taskId=' + item.taskId);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -67,7 +72,6 @@
|
||||
color: #0066ff;
|
||||
background-color: #daf0ff;
|
||||
}
|
||||
|
||||
.blod_color {
|
||||
color: #0066ff;
|
||||
}
|
||||
@@ -89,11 +93,11 @@
|
||||
// 已超时
|
||||
.subscript {
|
||||
color: #ffffff;
|
||||
background-color: #ef5705;
|
||||
background-color: #999 !important;
|
||||
}
|
||||
|
||||
.blod_color {
|
||||
color: #d70c18;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
.item {
|
||||
|
||||
@@ -32,37 +32,47 @@
|
||||
src="/static/images/me/man.png"
|
||||
></image>
|
||||
</view>
|
||||
<!-- 显示当前选中的徽章 -->
|
||||
<view class="dan" @click="common.navigateTo('/pages/pointsAndRank/rank')">
|
||||
<view class="dan_img">
|
||||
<image
|
||||
class="img_100"
|
||||
:src="`/static/images/me/${badgeList[currentIndex].png}`"
|
||||
mode="heightFix"
|
||||
alt="当前徽章"
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="fl1"></view>
|
||||
</view>
|
||||
<view class="work_number_div">工号:{{ user_info.loginName }}</view>
|
||||
<view class="fl1"></view>
|
||||
</view>
|
||||
<!-- 显示当前选中的徽章 -->
|
||||
<view class="dan" @click="changeBadge">
|
||||
<view class="dan_img">
|
||||
<image
|
||||
class="img_100"
|
||||
:src="`/static/images/me/${badgeList[currentIndex].png}`"
|
||||
mode="heightFix"
|
||||
alt="当前徽章"
|
||||
></image>
|
||||
</view>
|
||||
<view class="dan_text">{{ badgeList[currentIndex].name }}</view>
|
||||
</view>
|
||||
<view class="sign-in-div" @click="checkInFun">签到</view>
|
||||
</view>
|
||||
<!-- 用户学习时间信息 -->
|
||||
<view class="study_information">
|
||||
<view class="item" @click="common.navigateTo('/pages/pointsAndRank/points')">
|
||||
<view class="top">我的积分</view>
|
||||
<view class="bottom">
|
||||
<uv-count-to :startVal="0" :endVal="0" :useEasing="true" ref="accmTaskCntRef"></uv-count-to>
|
||||
<uv-count-to
|
||||
:startVal="statistics_data['startCurrentPoints']"
|
||||
:endVal="statistics_data['currentPoints']"
|
||||
:useEasing="true"
|
||||
ref="currentPointsRef"
|
||||
></uv-count-to>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-interval"></view>
|
||||
<view class="item" @click="common.navigateTo('/pages/pointsAndRank/rank')">
|
||||
<view class="item" @click="common.navigateTo('/pages/pointsAndRank/badge')">
|
||||
<view class="top">徽章墙</view>
|
||||
<view class="bottom">
|
||||
<uv-count-to :startVal="0" :endVal="0" :useEasing="true" ref="accmTaskCntRef"></uv-count-to>
|
||||
<uv-count-to
|
||||
:startVal="statistics_data['startBadgeNum']"
|
||||
:endVal="statistics_data['badgeNum']"
|
||||
:useEasing="true"
|
||||
ref="badgeNumRef"
|
||||
></uv-count-to>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -81,13 +91,13 @@
|
||||
|
||||
<view class="scroll-view-item" @click="common.navigateTo('/pages/examinationRecord/index')">
|
||||
<view class="item">
|
||||
<image class="icon" src="@/static/images/me/report.png"></image>
|
||||
<image class="icon" src="@/static/images/me/todo.png"></image>
|
||||
<view class="title">考试记录</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="scroll-view-item">
|
||||
<view class="item">
|
||||
<image class="icon" src="@/static/images/me/todo.png"></image>
|
||||
<image class="icon" src="@/static/images/me/report.png"></image>
|
||||
<view class="title">AI陪练记录</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -146,12 +156,13 @@
|
||||
@cancel="avatarOnCancel"
|
||||
@confirm="avatarOnConfirm"
|
||||
></avatar-cropper>
|
||||
<checkIn ref="checkInRef"></checkIn>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, reactive, nextTick } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import { onLoad, onShow, onHide } from '@dcloudio/uni-app';
|
||||
import common from '@/common/common';
|
||||
import { getUserInfo } from '@/common/common';
|
||||
import { onPullDownRefresh } from '@dcloudio/uni-app';
|
||||
@@ -164,12 +175,15 @@
|
||||
import { queryCurrentUser } from '@/api/login.js';
|
||||
import { defaultAvatar } from '@/enum.js';
|
||||
import useUpdateAvatar from '@/composables/useUpdateAvatar';
|
||||
import { queryCurrentStatus } from '@/api/pointsAndRank.js';
|
||||
import checkIn from '@/components/points-and-badge/check-in';
|
||||
const { avatarCropperUrl, click_update_avatar, avatarCropperStatus, avatarOnCancel, avatarOnConfirm } = useUpdateAvatar({
|
||||
success: (userData) => {
|
||||
user_info.imageAddr = userData.imageAddr;
|
||||
user_info.imagePath = '';
|
||||
}
|
||||
});
|
||||
|
||||
const badgeList = [
|
||||
{
|
||||
name: '黑铁',
|
||||
@@ -201,14 +215,12 @@
|
||||
}
|
||||
];
|
||||
const currentIndex = ref(0);
|
||||
const changeBadge = () => {
|
||||
// 计算下一个索引,如果当前是最后一个则回到0
|
||||
currentIndex.value = (currentIndex.value + 1) % badgeList.length;
|
||||
};
|
||||
|
||||
const socketStore = useSocketStore();
|
||||
const accmTaskCntRef = ref(null);
|
||||
const accmPointRef = ref(null);
|
||||
const stdtTmLenRef = ref(null);
|
||||
|
||||
const badgeNumRef = ref(null);
|
||||
const checkInRef = ref(null);
|
||||
const currentPointsRef = ref(null);
|
||||
|
||||
const user_info = reactive({
|
||||
userName: '',
|
||||
@@ -220,7 +232,9 @@
|
||||
console.log('goTest');
|
||||
// common.navigateTo('/pages/test/index');
|
||||
};
|
||||
|
||||
const checkInFun = () => {
|
||||
checkInRef.value.open();
|
||||
};
|
||||
const click_loginout = () => {
|
||||
common.show('确定退出登录?').then(async (res) => {
|
||||
if (!res) return;
|
||||
@@ -234,9 +248,80 @@
|
||||
common.reLaunch('/pages/login/login');
|
||||
});
|
||||
};
|
||||
|
||||
const statistics_data = reactive({
|
||||
badgeNum: 0, // 徽章数
|
||||
currentPoints: 0, // 当前积分
|
||||
currentRankAddr: '', // 当前段位名称
|
||||
currentRankName: '', // 当前段位名称
|
||||
|
||||
startBadgeNum: 0, // 起始 徽章数
|
||||
startCurrentPoints: 0, // 起始 积分
|
||||
request_time: 0, // 上次请求时间
|
||||
init: function () {
|
||||
// 初始化统计数据
|
||||
const points_data = common.getValue('points_data');
|
||||
if (points_data) {
|
||||
this.badgeNum = points_data.badgeNum; // 徽章数
|
||||
this.currentPoints = points_data.currentPoints; // 当前积分
|
||||
this.currentRankAddr = points_data.currentRankAddr;
|
||||
this.currentRankName = points_data.currentRankName;
|
||||
}
|
||||
},
|
||||
get_statistics_data: async function () {
|
||||
try {
|
||||
const time_now = Date.now();
|
||||
let points_data = common.getValue('points_data') || {};
|
||||
if (time_now - this.request_time > 300) {
|
||||
this.request_time = time_now;
|
||||
// 获取最新数据
|
||||
const res = await queryCurrentStatus();
|
||||
if (res && res.body) {
|
||||
// 合并新数据与缓存数据,确保只更新有效字段
|
||||
points_data = {
|
||||
badgeNum: res.body['badgeNum'], // 徽章数
|
||||
currentPoints: res.body['currentPoints'], // 当前积分
|
||||
currentRankAddr: res.body['ossAddr'], // 当前段位图片
|
||||
currentRankName: res.body['currentRankName'] // 当前段位名称
|
||||
};
|
||||
common.setValue('points_data', points_data);
|
||||
this.currentRankAddr = points_data.currentRankAddr;
|
||||
this.currentRankName = points_data.currentRankName;
|
||||
}
|
||||
}
|
||||
|
||||
// 解构赋值获取最新值
|
||||
const { badgeNum = 0, currentPoints = 0 } = points_data;
|
||||
|
||||
// 更新数据并触发动画
|
||||
if (badgeNum !== this.badgeNum) {
|
||||
this.startBadgeNum = this.badgeNum;
|
||||
this.badgeNum = badgeNum;
|
||||
badgeNumRef.value.start();
|
||||
}
|
||||
|
||||
if (currentPoints !== this.currentPoints) {
|
||||
this.startCurrentPoints = this.currentPoints;
|
||||
this.currentPoints = currentPoints;
|
||||
console.log('this.startCurrentPointsx', this.startCurrentPoints, this.currentPoints);
|
||||
currentPointsRef.value.start();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error);
|
||||
// 可以在这里添加更多的错误处理逻辑,比如显示错误提示
|
||||
}
|
||||
}
|
||||
});
|
||||
onLoad(() => {
|
||||
statistics_data['init'](); // 初始化数据
|
||||
});
|
||||
onShow(() => {
|
||||
setMascot();
|
||||
Object.assign(user_info, getUserInfo());
|
||||
statistics_data['get_statistics_data'](); // 获取统计数据
|
||||
});
|
||||
onHide(() => {
|
||||
checkInRef.value && checkInRef.value.close();
|
||||
});
|
||||
onPullDownRefresh(() => {
|
||||
uni.stopPullDownRefresh();
|
||||
@@ -251,7 +336,7 @@
|
||||
margin: 30rpx $bg-margin-left 0 $bg-margin-left;
|
||||
background: #ffffff;
|
||||
border-radius: 15rpx;
|
||||
z-index: 101;
|
||||
z-index: 90;
|
||||
overflow: hidden;
|
||||
|
||||
// 为了让线缩进来24单位
|
||||
@@ -268,15 +353,12 @@
|
||||
// 快捷导航栏
|
||||
.navigation_div {
|
||||
margin: 30rpx $bg-margin-left 0 $bg-margin-left;
|
||||
// padding: 24rpx 0 0 20rpx;
|
||||
padding: 24rpx 10rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 23rpx;
|
||||
display: flex;
|
||||
// flex-direction: column;
|
||||
justify-content: space-around;
|
||||
z-index: 101;
|
||||
|
||||
z-index: 90;
|
||||
.scroll-view {
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
@@ -375,6 +457,14 @@
|
||||
margin-left: 10rpx;
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.dan {
|
||||
font-size: 16rpx;
|
||||
padding-left: 20rpx;
|
||||
.dan_img {
|
||||
height: 46rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,19 +480,19 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dan {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 16rpx;
|
||||
align-items: center;
|
||||
|
||||
.dan_img {
|
||||
height: 50rpx;
|
||||
}
|
||||
|
||||
.dan_text {
|
||||
.sign-in-div {
|
||||
margin-top: 10rpx;
|
||||
width: 102rpx;
|
||||
height: 48rpx;
|
||||
font-weight: 500;
|
||||
font-size: 26rpx;
|
||||
line-height: 46rpx;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
background-image: url('@/static/images/me/sign-in-button-bg.png');
|
||||
background-size: 100%; /* 图片自适应元素大小(避免拉伸变形) */
|
||||
background-repeat: no-repeat; /* 禁止重复平铺 */
|
||||
background-position: center; /* 图片居中显示 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<z-paging
|
||||
ref="pagingRef"
|
||||
v-model="data_list"
|
||||
@query="queryList"
|
||||
class="scroll_div"
|
||||
:loading-more-enabled="false"
|
||||
:layout-only="true"
|
||||
empty-view-text="暂无积分记录"
|
||||
paging-class="z-paging-class"
|
||||
>
|
||||
<template #top>
|
||||
<view class="body-title">
|
||||
<view class="back_div" @click="common.navigateBack()">
|
||||
<view class="back-icon"></view>
|
||||
</view>
|
||||
<view>我的徽章</view>
|
||||
</view>
|
||||
<view class="personal_information">
|
||||
<view class="profile">
|
||||
<cached-avatar class="img"></cached-avatar>
|
||||
</view>
|
||||
<view class="personal_information_data">
|
||||
<view class="name_sex_div">
|
||||
<view class="name">
|
||||
{{ user_info.userName }}
|
||||
</view>
|
||||
<view class="fl1"></view>
|
||||
</view>
|
||||
<view class="work_number_div">工号:{{ user_info.loginName }}</view>
|
||||
<view class="fl1"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="main-div">
|
||||
<view class="item-array" v-for="(itemList, index) in data_list">
|
||||
<view class="title-div">
|
||||
<image class="img" src="@/static/images/pointsAndRank/badge-title-left.png" mode=""></image>
|
||||
<view class="title">
|
||||
{{ itemList.name }}
|
||||
</view>
|
||||
<image class="img" src="@/static/images/pointsAndRank/badge-title-right.png" mode=""></image>
|
||||
</view>
|
||||
<view class="item-list-container">
|
||||
<view class="item" v-for="item in itemList.child">
|
||||
<view class="badge-img">
|
||||
<image-preview
|
||||
class="img"
|
||||
:src="item.isOpen === 'Y' ? item.ossAddr : item.ossAddrOff"
|
||||
></image-preview>
|
||||
</view>
|
||||
<view class="badge-img-title light-up">
|
||||
{{ item.badgeName }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- </view> -->
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 竞赛列表
|
||||
import { ref, reactive, watch, onMounted } from 'vue';
|
||||
import { onLoad, onUnload } from '@dcloudio/uni-app';
|
||||
import common from '@/common/common';
|
||||
import useZpaging from '@/composables/useZpaging.js';
|
||||
import { queryRankWall } from '@/api/pointsAndRank.js';
|
||||
import { getUserInfo } from '@/common/common';
|
||||
const user_info = reactive({
|
||||
userName: '',
|
||||
userId: ''
|
||||
});
|
||||
const fetchFunction = (params) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
queryRankWall().then(({ body }) => {
|
||||
console.log('body', body);
|
||||
let originalData = body;
|
||||
// 处理逻辑:构建二维数组(外层分类列表 + 内层徽章列表)
|
||||
const twoDimensionalArray = [];
|
||||
|
||||
// 遍历每个分类,生成二维数组的每一项
|
||||
Object.entries(originalData).forEach(([categoryKey, badgeArray]) => {
|
||||
// 构建外层列表项:包含 name 和子列表 badgeList
|
||||
const categoryItem = {
|
||||
name: categoryKey, // 第一维度的 name 是原始键
|
||||
child: badgeArray // 子列表:直接放入当前分类的所有徽章数据
|
||||
};
|
||||
|
||||
// 将分类项加入二维数组
|
||||
twoDimensionalArray.push(categoryItem);
|
||||
});
|
||||
console.log('resultList', twoDimensionalArray);
|
||||
resolve({
|
||||
body: twoDimensionalArray
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
const { pagingRef, queryList, data_list, search } = useZpaging({
|
||||
fetchFunction
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
Object.assign(user_info, getUserInfo());
|
||||
pagingRef.value.reload();
|
||||
});
|
||||
// onUnload(() => {});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.personal_information {
|
||||
padding: 40rpx 26rpx 0 6vw;
|
||||
display: flex;
|
||||
|
||||
// 个人头像框框
|
||||
.profile {
|
||||
border-radius: 100%;
|
||||
// border: 2px solid #ffffff;
|
||||
width: 108rpx;
|
||||
height: 108rpx;
|
||||
overflow: hidden;
|
||||
border: 2rpx solid #b39e81;
|
||||
}
|
||||
|
||||
// 头像右边的信息
|
||||
.personal_information_data {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 26rpx;
|
||||
overflow: hidden;
|
||||
|
||||
// 上边的姓名性别盒子
|
||||
.name_sex_div {
|
||||
display: flex;
|
||||
margin-top: 6rpx;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
font-family: PingFangSC;
|
||||
font-weight: 600;
|
||||
font-size: 28rpx;
|
||||
color: #000000;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.work_number_div {
|
||||
font-family: PingFangSC;
|
||||
font-weight: 400;
|
||||
font-size: 25rpx;
|
||||
color: #666666;
|
||||
line-height: 48rpx;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title-div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 30rpx 0;
|
||||
.title {
|
||||
margin: 0 16rpx;
|
||||
font-weight: 600;
|
||||
font-size: 34rpx;
|
||||
color: #ae723b;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
}
|
||||
.img {
|
||||
width: 242rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
.item-list-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start; /* 改为居中对齐 */
|
||||
gap: 40rpx; /* 调大间距(根据需要调整数值) */
|
||||
padding: 6vw;
|
||||
box-sizing: border-box;
|
||||
|
||||
.item {
|
||||
/* 计算宽度:总宽度减去两倍间距后三等分(保证三列) */
|
||||
width: calc((100% - 80rpx) / 3); /* 间距40rpx时,左右两边间距总和为80rpx */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
.badge-img {
|
||||
width: calc(27vw - 14rpx); /* 基于父元素宽度计算,更适配 */
|
||||
height: calc(27vw - 14rpx);
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-img-title {
|
||||
margin-top: 10rpx;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 600;
|
||||
font-size: 30rpx;
|
||||
color: #ae723b;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
}
|
||||
.light-up {
|
||||
color: #8f4628;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body-title {
|
||||
width: 100%;
|
||||
height: 70rpx;
|
||||
font-size: 33rpx;
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin-top: var(--status-bar-height);
|
||||
.delete_div {
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 17rpx;
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
|
||||
.img {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.back_div {
|
||||
position: absolute;
|
||||
left: 26rpx;
|
||||
top: 17rpx;
|
||||
.back-icon {
|
||||
position: relative;
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.back-icon::before,
|
||||
.back-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.back-icon::after {
|
||||
top: 25%;
|
||||
left: 25%;
|
||||
width: 40%;
|
||||
height: 40%;
|
||||
border-top: 4rpx solid #000;
|
||||
border-left: 4rpx solid #000;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
.z-paging-class {
|
||||
min-height: 100vh;
|
||||
background-image: url('@/static/images/pointsAndRank/badge-bg.png');
|
||||
background-color: #000; /* 图片高度不足时的背景色 */
|
||||
background-size: 100% auto; /* 宽度填满,高度自适应 */
|
||||
background-position: center top; /* 图片顶部居中显示 */
|
||||
background-repeat: no-repeat; /* 不重复平铺 */
|
||||
background-color: #fef1dd;
|
||||
}
|
||||
</style>
|
||||
@@ -59,16 +59,17 @@
|
||||
import useZpaging from '@/composables/useZpaging.js';
|
||||
import common from '@/common/common';
|
||||
import { queryPointsDetail, queryCurrentStatus } from '@/api/pointsAndRank.js';
|
||||
|
||||
const startVal = ref(0);
|
||||
const endVal = ref(0);
|
||||
const countToRef = ref(null);
|
||||
const fetchFunction = (params) => {
|
||||
queryCurrentStatus().then(({ body }) => {
|
||||
const { currentPoints } = body;
|
||||
console.log('recurrentPointss', currentPoints);
|
||||
startVal.value = 9999;
|
||||
endVal.value = 99999;
|
||||
stdtTmLenRef.value.start();
|
||||
if (currentPoints !== endVal.value) {
|
||||
endVal.value = currentPoints;
|
||||
countToRef.value.start();
|
||||
}
|
||||
});
|
||||
return queryPointsDetail({ ...params });
|
||||
};
|
||||
@@ -76,13 +77,17 @@
|
||||
fetchFunction
|
||||
});
|
||||
|
||||
onLoad(() => {});
|
||||
onLoad(() => {
|
||||
const points_data = common.getValue('points_data');
|
||||
if (points_data) {
|
||||
startVal.value = points_data.currentPoints; // 徽章数
|
||||
endVal.value = points_data.currentPoints; // 徽章数
|
||||
}
|
||||
});
|
||||
onUnload(() => {});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
.z-paging-class {
|
||||
background-color: #e0e3ef;
|
||||
::v-deep .zp-view-super {
|
||||
@@ -230,7 +235,7 @@
|
||||
height: 34rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.back_div {
|
||||
position: absolute;
|
||||
left: 26rpx;
|
||||
|
||||
@@ -310,6 +310,15 @@
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/pointsAndRank/badge",
|
||||
"style": {
|
||||
"navigationBarTitleText": "徽章墙",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 262 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 508 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |