2026-05-21 16:52:23 +08:00
|
|
|
|
<template>
|
2026-05-26 18:06:07 +08:00
|
|
|
|
<view class="collection-page">
|
|
|
|
|
|
|
|
|
|
|
|
<custom-navbar title="我的收藏" :showBack="true" backgroundColor="#FFFFFF" :show-headle="true" borderBottom="none"
|
2026-06-04 11:45:11 +08:00
|
|
|
|
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png"
|
|
|
|
|
|
@onHeadleClick="goToSearch">
|
2026-05-26 18:06:07 +08:00
|
|
|
|
</custom-navbar>
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
<view class="tab-bar-wrap" :style="{ top: navHeight + 'px' }">
|
|
|
|
|
|
<view class="tab-bar">
|
|
|
|
|
|
<view class="tab-items">
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<view class="tab-item" :class="{ active: currentTab === 'brand' }" @tap="switchTab('brand')">
|
2026-05-26 18:06:07 +08:00
|
|
|
|
收藏的品牌
|
|
|
|
|
|
</view>
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<view class="tab-item" :class="{ active: currentTab === 'course' }" @tap="switchTab('course')">
|
2026-05-26 18:06:07 +08:00
|
|
|
|
收藏的课程
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-06-08 11:12:16 +08:00
|
|
|
|
<view class="filter-area" @tap="toggleFilterPanel"
|
|
|
|
|
|
v-if="currentTab === 'brand' ? rawBrandList.length > 0 : rawCourseList.length > 0">
|
2026-05-26 18:06:07 +08:00
|
|
|
|
<text class="filter-text">筛选</text>
|
|
|
|
|
|
<image class="filter-icon" :src="filterIcon" mode="aspectFit" :class="{ 'rotate': filterVisible }"></image>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<view class="filter-panel" :class="{ 'show': filterVisible }" :style="{ top: filterTop + 'px' }"
|
|
|
|
|
|
catchtouchmove="true">
|
2026-05-26 18:06:07 +08:00
|
|
|
|
<view class="filter-mask" @tap="closeFilterPanel"></view>
|
|
|
|
|
|
<view class="filter-container">
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
<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="group-title">{{ group.title }}</view>
|
|
|
|
|
|
<view class="pill-list">
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<view class="pill-item" v-for="sub in group.children" :key="sub.id"
|
|
|
|
|
|
:class="{ active: selectedSecondIds.includes(sub.id) }" @tap="toggleSecondSelect(sub.id)">
|
2026-05-26 18:06:07 +08:00
|
|
|
|
{{ sub.title }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
|
|
|
|
|
|
<view v-else class="course-filter">
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<view class="course-type-item" :class="{ active: selectedCourseType === 'platform' }"
|
|
|
|
|
|
@tap="selectCourseType('platform')">
|
2026-05-26 18:06:07 +08:00
|
|
|
|
平台课程
|
|
|
|
|
|
</view>
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<view class="course-type-item" :class="{ active: selectedCourseType === 'brand' }"
|
|
|
|
|
|
@tap="selectCourseType('brand')">
|
2026-05-26 18:06:07 +08:00
|
|
|
|
品牌课程
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="filter-footer">
|
|
|
|
|
|
<view class="reset-btn" @tap="resetFilter">重置</view>
|
|
|
|
|
|
<view class="confirm-btn" @tap="applyFilterAndClose">确定</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="collection-content" v-if="currentTab === 'brand'">
|
|
|
|
|
|
<view class="brand-grid" v-if="filteredBrandList.length > 0">
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<brand-card class="brand-card-wrap" v-for="(item, index) in filteredBrandList" :key="index" :item="item"
|
|
|
|
|
|
:tagKeys="item.tagKeys" @click="goToBrandDetail(item)"></brand-card>
|
2026-05-26 18:06:07 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view class="empty-state" v-else>
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<image class="empty-icon"
|
|
|
|
|
|
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/da814ede-1adc-4f90-8cdf-5357a12fab27.png">
|
|
|
|
|
|
</image>
|
2026-05-26 18:06:07 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="collection-content" v-else>
|
|
|
|
|
|
<view class="course-list" v-if="filteredCourseList.length > 0">
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<skill-card v-for="(item, index) in filteredCourseList" :key="index" :item="item"></skill-card>
|
2026-05-26 18:06:07 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view class="empty-state" v-else>
|
2026-06-04 11:45:11 +08:00
|
|
|
|
<image class="empty-icon"
|
|
|
|
|
|
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/da814ede-1adc-4f90-8cdf-5357a12fab27.png">
|
|
|
|
|
|
</image>
|
2026-05-26 18:06:07 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view style="height: 120rpx;"></view>
|
2026-06-04 11:51:30 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
</view>
|
2026-05-21 16:52:23 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2026-05-26 18:06:07 +08:00
|
|
|
|
import BrandCard from "@/pages/home/components/brand-card.vue"
|
2026-06-04 11:45:11 +08:00
|
|
|
|
import SkillCard from "@/pages/home/components/skill-card.vue"
|
2026-05-26 18:06:07 +08:00
|
|
|
|
import request from "@/utils/request"
|
|
|
|
|
|
|
|
|
|
|
|
// 数据模式开关:true=调用真实接口,false=使用模拟数据
|
|
|
|
|
|
const USE_REAL_API = true
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
components: {
|
|
|
|
|
|
BrandCard,
|
|
|
|
|
|
SkillCard
|
|
|
|
|
|
},
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
navHeight: 0, // 动态计算的导航栏高度
|
|
|
|
|
|
filterTop: 0, // 动态计算的筛选框顶部起点
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
currentTab: 'brand',
|
|
|
|
|
|
// 筛选相关
|
|
|
|
|
|
filterVisible: false,
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
// 品牌筛选下拉数据 (合并一级和二级)
|
|
|
|
|
|
brandFilterData: [],
|
|
|
|
|
|
selectedSecondIds: [], // 选中的二级ID数组
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
// 课程筛选
|
|
|
|
|
|
selectedCourseType: '', // 'platform' 或 'brand'
|
|
|
|
|
|
// 原始数据(收藏的数据)
|
|
|
|
|
|
rawBrandList: [],
|
|
|
|
|
|
rawCourseList: [],
|
|
|
|
|
|
// 展示数据(过滤后)
|
|
|
|
|
|
filteredBrandList: [],
|
|
|
|
|
|
filteredCourseList: []
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
filterIcon() {
|
2026-06-05 15:27:42 +08:00
|
|
|
|
return this.filterVisible
|
|
|
|
|
|
? 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e00ab2ac-bccf-42d8-820c-65727327e279.png'
|
|
|
|
|
|
: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/bfe7a104-bef8-4432-9487-42f05e3e9035.png'
|
2026-05-26 18:06:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
onLoad() {
|
|
|
|
|
|
// 精准计算吸顶高度与下拉框起点,防止蒙版遮挡白色的Tab区
|
|
|
|
|
|
const sysInfo = uni.getSystemInfoSync()
|
|
|
|
|
|
const sbh = sysInfo.statusBarHeight || 0
|
2026-06-04 11:45:11 +08:00
|
|
|
|
this.navHeight = sbh
|
2026-05-26 18:06:07 +08:00
|
|
|
|
// Tab栏自身的高度大约为 106rpx,转化为 px 叠加上去
|
|
|
|
|
|
this.filterTop = this.navHeight + uni.upx2px(106)
|
|
|
|
|
|
|
|
|
|
|
|
this.loadFilterCategories()
|
|
|
|
|
|
this.loadData()
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
2026-05-28 10:41:27 +08:00
|
|
|
|
// 跳转搜索页
|
|
|
|
|
|
goToSearch() {
|
|
|
|
|
|
// this.currentTab 的值刚好是 'brand' 或 'course'
|
2026-06-04 11:45:11 +08:00
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/search_common/search?type=collection_${this.currentTab}`
|
|
|
|
|
|
});
|
2026-05-28 10:41:27 +08:00
|
|
|
|
},
|
2026-05-26 18:06:07 +08:00
|
|
|
|
goBack() {
|
|
|
|
|
|
uni.navigateBack()
|
|
|
|
|
|
},
|
|
|
|
|
|
addAddress() {
|
|
|
|
|
|
// 外部方法占位
|
|
|
|
|
|
},
|
|
|
|
|
|
switchTab(tab) {
|
|
|
|
|
|
if (this.currentTab === tab) return
|
|
|
|
|
|
this.currentTab = tab
|
|
|
|
|
|
// 重置筛选(不同tab筛选条件独立)
|
|
|
|
|
|
if (tab === 'brand') {
|
|
|
|
|
|
this.resetBrandFilter()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.resetCourseFilter()
|
|
|
|
|
|
}
|
|
|
|
|
|
this.loadData()
|
|
|
|
|
|
this.closeFilterPanel()
|
|
|
|
|
|
},
|
|
|
|
|
|
// 筛选面板控制
|
|
|
|
|
|
toggleFilterPanel() {
|
|
|
|
|
|
this.filterVisible = !this.filterVisible
|
|
|
|
|
|
},
|
|
|
|
|
|
closeFilterPanel() {
|
|
|
|
|
|
this.filterVisible = false
|
|
|
|
|
|
},
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
// 加载一级分类并自动组装其对应的二级分类,兼容 state === 1
|
|
|
|
|
|
async loadFilterCategories() {
|
|
|
|
|
|
try {
|
2026-06-02 18:32:15 +08:00
|
|
|
|
const res = await request.post('/sj/firstclass', {})
|
2026-05-26 18:06:07 +08:00
|
|
|
|
if ((res.code === 200 || res.state === 1) && res.data) {
|
|
|
|
|
|
const firstClasses = res.data;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
// 并发请求所有的二级分类
|
|
|
|
|
|
const promises = firstClasses.map(async (first) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const subRes = await request.post('/sj/secondclass', { id: first.id })
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: first.id,
|
|
|
|
|
|
title: first.title,
|
|
|
|
|
|
children: ((subRes.code === 200 || subRes.state === 1) && subRes.data) ? subRes.data : []
|
|
|
|
|
|
}
|
2026-06-04 11:45:11 +08:00
|
|
|
|
} catch (e) {
|
2026-05-26 18:06:07 +08:00
|
|
|
|
return { id: first.id, title: first.title, children: [] }
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
const filterData = await Promise.all(promises);
|
|
|
|
|
|
// 过滤掉没有子分类的项,避免面板出现光秃秃的标题
|
|
|
|
|
|
this.brandFilterData = filterData.filter(group => group.children.length > 0);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.brandFilterData = []
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取分类字典失败', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
toggleSecondSelect(secId) {
|
|
|
|
|
|
const index = this.selectedSecondIds.indexOf(secId)
|
|
|
|
|
|
if (index === -1) {
|
|
|
|
|
|
this.selectedSecondIds.push(secId)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.selectedSecondIds.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
// 课程筛选
|
|
|
|
|
|
selectCourseType(type) {
|
|
|
|
|
|
// 点击已选中的则取消选中
|
2026-06-04 11:45:11 +08:00
|
|
|
|
if (this.selectedCourseType === type) {
|
2026-05-26 18:06:07 +08:00
|
|
|
|
this.selectedCourseType = ''
|
2026-06-04 11:45:11 +08:00
|
|
|
|
} else {
|
2026-05-26 18:06:07 +08:00
|
|
|
|
this.selectedCourseType = type
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
// 重置品牌筛选
|
|
|
|
|
|
resetBrandFilter() {
|
|
|
|
|
|
this.selectedSecondIds = []
|
|
|
|
|
|
this.applyBrandFilter()
|
|
|
|
|
|
},
|
|
|
|
|
|
resetCourseFilter() {
|
|
|
|
|
|
this.selectedCourseType = ''
|
|
|
|
|
|
this.applyCourseFilter()
|
|
|
|
|
|
},
|
|
|
|
|
|
resetFilter() {
|
|
|
|
|
|
if (this.currentTab === 'brand') {
|
|
|
|
|
|
this.resetBrandFilter()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.resetCourseFilter()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
applyFilterAndClose() {
|
|
|
|
|
|
if (this.currentTab === 'brand') {
|
|
|
|
|
|
this.applyBrandFilter()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.applyCourseFilter()
|
|
|
|
|
|
}
|
|
|
|
|
|
this.closeFilterPanel()
|
|
|
|
|
|
},
|
|
|
|
|
|
// 品牌过滤逻辑(根据二级分类ID相交判断)
|
|
|
|
|
|
applyBrandFilter() {
|
|
|
|
|
|
if (!this.rawBrandList.length) {
|
|
|
|
|
|
this.filteredBrandList = []
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (this.selectedSecondIds.length === 0) {
|
|
|
|
|
|
this.filteredBrandList = [...this.rawBrandList]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.filteredBrandList = this.rawBrandList.filter(brand => {
|
|
|
|
|
|
return brand.secondClassIds && brand.secondClassIds.some(id => this.selectedSecondIds.includes(id))
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
// 课程过滤逻辑(根据课程类型)
|
|
|
|
|
|
applyCourseFilter() {
|
|
|
|
|
|
if (!this.rawCourseList.length) {
|
|
|
|
|
|
this.filteredCourseList = []
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!this.selectedCourseType) {
|
|
|
|
|
|
this.filteredCourseList = [...this.rawCourseList]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.filteredCourseList = this.rawCourseList.filter(course => {
|
|
|
|
|
|
if (this.selectedCourseType === 'platform') return course.type === '平台课程'
|
|
|
|
|
|
if (this.selectedCourseType === 'brand') return course.type === '品牌课程'
|
|
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
// 加载原始数据(根据当前Tab)
|
|
|
|
|
|
loadData() {
|
|
|
|
|
|
if (this.currentTab === 'brand') {
|
|
|
|
|
|
this.loadBrandList()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.loadCourseList()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
async loadBrandList() {
|
|
|
|
|
|
if (USE_REAL_API) {
|
|
|
|
|
|
await this.fetchRealBrandList()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.mockBrandList()
|
|
|
|
|
|
}
|
|
|
|
|
|
this.applyBrandFilter()
|
|
|
|
|
|
},
|
|
|
|
|
|
async loadCourseList() {
|
|
|
|
|
|
if (USE_REAL_API) {
|
|
|
|
|
|
await this.fetchRealCourseList()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.mockCourseList()
|
|
|
|
|
|
}
|
|
|
|
|
|
this.applyCourseFilter()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// ============ 真实接口调用 ============
|
|
|
|
|
|
async fetchRealBrandList() {
|
|
|
|
|
|
try {
|
2026-06-02 18:21:35 +08:00
|
|
|
|
// 1. 接口路径换成真实的品牌收藏列表
|
|
|
|
|
|
const res = await request.post('/sj/brand/collectList', { page: 1, limit: 100 })
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
|
2026-06-02 18:21:35 +08:00
|
|
|
|
// 2. 不需要前端再做 filter(item => item.collect_state === 1) 了,直接用!
|
|
|
|
|
|
this.rawBrandList = res.data.list.map(item => {
|
2026-05-26 18:06:07 +08:00
|
|
|
|
let tags = []
|
2026-06-02 18:21:35 +08:00
|
|
|
|
if (item.franchise_state == 1) tags.push('franchise')
|
|
|
|
|
|
if (item.student_state == 1) tags.push('recruit')
|
2026-06-04 11:45:11 +08:00
|
|
|
|
if (tags.length === 0) tags.push('more')
|
|
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
return {
|
|
|
|
|
|
id: item.id,
|
|
|
|
|
|
name: item.name,
|
|
|
|
|
|
bgSrc: item.cover_img || 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/default.jpg',
|
|
|
|
|
|
avatar: item.logo_img || 'https://dummyimage.com/100x100/ccc/fff.png&text=Avatar',
|
|
|
|
|
|
tagKeys: tags,
|
2026-06-02 18:21:35 +08:00
|
|
|
|
// 3. 强制转字符串,防止后端返回纯数字导致 split 报错,同时兼容后端没返回此字段的情况
|
|
|
|
|
|
secondClassIds: item.second_class_ids ? String(item.second_class_ids).split(',').map(Number) : []
|
2026-05-26 18:06:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-06-02 18:21:35 +08:00
|
|
|
|
console.log('成功获取品牌收藏列表:', this.rawBrandList)
|
2026-05-26 18:06:07 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
this.rawBrandList = []
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2026-06-02 18:21:35 +08:00
|
|
|
|
console.error('获取品牌收藏失败详情:', error)
|
2026-05-26 18:06:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
async fetchRealCourseList() {
|
|
|
|
|
|
try {
|
2026-06-02 18:21:35 +08:00
|
|
|
|
// 1. 接口路径换成真实的技能收藏列表
|
|
|
|
|
|
const res = await request.post('/sj/skill/collectList', { page: 1, limit: 100 })
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
|
2026-06-02 18:21:35 +08:00
|
|
|
|
// 2. 同样不需要 filter,直接 map 映射数据
|
|
|
|
|
|
this.rawCourseList = res.data.list.map(item => {
|
2026-05-26 18:06:07 +08:00
|
|
|
|
return {
|
|
|
|
|
|
id: item.id,
|
|
|
|
|
|
title: item.name,
|
|
|
|
|
|
coverSrc: item.cover_img,
|
2026-06-02 18:21:35 +08:00
|
|
|
|
type: item.link_name || (item.link_type == 1 ? '平台课程' : '品牌课程'),
|
2026-05-26 18:06:07 +08:00
|
|
|
|
link_type: item.link_type,
|
|
|
|
|
|
views: item.browse_num || 0,
|
2026-06-02 18:21:35 +08:00
|
|
|
|
// 3. 使用安全的时间格式化
|
2026-06-04 11:45:11 +08:00
|
|
|
|
time: item.create_time ? this.formatTimeAgo(item.create_time) : '',
|
2026-05-26 18:06:07 +08:00
|
|
|
|
typeIcon: item.link_logo || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-06-02 18:21:35 +08:00
|
|
|
|
console.log('成功获取课程收藏列表:', this.rawCourseList)
|
2026-05-26 18:06:07 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
this.rawCourseList = []
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取课程收藏失败', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
formatTimeAgo(timeStr) {
|
2026-06-02 18:21:35 +08:00
|
|
|
|
if (!timeStr) return '';
|
|
|
|
|
|
const timeStrSafe = String(timeStr);
|
2026-05-26 18:06:07 +08:00
|
|
|
|
const now = new Date()
|
2026-06-02 18:21:35 +08:00
|
|
|
|
const time = new Date(timeStrSafe.replace(/-/g, '/'))
|
2026-05-26 18:06:07 +08:00
|
|
|
|
const diff = Math.floor((now - time) / 1000)
|
|
|
|
|
|
if (diff < 3600) return Math.floor(diff / 60) + '分钟前'
|
|
|
|
|
|
if (diff < 86400) return Math.floor(diff / 3600) + '小时前'
|
|
|
|
|
|
if (diff < 604800) return Math.floor(diff / 86400) + '天前'
|
2026-06-02 18:21:35 +08:00
|
|
|
|
return timeStrSafe.split(' ')[0]
|
2026-05-26 18:06:07 +08:00
|
|
|
|
},
|
|
|
|
|
|
goToBrandDetail(item) {
|
|
|
|
|
|
if (item.id) {
|
|
|
|
|
|
uni.navigateTo({ url: `/pages/brand/detail?id=${item.id}` })
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// ============ 模拟数据兜底 ============
|
|
|
|
|
|
mockBrandList() {
|
|
|
|
|
|
this.rawBrandList = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 1, name: '脸缘品牌', bgSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
|
|
|
|
|
|
avatar: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
|
|
|
|
|
|
tagKeys: ['franchise', 'recruit'], secondClassIds: [76, 77]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 2, name: '美肌工坊', bgSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
|
|
|
|
|
|
avatar: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
|
|
|
|
|
|
tagKeys: ['franchise'], secondClassIds: [78]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 3, name: '悦容堂', bgSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
|
|
|
|
|
|
avatar: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
|
|
|
|
|
|
tagKeys: ['recruit'], secondClassIds: [201, 202]
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
mockCourseList() {
|
|
|
|
|
|
this.rawCourseList = [
|
|
|
|
|
|
{ id: 1, title: '穿戴甲的操作视频扫码验券操作教程', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '平台课程', views: 836000, time: '7小时前' },
|
|
|
|
|
|
{ id: 2, title: '全身SPA的操作视频操作教程', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '平台课程', views: 2344, time: '3天前' },
|
|
|
|
|
|
{ id: 3, title: '高级面部护理手法详解', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '品牌课程', views: 56700, time: '1周前' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-21 16:52:23 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.collection-page {
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 将页面左右 padding 转移到内容区,保证 Tab 背景贯穿全屏 */
|
|
|
|
|
|
.tab-bar-wrap {
|
2026-06-04 11:45:11 +08:00
|
|
|
|
position: sticky;
|
|
|
|
|
|
/* 吸顶,保证滑动页面时一直显示在最前面 */
|
2026-05-26 18:06:07 +08:00
|
|
|
|
background: #fff;
|
|
|
|
|
|
padding: 10rpx 24rpx;
|
|
|
|
|
|
z-index: 101;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tab-bar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
padding: 10rpx 0;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.tab-items {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.tab-item {
|
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
color: #777777;
|
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
|
border-radius: 27rpx;
|
|
|
|
|
|
margin-right: 40rpx;
|
|
|
|
|
|
padding: 9rpx 24rpx;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
&.active {
|
|
|
|
|
|
color: #FF4767;
|
|
|
|
|
|
background: #FFECEF;
|
|
|
|
|
|
border: 1rpx solid #FFB0BD;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
border-radius: 29rpx;
|
2026-05-26 18:06:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.filter-area {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 10rpx 0;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.filter-text {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #666666;
|
|
|
|
|
|
margin-right: 6rpx;
|
|
|
|
|
|
}
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.filter-icon {
|
2026-06-05 15:27:42 +08:00
|
|
|
|
width: 24rpx;
|
|
|
|
|
|
height: 24rpx;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-06-05 15:27:42 +08:00
|
|
|
|
/* 展开时切换为上下箭头图标,调整大小 */
|
2026-05-26 18:06:07 +08:00
|
|
|
|
&.rotate {
|
2026-06-05 15:27:42 +08:00
|
|
|
|
width: 18rpx;
|
|
|
|
|
|
height: 12rpx;
|
2026-05-26 18:06:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 核心抽屉动画布局 */
|
|
|
|
|
|
.filter-panel {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
z-index: 100;
|
|
|
|
|
|
/* 低于 Tab区的101,保证隐藏时躲在其后 */
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
/* 面板关闭时,穿透点击到底部列表 */
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
/* 防止抽屉向上滑出时外溢 */
|
2026-05-26 18:06:07 +08:00
|
|
|
|
|
|
|
|
|
|
&.show {
|
2026-06-04 11:45:11 +08:00
|
|
|
|
pointer-events: auto;
|
|
|
|
|
|
|
|
|
|
|
|
/* 打开时恢复点击拦截 */
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.filter-mask {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.filter-container {
|
2026-06-04 11:45:11 +08:00
|
|
|
|
transform: translateY(50rpx);
|
|
|
|
|
|
/* 滑出 */
|
2026-05-26 18:06:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.filter-mask {
|
|
|
|
|
|
position: absolute;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.4);
|
2026-05-26 18:06:07 +08:00
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transition: opacity 0.3s ease-in-out;
|
|
|
|
|
|
}
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.filter-container {
|
|
|
|
|
|
position: absolute;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
2026-05-26 18:06:07 +08:00
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 0 0 30rpx 30rpx;
|
|
|
|
|
|
padding: 40rpx 30rpx;
|
|
|
|
|
|
max-height: 80vh;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
transform: translateY(-100%);
|
|
|
|
|
|
/* 默认隐藏在上方(即Tab背后) */
|
2026-05-26 18:06:07 +08:00
|
|
|
|
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 垂直分组设计的样式(图3) */
|
|
|
|
|
|
.filter-scroll {
|
|
|
|
|
|
max-height: 50vh;
|
|
|
|
|
|
}
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.filter-group {
|
|
|
|
|
|
margin-bottom: 30rpx;
|
|
|
|
|
|
|
|
|
|
|
|
.group-title {
|
|
|
|
|
|
font-weight: 500;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
line-height: 40rpx;
|
2026-05-26 18:06:07 +08:00
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pill-list {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
gap: 20rpx;
|
2026-05-26 18:06:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 通用胶囊按钮 */
|
|
|
|
|
|
.pill-item {
|
2026-06-04 11:45:11 +08:00
|
|
|
|
width: calc(25% - 16rpx);
|
|
|
|
|
|
/* 25%是一行四个,减去间距 */
|
2026-05-26 18:06:07 +08:00
|
|
|
|
height: 58rpx;
|
|
|
|
|
|
background: #F5F5F5;
|
|
|
|
|
|
font-weight: 400;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
line-height: 37rpx;
|
|
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
/* 使用 flex 居中 */
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
border-radius: 12rpx;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
/* 防止 padding 会撑大盒子 */
|
|
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
&.active {
|
|
|
|
|
|
background: #FFF0F2;
|
|
|
|
|
|
color: #FF4767;
|
|
|
|
|
|
border: 1rpx solid #FFB0BD;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.course-filter {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
|
/* padding: 20rpx 0 40rpx; */
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.course-type-item {
|
|
|
|
|
|
background: #F5F5F5;
|
|
|
|
|
|
color: #666666;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
padding: 12rpx 40rpx;
|
|
|
|
|
|
border-radius: 12rpx;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
&.active {
|
|
|
|
|
|
background: #FFF0F2;
|
|
|
|
|
|
color: #FF4767;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.filter-footer {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
margin-top: 20rpx;
|
|
|
|
|
|
padding-top: 20rpx;
|
|
|
|
|
|
/* border-top: 1rpx solid #F5F5F5; */
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
|
|
|
|
|
.reset-btn,
|
|
|
|
|
|
.confirm-btn {
|
2026-05-26 18:06:07 +08:00
|
|
|
|
width: 47%;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 20rpx 0;
|
|
|
|
|
|
border-radius: 40rpx;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
}
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.reset-btn {
|
|
|
|
|
|
background: #F5F5F5;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
}
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.confirm-btn {
|
|
|
|
|
|
background: #FF4767;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 内容区缩进补充 */
|
|
|
|
|
|
.collection-content {
|
|
|
|
|
|
padding: 0 24rpx;
|
|
|
|
|
|
margin-top: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.brand-grid {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
justify-content: space-between;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.brand-card-wrap {
|
|
|
|
|
|
width: 49.2%;
|
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.course-list {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-state {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 120rpx 0;
|
2026-06-04 11:45:11 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
.empty-icon {
|
|
|
|
|
|
width: 346rpx;
|
|
|
|
|
|
height: 373rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-21 16:52:23 +08:00
|
|
|
|
|
2026-05-26 18:06:07 +08:00
|
|
|
|
::v-deep .icon-headle {
|
2026-06-04 11:45:11 +08:00
|
|
|
|
width: 32rpx !important;
|
|
|
|
|
|
height: 32rpx !important;
|
2026-05-26 18:06:07 +08:00
|
|
|
|
}
|
2026-05-21 16:52:23 +08:00
|
|
|
|
</style>
|