Files
tra-app/src/pages/sparring/index.vue
T
2025-12-02 16:27:10 +08:00

819 lines
20 KiB
Vue

<template>
<view class="sparring-center max_page">
<view class="top-bg"></view>
<view class="bot-bg"></view>
<view class="sparring-center-body" v-if="!reSeting">
<view class="body-title">
<!-- @click="test.testApi" -->
<uv-icon class="arrow-icon" name="arrow-left" @click="common.switchTab('/pages/index/index')" />
AI陪练
</view>
<view class="input-view" @click="toSearchPage">
<uv-input shape="circle" placeholderStyle="color:#999999;font-size:28rpx;" placeholder="请输入" suffixIcon="search"
:customStyle="customStyle" :suffixIconStyle="suffixIconStyle" :readonly="true">
</uv-input>
</view>
<view class="tabs-view">
<uv-tabs :list="tabList" @click="changeTab" :current="currentTab" :scrollable="true" 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="tabSecList" @click="changeTabSec" :current="currentTabSec" :scrollable="true"
v-if="!resetingSec" class="font_pf my_tabs2"
:inactive-style="{ color: '#333333', fontSize: '24rpx', fontWeight: '500' }"
: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="
() => {
showFilterList = !showFilterList
activeTagPop = activeTag
}
">
<!-- 筛选 -->
<!-- <uv-icon class="filter-icon" name="list" /> -->
<image class="filter-icon" src="@/static/images/course/filter.png"></image>
</view>
<view :class="['filter-list', showFilterList ? 'show' : '']">
<scroll-view :scroll-y="true" class="scroll-box-tags">
<view class="filter-type" v-for="item in tagsTree" :key="item.value">
<!-- .filter(t=>(t.children||[]).length>0) -->
<view class="filter-type-title">{{ item.label }}</view>
<scroll-view :scroll-x="true" class="filter-type-list">
<view :class="['filter-type-item', activeTagPop.includes(itemT.value) ? 'is-active' : '']"
v-for="itemT in item.children || []" @click="chooseTagPop(itemT)">
{{ itemT.label }}
</view>
</scroll-view>
</view>
</scroll-view>
<view class="btns-box">
<view class="btn-sub cencle-btn" @click="showFilterList = false">取消</view>
<view class="btn-sub" @click="changeChooseTagsList">筛选</view>
</view>
</view>
<view :class="['model-filter-list', showFilterList ? 'show' : '']" @click="showFilterList = false"></view>
<uv-scroll-list class="tags-view" :indicator="false">
<uv-tags text="全部" bgColor="#f6f8fa" :borderColor="activeTag.length === 0 ? '#06f' : '#f6f8fa'"
:color="activeTag.length === 0 ? '#06f' : '#333'"
:class="activeTag.length === 0 ? 'tags-item active-tags-item' : 'tags-item'" size="medium"
:type="activeTag.length === 0 ? 'primary' : 'info'" @click="chooseTag({ value: '' })"></uv-tags>
<uv-tags :text="item.label" v-for="item in tagsList" :key="item.value" size="medium" :bgColor="'#f6f8fa'"
:borderColor="activeTag.includes(item.value) ? '#06f' : '#f6f8fa'"
:color="activeTag.includes(item.value) ? '#06f' : '#333'" @click="chooseTag(item)"
:class="activeTag.includes(item.value) ? 'tags-item active-tags-item' : 'tags-item'"></uv-tags>
</uv-scroll-list>
<view class="sparring-list">
<view class="sparring-list-body">
<scroll-view :class="['right-sparring-list']" :scroll-y="true" @scrolltolower="getMoreList()"
:refresher-enabled="refresherEnabled" refresher-default-style="none" :refresher-triggered="triggered"
@refresherrefresh="changeTab({
name: activeTab.value || '全部',
value: activeCourseType.value || ''
})">
<!-- <view class="all-item"> -->
<view class="left-items">
<view class="sparring-item-box" v-for="item in leftsparringList">
<SparringItem :itemInfo="item" :activeTag="activeTag" @clickItem="toSparringDetail(item)" />
</view>
</view>
<view class="right-items">
<view class="sparring-item-box" v-for="item in rightsparringList">
<SparringItem :itemInfo="item" :activeTag="activeTag" @clickItem="toSparringDetail(item)" />
</view>
</view>
<!-- </view> -->
<view class="loading-box" v-show="loading">
<CommonLoading color="#06f" class="loading-val" />
</view>
<list-no-data v-show="sparringList.length == 0 && !loading">暂无数据</list-no-data>
</scroll-view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import SparringItem from './components/sparring-item.vue'
import common from '@/common/common'
import {
LineBg
} from '@/enum.js';
import {
getTraTagListApi,
queryTraPartnerCatelogList,
queryCrsInfoByCatalogApi,
checkTraPartnerInfoById
} from '@/api/sparring.js'
import {
onMounted,
onActivated,
ref,
computed,
onUnmounted
} from 'vue'
import {
onShow,
onHide
} from '@dcloudio/uni-app'
import {
useSocketStore
} from '@/store/socket.js'
const socketStore = useSocketStore()
const customStyle = {
backgroundColor: '#fff',
paddingLeft: '40rpx',
height: '60rpx',
paddingTop: '5px',
paddingBottom: '5px',
border: 'none'
}
const suffixIconStyle = {
color: '#2c8ef2',
fontSize: '40rpx'
}
const tabsCustomStyle = {
backgroundColor: '#ffffff',
borderRadius: '23rpx'
}
const catelogList = ref([])
const currentTab = ref(0)
const currentTabSec = ref(0)
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 activeTabId = ref('')
const activeTabSec = ref('全部')
const activeTabSecId = ref('')
const activeCourseType = computed(() => (activeTabSecId.value || activeTabId.value || ''))
const activePrdlineType = ref('')
const activeTag = ref([])
const sparringTypeList = ref([])
const sparringPrdlineList = ref([])
const tagsTree = ref([])
const showFilterList = ref(false)
const {
statusBarHeight
} = uni.getWindowInfo()
const pageTopPadding = computed(() => statusBarHeight + 'px')
const activeTagPop = ref([])
const chooseTagPop = item => {
if (!item.value) {
activeTagPop.value = []
return
}
activeTagPop.value = activeTagPop.value.filter(t => !!t)
if (activeTagPop.value.includes(item.value)) {
activeTagPop.value = activeTagPop.value.filter(t => t !== item.value)
} else {
activeTagPop.value.push(item.value)
}
}
const triggered = ref(false)
// 点击标签
const chooseTag = item => {
if (!item.value) {
activeTag.value = []
} else {
activeTag.value = activeTag.value.filter(t => !!t)
if (activeTag.value.includes(item.value)) {
activeTag.value = activeTag.value.filter(t => t !== item.value)
} else {
activeTag.value.push(item.value)
}
}
pageObj.value.page = 1
pageObj.value.total = 0
getMoreList(1)
// showFilterList.value = false
}
const activeLeftIndex = ref(-1)
// 点击课程分类
const tagsList = computed(() => {
let _arr = []
tagsTree.value.forEach(t => {
let _tarr = t.children || []
_arr = _arr.concat(_tarr)
})
let _reqList = _arr.filter(t => t.required === 'Y')
let _reqListN = _arr.filter(t => t.required !== 'N')
return _reqList.concat(_reqListN)
})
const changeChooseTagsList = () => {
showFilterList.value = false
activeTag.value = activeTagPop.value
pageObj.value.page = 1
pageObj.value.total = 0
getMoreList(1)
}
const sparringListBase = ref([])
const sparringList = computed(() => {
return sparringListBase.value
})
const leftsparringList = computed(() => {
return sparringList.value.filter((t, index) => index % 2 === 0)
})
const rightsparringList = computed(() => {
return sparringList.value.filter((t, index) => index % 2 === 1)
})
const pageObj = ref({
page: 1,
limit: 20,
total: 0
})
const getMoreList = flag => {
if (loading.value) return
if (!flag) {
if (pageObj.value.total <= pageObj.value.page * pageObj.value.limit) return
pageObj.value.page += 1
} else {
sparringListBase.value = []
pageObj.value.page = 1
}
pageObj.value.total = 0
loading.value = true
queryCrsInfoByCatalogApi({
...pageObj.value,
tagId: activeTag.value.join(','),
catalogId: activeCourseType.value
})
.then(res => {
sparringListBase.value = sparringListBase.value.concat(res.body || [])
pageObj.value.total = res.total
})
.finally(() => {
loading.value = false
})
}
const loading = ref(false)
const refresherEnabled = ref(true)
const changeTab = item => {
if (!item) return
resetingSec.value = true
setTimeout(() => {
resetingSec.value = false
triggered.value = true
refresherEnabled.value = false
sparringListBase.value = []
currentTabSec.value = 0
activeTab.value = item.name
activeTabId.value = item.value || ''
activeTabSec.value = "全部"
activeTabSecId.value = ''
activeLeftIndex.value = -1
activeTag.value = []
pageObj.value.page = 1
pageObj.value.total = 0
// activeTag.value = ''
activePrdlineType.value = ''
loading.value = true
console.log(111)
queryCrsInfoByCatalogApi({
...pageObj.value,
catalogId: activeCourseType.value
})
.then(res => {
sparringListBase.value = res.body || []
pageObj.value.total = res.total
})
.finally(() => {
loading.value = false
triggered.value = false
refresherEnabled.value = true
})
})
}
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 toSparringDetail = item => {
if (item.traId) {
checkTraPartnerInfoById({
traId: item.traId
}).then(res => {
if (res.body.inRange) {
common.navigateTo('/pages/sparring/detail?traId=' + item.traId)
} else {
common.show('提示', res.body.message, false)
}
})
}
}
const reSeting = ref(false)
const resetingSec = ref(false)
onMounted(() => {
console.log('onMounted')
reSeting.value = true
setTimeout(() => {
reSeting.value = false
})
currentTab.value = 0
currentTabSec.value = 0
activeTabId.value = ''
activeTabSecId.value = ''
queryTraPartnerCatelogList().then(res => {
catelogList.value = res.body || []
})
getTraTagListApi().then(res => {
tagsTree.value = res.body || []
})
changeTab({
name: '全部',
value: ''
})
uni.$on('refresh_sparring_list', () => {
changeTab({
name: activeTab.value || '全部',
value: activeCourseType.value || ''
})
})
})
onUnmounted(() => {
uni.$off('refresh_sparring_list')
})
onActivated(() => {
console.log('onActivated')
reSeting.value = true
setTimeout(() => {
reSeting.value = false
})
currentTab.value = 0
currentTabSec.value = 0
activeTabId.value = ''
activeTabSecId.value = ''
// 重新获取标签信息
getTraTagListApi().then(res => {
tagsTree.value = res.body || []
})
changeTab({
name: '全部',
value: ''
})
})
onShow(() => {
console.log('onShow')
activeTag.value = []
getMoreList(1)
socketStore.closeSocket()
uni.$on('refresh_sparring_list', () => {
changeTab({
name: '全部',
value: ''
})
})
})
onHide(() => {
uni.$off('refresh_sparring_list')
})
</script>
<style lang="scss" scoped>
.my_tabs {
// 解决这个圆角的图片,开穿透
:deep(.uv-tabs__wrapper__nav__line) {
// 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) {
padding: 0 30rpx !important;
}
}
.sparring-center {
position: relative;
display: flex;
flex-direction: column;
.top-bg {
background-color: #fff;
background: linear-gradient(16deg, rgba(234, 246, 255, 0) 0%, #c3e6ff 100%);
height: 480rpx;
width: 100%;
}
.bot-bg {
background-color: #fff;
background: linear-gradient(136deg, rgba(234, 246, 255, 0) 0%, #c3e6ff 100%);
width: 100%;
flex: 1;
}
.sparring-center-body {
position: absolute;
overflow: hidden;
left: 0;
top: 0;
height: 100%;
width: 100%;
padding: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
padding-top: v-bind(pageTopPadding);
// padding-top: var(--status-bar-height);
.body-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: #000;
position: relative;
// margin-bottom: 23rpx;
.arrow-icon {
position: absolute;
left: 36rpx;
top: 20rpx;
color: #000;
}
}
.input-view {
padding: 0 15rpx;
}
.tabs-view {
background: #ffffff;
border-radius: 23rpx 23rpx 0 0;
padding: 6rpx 10rpx 12rpx;
margin: 20rpx 15rpx 0;
}
.list-view {
flex: 1;
overflow: hidden;
// margin-bottom: 15rpx;
margin: 0rpx 15rpx 0;
background: #ffffff;
// border-radius: 23rpx;
display: flex;
flex-direction: column;
position: relative;
.filter-list {
position: absolute;
top: 84rpx;
left: 0;
width: 100%;
height: 588rpx;
padding-bottom: 80rpx;
overflow-y: hidden;
z-index: 15;
background-color: #f9f9f9;
box-shadow: 0rpx 12rpx 20rpx rgba(160, 160, 160, 0.25);
border-radius: 0 0 0rpx 0rpx;
box-sizing: border-box;
position: relative;
.scroll-box-tags {
height: 508rpx;
padding: 0rpx 25rpx;
box-sizing: border-box;
}
.filter-type {
padding: 10rpx 0;
box-sizing: border-box;
.filter-type-title {
font-size: 29rpx;
line-height: 40rpx;
margin-bottom: 15rpx;
color: #000;
}
.filter-type-list {
overflow: hidden;
.filter-type-item {
// width: calc(25% - 18rpx);
display: inline-block;
font-size: 29rpx;
box-sizing: border-box;
padding: 0 20rpx;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
text-align: center;
float: left;
margin-right: 24rpx;
height: 65rpx;
line-height: 65rpx;
background-color: #f1f3f8;
border-radius: 6rpx;
margin-bottom: 20rpx;
&:nth-child(4n) {
margin-right: 24rpx;
}
&.is-active {
color: #06f;
}
}
}
&+.filter-type {
margin-top: 20rpx;
}
}
.btns-box {
height: 80rpx;
position: absolute;
background-color: #fff;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
padding-top: 10rpx;
.btn-sub {
display: inline-block;
text-align: center;
background-color: #06f;
height: 60rpx;
line-height: 58rpx;
border: 1rpx solid #06f;
padding: 0 40rpx;
color: #fff;
border-radius: 8rpx;
&.cencle-btn {
background-color: #fff;
color: #06f;
margin-right: 40rpx;
}
}
}
}
.model-filter-list {
position: fixed;
width: calc(100vw - 33rpx);
height: 80vh;
overflow: hidden;
top: 500rpx;
left: 17rpx;
z-index: 1;
background-color: rgba(0, 0, 0, 0.2);
}
.filter-list,
.model-filter-list {
display: none;
&.show {
display: block;
}
}
.filter-btn {
position: absolute;
right: 0;
top: 0;
height: 83rpx;
width: 80rpx;
line-height: 83rpx;
font-size: 26rpx;
background: #fff;
z-index: 2;
text-align: center;
box-shadow: -4rpx 0 6rpx rgba(0, 0, 0, 0.05);
white-space: nowrap;
// padding-right: 30rpx;
box-sizing: border-box;
.filter-icon {
// display: inline-block;
width: 32rpx;
height: 32rpx;
font-size: 26rpx;
position: absolute;
right: 24rpx;
top: 50%;
transform: translateY(-50%);
}
}
.tags-view {
height: 84rpx;
display: block;
padding: 15rpx 80rpx 15rpx 20rpx;
box-sizing: border-box;
white-space: nowrap;
// border-bottom: 2rpx solid #eee;
.tags-item {
margin-right: 30rpx;
display: inline-block;
&.active-tags-item {
position: relative;
overflow: hidden;
border-radius: 4rpx;
overflow: hidden;
&::before {
content: '';
display: block;
width: 40rpx;
height: 40rpx;
position: absolute;
right: -20rpx;
bottom: -20rpx;
transform: rotateZ(-45deg);
background-color: #06f;
overflow: hidden;
z-index: 10;
}
&::after {
content: '';
display: block;
width: 8rpx;
height: 4rpx;
position: absolute;
right: 3rpx;
bottom: 8rpx;
transform: rotateZ(-45deg);
border-left: 1px solid #fff;
border-bottom: 1px solid #fff;
z-index: 11;
}
}
::v-deep .uv-tags {
padding: 0 20rpx;
.uv-tags__text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200rpx;
}
}
}
}
.sparring-list {
display: flex;
flex: 1;
overflow: hidden;
background-color: #f1f5fa;
margin-top: 12rpx;
.sparring-list-body {
height: 100%;
display: flex;
flex: 1;
overflow: hidden;
.right-sparring-list {
flex: 1;
overflow-y: auto;
background-color: #fff;
// margin: 0 10rpx;
&.no-radius {
border-radius: 0rpx 0 0 0;
}
.left-items {
width: 50%;
float: left;
overflow: hidden;
}
.right-items {
width: 50%;
float: left;
overflow: hidden;
}
.sparring-item-box {
// width: 50%;
width: 100%;
float: left;
}
}
}
}
}
.loading-box {
width: 100%;
height: 100rpx;
position: relative;
.loading-val {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
}
</style>