我的收藏页面+技能集市组件
This commit is contained in:
parent
f6ed231d12
commit
2f9055454c
|
|
@ -985,7 +985,8 @@
|
|||
{
|
||||
"path": "pages/my/myFavorite",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的收藏"
|
||||
"navigationBarTitleText": "我的收藏",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
<template>
|
||||
<view class="skill-item" @click="handleClick">
|
||||
<view class="cover-box">
|
||||
<image class="cover" :src="item.coverSrc" mode="aspectFill"></image>
|
||||
<image
|
||||
class="play-icon"
|
||||
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/5c1ddfef-a200-424a-a9dc-03d9395568f6.png"
|
||||
></image>
|
||||
</view>
|
||||
<view class="skill-info">
|
||||
<view class="skill-title line-2">{{ item.title }}</view>
|
||||
<view class="skill-type" :class="typeClass">
|
||||
<image class="type-icon" :src="item.typeIcon || defaultTypeIcon"></image>
|
||||
<text class="type-text">{{ item.type || '平台课程' }}</text>
|
||||
</view>
|
||||
<view class="skill-stats">
|
||||
<image
|
||||
class="view-icon"
|
||||
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/2409b64a-56bb-4d9e-a4cc-5b3444fcf097.png"
|
||||
></image>
|
||||
{{ formatViews(item.views) }}观看 · {{ item.time }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SkillItem',
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({
|
||||
id: null,
|
||||
title: '',
|
||||
coverSrc: '',
|
||||
type: '平台课程',
|
||||
views: 0,
|
||||
time: '',
|
||||
typeIcon: ''
|
||||
})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultTypeIcon: 'https://dummyimage.com/30x30/ccc/fff.png&text=I' // 用于占位
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
typeClass() {
|
||||
const typeMap = {
|
||||
'平台课程': 'platform-course',
|
||||
'脸缘品牌': 'brand-course',
|
||||
'品牌课程': 'brand-course'
|
||||
}
|
||||
return typeMap[this.item.type] || 'platform-course'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatViews(views) {
|
||||
if (!views) return '0'
|
||||
if (views >= 10000) {
|
||||
return (views / 10000).toFixed(1) + 'w'
|
||||
}
|
||||
return views.toString()
|
||||
},
|
||||
handleClick() {
|
||||
// 跳转到技能/课程详情页
|
||||
if (this.item.id) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/skill/detail?id=${this.item.id}`
|
||||
})
|
||||
} else {
|
||||
uni.showToast({ title: '详情页开发中', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.skill-item {
|
||||
display: flex;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.cover-box {
|
||||
position: relative;
|
||||
width: 224rpx;
|
||||
height: 126rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.play-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 39rpx;
|
||||
height: 39rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.skill-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.skill-title {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 400;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.skill-type {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 22rpx;
|
||||
margin-top: 10rpx;
|
||||
|
||||
.type-icon {
|
||||
width: 26rpx;
|
||||
height: 26rpx;
|
||||
margin-right: 8rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.type-text {
|
||||
border-radius: 7rpx;
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
line-height: 33rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
}
|
||||
|
||||
&.platform-course .type-text {
|
||||
background: #f9d3d760;
|
||||
color: #E55463;
|
||||
}
|
||||
|
||||
&.brand-course .type-text {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
.skill-stats {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.view-icon {
|
||||
width: 26rpx;
|
||||
height: 22rpx;
|
||||
margin-right: 8rpx;
|
||||
margin-top: -2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -129,20 +129,11 @@
|
|||
<view class="header-right">更多技能 <image class="more-right-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/ee119e24-1cb2-43df-86e6-61af8e9fd0ad.png"></image> </view>
|
||||
</view>
|
||||
<view class="skill-list">
|
||||
<view class="skill-item" v-for="(item, index) in skills" :key="index">
|
||||
<view class="cover-box">
|
||||
<image class="cover" :src="item.coverSrc"></image>
|
||||
<image class="play-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/5c1ddfef-a200-424a-a9dc-03d9395568f6.png"></image>
|
||||
</view>
|
||||
<view class="skill-info">
|
||||
<view class="skill-title line-2">{{ item.title }}</view>
|
||||
<view class="skill-type" :class="item.typeClass">
|
||||
<image class="type-icon" src="https://dummyimage.com/30x30/ccc/fff.png&text=I"></image>
|
||||
<text class="type-text">{{ item.type }}</text>
|
||||
</view>
|
||||
<view class="skill-stats"> <image class="view-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/2409b64a-56bb-4d9e-a4cc-5b3444fcf097.png"></image>{{ item.views }}观看 · {{ item.time }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<skill-card
|
||||
v-for="(item, index) in skills"
|
||||
:key="index"
|
||||
:item="item"
|
||||
></skill-card>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -158,6 +149,7 @@ import request from "@/utils/request";
|
|||
import filters from "./components/filters.vue";
|
||||
import CustomNavbar from "@/components/custom-navbar/custom-navbar.vue";
|
||||
import BrandCard from "./components/brand-card.vue";
|
||||
import SkillCard from "./components/skill-card.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -165,7 +157,8 @@ export default {
|
|||
projectTabs,
|
||||
CustomNavbar,
|
||||
TabBar,
|
||||
BrandCard
|
||||
BrandCard,
|
||||
SkillCard
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,22 +1,666 @@
|
|||
<template>
|
||||
<view>
|
||||
<view class="collection-page">
|
||||
|
||||
<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="addAddress">
|
||||
</custom-navbar>
|
||||
|
||||
<view class="nav-placeholder" :style="{ height: navHeight + 'px' }"></view>
|
||||
|
||||
<view class="tab-bar-wrap" :style="{ top: navHeight + 'px' }">
|
||||
<view class="tab-bar">
|
||||
<view class="tab-items">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === 'brand' }"
|
||||
@tap="switchTab('brand')"
|
||||
>
|
||||
收藏的品牌
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === 'course' }"
|
||||
@tap="switchTab('course')"
|
||||
>
|
||||
收藏的课程
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-area" @tap="toggleFilterPanel">
|
||||
<text class="filter-text">筛选</text>
|
||||
<image class="filter-icon" :src="filterIcon" mode="aspectFit" :class="{ 'rotate': filterVisible }"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-panel" :class="{ 'show': filterVisible }" :style="{ top: filterTop + 'px' }" catchtouchmove="true">
|
||||
<view class="filter-mask" @tap="closeFilterPanel"></view>
|
||||
<view class="filter-container">
|
||||
|
||||
<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">
|
||||
<view
|
||||
class="pill-item"
|
||||
v-for="sub in group.children"
|
||||
:key="sub.id"
|
||||
:class="{ active: selectedSecondIds.includes(sub.id) }"
|
||||
@tap="toggleSecondSelect(sub.id)"
|
||||
>
|
||||
{{ sub.title }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view v-else class="course-filter">
|
||||
<view
|
||||
class="course-type-item"
|
||||
:class="{ active: selectedCourseType === 'platform' }"
|
||||
@tap="selectCourseType('platform')"
|
||||
>
|
||||
平台课程
|
||||
</view>
|
||||
<view
|
||||
class="course-type-item"
|
||||
:class="{ active: selectedCourseType === 'brand' }"
|
||||
@tap="selectCourseType('brand')"
|
||||
>
|
||||
品牌课程
|
||||
</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">
|
||||
<brand-card
|
||||
class="brand-card-wrap"
|
||||
v-for="(item, index) in filteredBrandList"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:tagKeys="item.tagKeys"
|
||||
@click="goToBrandDetail(item)"
|
||||
></brand-card>
|
||||
</view>
|
||||
<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>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="collection-content" v-else>
|
||||
<view class="course-list" v-if="filteredCourseList.length > 0">
|
||||
<skill-card
|
||||
v-for="(item, index) in filteredCourseList"
|
||||
:key="index"
|
||||
:item="item"
|
||||
></skill-card>
|
||||
</view>
|
||||
<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>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view style="height: 120rpx;"></view>
|
||||
<TabBar :selected="3"></TabBar>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabBar from "@/components/tabbar/tabbar.vue"
|
||||
import BrandCard from "@/pages/home/components/brand-card.vue"
|
||||
import SkillCard from "@/pages/home/components/skill-card.vue"
|
||||
import request from "@/utils/request"
|
||||
|
||||
// 数据模式开关:true=调用真实接口,false=使用模拟数据
|
||||
const USE_REAL_API = true
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TabBar,
|
||||
BrandCard,
|
||||
SkillCard
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
navHeight: 0, // 动态计算的导航栏高度
|
||||
filterTop: 0, // 动态计算的筛选框顶部起点
|
||||
|
||||
currentTab: 'brand',
|
||||
// 筛选相关
|
||||
filterVisible: false,
|
||||
|
||||
// 品牌筛选下拉数据 (合并一级和二级)
|
||||
brandFilterData: [],
|
||||
selectedSecondIds: [], // 选中的二级ID数组
|
||||
|
||||
// 课程筛选
|
||||
selectedCourseType: '', // 'platform' 或 'brand'
|
||||
// 原始数据(收藏的数据)
|
||||
rawBrandList: [],
|
||||
rawCourseList: [],
|
||||
// 展示数据(过滤后)
|
||||
filteredBrandList: [],
|
||||
filteredCourseList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
computed: {
|
||||
filterIcon() {
|
||||
// 这里的图标链接可以统一成一个,配合CSS的旋转动画更好看
|
||||
return 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e00ab2ac-bccf-42d8-820c-65727327e279.png'
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 精准计算吸顶高度与下拉框起点,防止蒙版遮挡白色的Tab区
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
const sbh = sysInfo.statusBarHeight || 0
|
||||
this.navHeight = sbh
|
||||
// Tab栏自身的高度大约为 106rpx,转化为 px 叠加上去
|
||||
this.filterTop = this.navHeight + uni.upx2px(106)
|
||||
|
||||
this.loadFilterCategories()
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
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
|
||||
},
|
||||
|
||||
// 加载一级分类并自动组装其对应的二级分类,兼容 state === 1
|
||||
async loadFilterCategories() {
|
||||
try {
|
||||
const syrId = uni.getStorageSync('syrId') || 1;
|
||||
const res = await request.post('/sj/firstclass', { id: syrId })
|
||||
if ((res.code === 200 || res.state === 1) && res.data) {
|
||||
const firstClasses = res.data;
|
||||
|
||||
// 并发请求所有的二级分类
|
||||
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 : []
|
||||
}
|
||||
} catch(e) {
|
||||
return { id: first.id, title: first.title, children: [] }
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
// 点击已选中的则取消选中
|
||||
if(this.selectedCourseType === type){
|
||||
this.selectedCourseType = ''
|
||||
}else{
|
||||
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 {
|
||||
const res = await request.post('/sj/brand/list', { page: 1, limit: 100 })
|
||||
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
|
||||
const collectedBrands = res.data.list.filter(item => item.collect_state === 1)
|
||||
this.rawBrandList = collectedBrands.map(item => {
|
||||
let tags = []
|
||||
if (item.franchise_state === 1) tags.push('franchise')
|
||||
if (item.student_state === 1) tags.push('recruit')
|
||||
if (tags.length === 0) tags.push('more')
|
||||
|
||||
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,
|
||||
secondClassIds: item.second_class_ids ? item.second_class_ids.split(',').map(Number) : []
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.rawBrandList = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取品牌收藏失败', error)
|
||||
}
|
||||
},
|
||||
async fetchRealCourseList() {
|
||||
try {
|
||||
const res = await request.post('/sj/skill/list', { page: 1, limit: 100 })
|
||||
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
|
||||
const collectedCourses = res.data.list.filter(item => item.collect_state === 1)
|
||||
this.rawCourseList = collectedCourses.map(item => {
|
||||
return {
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
coverSrc: item.cover_img,
|
||||
type: item.link_name || (item.link_type === 1 ? '平台课程' : '品牌课程'),
|
||||
link_type: item.link_type,
|
||||
views: item.browse_num || 0,
|
||||
time: item.create_time ? this.formatTimeAgo(item.create_time) : '',
|
||||
typeIcon: item.link_logo || ''
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.rawCourseList = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取课程收藏失败', error)
|
||||
}
|
||||
},
|
||||
|
||||
formatTimeAgo(timeStr) {
|
||||
const now = new Date()
|
||||
const time = new Date(timeStr.replace(/-/g, '/'))
|
||||
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) + '天前'
|
||||
return timeStr.split(' ')[0]
|
||||
},
|
||||
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周前' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="scss" scoped>
|
||||
.collection-page {
|
||||
min-height: 100vh;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* 将页面左右 padding 转移到内容区,保证 Tab 背景贯穿全屏 */
|
||||
.tab-bar-wrap {
|
||||
position: sticky; /* 吸顶,保证滑动页面时一直显示在最前面 */
|
||||
background: #fff;
|
||||
padding: 10rpx 24rpx;
|
||||
z-index: 101;
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: transparent;
|
||||
padding: 10rpx 0;
|
||||
|
||||
.tab-items {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tab-item {
|
||||
font-weight: 400;
|
||||
font-size: 26rpx;
|
||||
color: #777777;
|
||||
background: #f5f5f5;
|
||||
border-radius: 27rpx;
|
||||
margin-right: 40rpx;
|
||||
padding: 9rpx 24rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.active {
|
||||
color: #FF4767;
|
||||
background: #FFECEF;
|
||||
border: 1rpx solid #FFB0BD;
|
||||
border-radius: 29rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10rpx 0;
|
||||
|
||||
.filter-text {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
.filter-icon {
|
||||
width: 16rpx;
|
||||
height: 10rpx;
|
||||
transition: transform 0.3s ease;
|
||||
/* 展开时图标倒转 */
|
||||
&.rotate {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 核心抽屉动画布局 */
|
||||
.filter-panel {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 100; /* 低于 Tab区的101,保证隐藏时躲在其后 */
|
||||
pointer-events: none; /* 面板关闭时,穿透点击到底部列表 */
|
||||
overflow: hidden; /* 防止抽屉向上滑出时外溢 */
|
||||
|
||||
&.show {
|
||||
pointer-events: auto; /* 打开时恢复点击拦截 */
|
||||
.filter-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
.filter-container {
|
||||
transform: translateY(50rpx); /* 滑出 */
|
||||
}
|
||||
}
|
||||
|
||||
.filter-mask {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.4);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
background: #fff;
|
||||
border-radius: 0 0 30rpx 30rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 10rpx 20rpx rgba(0,0,0,0.05);
|
||||
transform: translateY(-100%); /* 默认隐藏在上方(即Tab背后) */
|
||||
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 垂直分组设计的样式(图3) */
|
||||
.filter-scroll {
|
||||
max-height: 50vh;
|
||||
}
|
||||
.filter-group {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.group-title {
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
line-height: 40rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.pill-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 通用胶囊按钮 */
|
||||
.pill-item {
|
||||
width: calc(25% - 16rpx); /* 25%是一行四个,减去间距 */
|
||||
height: 58rpx;
|
||||
background: #F5F5F5;
|
||||
font-weight: 400;
|
||||
font-size: 26rpx;
|
||||
color: #333333;
|
||||
line-height: 37rpx;
|
||||
|
||||
/* 使用 flex 居中 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
border-radius: 12rpx;
|
||||
transition: all 0.3s;
|
||||
box-sizing: border-box; /* 防止 padding 会撑大盒子 */
|
||||
|
||||
&.active {
|
||||
background: #FFF0F2;
|
||||
color: #FF4767;
|
||||
border: 1rpx solid #FFB0BD;
|
||||
}
|
||||
}
|
||||
|
||||
.course-filter {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
/* padding: 20rpx 0 40rpx; */
|
||||
|
||||
.course-type-item {
|
||||
background: #F5F5F5;
|
||||
color: #666666;
|
||||
font-size: 26rpx;
|
||||
padding: 12rpx 40rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&.active {
|
||||
background: #FFF0F2;
|
||||
color: #FF4767;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
/* border-top: 1rpx solid #F5F5F5; */
|
||||
|
||||
.reset-btn, .confirm-btn {
|
||||
width: 47%;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
background: #F5F5F5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
.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;
|
||||
|
||||
.empty-icon {
|
||||
width: 346rpx;
|
||||
height: 373rpx;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .icon-headle {
|
||||
width: 32rpx !important;
|
||||
height: 32rpx !important;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue