增加品牌方案管理
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<view class="search-page">
|
||||
<view class="header-wrap" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-bar" :style="{ height: navBarHeight + 'px' }">
|
||||
<view class="header-wrap" :style="headerStyle">
|
||||
<view class="nav-bar" :style="navStyle">
|
||||
<text class="nav-title">⌕ 团课搜索</text>
|
||||
</view>
|
||||
<view class="search-area">
|
||||
<view class="search-area" :style="{ backgroundColor: brandConfig.secondaryColor }">
|
||||
<view class="search-box">
|
||||
<text class="search-symbol">⌕</text>
|
||||
<input class="search-input" v-model="searchKeyword" placeholder="搜索课程名称、教练、场地..."
|
||||
@@ -31,7 +31,8 @@
|
||||
|
||||
<view class="period-row">
|
||||
<view v-for="(period, idx) in periods" :key="idx" class="period-item"
|
||||
:class="{ active: currentPeriod === period.value }" @click="selectPeriod(period.value)">
|
||||
:class="{ active: currentPeriod === period.value }" @click="selectPeriod(period.value)"
|
||||
:style="currentPeriod === period.value ? activeBrandStyle : ''">
|
||||
<text>{{ period.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -40,18 +41,20 @@
|
||||
|
||||
<view class="type-section">
|
||||
<view class="type-all-row">
|
||||
<view class="filter-tab" :class="{ active: currentFilter === 'all' }" @click="switchFilter('all')">全部</view>
|
||||
<view class="filter-tab" :class="{ active: currentFilter === 'all' }" @click="switchFilter('all')"
|
||||
:style="currentFilter === 'all' ? activeBrandStyle : ''">全部</view>
|
||||
</view>
|
||||
<view class="type-grid" :class="{ expanded: typesExpanded }">
|
||||
<view v-for="(row, ri) in typeRows" :key="ri" class="type-row" :class="{ 'row-visible': isRowVisible(ri) }">
|
||||
<view v-for="(tab, ti) in row" :key="ti" class="filter-tab"
|
||||
:class="{ active: currentFilter === tab.value }" @click="selectType(tab)">
|
||||
:class="{ active: currentFilter === tab.value }" @click="selectType(tab)"
|
||||
:style="currentFilter === tab.value ? activeBrandStyle : ''">
|
||||
{{ tab.label }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="typeRows.length > 1" class="type-expand-row">
|
||||
<text class="type-expand-btn" @click="typesExpanded = !typesExpanded">
|
||||
<text class="type-expand-btn" @click="typesExpanded = !typesExpanded" :style="{ color: brandConfig.primaryColor }">
|
||||
{{ typesExpanded ? '收起 ▲' : '展开更多 ▾' }}
|
||||
</text>
|
||||
</view>
|
||||
@@ -61,7 +64,8 @@
|
||||
<text class="result-count">共 {{ filteredCourses.length }} 个课程</text>
|
||||
<view class="sort-options">
|
||||
<text v-for="(sort, idx) in sortOptions" :key="idx" class="sort-item"
|
||||
:class="{ active: currentSort === sort.value }" @click="switchSort(sort.value)">
|
||||
:class="{ active: currentSort === sort.value }" @click="switchSort(sort.value)"
|
||||
:style="currentSort === sort.value ? 'color:' + brandConfig.primaryColor : ''">
|
||||
{{ sort.label }}
|
||||
</text>
|
||||
</view>
|
||||
@@ -88,20 +92,20 @@
|
||||
<view class="course-body">
|
||||
<view class="course-top">
|
||||
<text class="course-name">{{ course.courseName }}</text>
|
||||
<text class="course-price" :class="{ free: course.storedValueAmount === 0 }">
|
||||
<text class="course-price" :class="{ free: course.storedValueAmount === 0 }" :style="{ color: brandConfig.primaryColor }">
|
||||
{{ course.storedValueAmount > 0 ? '¥' + course.storedValueAmount + '/次' : '免费' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="course-labels" v-if="course.labels && course.labels.length">
|
||||
<text v-for="(label, li) in course.labels" :key="li" class="course-label"
|
||||
:style="{ background: label.color + '1a', color: label.color }">{{ label.labelName }}</text>
|
||||
:style="label._style">{{ label.labelName }}</text>
|
||||
</view>
|
||||
|
||||
<view class="course-meta">
|
||||
<text class="meta-icon">🕓</text>
|
||||
<text class="course-date">{{ course.shortDate }}</text>
|
||||
<text class="course-time">{{ course.shortTime }} - {{ course.endShortTime }}</text>
|
||||
<text class="course-duration" v-if="course.duration">{{ course.duration }}</text>
|
||||
<text class="course-duration" v-if="course.duration" :style="{ color: brandConfig.primaryColor }">{{ course.duration }}</text>
|
||||
</view>
|
||||
<view class="course-meta">
|
||||
<text class="meta-icon">📍</text>
|
||||
@@ -110,7 +114,8 @@
|
||||
<view class="course-bottom">
|
||||
<text class="course-capacity">已预约 {{ course.currentMembers }}/{{ course.maxMembers }}人</text>
|
||||
<button class="btn-book" :class="{ disabled: !course.canBook }" :disabled="!course.canBook"
|
||||
@click.stop="bookCourse(course)">
|
||||
@click.stop="bookCourse(course)"
|
||||
:style="course.canBook ? activeBrandStyle : ''">
|
||||
{{ course.canBook ? '立即预约' : course.statusLabel }}
|
||||
</button>
|
||||
</view>
|
||||
@@ -133,6 +138,7 @@
|
||||
const courseApi = require('../../api/course')
|
||||
const bookingApi = require('../../api/booking')
|
||||
const store = require('../../store/index')
|
||||
const brandStore = require('../../store/brand')
|
||||
const { resolveCoverUrl } = require('../../utils/request')
|
||||
|
||||
export default {
|
||||
@@ -161,7 +167,8 @@
|
||||
{ label: '热度', value: 'popular' }
|
||||
],
|
||||
courses: [],
|
||||
activeBookedCourseIds: new Set() // 用户当前已预约(status=0)的课程ID集合
|
||||
activeBookedCourseIds: new Set(), // 用户当前已预约(status=0)的课程ID集合
|
||||
brandConfig: brandStore.config
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
@@ -179,15 +186,28 @@
|
||||
this.totalHeaderHeight = statusBarHeight + navBarHeight
|
||||
this.loadCourses()
|
||||
this.loadTypes()
|
||||
uni.$on('brand:updated', (config) => { this.brandConfig = config })
|
||||
},
|
||||
onUnload() {
|
||||
uni.$off('brand:updated')
|
||||
},
|
||||
onShow() {
|
||||
this.loadActiveBookings()
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
Promise.all([this.loadCourses(), this.loadTypes(), this.loadActiveBookings()])
|
||||
Promise.all([this.loadCourses(), this.loadTypes(), this.loadActiveBookings(), brandStore.fetchConfig()])
|
||||
.finally(() => { uni.stopPullDownRefresh() })
|
||||
},
|
||||
computed: {
|
||||
headerStyle() {
|
||||
return 'padding-top:' + this.statusBarHeight + 'px;background-color:' + this.brandConfig.secondaryColor
|
||||
},
|
||||
navStyle() {
|
||||
return 'height:' + this.navBarHeight + 'px;background-color:' + this.brandConfig.secondaryColor
|
||||
},
|
||||
activeBrandStyle() {
|
||||
return 'background:' + this.brandConfig.primaryColor + ';color:' + this.brandConfig.secondaryColor
|
||||
},
|
||||
// 将类型列表按每行 COLS_PER_ROW 个拆分为二维数组
|
||||
typeRows() {
|
||||
const tabs = this.courseTypes.map(t => ({
|
||||
@@ -266,7 +286,7 @@
|
||||
status: statusStr,
|
||||
statusLabel: statusLabel,
|
||||
storedValueAmount: c.storedValueAmount != null ? c.storedValueAmount : 0,
|
||||
labels: this.getTypeLabels(c.courseType),
|
||||
labels: this.getTypeLabels(c.courseType).map(l => ({ ...l, _style: { background: l.color + '1a', color: l.color } })),
|
||||
coverImage: resolveCoverUrl(c.coverImage),
|
||||
}
|
||||
})
|
||||
@@ -305,6 +325,9 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
labelStyle(label) {
|
||||
return { background: label.color + '1a', color: label.color }
|
||||
},
|
||||
// 从已加载的课程类型中匹配标签
|
||||
getTypeLabels(courseType) {
|
||||
const type = this.getTypeInfo(courseType)
|
||||
|
||||
Reference in New Issue
Block a user