修正多处问题
This commit is contained in:
@@ -1,189 +1,334 @@
|
||||
<template>
|
||||
<view class="scroll-container theme-light">
|
||||
<view class="bt-page">
|
||||
<MemberInfoSubNav title="我的课程" @back="goBack" />
|
||||
<view class="mi-mod-tabs">
|
||||
<view
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
class="bt-tab"
|
||||
:class="{ 'bt-tab--active': activeTab === tab.key }"
|
||||
hover-class="mi-tap-tab--hover"
|
||||
@tap="activeTab = tab.key"
|
||||
>
|
||||
<text class="bt-tab__text">{{ tab.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<PageHeader title="我的课程" subtitle="" :show-back="true">
|
||||
<template #right>
|
||||
<view class="header-scan-btn" @click="handleScan">
|
||||
<text class="header-scan-icon">📷</text>
|
||||
</view>
|
||||
</template>
|
||||
</PageHeader>
|
||||
<view class="bt-page__body">
|
||||
<!-- 团课 -->
|
||||
<template v-if="activeTab === 'group'">
|
||||
<view class="mi-mod-subtabs">
|
||||
<text
|
||||
class="mi-mod-subtab"
|
||||
:class="{ 'mi-mod-subtab--on': groupSub === 'ongoing' }"
|
||||
@tap="groupSub = 'ongoing'"
|
||||
>进行中</text>
|
||||
<text
|
||||
class="mi-mod-subtab"
|
||||
:class="{ 'mi-mod-subtab--on': groupSub === 'completed' }"
|
||||
@tap="groupSub = 'completed'"
|
||||
>已完成</text>
|
||||
</view>
|
||||
<view
|
||||
v-for="item in groupList"
|
||||
:key="item.id"
|
||||
class="mi-mod-course-card"
|
||||
hover-class="mi-tap-card--hover"
|
||||
@tap="onGroupCourse(item)"
|
||||
>
|
||||
<image class="mi-mod-course-card__banner" :src="item.banner" mode="aspectFill" />
|
||||
<view class="mi-mod-course-card__content">
|
||||
<!-- 子标签:进行中 / 已完成 -->
|
||||
<view class="mi-mod-subtabs">
|
||||
<text
|
||||
class="mi-mod-subtab"
|
||||
:class="{ 'mi-mod-subtab--on': activeTab === 'ongoing' }"
|
||||
@tap="activeTab = 'ongoing'"
|
||||
>进行中</text>
|
||||
<text
|
||||
class="mi-mod-subtab"
|
||||
:class="{ 'mi-mod-subtab--on': activeTab === 'completed' }"
|
||||
@tap="activeTab = 'completed'"
|
||||
>已完成</text>
|
||||
</view>
|
||||
|
||||
<!-- 预约列表 -->
|
||||
<view
|
||||
v-for="item in displayedBookings"
|
||||
:key="item.id"
|
||||
class="mi-mod-course-card"
|
||||
hover-class="mi-tap-card--hover"
|
||||
>
|
||||
<image
|
||||
class="mi-mod-course-card__banner"
|
||||
:src="item.coverImage || 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/default-course.png'"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="mi-mod-course-card__content">
|
||||
<view class="mi-mod-course-card__header">
|
||||
<text class="mi-mod-course-card__title">{{ item.title }}</text>
|
||||
<text class="mi-mod-course-card__coach">{{ item.coach }}</text>
|
||||
<view class="mi-mod-course-card__progress">
|
||||
<view class="mi-mod-course-card__progress-bar">
|
||||
<view class="mi-mod-course-card__progress-fill" :style="{ width: pct(item) + '%' }"></view>
|
||||
</view>
|
||||
<text class="mi-mod-course-card__progress-text">{{ item.progress }}/{{ item.total }}</text>
|
||||
</view>
|
||||
<text class="mi-mod-course-card__meta">{{ item.schedule }} · {{ item.location }}</text>
|
||||
<view v-if="groupSub === 'ongoing' && item.canCancel" class="mi-mod-course-card__action" @tap.stop="goBooking">
|
||||
<text>取消预约</text>
|
||||
</view>
|
||||
<view v-if="groupSub === 'completed' && item.canEvaluate" class="mi-mod-course-card__action" @tap.stop="evaluate(item)">
|
||||
<text>去评价</text>
|
||||
</view>
|
||||
<text class="mi-mod-course-card__status" :class="'mi-mod-course-card__status--' + item.statusClass">{{ item.statusLabel }}</text>
|
||||
</view>
|
||||
<text class="mi-mod-course-card__meta">{{ item.schedule }} · {{ item.location }}</text>
|
||||
<!-- 仅已预约状态显示取消按钮 -->
|
||||
<view v-if="activeTab === 'ongoing' && item.statusClass === 'booked'" class="mi-mod-course-card__cancel" @tap.stop="handleCancel(item)">
|
||||
<text>取消预约</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<!-- 私教 -->
|
||||
<template v-else-if="activeTab === 'private'">
|
||||
<view class="bt-card">
|
||||
<text class="bt-card__title">剩余课时 {{ privateData.remaining }} 节</text>
|
||||
<view class="mi-detail-coach">
|
||||
<image class="mi-detail-coach__avatar" :src="privateData.coachAvatar" mode="aspectFill" />
|
||||
<view>
|
||||
<text class="mi-detail-coach__name">{{ privateData.coach }}</text>
|
||||
<text class="mi-detail-coach__rating">下次 {{ privateData.nextClass }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bt-card">
|
||||
<text class="bt-card__title">已约课程</text>
|
||||
<view v-for="b in privateData.bookings" :key="b.id" class="mi-mod-session">
|
||||
<text class="mi-mod-session__title">{{ b.title }}</text>
|
||||
<text class="mi-mod-session__meta">{{ b.time }} · {{ b.location }} · {{ b.status }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 线上课 -->
|
||||
<template v-else-if="activeTab === 'online'">
|
||||
<view
|
||||
v-for="item in onlineList"
|
||||
:key="item.id"
|
||||
class="mi-mod-course-card"
|
||||
hover-class="mi-tap-card--hover"
|
||||
@tap="goOnline(item)"
|
||||
>
|
||||
<image class="mi-mod-course-card__banner" :src="item.cover" mode="aspectFill" />
|
||||
<view class="mi-mod-course-card__content">
|
||||
<text class="mi-mod-course-card__title">{{ item.title }}</text>
|
||||
<text class="mi-mod-course-card__meta">{{ item.duration }} · 进度 {{ item.progress }}%</text>
|
||||
<text v-if="item.type === 'live'" class="mi-mod-course-card__next">直播 {{ item.liveTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 训练营 -->
|
||||
<template v-else>
|
||||
<view
|
||||
v-for="item in packageList"
|
||||
:key="item.id"
|
||||
class="mi-mod-course-card"
|
||||
>
|
||||
<image class="mi-mod-course-card__banner" :src="item.banner" mode="aspectFill" />
|
||||
<view class="mi-mod-course-card__content">
|
||||
<text class="mi-mod-course-card__title">{{ item.title }}</text>
|
||||
<text class="mi-mod-course-card__coach">{{ item.coach }}</text>
|
||||
<view class="mi-mod-course-card__progress">
|
||||
<view class="mi-mod-course-card__progress-bar">
|
||||
<view class="mi-mod-course-card__progress-fill" :style="{ width: pct(item) + '%' }"></view>
|
||||
</view>
|
||||
<text class="mi-mod-course-card__progress-text">{{ item.progress }}/{{ item.total }} 节</text>
|
||||
</view>
|
||||
<text class="mi-mod-course-card__meta">{{ item.schedule }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view v-if="isEmpty" class="bt-empty">
|
||||
<text class="bt-empty__text">暂无课程</text>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="!displayedBookings.length" class="bt-empty">
|
||||
<text class="bt-empty__text">
|
||||
{{ activeTab === 'ongoing' ? '暂无进行中的预约' : '暂无历史预约' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付密码弹窗 -->
|
||||
<PayPasswordModal
|
||||
:visible="showPayPwd"
|
||||
:title="payPwdTitle"
|
||||
:subtitle="payPwdSubtitle"
|
||||
:cancel-notice="payPwdNotice"
|
||||
@confirm="onPayPasswordConfirm"
|
||||
@cancel="onPayPasswordCancel"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
|
||||
import { PAGE, navigateToPage } from '@/common/constants/routes.js'
|
||||
import { moduleMock, getMyCoursesData } from '@/common/memberInfo/moduleStore.js'
|
||||
import { loadMemberStore } from '@/common/memberInfo/store.js'
|
||||
import { subPageMixin } from '@/common/memberInfo/mixins.js'
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import PageHeader from '@/components/index/PageHeader.vue'
|
||||
import PayPasswordModal from '@/components/global/PayPasswordModal.vue'
|
||||
import { getMemberId } from '@/utils/request.js'
|
||||
import { getMemberBookings, qrSignInGroupCourse } from '@/api/groupCourse.js'
|
||||
import { cancelBooking as cancelBookingApi } from '@/api/groupCourse.js'
|
||||
import { getConfigByKey } from '@/api/main.js'
|
||||
|
||||
export default {
|
||||
components: { MemberInfoSubNav },
|
||||
mixins: [subPageMixin],
|
||||
data() {
|
||||
return {
|
||||
tabs: moduleMock.myCourseTabs,
|
||||
activeTab: 'group',
|
||||
groupSub: 'ongoing',
|
||||
privateData: {},
|
||||
onlineList: [],
|
||||
packageList: [],
|
||||
groupData: { ongoing: [], completed: [] }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
groupList() {
|
||||
return this.groupData[this.groupSub] || []
|
||||
},
|
||||
isEmpty() {
|
||||
if (this.activeTab === 'group') return !this.groupList.length
|
||||
if (this.activeTab === 'private') return !this.privateData.bookings?.length
|
||||
if (this.activeTab === 'online') return !this.onlineList.length
|
||||
return !this.packageList.length
|
||||
}
|
||||
},
|
||||
onShow() { this.refresh() },
|
||||
methods: {
|
||||
refresh() {
|
||||
const store = loadMemberStore()
|
||||
this.groupData = getMyCoursesData(store, 'group')
|
||||
this.privateData = getMyCoursesData(store, 'private')
|
||||
this.onlineList = getMyCoursesData(store, 'online').list || []
|
||||
this.packageList = getMyCoursesData(store, 'package').list || []
|
||||
},
|
||||
pct(item) {
|
||||
return item.total ? Math.min(100, Math.round((item.progress / item.total) * 100)) : 0
|
||||
},
|
||||
goBooking() { navigateToPage(PAGE.BOOKING) },
|
||||
goOnline(item) { navigateToPage(`${PAGE.ONLINE_COURSE}?id=${item.id}`) },
|
||||
evaluate(item) {
|
||||
navigateToPage(`${PAGE.COURSE_EVALUATE}?title=${encodeURIComponent(item.title)}`)
|
||||
},
|
||||
onGroupCourse(item) {
|
||||
uni.showModal({
|
||||
title: item.title,
|
||||
content: `${item.coach}\n${item.schedule}\n${item.location}`,
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
const activeTab = ref('ongoing')
|
||||
const ongoingBookings = ref([])
|
||||
const completedBookings = ref([])
|
||||
const loading = ref(false)
|
||||
|
||||
// 支付密码弹窗状态
|
||||
const showPayPwd = ref(false)
|
||||
const payPwdTitle = ref('')
|
||||
const payPwdSubtitle = ref('')
|
||||
const payPwdNotice = ref('')
|
||||
let payPwdResolve = null
|
||||
let cancelTarget = null
|
||||
|
||||
// 已取消次数
|
||||
const cancelCount = ref(0)
|
||||
const freeCancelLimit = ref(3) // 默认值,从API获取
|
||||
const freeCampaignInitialized = ref(false)
|
||||
const freeCancelsLeft = computed(() => Math.max(0, freeCancelLimit.value - cancelCount.value))
|
||||
|
||||
async function fetchFreeLimit() {
|
||||
try {
|
||||
const res = await getConfigByKey('group_course.cancel_free_limit')
|
||||
const value = res?.configValue || res?.data?.configValue || '3'
|
||||
freeCancelLimit.value = parseInt(value) || 3
|
||||
freeCampaignInitialized.value = true
|
||||
console.log('[myCourses] 免费取消次数阈值:', freeCancelLimit.value, '已取消次数:', cancelCount.value, '剩余:', freeCancelsLeft.value)
|
||||
} catch (e) {
|
||||
console.warn('[myCourses] 获取免费取消次数配置失败,使用默认值3:', e)
|
||||
freeCampaignInitialized.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const displayedBookings = computed(() => {
|
||||
return activeTab.value === 'ongoing'
|
||||
? ongoingBookings.value
|
||||
: completedBookings.value
|
||||
})
|
||||
|
||||
async function fetchBookings() {
|
||||
if (loading.value) return
|
||||
loading.value = true
|
||||
console.log('[myCourses] 开始获取预约记录')
|
||||
|
||||
const memberId = getMemberId()
|
||||
if (!memberId) {
|
||||
console.warn('[myCourses] 未登录')
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await getMemberBookings(memberId)
|
||||
const bookings = Array.isArray(res) ? res : (res?.data || res?.content || [])
|
||||
console.log('[myCourses] 预约记录:', bookings.length, '条', JSON.stringify(bookings))
|
||||
|
||||
if (Array.isArray(bookings)) {
|
||||
// 统计已取消次数(status='1')
|
||||
cancelCount.value = bookings.filter(b => String(b.status) === '1').length
|
||||
const mapped = bookings.map(mapBooking)
|
||||
ongoingBookings.value = mapped.filter(b => b.status === 'ongoing')
|
||||
completedBookings.value = mapped.filter(b => b.status === 'completed')
|
||||
} else {
|
||||
ongoingBookings.value = []
|
||||
completedBookings.value = []
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[myCourses] 获取预约记录失败:', e)
|
||||
ongoingBookings.value = []
|
||||
completedBookings.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function mapBooking(b) {
|
||||
const courseStartTime = b.courseStartTime || b.startTime || ''
|
||||
const bookStatus = String(b.status || '0')
|
||||
|
||||
// status: 0-已预约,1-已取消,2-已出席,3-缺席
|
||||
let status = 'ongoing'
|
||||
let statusLabel = '已预约'
|
||||
let statusClass = 'booked'
|
||||
|
||||
if (bookStatus === '1') {
|
||||
status = 'completed'
|
||||
statusLabel = '已取消'
|
||||
statusClass = 'cancelled'
|
||||
} else if (bookStatus === '2') {
|
||||
// 已签到保留在进行中,方便用户查看签到结果
|
||||
status = 'ongoing'
|
||||
statusLabel = '已签到'
|
||||
statusClass = 'attended'
|
||||
} else if (bookStatus === '3') {
|
||||
status = 'ongoing'
|
||||
statusLabel = '缺席'
|
||||
statusClass = 'absent'
|
||||
} else if (bookStatus === '0') {
|
||||
status = 'ongoing'
|
||||
statusLabel = '已预约'
|
||||
statusClass = 'booked'
|
||||
}
|
||||
|
||||
return {
|
||||
id: b.id,
|
||||
courseId: b.courseId,
|
||||
title: b.courseName || '预约课程',
|
||||
status: status,
|
||||
originalStatus: bookStatus,
|
||||
statusLabel: statusLabel,
|
||||
statusClass: statusClass,
|
||||
schedule: formatBookingTime(courseStartTime),
|
||||
location: b.location || '',
|
||||
coverImage: b.coverImage || ''
|
||||
}
|
||||
}
|
||||
|
||||
function formatBookingTime(timeStr) {
|
||||
if (!timeStr) return ''
|
||||
try {
|
||||
const d = new Date(timeStr)
|
||||
const month = d.getMonth() + 1
|
||||
const day = d.getDate()
|
||||
const hour = d.getHours().toString().padStart(2, '0')
|
||||
const minute = d.getMinutes().toString().padStart(2, '0')
|
||||
return `${month}月${day}日 ${hour}:${minute}`
|
||||
} catch {
|
||||
return timeStr
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCancel(item) {
|
||||
uni.showModal({
|
||||
title: '取消预约',
|
||||
content: `确定要取消「${item.title}」吗?`,
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
|
||||
// 弹出支付密码输入
|
||||
cancelTarget = item
|
||||
payPwdTitle.value = '取消验证'
|
||||
payPwdSubtitle.value = item.title
|
||||
// 实时获取最新免费次数阈值
|
||||
await fetchFreeLimit()
|
||||
// 设置退款提示
|
||||
if (freeCancelsLeft.value > 0) {
|
||||
payPwdNotice.value = `您还有 ${freeCancelsLeft.value} 次免手续费退款机会(超出后将扣除10%手续费)`
|
||||
} else {
|
||||
payPwdNotice.value = '本次取消将扣除10%手续费'
|
||||
}
|
||||
console.log('[myCourses] 取消弹窗: payPwdNotice=', payPwdNotice.value, 'freeCancelsLeft=', freeCancelsLeft.value, 'cancelCount=', cancelCount.value, 'freeLimit=', freeCancelLimit.value)
|
||||
const payPassword = await requestPayPassword()
|
||||
if (!payPassword) return
|
||||
|
||||
uni.showLoading({ title: '取消中...' })
|
||||
try {
|
||||
const result = await cancelBookingApi(Number(item.id), {
|
||||
memberId: getMemberId(),
|
||||
payPassword: payPassword
|
||||
})
|
||||
uni.hideLoading()
|
||||
if (result.success) {
|
||||
uni.showToast({ title: '已取消', icon: 'success' })
|
||||
fetchBookings()
|
||||
} else {
|
||||
uni.showToast({ title: result.message || '取消失败', icon: 'none', duration: 3000 })
|
||||
}
|
||||
} catch (e) {
|
||||
uni.hideLoading()
|
||||
console.error('[myCourses] 取消预约失败:', e)
|
||||
const errMsg = e?.message || '取消失败'
|
||||
uni.showToast({ title: errMsg, icon: 'none', duration: 3000 })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function requestPayPassword() {
|
||||
return new Promise((resolve) => {
|
||||
payPwdResolve = resolve
|
||||
showPayPwd.value = true
|
||||
})
|
||||
}
|
||||
|
||||
function onPayPasswordConfirm(password) {
|
||||
payPwdResolve?.(password)
|
||||
showPayPwd.value = false
|
||||
}
|
||||
|
||||
function onPayPasswordCancel() {
|
||||
payPwdResolve?.(null)
|
||||
showPayPwd.value = false
|
||||
}
|
||||
|
||||
// ===== 扫码签到 =====
|
||||
function handleScan() {
|
||||
uni.scanCode({
|
||||
success: async (res) => {
|
||||
const qrContent = res.result
|
||||
console.log('[myCourses] 扫码结果:', qrContent)
|
||||
|
||||
// 解析二维码内容获取 courseId
|
||||
let courseId = null
|
||||
try {
|
||||
const parsed = JSON.parse(qrContent)
|
||||
courseId = Number(parsed.courseId)
|
||||
} catch {
|
||||
const num = Number(qrContent)
|
||||
if (!isNaN(num)) {
|
||||
courseId = num
|
||||
}
|
||||
}
|
||||
|
||||
if (!courseId) {
|
||||
uni.showToast({ title: '无效的签到二维码', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const memberId = getMemberId()
|
||||
if (!memberId) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.showLoading({ title: '签到中...' })
|
||||
try {
|
||||
const result = await qrSignInGroupCourse(courseId, memberId)
|
||||
uni.hideLoading()
|
||||
if (result.success) {
|
||||
uni.showToast({ title: '签到成功', icon: 'success' })
|
||||
// 签到成功后刷新列表
|
||||
await fetchBookings()
|
||||
} else {
|
||||
uni.showToast({ title: result.message || '签到失败', icon: 'none', duration: 3000 })
|
||||
}
|
||||
} catch (e) {
|
||||
uni.hideLoading()
|
||||
console.error('[myCourses] 扫码签到失败:', e)
|
||||
const errMsg = e?.message || e?.data?.message || '签到失败'
|
||||
uni.showToast({ title: errMsg, icon: 'none', duration: 3000 })
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('[myCourses] 扫码取消或失败:', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchBookings()
|
||||
fetchFreeLimit()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -191,7 +336,6 @@ export default {
|
||||
@import '@/common/style/memberInfo/pages/page-reset.css';
|
||||
@import '@/common/style/memberInfo/pages/sub-page-base.css';
|
||||
@import '@/common/style/memberInfo/member-info-component-reset.css';
|
||||
@import '@/common/style/memberInfo/member-info-sub-nav.css';
|
||||
@import '@/common/style/memberInfo/member-info-tap.css';
|
||||
@import '@/common/style/memberInfo/pages/body-test-common.css';
|
||||
@import '@/common/style/memberInfo/pages/module-pages-common.css';
|
||||
@@ -213,13 +357,80 @@ export default {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mi-mod-course-card__action {
|
||||
margin-top: 6px;
|
||||
.mi-mod-course-card__cancel {
|
||||
margin-top: 10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8rpx 24rpx;
|
||||
border-radius: 16rpx;
|
||||
background: rgba(255, 107, 53, 0.1);
|
||||
border: 2rpx solid var(--accent-orange, #FF6B35);
|
||||
}
|
||||
|
||||
.mi-mod-course-card__action text {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--accent-orange, #FF6B35);
|
||||
.mi-mod-course-card__cancel text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
color: var(--accent-orange, #FF6B35);
|
||||
}
|
||||
|
||||
/* 状态标签 */
|
||||
.mi-mod-course-card__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.mi-mod-course-card__status {
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 12rpx;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mi-mod-course-card__status--booked {
|
||||
background: rgba(24, 144, 255, 0.1);
|
||||
color: #1890ff;
|
||||
border: 2rpx solid rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.mi-mod-course-card__status--attended {
|
||||
background: rgba(82, 196, 26, 0.1);
|
||||
color: #389e0d;
|
||||
border: 2rpx solid rgba(82, 196, 26, 0.2);
|
||||
}
|
||||
|
||||
.mi-mod-course-card__status--cancelled {
|
||||
background: rgba(153, 153, 153, 0.1);
|
||||
color: #999;
|
||||
border: 2rpx solid rgba(153, 153, 153, 0.2);
|
||||
}
|
||||
|
||||
.mi-mod-course-card__status--absent {
|
||||
background: rgba(250, 173, 20, 0.1);
|
||||
color: #d48806;
|
||||
border: 2rpx solid rgba(250, 173, 20, 0.2);
|
||||
}
|
||||
|
||||
/* 头部扫码按钮 */
|
||||
.header-scan-btn {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background: rgba(24, 144, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.header-scan-btn:active {
|
||||
background: rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.header-scan-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user