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

This commit is contained in:
田岩
2025-11-04 17:48:51 +08:00
23 changed files with 1348 additions and 57 deletions
+1 -1
View File
@@ -4,6 +4,6 @@ ENV = 'development.ty'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://192.168.108.129'
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://192.168.108.129'
+1 -2
View File
@@ -4,8 +4,7 @@ ENV = 'development.yh'
VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.78:9786'
# VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.32.154:9604'
# VITE_APP_BASE_H5_API_Url_TRAPRACTICE = 'http://25.64.32.154:9603'
+22 -6
View File
@@ -1,11 +1,11 @@
import request from '@/api/request'
const course_base_url = '/trapractice'
const base_url = '/trapractice'
// 获取课程分类树接口
export const getCourseTypeListApi = (data) => {
// 获取分类树接口
export const queryTraPartnerCatelogList = (data) => {
return request({
url: course_base_url + '/traPartnerCatelog/queryTraPartnerCatelog',
url: base_url + '/traPartnerCatelog/queryTraPartnerCatelogList',
method: 'post',
data
});
@@ -13,7 +13,7 @@ export const getCourseTypeListApi = (data) => {
// 获取标签分类树 接口
export const getTraTagListApi = (data) => {
return request({
url: course_base_url + '/traPartnerTag/queryTraTagCatalogInfoList',
url: base_url + '/traPartnerTag/queryTraTagCatalogInfoList',
method: 'post',
data
});
@@ -21,7 +21,23 @@ export const getTraTagListApi = (data) => {
// 全部模块
export const queryCrsInfoByCatalogApi = (data) => {
return request({
url: course_course_base_url + '/traPartnerInfo/queryTraPartnerInfoPaging',
url: base_url + '/traPartnerInfo/queryTraPartnerInfoPaging',
method: 'post',
data
});
};
// id 查详情/trapractice/traPartnerInfo/queryTraPartnerInfoById
export const queryTraPartnerInfoById = (data) => {
return request({
url: base_url + '/traPartnerInfo/queryTraPartnerInfoById',
method: 'post',
data
});
};
// 陪练角色列表 traId
export const queryTraPartnerCharacterInfoList = (data) => {
return request({
url: base_url + '/traPartnerCharacterInfo/queryTraPartnerCharacterInfoList',
method: 'post',
data
});
+27
View File
@@ -456,6 +456,33 @@
"titleNView": false
}
}
},
{
"path": "pages/sparring/detail",
"style": {
"navigationBarTitleText": "AI陪练详情",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/sparring/tip",
"style": {
"navigationBarTitleText": "AI陪练提示",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/sparring/chooseRole",
"style": {
"navigationBarTitleText": "AI陪练选择角色",
"app-plus": {
"titleNView": false
}
}
}
],
"tabBar": {
+316
View File
@@ -0,0 +1,316 @@
<template>
<view class="sparring-detail max_page">
<view class="sparring-detail-body">
<view class="top-box">
<view class="top-title">
<view class="back-icon" @click="common.navigateBack()"></view>
</view>
</view>
<view class="body-swiper">
<view class="body-swiper-title">选择本次陪练的对象</view>
<view class="body-swiper-box">
<CardSwiper @changeChoose="onSwiperChange" :list="roleList" />
</view>
</view>
<!-- <view class="body-card">
<view class="card">
<ImagePreview class="img-box" :src="activeRole.ossAddr" :isCache="true">
</ImagePreview>
<view class="card-title">
<text class="card-title-name">{{ activeRole.chaName }}</text>
<text class="card-title-age">{{ activeRole.chaAge }}</text>
<text class="card-title-img">
<image class="card-title-icon" v-if="activeRole.gender === 'm'" src="@/static/images/me/man.png"></image>
<image class="card-title-icon" v-if="activeRole.gender === 'f'" src="@/static/images/me/wuman.png"></image>
</text>
</view>
<view class="card-desc">
{{ activeRole.occupation }}
</view>
<view class="card-item">
<view class="card-item-title">
<image class="card-item-title-icon" src="@/static/images/sparring/role-icon1.png"></image>
身份情况
</view>
<text class="card-item-desc">{{ activeRole.occupation }}</text>
</view>
<view class="card-item">
<view class="card-item-title">
<image class="card-item-title-icon" src="@/static/images/sparring/role-icon2.png"></image>
性格特点
</view>
<text class="card-item-desc">{{ activeRole.personality }}</text>
</view>
</view>
</view> -->
<view class="body-bottom">
<view class="body-bottom-btn" @click="toTip">开始陪练</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted,
computed
} from 'vue'
import {
onShow,
onLoad,
onUnload
} from '@dcloudio/uni-app';
import common from '@/common/common';
import ImagePreview from '@/components/image-preview/image-preview.vue';
import {
queryTraPartnerCharacterInfoList
} from "@/api/sparring.js"
import CardSwiper from './components/card-swiper.vue';
const traId = ref('')
const detailInfo = ref({})
const roleList = ref([])
const currentIndex = ref(0)
const activeRole = computed(()=>{
if(roleList.value.length>0){
return roleList.value[currentIndex.value]
}else{
return {}
}
})
const getDetailInfo = () => {
queryTraPartnerCharacterInfoList({
traId: traId.value
}).then(res => {
roleList.value = res.body || []
})
}
const onSwiperChange = (data) => {
console.log('轮播图切换:', e);
currentIndex.value = data.index;
}
const toTip = () => {
common.navigateTo('/pages/sparring/tip?traId=' + traId.value + '&roleId=' + activeRole.value.chaId)
}
onLoad((params) => {
traId.value = params.traId;
});
onMounted(() => {
getDetailInfo()
})
</script>
<style lang="scss" scoped>
.sparring-detail {
position: relative;
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: column;
background-image: url(@/static/images/sparring/bg.png);
background-size: cover;
.sparring-detail-body {
position: absolute;
overflow-x: hidden;
overflow-y: hidden;
left: 0;
top: 0;
min-height: 100%;
width: 100%;
box-sizing: border-box;
// background-color: #f6f8ff;
display: flex;
z-index: 1;
padding: calc(var(--status-bar-height) + 90rpx) 0 180rpx;
.top-box {
height: 90rpx;
width: 100%;
position: absolute;
top: var(--status-bar-height);
left: 0;
z-index: 2;
.top-title {
height: 90rpx;
// background-color: red;
text-align: center;
font-size: 33rpx;
padding-top: 10rpx;
line-height: 46rpx;
box-sizing: border-box;
font-weight: 500;
color: #fff;
position: relative;
// margin-bottom: 23rpx;
.arrow-icon {
position: absolute;
left: 36rpx;
top: 20rpx;
color: #fff !important;
}
.back-icon {
position: absolute;
width: 40rpx;
height: 40rpx;
cursor: pointer;
left: 50rpx;
top: 33rpx;
transform: translateY(-50%);
}
.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);
}
}
}
.body-swiper {
flex: 1;
&-title {
height: 90rpx;
line-height: 90rpx;
text-align: center;
font-size: 33rpx;
margin-bottom: 23rpx;
}
&-box {
height: calc(100% - 160rpx);
overflow: hidden;
}
}
// .body-card {
// flex: 1;
// .card {
// width: calc(100% - 90rpx);
// margin: 0 auto;
// height: 100%;
// border-radius: 24rpx;
// background: #e5eafd;
// overflow-x: hidden;
// overflow-Y: auto;
// transition: all 0.3s ease;
// position: relative;
// padding: 50rpx 23rpx 23rpx;
// .img-box {
// width: 300rpx;
// height: 300rpx;
// border-radius: 50%;
// overflow: hidden;
// margin: 0 auto;
// }
// .card-content {
// padding: 24rpx;
// }
// .card-title {
// display: flex;
// font-size: 29rpx;
// line-height: 44rpx;
// font-weight: 500;
// color: #000;
// overflow: hidden;
// position: relative;
// margin-bottom: 12rpx;
// text-align: center;
// margin: 33rpx auto;
// justify-content: center;
// &-name{
// margin-right: 20rpx;
// line-height: 44rpx;
// }
// &-age{
// margin-right: 20rpx;
// line-height: 44rpx;
// }
// &-img{
// height: 44rpx;
// width: 30rpx;
// padding: 7rpx 0;
// box-sizing: border-box;
// }
// &-icon{
// height: 30rpx;
// width: 30rpx;
// }
// }
// .card-item {
// width: 100%;
// display: block;
// background: linear-gradient(209deg, #fefeff 0%, #ddeafc 100%);
// border: 2rpx solid rgba(255, 255, 255, .65);
// margin-bottom: 15rpx;
// border-radius: 15rpx;
// box-sizing: border-box;
// padding: 23rpx;
// font-size: 28rpx;
// line-height: 40rpx;
// &-title{
// color: #06f;
// padding-left: 38rpx;
// position: relative;
// margin-bottom: 8rpx;
// &-icon{
// width: 30rpx;
// height: 30rpx;
// position: absolute;
// left: 0;
// top: 50%;
// transform: translateY(-50%);
// }
// }
// &-desc{
// }
// }
// }
// }
.body-bottom {
height: 180rpx;
// background-color: #fff;
padding: 36rpx 23rpx 0;
position: fixed;
width: 100vw;
bottom: 0;
left: 0;
&-btn {
width: 100%;
height: 92rpx;
line-height: 92rpx;
background-color: #06f;
text-align: center;
color: #fff;
font-size: 29rpx;
border-radius: 23rpx;
}
}
}
}
</style>
@@ -0,0 +1,267 @@
<template>
<view class="container">
<!-- 堆叠轮播区域 -->
<view class="swiper-wrapper">
<swiper class="stack-swiper" :current="currentIndex" :circular="circular" :previous-margin="previousMargin"
:next-margin="nextMargin" @change="onSwiperChange" :autoplay="autoplay" :interval="interval"
:duration="duration">
<swiper-item v-for="(item, index) in list" :key="index" class="swiper-item">
<view class="card" :class="{ 'active': currentIndex === index }" @click="onCardClick(item, index)">
<ImagePreview class="img-box" :src="item.ossAddr" :isCache="true">
</ImagePreview>
<view class="card-title">
<text class="card-title-name">{{ item.chaName }}</text>
<text class="card-title-age">{{ item.chaAge }}</text>
<text class="card-title-img">
<image class="card-title-icon" v-if="item.gender === 'm'" src="@/static/images/me/man.png"></image>
<image class="card-title-icon" v-if="item.gender === 'f'" src="@/static/images/me/wuman.png"></image>
</text>
</view>
<view class="card-item">
<view class="card-item-title">
<image class="card-item-title-icon" src="@/static/images/sparring/role-icon1.png"></image>
身份情况
</view>
<text class="card-item-desc">{{ item.occupation }}</text>
</view>
<view class="card-item">
<view class="card-item-title">
<image class="card-item-title-icon" src="@/static/images/sparring/role-icon2.png"></image>
性格特点
</view>
<text class="card-item-desc">{{ item.personality }}</text>
</view>
<view class="card-item">
<view class="card-item-title">
<image class="card-item-title-icon" src="@/static/images/sparring/role-icon3.png"></image>
提问思路
</view>
<text class="card-item-desc">{{ item.thoughtProcess }}</text>
</view>
<view class="card-item">
<view class="card-item-title">
<image class="card-item-title-icon" src="@/static/images/sparring/role-icon4.png"></image>
人物描述
</view>
<text class="card-item-desc">{{ item.chaDesc }}</text>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</template>
<script>
import ImagePreview from '@/components/image-preview/image-preview.vue';
export default {
name: 'CardSwiper',
components: {
ImagePreview
},
props: {
list: {
type: Array,
default: () => [{
image: 'https://picsum.photos/300/400?random=1',
title: '卡片标题1',
desc: '这是卡片描述信息1'
},
{
image: 'https://picsum.photos/300/400?random=2',
title: '卡片标题2',
desc: '这是卡片描述信息2'
},
{
image: 'https://picsum.photos/300/400?random=3',
title: '卡片标题3',
desc: '这是卡片描述信息3'
}
]
},
autoplay: {
type: Boolean,
default: false
},
interval: {
type: Number,
default: 3000
},
duration: {
type: Number,
default: 500
},
circular: {
type: Boolean,
default: true
}
},
data() {
return {
currentIndex: 0,
previousMargin: '64rpx',
nextMargin: '64rpx'
};
},
methods: {
onSwiperChange(e) {
this.currentIndex = e.detail.current;
this.$emit('changeChoose', {
index: this.currentIndex,
item: this.list[this.currentIndex]
});
},
onCardClick(item, index) {
this.$emit('cardClick', {
item: item,
index: index
});
if (this.currentIndex === index) return
this.swipeTo(index)
},
// 手动切换到指定索引
swipeTo(index) {
if (index >= 0 && index < this.list.length) {
this.currentIndex = index;
}
},
// 切换到下一张
next() {
if (this.currentIndex === index) return
let nextIndex = this.currentIndex + 1;
if (nextIndex >= this.list.length) {
nextIndex = this.circular ? 0 : this.list.length - 1;
}
this.swipeTo(nextIndex);
},
// 切换到上一张
prev() {
if (this.currentIndex === index) return
let prevIndex = this.currentIndex - 1;
if (prevIndex < 0) {
prevIndex = this.circular ? this.list.length - 1 : 0;
}
this.swipeTo(prevIndex);
}
}
};
</script>
<style scoped lang="scss">
.container {
padding: 0rpx 0;
height: 100%;
}
.swiper-wrapper {
position: relative;
height: 100%;
}
.stack-swiper {
height: 100%;
}
.swiper-item {
display: flex;
box-sizing: border-box;
justify-content: center;
align-items: center;
}
.card {
width: 590rpx;
height: 100%;
border-radius: 24rpx;
background: #e5eafd;
overflow-x: hidden;
overflow-Y: auto;
transition: all 0.3s ease;
position: relative;
padding: 50rpx 23rpx 23rpx;
.card-content {
padding: 24rpx;
}
.card-title {
display: flex;
font-size: 29rpx;
line-height: 44rpx;
font-weight: 500;
color: #000;
overflow: hidden;
position: relative;
margin-bottom: 12rpx;
text-align: center;
margin: 33rpx auto;
justify-content: center;
&-name{
margin-right: 20rpx;
line-height: 44rpx;
}
&-age{
margin-right: 20rpx;
line-height: 44rpx;
}
&-img{
height: 44rpx;
width: 30rpx;
padding: 7rpx 0;
box-sizing: border-box;
}
&-icon{
height: 30rpx;
width: 30rpx;
}
}
.card-item {
width: 100%;
display: block;
background: linear-gradient(209deg, #fefeff 0%, #ddeafc 100%);
border: 2rpx solid rgba(255, 255, 255, .65);
margin-bottom: 15rpx;
border-radius: 15rpx;
box-sizing: border-box;
padding: 23rpx;
font-size: 28rpx;
line-height: 40rpx;
&-title{
color: #06f;
padding-left: 38rpx;
position: relative;
margin-bottom: 8rpx;
&-icon{
width: 30rpx;
height: 30rpx;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
}
}
&-desc{
}
}
}
.card:not(.active) {
transform: scaleY(0.8) scaleX(1);
}
.img-box {
width: 300rpx;
height: 300rpx;
border-radius: 50%;
overflow: hidden;
margin: 0 auto;
}
</style>
@@ -5,31 +5,31 @@
<image
class="type-icon"
src="@/static/images/sparring/video-icon.png"
v-if="Number(itemInfo.accmStdyCnt)%3 === 0"
v-if="itemInfo.traInterTyp === '03'"
></image>
<image
class="type-icon"
src="@/static/images/sparring/phone-icon.png"
v-if="Number(itemInfo.accmStdyCnt)%3 === 1"
v-if="itemInfo.traInterTyp === '02'"
></image>
<image
class="type-icon"
src="@/static/images/sparring/talk-icon.png"
v-if="Number(itemInfo.accmStdyCnt)%3 === 2"
v-if="itemInfo.traInterTyp === '01'"
></image>
{{'视频对话'}}
{{itemInfo.traInterName}}
</view>
<ImagePreview class="img-box" :imgId="itemInfo.imgId" :isCache="true" width="225" height="170" mode="scaleToFill">
<ImagePreview class="img-box" :src="itemInfo.ossAddr" :isCache="true" width="225" height="170" mode="scaleToFill">
</ImagePreview>
<view class="item-title">{{itemInfo.crsName}}</view>
<view class="item-title">{{itemInfo.traName}}</view>
<scroll-view class="item-tags" :scroll-x="true">
<view class="item-tag-view">
<view :class="['item-tag', this.activeTag.includes(item.tagId)?'active-tag':'']" v-for="item in tagsShowList">
{{item.tagName}}</view>
</view>
</scroll-view>
<view class="item-publish">截至时间{{itemInfo.mtTime}}</view>
<view class="item-num">陪练次数{{itemInfo.accmStdyCnt}}人次</view>
<view class="item-publish">截至时间{{itemInfo.endTm}}</view>
<view class="item-num">陪练次数{{itemInfo.practiceTimes }}人次</view>
</view>
</view>
</template>
+402
View File
@@ -0,0 +1,402 @@
<template>
<view class="sparring-detail max_page">
<view class="sparring-detail-body">
<view class="top-box">
<view class="top-title">
<view class="back-icon" @click="common.navigateBack()"></view>
<!-- <uv-icon class="arrow-icon" name="arrow-left" @click="common.switchTab('/pages/index/index')" /> -->
陪练详情
</view>
<ImagePreview class="img-box" :src="detailInfo.ossAddr" :isCache="true" width="704" height="350" />
</view>
<view class="body-top">
<view class="body-top-title">
{{detailInfo.traName}}
</view>
<view class="body-top-item">
已练习次数{{detailInfo.practiceTimes}}
</view>
<view class="body-top-item">
类型{{detailInfo.traInterTypName}}
</view>
<view class="body-top-item">
截止时间{{detailInfo.endTm}}
</view>
<scroll-view class="body-top-tags" :scroll-x="true">
<view class="item-tag-view">
<view class="item-tag active-tag" v-for="item in tagsShowList">
{{item.tagName}}
</view>
</view>
</scroll-view>
</view>
<view class="body-item">
<view class="body-item-title">
<image class="title-icon" src="@/static/images/sparring/detail-icon-1.png"></image>
陪练介绍
</view>
<view class="body-item-detail">
{{detailInfo.introduction}}
</view>
</view>
<view class="body-item">
<view class="body-item-title">
<image class="title-icon" src="@/static/images/sparring/detail-icon-2.png"></image>
陪练目标
</view>
<view class="body-item-detail">
{{detailInfo.traTarget}}
</view>
</view>
<view class="body-item">
<view class="body-item-title">
<image class="title-icon" src="@/static/images/sparring/detail-icon-3.png"></image>
陪练预计时长
</view>
<view class="body-item-detail">
{{detailInfo.traStartLimit+ ' ~ ' + detailInfo.traEndLimit}} 分钟
</view>
</view>
<view class="body-item">
<view class="body-item-title">
<image class="title-icon" src="@/static/images/sparring/detail-icon-4.png"></image>
陪练参考资料
</view>
<view class="body-item-detail">
<view class="body-item-detail-file" v-for="item in 6" @click="previewFile(item)">
文件名称{{item}}.docx
</view>
</view>
</view>
<view class="body-bottom">
<view class="body-bottom-btn" @click="toChooseRole">AI陪练</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted
} from 'vue'
import {
onShow,
onLoad,
onUnload
} from '@dcloudio/uni-app';
import common from '@/common/common';
import ImagePreview from '@/components/image-preview/image-preview.vue';
import {
queryTraPartnerInfoById
} from "@/api/sparring.js"
import {
getPhoneEnvBool
} from '@/common/common';
const traId = ref('')
const detailInfo = ref({})
const isDownloading = ref(false)
const tagsShowList = ref([{
tagName: '公金业务'
}, {
tagName: '初级'
}, {
tagName: '销售类'
}, {
tagName: '公金业务'
}, {
tagName: '初级'
}, {
tagName: '销售类'
}, {
tagName: '公金业务'
}, {
tagName: '初级'
}, {
tagName: '销售类'
}])
const getDetailInfo = () => {
queryTraPartnerInfoById({
id: traId.value
}).then(res => {
console.log(res)
detailInfo.value = res.body || {}
})
}
const previewFile = (info) => {
if (getPhoneEnvBool('AND')) {
common.navigateTo('/pages/index/preview?fileId=' + info.docId);
} else {
if (isDownloading.value) return
const base_url = get_base_url('/traask/traFilePreview/');
let fileUrl = base_url + '/traask/traFilePreview/' + info.docId + userParams;
const localFilePath = '_doc/pdf_view/' + info.docName + '.pdf';
isDownloading.value = true
downloadFileFnc(fileUrl, localFilePath).then((res) => {
uni.openDocument({
filePath: res,
showMenu: true,
success: function() {
console.log('打开文档成功');
}
})
}).finally(() => {
setTimeout(() => {
isDownloading.value = false
}, 1000)
});
}
}
const downloadFileFnc = async (fileUrl, localFilePath) => {
try {
//#ifdef APP-PLUS
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();
});
// #endif
//#ifdef H5
return new Promise((resolve, reject) => {
resolve();
});
// #endif
} catch (error) {
console.error('创建目录失败:', error);
throw error;
}
};
const toChooseRole = () => {
common.navigateTo('/pages/sparring/chooseRole?traId=' + traId.value)
}
onLoad((params) => {
traId.value = params.traId;
});
onMounted(() => {
getDetailInfo()
})
</script>
<style lang="scss" scoped>
.sparring-detail {
position: relative;
overflow-x: hidden;
overflow-y: auto;
.sparring-detail-body {
position: absolute;
overflow-x: hidden;
overflow-y: hidden;
left: 0;
top: 0;
min-height: 100%;
width: 100%;
box-sizing: border-box;
background-color: #f6f8ff;
z-index: 1;
padding: calc(var(--status-bar-height) + 336rpx) 23rpx 180rpx;
.top-box {
background-image: url(@/static/images/sparring/top-bg.png);
background-repeat: no-repeat;
background-size: cover;
height: 436rpx;
clip-path: ellipse(650rpx 327rpx at 375rpx 109rpx);
width: 100%;
position: absolute;
top: var(--status-bar-height);
left: 0;
z-index: 2;
.img-box {
width: 704rpx;
height: 350rpx;
margin: 0 auto;
border-radius: 23rpx;
overflow: hidden;
}
.top-title {
height: 90rpx;
// background-color: red;
text-align: center;
font-size: 33rpx;
padding-top: 10rpx;
line-height: 46rpx;
box-sizing: border-box;
font-weight: 500;
color: #fff;
position: relative;
// margin-bottom: 23rpx;
.arrow-icon {
position: absolute;
left: 36rpx;
top: 20rpx;
color: #fff !important;
}
.back-icon {
position: absolute;
width: 40rpx;
height: 40rpx;
cursor: pointer;
left: 50rpx;
top: 33rpx;
transform: translateY(-50%);
}
.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 #ffffff;
border-left: 4rpx solid #ffffff;
transform: rotate(-45deg);
}
}
}
.body-top {
background-color: #fff;
border-radius: 23rpx;
padding: 120rpx 28rpx 20rpx;
margin-bottom: 30rpx;
&-title {
min-height: 50rpx;
padding: 4rpx 0;
line-height: 42rpx;
font-size: 32rpx;
margin-bottom: 12rpx;
font-weight: 500;
}
&-item {
height: 35rpx;
white-space: nowrap;
line-height: 35rpx;
font-size: 25rpx;
color: #666;
display: inline-block;
width: 50%;
margin-bottom: 15rpx;
}
&-tags {
margin-bottom: 0rpx;
box-sizing: border-box;
padding-top: 5rpx;
.item-tag-view {
white-space: nowrap;
height: 50rpx;
.item-tag {
height: 38rpx;
line-height: 34rpx;
box-sizing: border-box;
font-size: 20rpx;
background-color: #fff;
color: #06f;
border: 2rpx solid #06f;
padding: 0 20rpx;
border-radius: 8rpx;
margin-right: 20rpx;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200rpx;
box-sizing: border-box;
&.active-tag {
color: #ffa150;
border: 2rpx solid #ffa150;
}
}
}
}
}
.body-bottom {
height: 180rpx;
background-color: #fff;
padding: 36rpx 23rpx 0;
position: fixed;
width: 100vw;
bottom: 0;
left: 0;
&-btn {
width: 100%;
height: 92rpx;
line-height: 92rpx;
background-color: #06f;
text-align: center;
color: #fff;
font-size: 29rpx;
border-radius: 23rpx;
}
}
.body-item {
padding: 28rpx;
background-color: #fff;
margin-bottom: 30rpx;
border-radius: 23rpx;
&-title {
position: relative;
font-size: 29rpx;
line-height: 40rpx;
padding-left: 56rpx;
margin-bottom: 23rpx;
.title-icon {
position: absolute;
width: 38rpx;
height: 38rpx;
left: 0;
top: 1rpx;
}
}
&-detail {
font-size: 28rpx;
line-height: 52rpx;
color: #333;
&-file {
color: #06f;
}
}
}
}
}
</style>
+102 -40
View File
@@ -14,13 +14,14 @@
</uv-input>
</view>
<view class="tabs-view">
<uv-tabs :list="tabList" @click="changeTab" :scrollable="false" active-style="font-weight:bold; color:#000;"
<uv-tabs :list="tabList" @click="changeTab" :scrollable="true" active-style="font-weight:bold; color:#000;"
class="font_pf my_tabs" :inactive-style="{ color: '#333333', fontSize: '30rpx', fontWeight: '500' }"
:activeStyle="{ color: '#333333', fontSize: '30rpx', fontWeight: '600' }"></uv-tabs>
<uv-tabs :list="tabList" @click="changeTabSec" :scrollable="true" active-style="font-weight:bold; color:#000;"
<uv-tabs :list="tabSecList" @click="changeTabSec" :scrollable="true" active-style="font-weight:bold; color:#000;"
class="font_pf my_tabs2" :inactive-style="{ color: '#333333', fontSize: '24rpx', fontWeight: '500' }"
:activeStyle="{ color: '#0066FF', fontSize: '24rpx', fontWeight: '500' }"
:lineColor="`url(${LineBg}) 2% 2%`"></uv-tabs>
:activeStyle="{ color: '#0066FF', fontSize: '24rpx', fontWeight: '500' }" :lineColor="`#06f`"
></uv-tabs>
<!-- :lineColor="`url(${LineBg}) 2% 2%`" -->
</view>
<view class="list-view">
<view class="filter-btn" @click="
@@ -68,7 +69,7 @@
<scroll-view :class="['right-sparring-list', getActiveItemClass()]" :scroll-y="true"
@scrolltolower="getMoreList()">
<view class="sparring-item-box" v-for="item in sparringList">
<SparringItem :itemInfo="item" :activeTag="activeTag" @clickItem="toCourseDetail(item)" />
<SparringItem :itemInfo="item" :activeTag="activeTag" @clickItem="toSparringDetail(item)" />
</view>
<view class="loading-box" v-show="loading">
<CommonLoading color="#06f" class="loading-val" />
@@ -89,7 +90,7 @@
} from '@/enum.js';
import {
getTraTagListApi,
getCourseTypeListApi,
queryTraPartnerCatelogList,
queryCrsInfoByCatalogApi
} from '@/api/sparring.js'
import {
@@ -123,19 +124,52 @@
backgroundColor: '#ffffff',
borderRadius: '23rpx'
}
const tabList = [{
name: '全部'
}, {
name: '热门'
}, {
name: '最新'
}, {
name: '推荐'
}, {
name: '生产线'
}]
const catelogList = ref([])
const tabList = computed(()=>{
return [{
name: '全 部',
value: ''
}, ...catelogList.value.map(t=>{
return {
name: t.label,
value: t.value
}
})]
})
const tabSecList = computed(()=> {
let _arr = []
if(!activeTabId.value){
catelogList.value.forEach(t=>{
let _children = (t.children||[])
_arr = _arr.concat(_children.map(k=>{
return {
name: k.label,
value: k.value
}
}))
})
}else{
let _obj = catelogList.value.find(t=>t.value === activeTabId.value)
if(_obj){
let _children = (_obj.children||[])
_arr = _children.map(k=>{
return {
name: k.label,
value: k.value
}
})
}
}
return [{
name: '全 部',
value: ''
}, ..._arr]
})
const activeTab = ref('全部')
const activeCourseType = ref('')
const activeTabId= ref('')
const activeTabSec = ref('全部')
const activeTabSecId= ref('')
const activeCourseType = computed(()=> (activeTabSecId.value || activeTabId.value || ''))
const activePrdlineType = ref('')
const activeTag = ref([])
const sparringTypeList = ref([])
@@ -197,15 +231,6 @@
getMoreList(1)
}
// 点击课程分类
const changeCourseType = (item, index) => {
activeCourseType.value = item.value === activeCourseType.value ? '' : item.value
activeLeftIndex.value = index
getMoreList(1)
}
const showTypeName = computed(() => {
return (activeTab.value === '全部' && activeCourseType.value === '') || (activeTab.value === '生产线' &&
activePrdlineType.value === '')
})
const tagsList = computed(() => {
let _arr = []
tagsTree.value.forEach(t => {
@@ -213,7 +238,7 @@
_arr = _arr.concat(_tarr)
})
let _reqList = _arr.filter(t => t.required === 'Y')
let _reqListN = _arr.filter(t => t.required === 'N')
let _reqListN = _arr.filter(t => t.required !== 'N')
return _reqList.concat(_reqListN)
})
const changeChooseTagsList = () => {
@@ -262,16 +287,19 @@
if (!item) return
sparringListBase.value = []
activeTab.value = item.name
activeTabId.value = item.value || ''
activeLeftIndex.value = -1
activeTag.value = []
pageObj.value.page = 1
pageObj.value.total = 0
// activeTag.value = ''
activeCourseType.value = ''
activePrdlineType.value = ''
loading.value = true
queryCrsInfoByCatalogApi(pageObj.value)
queryCrsInfoByCatalogApi({
...pageObj.value,
catalogId: activeCourseType.value
})
.then(res => {
sparringListBase.value = res.body || []
pageObj.value.total = res.total
@@ -280,21 +308,41 @@
loading.value = false
})
}
const changeTabSec = () => {
const changeTabSec = (item) => {
sparringListBase.value = []
activeLeftIndex.value = -1
activeTabSec.value = item.name
activeTabSecId.value = item.value || ''
activeTag.value = []
pageObj.value.page = 1
pageObj.value.total = 0
// activeTag.value = ''
activePrdlineType.value = ''
loading.value = true
queryCrsInfoByCatalogApi({
...pageObj.value,
catalogId: activeCourseType.value
})
.then(res => {
sparringListBase.value = res.body || []
pageObj.value.total = res.total
})
.finally(() => {
loading.value = false
})
}
// 跳转搜索页
const toSearchPage = () => {
common.navigateTo('/pages/search/search?from=sparring')
}
// 跳转 课程详情
const toCourseDetail = item => {
common.navigateTo('/pages/sparringDetail/index?crsNum=' + item.crsNum + '&crsId=' + item.crsId)
// common.navigateTo('/pages/sparring/study?crsNum=' + item.crsNum)
const toSparringDetail = item => {
common.navigateTo('/pages/sparring/detail?traId=' + item.traId)
}
onMounted(() => {
getCourseTypeListApi().then(res => {
sparringTypeList.value = res.body || []
queryTraPartnerCatelogList().then(res => {
catelogList.value = res.body || []
})
getTraTagListApi().then(res => {
tagsTree.value = res.body || []
@@ -329,14 +377,28 @@
</script>
<style lang="scss" scoped>
.my_tabs2 {
.my_tabs {
// 解决这个圆角的图片,开穿透
:deep(.uv-tabs__wrapper__nav__line) {
height: 30rpx !important;
left: 42rpx;
// height: 30rpx !important;
// left: 60rpx;
// background-size: 24rpx !important;
// background-repeat: no-repeat !important;
background: linear-gradient(90deg, #06f 0%, rgb(0,159,255) 50%, rgb(255,255,255) 100%) !important;
}
:deep(.uv-tabs__wrapper__nav__item) {
padding: 0 30rpx !important;
}
}
.my_tabs2 {
// 解决这个圆角的图片,开穿透
:deep(.uv-tabs__wrapper__nav__line) {
// display: none;
// height: 30rpx !important;
background-size: 24rpx !important;
background-repeat: no-repeat !important;
// background: linear-gradient(90deg, #06f 0%, rgb(0,159,255) 50%, rgb(255,255,255) 100%) !important;
}
:deep(.uv-tabs__wrapper__nav__item) {
+175
View File
@@ -0,0 +1,175 @@
<template>
<view class="sparring-detail max_page">
<view class="sparring-detail-body">
<view class="top-box">
<view class="top-title">
<view class="back-icon" @click="common.navigateBack()"></view>
{{ '陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称陪练名称' }}
</view>
</view>
<view class="body-bottom">
<view class="body-bottom-btn" @click="toTip">开始陪练</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted,
computed
} from 'vue'
import {
onShow,
onLoad,
onUnload
} from '@dcloudio/uni-app';
import common from '@/common/common';
import ImagePreview from '@/components/image-preview/image-preview.vue';
import {
queryTraPartnerCharacterInfoList
} from "@/api/sparring.js"
import CardSwiper from './components/card-swiper.vue';
const traId = ref('')
const detailInfo = ref({})
const roleList = ref([])
const currentIndex = ref(0)
const activeRole = computed(()=>{
if(roleList.value.length>0){
return roleList.value[currentIndex.value]
}else{
return {}
}
})
const getDetailInfo = () => {
queryTraPartnerCharacterInfoList({
traId: traId.value
}).then(res => {
roleList.value = res.body || []
})
}
const onSwiperChange = (data) => {
console.log('轮播图切换:', e);
currentIndex.value = data.index;
}
const toTip = () => {
common.navigateTo('/pages/sparring/tip?traId=' + traId.value + '&roleId=' + activeRole.value.chaId)
}
onLoad((params) => {
traId.value = params.traId;
});
onMounted(() => {
getDetailInfo()
})
</script>
<style lang="scss" scoped>
.sparring-detail {
position: relative;
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: column;
background-image: url(@/static/images/sparring/bg.png);
background-size: cover;
.sparring-detail-body {
position: absolute;
overflow-x: hidden;
overflow-y: hidden;
left: 0;
top: 0;
min-height: 100%;
width: 100%;
box-sizing: border-box;
// background-color: #f6f8ff;
display: flex;
z-index: 1;
padding: calc(var(--status-bar-height) + 90rpx) 0 180rpx;
.top-box {
height: 90rpx;
width: 100%;
position: absolute;
top: var(--status-bar-height);
left: 0;
z-index: 2;
.top-title {
height: 90rpx;
// background-color: red;
text-align: center;
font-size: 33rpx;
padding: 10rpx 100rpx 0;
line-height: 46rpx;
box-sizing: border-box;
font-weight: 500;
color: #000;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
// margin-bottom: 23rpx;
.arrow-icon {
position: absolute;
left: 36rpx;
top: 20rpx;
color: #fff !important;
}
.back-icon {
position: absolute;
width: 40rpx;
height: 40rpx;
cursor: pointer;
left: 50rpx;
top: 33rpx;
transform: translateY(-50%);
}
.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);
}
}
}
.body-bottom {
height: 180rpx;
// background-color: #fff;
padding: 36rpx 23rpx 0;
position: fixed;
width: 100vw;
bottom: 0;
left: 0;
&-btn {
width: 100%;
height: 92rpx;
line-height: 92rpx;
background-color: #06f;
text-align: center;
color: #fff;
font-size: 29rpx;
border-radius: 23rpx;
}
}
}
}
</style>
+27
View File
@@ -63,5 +63,32 @@
}
}
},
{
"path": "pages/sparring/detail",
"style": {
"navigationBarTitleText": "AI陪练详情",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/sparring/tip",
"style": {
"navigationBarTitleText": "AI陪练提示",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/sparring/chooseRole",
"style": {
"navigationBarTitleText": "AI陪练选择角色",
"app-plus": {
"titleNView": false
}
}
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB