修复若干bug

This commit is contained in:
丁杰 2026-06-04 11:45:11 +08:00
parent fc3bd046e9
commit d8d6be6206
5 changed files with 2125 additions and 1763 deletions

View File

@ -1,212 +1,228 @@
<template> <template>
<view class="brand-card-comp" @click="onClick"> <view class="brand-card-comp" @click="onClick">
<image class="brand-bg" :src="item.bgSrc" mode="aspectFill"></image> <image class="brand-bg" :src="item.bgSrc" mode="aspectFill"></image>
<image class="mask-bg" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/9bca353c-fa74-407e-a69e-6f5e9aa43955.png" mode="widthFix"></image> <image class="mask-bg"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/9bca353c-fa74-407e-a69e-6f5e9aa43955.png"
<view class="card-content"> mode="widthFix"></image>
<view class="avatar-wrapper" style="border-color: #F7E5D7;">
<image class="avatar-img" :src="item.avatar || 'https://dummyimage.com/100x100/ccc/fff.png&text=logo'"></image> <view class="card-content">
</view> <view class="avatar-wrapper" style="border-color: #F7E5D7;">
<image class="avatar-img" :src="item.avatar || 'https://dummyimage.com/100x100/ccc/fff.png&text=logo'">
<text class="brand-name">{{ item.name }}</text> </image>
</view>
<view class="tags-container">
<view <text class="brand-name">{{ item.name }}</text>
class="tag-item"
v-for="(tag, idx) in displayTags" <view class="tags-container">
:key="idx" <view class="tag-item" v-for="(tag, idx) in displayTags" :key="idx" :style="getTagStyle(tag)">
:style="getTagStyle(tag)" <image class="tag-icon" v-if="tag.icon" :src="tag.icon"></image>
> <text class="tag-text">{{ tag.text }}</text>
<image class="tag-icon" v-if="tag.icon" :src="tag.icon"></image> </view>
<text class="tag-text">{{ tag.text }}</text> </view>
</view> </view>
</view> </view>
</view> </template>
</view>
</template> <script>
//
<script> const TAG_LIBRARY = {
// franchise: { //
const TAG_LIBRARY = { text: '品牌加盟',
franchise: { // color: '#8D4C1B ',
text: '品牌加盟', borderColor: '#D6CAC0 ',
color: '#8D4C1B ', icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/60816ae2-92ee-4473-9eca-562e1200dbca.png'
borderColor: '#D6CAC0 ', },
icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/60816ae2-92ee-4473-9eca-562e1200dbca.png' recruit: { //
}, text: '学员招募',
recruit: { // color: '#E5505F',
text: '学员招募', borderColor: '#EFCFD2 ',
color: '#E5505F', icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/fd0699b9-9f3c-4b1e-8693-d0c9ec7eabc3.png'
borderColor: '#EFCFD2 ', },
icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/fd0699b9-9f3c-4b1e-8693-d0c9ec7eabc3.png' more: { //
}, text: '更多详情',
more: { // color: '#333333',
text: '更多详情', borderColor: '#ECECEC',
color: '#333333', icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/cec453ce-f3fc-4881-8f2c-9ec5c61e759c.png'
borderColor: '#ECECEC', }
icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/cec453ce-f3fc-4881-8f2c-9ec5c61e759c.png' }
}
} export default {
name: 'brand-card',
export default { props: {
name: 'brand-card', //
props: { item: {
// type: Object,
item: { default: () => ({})
type: Object, },
default: () => ({}) // key ['franchise', 'recruit']
}, tagKeys: {
// key ['franchise', 'recruit'] type: Array,
tagKeys: { default: null
type: Array, }
default: null },
} computed: {
}, // 使 tagKeys item.tags
computed: { displayTags() {
// 使 tagKeys item.tags if (this.tagKeys && this.tagKeys.length) {
displayTags() { return this.tagKeys.map(key => TAG_LIBRARY[key]).filter(v => v)
if (this.tagKeys && this.tagKeys.length) { }
return this.tagKeys.map(key => TAG_LIBRARY[key]).filter(v => v) // item.tags 使
} if (this.item.tags && Array.isArray(this.item.tags)) {
// item.tags 使 return this.item.tags
if (this.item.tags && Array.isArray(this.item.tags)) { }
return this.item.tags return []
} },
return [] tagsLength() {
}, return this.displayTags.length
tagsLength() { }
return this.displayTags.length },
} methods: {
}, //
methods: { onClick() {
// // 1. emit便
onClick() { this.$emit('click', this.item);
// 1. emit便
this.$emit('click', this.item); // 2.
if (!uni.getStorageSync("accessToken")) {
// 2. uni.navigateTo({
if (this.item && this.item.id) { url: '/pages/blogPopup/blogPopup'
uni.navigateTo({ });
url: `/pages/jingxuan/brand-detail?id=${this.item.id}` return;
}); }
}
}, // 3.
getTagStyle(tag) { if (this.item && this.item.id) {
const len = this.tagsLength uni.navigateTo({
let sizeStyle = {} url: `/pages/jingxuan/brand-detail?id=${this.item.id}`
if (len === 1) { });
sizeStyle = { }
width: '288rpx', },
height: '42rpx', getTagStyle(tag) {
padding: '0 12rpx', const len = this.tagsLength
boxSizing: 'border-box' let sizeStyle = {}
} if (len === 1) {
} else if (len === 2) { sizeStyle = {
sizeStyle = { width: '288rpx',
width: '134rpx', height: '42rpx',
height: '42rpx', padding: '0 12rpx',
padding: '0 12rpx', boxSizing: 'border-box'
boxSizing: 'border-box' }
} } else if (len === 2) {
} sizeStyle = {
return { width: '134rpx',
...sizeStyle, height: '42rpx',
color: tag.color, padding: '0 12rpx',
borderColor: tag.borderColor boxSizing: 'border-box'
} }
} }
} return {
} ...sizeStyle,
</script> color: tag.color,
borderColor: tag.borderColor
<style lang="scss"> }
.brand-card-comp { }
width: 328rpx; }
height: 339rpx; }
position: relative; </script>
border-radius: 21rpx;
overflow: hidden; <style lang="scss">
background-color: #fff; .brand-card-comp {
} width: 328rpx;
.brand-bg { height: 339rpx;
width: 100%; position: relative;
height: 184rpx; border-radius: 21rpx;
position: absolute; overflow: hidden;
top: 0; background-color: #fff;
left: 0; }
z-index: 1;
} .brand-bg {
.mask-bg { width: 100%;
width: 100%; height: 184rpx;
height: 251rpx; position: absolute;
position: absolute; top: 0;
bottom: 0; left: 0;
left: 0; z-index: 1;
z-index: 2; }
display: block;
} .mask-bg {
.card-content { width: 100%;
position: absolute; height: 251rpx;
bottom: 0; position: absolute;
left: 0; bottom: 0;
width: 100%; left: 0;
z-index: 3; z-index: 2;
display: flex; display: block;
flex-direction: column; }
align-items: center;
padding-bottom: 24rpx; .card-content {
} position: absolute;
.avatar-wrapper { bottom: 0;
width: 80rpx; left: 0;
height: 80rpx; width: 100%;
border-radius: 50%; z-index: 3;
border: 2rpx solid; display: flex;
background-color: #fff; flex-direction: column;
display: flex; align-items: center;
justify-content: center; padding-bottom: 24rpx;
align-items: center; }
overflow: hidden;
margin-top: -38rpx; .avatar-wrapper {
margin-bottom: 12rpx; width: 80rpx;
} height: 80rpx;
.avatar-img { border-radius: 50%;
width: 100%; border: 2rpx solid;
height: 100%; background-color: #fff;
} display: flex;
.brand-name { justify-content: center;
font-size: 24rpx; align-items: center;
font-weight: 500; overflow: hidden;
color: #333333; margin-top: -38rpx;
line-height: 34rpx; margin-bottom: 12rpx;
margin-bottom: 12rpx; }
}
.tags-container { .avatar-img {
display: flex; width: 100%;
justify-content: center; height: 100%;
align-items: center; }
gap: 20rpx;
flex-wrap: wrap; .brand-name {
} font-size: 24rpx;
.tag-item { font-weight: 500;
display: flex; color: #333333;
align-items: center; line-height: 34rpx;
justify-content: center; margin-bottom: 12rpx;
border-radius: 21rpx; }
border: 1rpx solid;
background-color: transparent; .tags-container {
padding: 7rpx 12rpx; display: flex;
} justify-content: center;
.tag-icon { align-items: center;
width: 24rpx; gap: 20rpx;
height: 22rpx; flex-wrap: wrap;
margin-right: 6rpx; }
flex-shrink: 0;
} .tag-item {
.tag-text { display: flex;
font-size: 20rpx; align-items: center;
line-height: 28rpx; justify-content: center;
white-space: nowrap; border-radius: 21rpx;
margin-top: 4rpx; border: 1rpx solid;
padding-right: 2rpx; background-color: transparent;
} padding: 7rpx 12rpx;
}
.tag-icon {
width: 24rpx;
height: 22rpx;
margin-right: 6rpx;
flex-shrink: 0;
}
.tag-text {
font-size: 20rpx;
line-height: 28rpx;
white-space: nowrap;
margin-top: 4rpx;
padding-right: 2rpx;
}
</style> </style>

View File

@ -2,10 +2,8 @@
<view class="skill-item" @click="handleClick"> <view class="skill-item" @click="handleClick">
<view class="cover-box"> <view class="cover-box">
<image class="cover" :src="item.coverSrc" mode="aspectFill"></image> <image class="cover" :src="item.coverSrc" mode="aspectFill"></image>
<image <image v-if="item.video" class="play-icon"
class="play-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/5c1ddfef-a200-424a-a9dc-03d9395568f6.png"></image>
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/5c1ddfef-a200-424a-a9dc-03d9395568f6.png"
></image>
</view> </view>
<view class="skill-info"> <view class="skill-info">
<view class="skill-title line-2">{{ item.title }}</view> <view class="skill-title line-2">{{ item.title }}</view>
@ -14,10 +12,9 @@
<text class="type-text">{{ item.type || '平台课程' }}</text> <text class="type-text">{{ item.type || '平台课程' }}</text>
</view> </view>
<view class="skill-stats"> <view class="skill-stats">
<image <image class="view-icon"
class="view-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/2409b64a-56bb-4d9e-a4cc-5b3444fcf097.png">
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/2409b64a-56bb-4d9e-a4cc-5b3444fcf097.png" </image>
></image>
{{ formatViews(item.views) }}观看 · {{ item.time }} {{ formatViews(item.views) }}观看 · {{ item.time }}
</view> </view>
</view> </view>
@ -68,14 +65,21 @@ export default {
handleClick() { handleClick() {
// 1. emit便 // 1. emit便
this.$emit('click', this.item); this.$emit('click', this.item);
// 2. // 2.
if (!uni.getStorageSync("accessToken")) {
uni.navigateTo({
url: '/pages/blogPopup/blogPopup'
});
return;
}
// 3.
if (this.item && this.item.id) { if (this.item && this.item.id) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/jingxuan/skill-detail?id=${this.item.id}` url: `/pages/jingxuan/skill-detail?id=${this.item.id}`
}); });
} }
// /
} }
} }
} }
@ -85,7 +89,7 @@ export default {
.skill-item { .skill-item {
display: flex; display: flex;
margin-bottom: 20rpx; margin-bottom: 20rpx;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@ -99,17 +103,17 @@ export default {
margin-right: 24rpx; margin-right: 24rpx;
flex-shrink: 0; flex-shrink: 0;
.cover { .cover {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.play-icon { .play-icon {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
width: 39rpx; width: 39rpx;
height: 39rpx; height: 39rpx;
} }
} }
@ -132,20 +136,20 @@ export default {
font-weight: 400; font-weight: 400;
line-height: 40rpx; line-height: 40rpx;
} }
.skill-type { .skill-type {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 22rpx; font-size: 22rpx;
margin-top: 10rpx; margin-top: 10rpx;
.type-icon { .type-icon {
width: 26rpx; width: 26rpx;
height: 26rpx; height: 26rpx;
margin-right: 8rpx; margin-right: 8rpx;
border-radius: 50%; border-radius: 50%;
} }
.type-text { .type-text {
border-radius: 7rpx; border-radius: 7rpx;
font-weight: 400; font-weight: 400;
@ -153,25 +157,25 @@ export default {
line-height: 33rpx; line-height: 33rpx;
padding: 2rpx 10rpx; padding: 2rpx 10rpx;
} }
&.platform-course .type-text { &.platform-course .type-text {
background: #f9d3d760; background: #f9d3d760;
color: #E55463; color: #E55463;
} }
&.brand-course .type-text { &.brand-course .type-text {
background: #f9d3d760; background: #f9d3d760;
color: #E55463; color: #E55463;
} }
} }
.skill-stats { .skill-stats {
font-size: 22rpx; font-size: 22rpx;
color: #999; color: #999;
margin-top: 10rpx; margin-top: 10rpx;
display: flex; display: flex;
align-items: center; align-items: center;
.view-icon { .view-icon {
width: 26rpx; width: 26rpx;
height: 22rpx; height: 22rpx;
@ -188,5 +192,4 @@ export default {
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
overflow: hidden; overflow: hidden;
} }
</style> </style>

File diff suppressed because it is too large Load Diff

View File

@ -17,39 +17,25 @@
<text>{{ useMockData ? '关闭模拟数据' : '开启模拟数据' }}</text> <text>{{ useMockData ? '关闭模拟数据' : '开启模拟数据' }}</text>
</view> --> </view> -->
<view class="banner-section" v-if="bannerList && bannerList.length || info.video"> <view class="banner-section" v-if="bannerList && bannerList.length">
<!-- 原生视频组件 (就地播放) --> <!-- 轮播图区域 -->
<video v-if="info.video && isVideoPlaying" id="myVideo" class="banner-video" :src="info.video" autoplay controls <swiper class="banner-swiper" circular @change="onSwiperChange" :autoplay="false" :current="currentBannerIndex">
object-fit="cover"></video> <!-- 视频作为第一项 -->
<swiper-item v-if="info.video">
<!-- 轮播图区域 (当视频播放时隐藏) --> <video id="myVideo" class="banner-video" :src="info.video" :autoplay="currentBannerIndex === 0" controls
<swiper v-else class="banner-swiper" circular @change="onSwiperChange" :autoplay="!info.video"> object-fit="cover" :show-center-play-btn="false" :show-play-btn="false" @play="onVideoPlay"
@pause="onVideoPause" @ended="onVideoEnded"></video>
</swiper-item>
<!-- 图片轮播 -->
<swiper-item v-for="(item, index) in bannerList" :key="index"> <swiper-item v-for="(item, index) in bannerList" :key="index">
<image class="banner-img" :src="item" mode="aspectFill"></image> <image class="banner-img" :src="item" mode="aspectFill"></image>
</swiper-item> </swiper-item>
</swiper> </swiper>
<!-- 居中的视频播放大图标 (当视频播放时隐藏) --> <!-- 指示器 -->
<image v-if="info.video && !isVideoPlaying" class="center-play-icon" <view class="indicator" v-if="bannerList.length > 0">
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/bb720bb0-4ada-49f6-9ce5-38abfd613d5d.png" <text>{{ currentBannerIndex + 1 }} / {{ totalSlides }}</text>
@click="playVideoInPlace"></image>
<!-- 视频/图片 相册切换组件 (当视频播放时隐藏) -->
<view class="album" v-if="(info.video || bannerList.length > 0) && !isVideoPlaying">
<view class="album-left" @click="info.video ? playVideoInPlace() : goAlbum()">
{{ info.video ? "视频" : "图片" }}
</view>
<view class="album-line"></view>
<view class="album-right" @click="goAlbum">
<view class="album-right-text">相册</view>
<image src="/static/images/icons/right7_13.png" class="album-right-img"></image>
</view>
</view>
<!-- 指示器 (当视频播放时隐藏) -->
<view class="indicator" v-if="!isVideoPlaying && bannerList.length > 0">
<text>{{ currentBannerIndex + 1 }} / {{ bannerList.length }}</text>
</view> </view>
</view> </view>
@ -164,6 +150,27 @@
</view> </view>
</view> </view>
<!-- 拨打电话选择弹窗 -->
<view class="phone-popup" :class="{ show: showPhonePopup }">
<view class="phone-popup-mask" @click="closePhonePopup"></view>
<view class="phone-popup-content">
<view class="phone-popup-header">
<text class="phone-popup-title">拨打电话</text>
</view>
<view class="phone-popup-body">
<view class="phone-option" v-if="info.contact_phone" @click="selectPhone(info.contact_phone)">
<text class="phone-number">{{ info.contact_phone }}</text>
</view>
<view class="phone-option" v-if="info.alternate_phone" @click="selectPhone(info.alternate_phone)">
<text class="phone-number">{{ info.alternate_phone }}</text>
</view>
</view>
<view class="phone-popup-cancel" @click="closePhonePopup">
<text class="cancel-text">取消</text>
</view>
</view>
</view>
<tipsPopup2 :show="tipShow" @closePopup="closePopup" sureText="确认" @okBtn="okBtn"> <tipsPopup2 :show="tipShow" @closePopup="closePopup" sureText="确认" @okBtn="okBtn">
{{ tipsText }} {{ tipsText }}
</tipsPopup2> </tipsPopup2>
@ -196,6 +203,7 @@ export default {
tipShow: false, // tipShow: false, //
tipsText: "", // tipsText: "", //
tipType: "", // tipType: "", //
showPhonePopup: false, //
isCollected: false, // isCollected: false, //
// //
@ -204,7 +212,6 @@ export default {
iconCollectOn: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/fe2011b0-a34d-46ee-911a-0ea6f099ad77.png', iconCollectOn: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/fe2011b0-a34d-46ee-911a-0ea6f099ad77.png',
// ================================== // ==================================
isVideoPlaying: false, //
phoneIconTop: 20, // Top phoneIconTop: 20, // Top
phoneIconRight: 50, // Right phoneIconRight: 50, // Right
@ -296,6 +303,10 @@ export default {
showTabSection() { showTabSection() {
return this.availableTabs.length > 0; return this.availableTabs.length > 0;
}, },
// +
totalSlides() {
return this.bannerList.length + (this.info.video ? 1 : 0);
},
canSubmit() { canSubmit() {
const d = this.formData; const d = this.formData;
const baseValid = d.contact_name && d.contact_phone && d.province_code && d.city_code && d.area_code && d.intention; const baseValid = d.contact_name && d.contact_phone && d.province_code && d.city_code && d.area_code && d.intention;
@ -439,29 +450,69 @@ export default {
// //
makeCall() { makeCall() {
// if (!this.isLogin) {
const phone = this.info.contact_phone || ''; this.tipsText = "您现在还未登录,是否确定登录?";
if (phone) { this.tipShow = true;
uni.makePhoneCall({ this.tipType = "login";
phoneNumber: String(phone), return; //
fail: (err) => { }
console.log('取消拨打或拨打失败', err);
} const contactPhone = this.info.contact_phone || '';
}); const alternatePhone = this.info.alternate_phone || '';
//
if (contactPhone && alternatePhone) {
this.showPhonePopup = true;
} else if (contactPhone) {
//
this.doMakeCall(contactPhone);
} else if (alternatePhone) {
//
this.doMakeCall(alternatePhone);
} else { } else {
uni.showToast({ title: '暂无联系电话', icon: 'none' }); uni.showToast({ title: '暂无联系电话', icon: 'none' });
} }
}, },
//
selectPhone(phone) {
this.closePhonePopup();
this.doMakeCall(phone);
},
//
closePhonePopup() {
this.showPhonePopup = false;
},
//
doMakeCall(phone) {
uni.makePhoneCall({
phoneNumber: String(phone),
fail: (err) => {
console.log('取消拨打或拨打失败', err);
}
});
},
goAlbum() { goAlbum() {
uni.navigateTo({ uni.navigateTo({
url: `/pages/album/index?id=${this.brandId}&user_type=2` url: `/pages/album/index?id=${this.brandId}&user_type=2`
}); });
}, },
// //
playVideoInPlace() { onVideoPlay() {
this.isVideoPlaying = true; console.log('视频开始播放');
},
onVideoPause() {
console.log('视频暂停');
},
onVideoEnded() {
//
if (this.bannerList.length > 0) {
this.currentBannerIndex = 1;
}
}, },
// Mock // Mock
@ -472,7 +523,6 @@ export default {
}, },
getBrandDetails() { getBrandDetails() {
this.isVideoPlaying = false; //
if (this.useMockData) { if (this.useMockData) {
this.injectMockData(); this.injectMockData();
return; return;
@ -1132,4 +1182,100 @@ export default {
.popup-btn.confirm .btn-text { .popup-btn.confirm .btn-text {
color: #FFFFFF; color: #FFFFFF;
} }
/* 拨打电话弹窗样式 */
.phone-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
visibility: hidden;
opacity: 0;
transition: all 0.3s ease;
}
.phone-popup.show {
visibility: visible;
opacity: 1;
}
.phone-popup-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.phone-popup-content {
position: absolute;
left: 0;
right: 0;
bottom: 0;
background-color: #F5F5F5;
border-radius: 24rpx 24rpx 0 0;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.phone-popup.show .phone-popup-content {
transform: translateY(0);
}
.phone-popup-header {
padding: 30rpx;
text-align: center;
background-color: #FFFFFF;
border-radius: 24rpx 24rpx 0 0;
}
.phone-popup-title {
font-size: 32rpx;
font-weight: 500;
color: #333333;
}
.phone-popup-body {
background-color: #FFFFFF;
margin-top: 2rpx;
}
.phone-option {
display: flex;
flex-direction: column;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #EEEEEE;
}
.phone-option:last-child {
border-bottom: none;
}
.phone-label {
font-size: 26rpx;
color: #999999;
margin-bottom: 10rpx;
}
.phone-number {
font-size: 34rpx;
color: #333333;
font-weight: 500;
}
.phone-popup-cancel {
margin-top: 16rpx;
padding: 30rpx;
text-align: center;
background-color: #FFFFFF;
}
.cancel-text {
font-size: 32rpx;
color: #333333;
}
</style> </style>

View File

@ -2,26 +2,19 @@
<view class="collection-page"> <view class="collection-page">
<custom-navbar title="我的收藏" :showBack="true" backgroundColor="#FFFFFF" :show-headle="true" borderBottom="none" <custom-navbar title="我的收藏" :showBack="true" backgroundColor="#FFFFFF" :show-headle="true" borderBottom="none"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png" @onHeadleClick="goToSearch"> headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png"
@onHeadleClick="goToSearch">
</custom-navbar> </custom-navbar>
<view class="nav-placeholder" :style="{ height: navHeight + 'px' }"></view> <view class="nav-placeholder" :style="{ height: navHeight + 'px' }"></view>
<view class="tab-bar-wrap" :style="{ top: navHeight + 'px' }"> <view class="tab-bar-wrap" :style="{ top: navHeight + 'px' }">
<view class="tab-bar"> <view class="tab-bar">
<view class="tab-items"> <view class="tab-items">
<view <view class="tab-item" :class="{ active: currentTab === 'brand' }" @tap="switchTab('brand')">
class="tab-item"
:class="{ active: currentTab === 'brand' }"
@tap="switchTab('brand')"
>
收藏的品牌 收藏的品牌
</view> </view>
<view <view class="tab-item" :class="{ active: currentTab === 'course' }" @tap="switchTab('course')">
class="tab-item"
:class="{ active: currentTab === 'course' }"
@tap="switchTab('course')"
>
收藏的课程 收藏的课程
</view> </view>
</view> </view>
@ -32,21 +25,17 @@
</view> </view>
</view> </view>
<view class="filter-panel" :class="{ 'show': filterVisible }" :style="{ top: filterTop + 'px' }" catchtouchmove="true"> <view class="filter-panel" :class="{ 'show': filterVisible }" :style="{ top: filterTop + 'px' }"
catchtouchmove="true">
<view class="filter-mask" @tap="closeFilterPanel"></view> <view class="filter-mask" @tap="closeFilterPanel"></view>
<view class="filter-container"> <view class="filter-container">
<scroll-view scroll-y class="filter-scroll" v-if="currentTab === 'brand'"> <scroll-view scroll-y class="filter-scroll" v-if="currentTab === 'brand'">
<view class="filter-group" v-for="group in brandFilterData" :key="group.id"> <view class="filter-group" v-for="group in brandFilterData" :key="group.id">
<view class="group-title">{{ group.title }}</view> <view class="group-title">{{ group.title }}</view>
<view class="pill-list"> <view class="pill-list">
<view <view class="pill-item" v-for="sub in group.children" :key="sub.id"
class="pill-item" :class="{ active: selectedSecondIds.includes(sub.id) }" @tap="toggleSecondSelect(sub.id)">
v-for="sub in group.children"
:key="sub.id"
:class="{ active: selectedSecondIds.includes(sub.id) }"
@tap="toggleSecondSelect(sub.id)"
>
{{ sub.title }} {{ sub.title }}
</view> </view>
</view> </view>
@ -54,18 +43,12 @@
</scroll-view> </scroll-view>
<view v-else class="course-filter"> <view v-else class="course-filter">
<view <view class="course-type-item" :class="{ active: selectedCourseType === 'platform' }"
class="course-type-item" @tap="selectCourseType('platform')">
:class="{ active: selectedCourseType === 'platform' }"
@tap="selectCourseType('platform')"
>
平台课程 平台课程
</view> </view>
<view <view class="course-type-item" :class="{ active: selectedCourseType === 'brand' }"
class="course-type-item" @tap="selectCourseType('brand')">
:class="{ active: selectedCourseType === 'brand' }"
@tap="selectCourseType('brand')"
>
品牌课程 品牌课程
</view> </view>
</view> </view>
@ -79,30 +62,24 @@
<view class="collection-content" v-if="currentTab === 'brand'"> <view class="collection-content" v-if="currentTab === 'brand'">
<view class="brand-grid" v-if="filteredBrandList.length > 0"> <view class="brand-grid" v-if="filteredBrandList.length > 0">
<brand-card <brand-card class="brand-card-wrap" v-for="(item, index) in filteredBrandList" :key="index" :item="item"
class="brand-card-wrap" :tagKeys="item.tagKeys" @click="goToBrandDetail(item)"></brand-card>
v-for="(item, index) in filteredBrandList"
:key="index"
:item="item"
:tagKeys="item.tagKeys"
@click="goToBrandDetail(item)"
></brand-card>
</view> </view>
<view class="empty-state" v-else> <view class="empty-state" v-else>
<image class="empty-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/da814ede-1adc-4f90-8cdf-5357a12fab27.png"></image> <image class="empty-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/da814ede-1adc-4f90-8cdf-5357a12fab27.png">
</image>
</view> </view>
</view> </view>
<view class="collection-content" v-else> <view class="collection-content" v-else>
<view class="course-list" v-if="filteredCourseList.length > 0"> <view class="course-list" v-if="filteredCourseList.length > 0">
<skill-card <skill-card v-for="(item, index) in filteredCourseList" :key="index" :item="item"></skill-card>
v-for="(item, index) in filteredCourseList"
:key="index"
:item="item"
></skill-card>
</view> </view>
<view class="empty-state" v-else> <view class="empty-state" v-else>
<image class="empty-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/da814ede-1adc-4f90-8cdf-5357a12fab27.png"></image> <image class="empty-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/da814ede-1adc-4f90-8cdf-5357a12fab27.png">
</image>
</view> </view>
</view> </view>
@ -114,7 +91,7 @@
<script> <script>
import TabBar from "@/components/tabbar/tabbar.vue" import TabBar from "@/components/tabbar/tabbar.vue"
import BrandCard from "@/pages/home/components/brand-card.vue" import BrandCard from "@/pages/home/components/brand-card.vue"
import SkillCard from "@/pages/home/components/skill-card.vue" import SkillCard from "@/pages/home/components/skill-card.vue"
import request from "@/utils/request" import request from "@/utils/request"
// true=false=使 // true=false=使
@ -130,15 +107,15 @@ export default {
return { return {
navHeight: 0, // navHeight: 0, //
filterTop: 0, // filterTop: 0, //
currentTab: 'brand', currentTab: 'brand',
// //
filterVisible: false, filterVisible: false,
// () // ()
brandFilterData: [], brandFilterData: [],
selectedSecondIds: [], // ID selectedSecondIds: [], // ID
// //
selectedCourseType: '', // 'platform' 'brand' selectedCourseType: '', // 'platform' 'brand'
// //
@ -159,7 +136,7 @@ export default {
// Tab // Tab
const sysInfo = uni.getSystemInfoSync() const sysInfo = uni.getSystemInfoSync()
const sbh = sysInfo.statusBarHeight || 0 const sbh = sysInfo.statusBarHeight || 0
this.navHeight = sbh this.navHeight = sbh
// Tab 106rpx px // Tab 106rpx px
this.filterTop = this.navHeight + uni.upx2px(106) this.filterTop = this.navHeight + uni.upx2px(106)
@ -170,9 +147,9 @@ export default {
// //
goToSearch() { goToSearch() {
// this.currentTab 'brand' 'course' // this.currentTab 'brand' 'course'
uni.navigateTo({ uni.navigateTo({
url: `/pages/search_common/search?type=collection_${this.currentTab}` url: `/pages/search_common/search?type=collection_${this.currentTab}`
}); });
}, },
goBack() { goBack() {
uni.navigateBack() uni.navigateBack()
@ -199,14 +176,14 @@ export default {
closeFilterPanel() { closeFilterPanel() {
this.filterVisible = false this.filterVisible = false
}, },
// state === 1 // state === 1
async loadFilterCategories() { async loadFilterCategories() {
try { try {
const res = await request.post('/sj/firstclass', {}) const res = await request.post('/sj/firstclass', {})
if ((res.code === 200 || res.state === 1) && res.data) { if ((res.code === 200 || res.state === 1) && res.data) {
const firstClasses = res.data; const firstClasses = res.data;
// //
const promises = firstClasses.map(async (first) => { const promises = firstClasses.map(async (first) => {
try { try {
@ -216,11 +193,11 @@ export default {
title: first.title, title: first.title,
children: ((subRes.code === 200 || subRes.state === 1) && subRes.data) ? subRes.data : [] children: ((subRes.code === 200 || subRes.state === 1) && subRes.data) ? subRes.data : []
} }
} catch(e) { } catch (e) {
return { id: first.id, title: first.title, children: [] } return { id: first.id, title: first.title, children: [] }
} }
}); });
const filterData = await Promise.all(promises); const filterData = await Promise.all(promises);
// //
this.brandFilterData = filterData.filter(group => group.children.length > 0); this.brandFilterData = filterData.filter(group => group.children.length > 0);
@ -243,9 +220,9 @@ export default {
// //
selectCourseType(type) { selectCourseType(type) {
// //
if(this.selectedCourseType === type){ if (this.selectedCourseType === type) {
this.selectedCourseType = '' this.selectedCourseType = ''
}else{ } else {
this.selectedCourseType = type this.selectedCourseType = type
} }
}, },
@ -333,15 +310,15 @@ export default {
try { try {
// 1. // 1.
const res = await request.post('/sj/brand/collectList', { page: 1, limit: 100 }) const res = await request.post('/sj/brand/collectList', { page: 1, limit: 100 })
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) { if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
// 2. filter(item => item.collect_state === 1) // 2. filter(item => item.collect_state === 1)
this.rawBrandList = res.data.list.map(item => { this.rawBrandList = res.data.list.map(item => {
let tags = [] let tags = []
if (item.franchise_state == 1) tags.push('franchise') if (item.franchise_state == 1) tags.push('franchise')
if (item.student_state == 1) tags.push('recruit') if (item.student_state == 1) tags.push('recruit')
if (tags.length === 0) tags.push('more') if (tags.length === 0) tags.push('more')
return { return {
id: item.id, id: item.id,
name: item.name, name: item.name,
@ -364,7 +341,7 @@ export default {
try { try {
// 1. // 1.
const res = await request.post('/sj/skill/collectList', { page: 1, limit: 100 }) const res = await request.post('/sj/skill/collectList', { page: 1, limit: 100 })
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) { if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
// 2. filter map // 2. filter map
this.rawCourseList = res.data.list.map(item => { this.rawCourseList = res.data.list.map(item => {
@ -376,7 +353,7 @@ export default {
link_type: item.link_type, link_type: item.link_type,
views: item.browse_num || 0, views: item.browse_num || 0,
// 3. 使 // 3. 使
time: item.create_time ? this.formatTimeAgo(item.create_time) : '', time: item.create_time ? this.formatTimeAgo(item.create_time) : '',
typeIcon: item.link_logo || '' typeIcon: item.link_logo || ''
} }
}) })
@ -445,7 +422,8 @@ export default {
/* 将页面左右 padding 转移到内容区,保证 Tab 背景贯穿全屏 */ /* 将页面左右 padding 转移到内容区,保证 Tab 背景贯穿全屏 */
.tab-bar-wrap { .tab-bar-wrap {
position: sticky; /* 吸顶,保证滑动页面时一直显示在最前面 */ position: sticky;
/* 吸顶,保证滑动页面时一直显示在最前面 */
background: #fff; background: #fff;
padding: 10rpx 24rpx; padding: 10rpx 24rpx;
z-index: 101; z-index: 101;
@ -457,11 +435,11 @@ export default {
justify-content: space-between; justify-content: space-between;
background: transparent; background: transparent;
padding: 10rpx 0; padding: 10rpx 0;
.tab-items { .tab-items {
display: flex; display: flex;
align-items: center; align-items: center;
.tab-item { .tab-item {
font-weight: 400; font-weight: 400;
font-size: 26rpx; font-size: 26rpx;
@ -471,31 +449,32 @@ export default {
margin-right: 40rpx; margin-right: 40rpx;
padding: 9rpx 24rpx; padding: 9rpx 24rpx;
transition: all 0.3s ease; transition: all 0.3s ease;
&.active { &.active {
color: #FF4767; color: #FF4767;
background: #FFECEF; background: #FFECEF;
border: 1rpx solid #FFB0BD; border: 1rpx solid #FFB0BD;
border-radius: 29rpx; border-radius: 29rpx;
} }
} }
} }
.filter-area { .filter-area {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 10rpx 0; padding: 10rpx 0;
.filter-text { .filter-text {
font-size: 24rpx; font-size: 24rpx;
color: #666666; color: #666666;
margin-right: 6rpx; margin-right: 6rpx;
} }
.filter-icon { .filter-icon {
width: 16rpx; width: 16rpx;
height: 10rpx; height: 10rpx;
transition: transform 0.3s ease; transition: transform 0.3s ease;
/* 展开时图标倒转 */ /* 展开时图标倒转 */
&.rotate { &.rotate {
transform: rotate(180deg); transform: rotate(180deg);
@ -510,39 +489,52 @@ export default {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
z-index: 100; /* 低于 Tab区的101保证隐藏时躲在其后 */ z-index: 100;
pointer-events: none; /* 面板关闭时,穿透点击到底部列表 */ /* 低于 Tab区的101保证隐藏时躲在其后 */
overflow: hidden; /* 防止抽屉向上滑出时外溢 */ pointer-events: none;
/* 面板关闭时,穿透点击到底部列表 */
overflow: hidden;
/* 防止抽屉向上滑出时外溢 */
&.show { &.show {
pointer-events: auto; /* 打开时恢复点击拦截 */ pointer-events: auto;
/* 打开时恢复点击拦截 */
.filter-mask { .filter-mask {
opacity: 1; opacity: 1;
} }
.filter-container { .filter-container {
transform: translateY(50rpx); /* 滑出 */ transform: translateY(50rpx);
/* 滑出 */
} }
} }
.filter-mask { .filter-mask {
position: absolute; position: absolute;
top: 0; left: 0; right: 0; bottom: 0; top: 0;
background: rgba(0,0,0,0.4); left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
opacity: 0; opacity: 0;
transition: opacity 0.3s ease-in-out; transition: opacity 0.3s ease-in-out;
} }
.filter-container { .filter-container {
position: absolute; position: absolute;
top: 0; left: 0; right: 0; top: 0;
left: 0;
right: 0;
background: #fff; background: #fff;
border-radius: 0 0 30rpx 30rpx; border-radius: 0 0 30rpx 30rpx;
padding: 40rpx 30rpx; padding: 40rpx 30rpx;
max-height: 80vh; max-height: 80vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-shadow: 0 10rpx 20rpx rgba(0,0,0,0.05); box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.05);
transform: translateY(-100%); /* 默认隐藏在上方即Tab背后 */ transform: translateY(-100%);
/* 默认隐藏在上方即Tab背后 */
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
} }
} }
@ -551,43 +543,46 @@ export default {
.filter-scroll { .filter-scroll {
max-height: 50vh; max-height: 50vh;
} }
.filter-group { .filter-group {
margin-bottom: 30rpx; margin-bottom: 30rpx;
.group-title { .group-title {
font-weight: 500; font-weight: 500;
font-size: 28rpx; font-size: 28rpx;
color: #333333; color: #333333;
line-height: 40rpx; line-height: 40rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.pill-list { .pill-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 20rpx; gap: 20rpx;
} }
} }
/* 通用胶囊按钮 */ /* 通用胶囊按钮 */
.pill-item { .pill-item {
width: calc(25% - 16rpx); /* 25%是一行四个,减去间距 */ width: calc(25% - 16rpx);
/* 25%是一行四个,减去间距 */
height: 58rpx; height: 58rpx;
background: #F5F5F5; background: #F5F5F5;
font-weight: 400; font-weight: 400;
font-size: 26rpx; font-size: 26rpx;
color: #333333; color: #333333;
line-height: 37rpx; line-height: 37rpx;
/* 使用 flex 居中 */ /* 使用 flex 居中 */
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 12rpx; border-radius: 12rpx;
transition: all 0.3s; transition: all 0.3s;
box-sizing: border-box; /* 防止 padding 会撑大盒子 */ box-sizing: border-box;
/* 防止 padding 会撑大盒子 */
&.active { &.active {
background: #FFF0F2; background: #FFF0F2;
color: #FF4767; color: #FF4767;
@ -599,14 +594,14 @@ export default {
display: flex; display: flex;
gap: 20rpx; gap: 20rpx;
/* padding: 20rpx 0 40rpx; */ /* padding: 20rpx 0 40rpx; */
.course-type-item { .course-type-item {
background: #F5F5F5; background: #F5F5F5;
color: #666666; color: #666666;
font-size: 26rpx; font-size: 26rpx;
padding: 12rpx 40rpx; padding: 12rpx 40rpx;
border-radius: 12rpx; border-radius: 12rpx;
&.active { &.active {
background: #FFF0F2; background: #FFF0F2;
color: #FF4767; color: #FF4767;
@ -620,20 +615,21 @@ export default {
margin-top: 20rpx; margin-top: 20rpx;
padding-top: 20rpx; padding-top: 20rpx;
/* border-top: 1rpx solid #F5F5F5; */ /* border-top: 1rpx solid #F5F5F5; */
.reset-btn, .confirm-btn { .reset-btn,
.confirm-btn {
width: 47%; width: 47%;
text-align: center; text-align: center;
padding: 20rpx 0; padding: 20rpx 0;
border-radius: 40rpx; border-radius: 40rpx;
font-size: 28rpx; font-size: 28rpx;
} }
.reset-btn { .reset-btn {
background: #F5F5F5; background: #F5F5F5;
color: #666; color: #666;
} }
.confirm-btn { .confirm-btn {
background: #FF4767; background: #FF4767;
color: #fff; color: #fff;
@ -650,7 +646,7 @@ export default {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
.brand-card-wrap { .brand-card-wrap {
width: 49.2%; width: 49.2%;
margin-bottom: 20rpx; margin-bottom: 20rpx;
@ -668,7 +664,7 @@ export default {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 120rpx 0; padding: 120rpx 0;
.empty-icon { .empty-icon {
width: 346rpx; width: 346rpx;
height: 373rpx; height: 373rpx;
@ -676,7 +672,7 @@ export default {
} }
::v-deep .icon-headle { ::v-deep .icon-headle {
width: 32rpx !important; width: 32rpx !important;
height: 32rpx !important; height: 32rpx !important;
} }
</style> </style>