This commit is contained in:
田岩
2025-12-12 13:46:02 +08:00
parent 31279fcef0
commit f3b77802b9
5 changed files with 285 additions and 23 deletions
+14
View File
@@ -0,0 +1,14 @@
# 启用严格模式,确保依赖版本一致
strict-peer-dependencies=false
# 配置依赖安装目录(pnpm 特有,节省磁盘空间)
store-dir=~/.pnpm-store
# 兼容 uniapp 对依赖的查找逻辑
node-linker=hoisted
# 忽略废弃依赖警告
ignore-deprecations=all
# 忽略对等依赖警告
strict-peer-dependencies=false
+182 -17
View File
@@ -42,7 +42,7 @@
<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="item" v-for="item in itemList.child" @click="showDetailed(item)">
<view class="badge-img">
<image-preview
class="img"
@@ -56,8 +56,32 @@
</view>
</view>
</view>
<!-- </view> -->
</z-paging>
<uni-popup ref="popup" class="popup">
<view class="popup-content" @touchmove.stop>
<view class="fl1"></view>
<view class="fl1"></view>
<view class="max-div">
<view class="points-img-div">
<image-preview class="img" :src="detailedInfo.ossAddr" mode="widthFix"></image-preview>
</view>
<view class="points-title">{{ detailedInfo.badgeName }}</view>
<view class="points-div select-institution-div">
<view class="points-remark">{{ detailedInfo.remark }}</view>
<view class="points-time">{{ detailedInfo.obtainTm }}</view>
</view>
<view class="points-button-div">
<uv-button
class="button"
text="关闭"
:custom-style="customStyle"
@click="closeDetailed()"
></uv-button>
</view>
</view>
<view class="fl1"></view>
</view>
</uni-popup>
</template>
<script setup>
@@ -68,29 +92,63 @@
import useZpaging from '@/composables/useZpaging.js';
import { queryRankWall } from '@/api/pointsAndRank.js';
import { getUserInfo } from '@/common/common';
const buttonColor = '';
const customStyle = {
background:"linear-gradient( 0deg, RGBA(8, 9, 4, 1) 0%, RGBA(100, 100, 100, .5) 100%)",
color:'#fff',
width: '290rpx',
height: '100rpx',
border: '4rpx solid #EEDA9B',
borderRadius: '180rpx'
}
const popup = ref(null);
const user_info = reactive({
userName: '',
userId: ''
});
const detailedInfo = ref({
ossAddr: '',
message: '',
num: ''
});
const fetchFunction = (params) => {
return new Promise((resolve, reject) => {
queryRankWall().then(({ body }) => {
let originalData = body;
// 处理逻辑:构建二维数组(外层分类列表 + 内层徽章列表)
const twoDimensionalArray = [];
// 遍历每个分类,生成二维数组的每一项
Object.entries(originalData).forEach(([categoryKey, badgeArray]) => {
// 构建外层列表项:包含 name 和子列表 badgeList
const categoryItem = {
name: categoryKey, // 第一维度的 name 是原始键
child: badgeArray // 子列表:直接放入当前分类的所有徽章数据
};
// 将分类项加入二维数组
twoDimensionalArray.push(categoryItem);
const scoreRankBadges = originalData['积分段位徽章'];
// 2. 动态合并除“积分段位徽章”外的所有徽章数组
const otherBadges = [];
// 遍历原始数据的所有键
Object.keys(originalData).forEach((key) => {
// 排除固定键,其余全部合并
if (key !== '积分段位徽章') {
// 确保对应值是数组,避免非数组类型导致错误
if (Array.isArray(originalData[key])) {
otherBadges.push(...originalData[key]);
}
}
});
// // 处理逻辑:构建二维数组(外层分类列表 + 内层徽章列表)
// const twoDimensionalArray = [];
// // 遍历每个分类,生成二维数组的每一项
// Object.entries(originalData).forEach(([categoryKey, badgeArray]) => {
// // 构建外层列表项:包含 name 和子列表 badgeList
// const categoryItem = {
// name: categoryKey, // 第一维度的 name 是原始键
// child: badgeArray // 子列表:直接放入当前分类的所有徽章数据
// };
// // 将分类项加入二维数组
// twoDimensionalArray.push(categoryItem);
// });
uni.$emit('update_me_data', true);
resolve({
body: twoDimensionalArray
body: [
{ name: '段位徽章', child: scoreRankBadges },
{ name: '节气徽章', child: otherBadges }
]
});
});
});
@@ -98,7 +156,16 @@
const { pagingRef, queryList, data_list, search } = useZpaging({
fetchFunction
});
// 显示详细信息
const showDetailed = (item) => {
if(item.isOpen !== 'Y') return;
detailedInfo.value = item;
popup.value.open();
};
const closeDetailed = () => {
popup.value.close();
};
onMounted(() => {
Object.assign(user_info, getUserInfo());
pagingRef.value.reload();
@@ -107,6 +174,33 @@
</script>
<style lang="scss" scoped>
.popup {
.popup-content {
width: 100vw;
height: 100vh;
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
background-color: rgba(0,0,0,.85);
}
.max-div {
position: relative;
overflow: hidden;
margin-top: var(--status-bar-height);
display: flex;
flex-direction: column;
align-items: center;
.points-img-div {
width: 420rpx;
height: 420rpx;
}
}
}
.main-div {
padding-top: 30rpx;
}
@@ -187,6 +281,7 @@
gap: 40rpx; /* 调大间距(根据需要调整数值) */
padding: 6vw;
box-sizing: border-box;
.item {
/* 计算宽度:总宽度减去两倍间距后三等分(保证三列) */
@@ -195,7 +290,6 @@
flex-direction: column;
align-items: center;
box-sizing: border-box;
.badge-img {
width: calc(27vw - 14rpx); /* 基于父元素宽度计算,更适配 */
height: calc(27vw - 14rpx);
@@ -218,7 +312,6 @@
text-align: center;
font-style: normal;
color: #e2d1cc;
}
}
}
@@ -285,4 +378,76 @@
background-repeat: no-repeat; /* 不重复平铺 */
background-color: #fef1dd;
}
.points-title{
font-weight: 400;
font-size: 54rpx;
text-align: center;
color: #ffffff;
margin: 20rpx 0 60rpx 0;
}
.points-button-div{
margin-bottom: 200rpx;
margin-top: 180rpx;
.button{
}
}
.select-institution-div {
position: relative;
border-radius: 20rpx;
width: 560rpx;
height: 130rpx;
padding: 12rpx 0;
border: 2rpx solid #EEDA9B;
background: linear-gradient(150deg, RGBA(86, 82, 73, 1), RGBA(17, 17, 17, .4)) border-box;
display: flex;
flex-direction: column;
justify-content: space-around;
.points-remark{
font-weight: 400;
font-size: 26rpx;
text-align: center;
color: #ffffff;
}
.points-time{
font-weight: 400;
font-size: 26rpx;
text-align: center;
color: #ffffff;
}
}
/* 边框高光效果 */
.select-institution-div2::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 4rpx solid transparent;
background: linear-gradient(90deg, transparent, RGBA(251, 231, 178, 1), transparent) border-box;
-webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
border-radius: 20rpx;
/* 初始位置在左侧外部 */
background-size: 200% 100%;
/* 动画:高光从左到右流动 */
animation: highlightFlow 20s linear infinite;
}
/* 定义高光流动动画 */
@keyframes highlightFlow {
0% {
background-position: -100% 0;
}
100% {
background-position: 100% 0;
}
}
</style>
@@ -81,7 +81,7 @@
* 音频路由兼容方案(适配 H5/小程序/不支持 setAudioRoute 的设备)
* @param {string} type - earpiece(听筒)/speaker(扬声器)
*/
export default {
mounted() {
const _this = this;
+7 -5
View File
@@ -64,7 +64,7 @@
// 关闭通话
const closeTalking = async () => {
// todo 防抖
common.show('', '挂断'+(isVideoStyle.value?'视频':'电话')+'将结束训练,是否确认?').then((res) => {
common.show('', '挂断' + (isVideoStyle.value ? '视频' : '电话') + '将结束训练,是否确认?').then((res) => {
if (res) {
destroy(); // 走关闭销毁流程
}
@@ -113,7 +113,6 @@
console.log('当前分贝', res);
};
const targetTimestamp = ref(0);
// 定时器实例
let timer = null;
@@ -127,13 +126,15 @@
return `${minutes}:${seconds.toString().padStart(2, '0')}`; // 秒补零
};
const targetTimestamp = ref(0);
// 计算属性:实时格式化剩余时间
const formattedTime = ref(0);
const formattedTime = ref('00:00');
// 3. 更新剩余时间的核心方法
const updateRemaining = () => {
const now = Date.now();
const diff = now - targetTimestamp.value;
formattedTime.value = formatTime(Math.max(0, Math.floor(diff / 1000)));
const seconds = Math.max(0, Math.floor(diff / 1000));
formattedTime.value = formatTime(seconds);
};
const onStartRecord = (body) => {
@@ -143,7 +144,8 @@
console.log('连接成功开始' + dataInfo.traInterTypDesc);
isRecording.value = true;
isVideo.value && talkCameraRef.value.startPreview();
timer = setInterval(updateRemaining, 1000);
if (timer) clearInterval(timer); // 清除旧的定时器,避免多个定时器叠加
timer = setInterval(updateRemaining, 400);
// 立即执行一次
updateRemaining();
talkMicrophoneRef.value.start({
+81
View File
@@ -0,0 +1,81 @@
const { defineConfig } = require('@vue/cli-service');
const path = require('path');
module.exports = defineConfig({
// 基础路径(根据你的部署环境调整)
publicPath: './',
// 输出目录
outputDir: 'dist',
// 静态资源目录
assetsDir: 'static',
// 关闭 eslint 校验(可选,根据项目需求)
lintOnSave: false,
// webpack 配置
configureWebpack: {
// 配置路径别名(和 Vite 的 alias 功能一致)
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@/components': path.resolve(__dirname, 'src/components'),
'@/utils': path.resolve(__dirname, 'src/utils')
}
},
// 优化打包(可选)
optimization: {
minimizer: [
// 已安装 terser,可配置压缩
require('terser-webpack-plugin')({
terserOptions: {
compress: {
drop_console: process.env.NODE_ENV === 'production', // 生产环境移除 console
drop_debugger: true // 移除 debugger
}
}
})
]
},
// 解决可能的依赖冲突
resolve: {
extensions: ['.vue', '.js', '.jsx', '.json'],
fallback: {
"crypto": require.resolve("crypto-js"),
"text-encoding": require.resolve("text-encoding")
}
}
},
// 链式修改 webpack 配置
chainWebpack: config => {
// 处理 Vue 文件
config.module
.rule('vue')
.test(/\.vue$/)
.use('vue-loader')
.loader('vue-loader')
.end();
// 处理 SCSS(项目依赖了 sass,需配置 loader
config.module
.rule('scss')
.test(/\.scss$/)
.use('sass-loader')
.loader('sass-loader')
.options({
implementation: require('sass')
})
.end();
},
// 环境变量配置(适配你的多环境脚本)
devServer: {
// 开发服务器配置(如代理、端口等)
port: 8080,
open: true,
proxy: {
// 示例:配置接口代理(根据你的项目需求调整)
'/api': {
target: 'https://your-api-domain.com',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
});