1025 lines
25 KiB
Plaintext
1025 lines
25 KiB
Plaintext
<template>
|
|
<view class="coupons-page">
|
|
<!-- 顶部 Tabs -->
|
|
<view class="tab-bar">
|
|
<view class="tab-item" :class="[activeStatus == 'unused' ? 'active' : '']" @click="switchStatus('unused')">
|
|
<text class="tab-text">待使用 {{ unusedCount }}</text>
|
|
<view v-if="activeStatus == 'unused'" class="tab-line"></view>
|
|
</view>
|
|
<view class="tab-item" :class="[activeStatus == 'used' ? 'active' : '']" @click="switchStatus('used')">
|
|
<text class="tab-text">已使用 {{ usedCount }}</text>
|
|
<view v-if="activeStatus == 'used'" class="tab-line"></view>
|
|
</view>
|
|
<view class="tab-item" :class="[activeStatus == 'expired' ? 'active' : '']" @click="switchStatus('expired')">
|
|
<text class="tab-text">已过期 {{ expiredCount }}</text>
|
|
<view v-if="activeStatus == 'expired'" class="tab-line"></view>
|
|
</view>
|
|
<view class="tab-manage" @click="toggleManage">
|
|
<text class="manage-text">{{ manageMode ? '完成' : '管理' }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 筛选 tabs -->
|
|
<view class="filter-tabs">
|
|
<view class="filter-tab" :class="[activeFilterPanel == 'type' ? 'active' : '']" @click="toggleFilterPanel('type')">
|
|
<text class="filter-tab-text">类型</text>
|
|
<text class="filter-arrow">⌄</text>
|
|
</view>
|
|
<view class="filter-tab" :class="[activeFilterPanel == 'sort' ? 'active' : '']" @click="toggleFilterPanel('sort')">
|
|
<text class="filter-tab-text">优惠力度</text>
|
|
<text class="filter-arrow">⌄</text>
|
|
</view>
|
|
<view class="filter-tab filter-collapse" @click="closeFilterPanel">
|
|
<text class="filter-arrow-big">{{ activeFilterPanel != '' ? '⌃' : '⌄' }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 筛选下拉面板 -->
|
|
<view v-if="activeFilterPanel != ''" class="filter-dropdown">
|
|
<view v-if="activeFilterPanel == 'type'" class="dropdown-list">
|
|
<view class="dropdown-row" :class="[typeFilter == 'all' ? 'selected' : '']" @click="setTypeFilter('all')">
|
|
<text class="dropdown-text">全部</text>
|
|
<text v-if="typeFilter == 'all'" class="dropdown-check">✓</text>
|
|
</view>
|
|
<view class="dropdown-row" :class="[typeFilter == 'store' ? 'selected' : '']" @click="setTypeFilter('store')">
|
|
<text class="dropdown-text">店铺券</text>
|
|
<text v-if="typeFilter == 'store'" class="dropdown-check">✓</text>
|
|
</view>
|
|
<view class="dropdown-row" :class="[typeFilter == 'product' ? 'selected' : '']" @click="setTypeFilter('product')">
|
|
<text class="dropdown-text">商品券</text>
|
|
<text v-if="typeFilter == 'product'" class="dropdown-check">✓</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="activeFilterPanel == 'sort'" class="dropdown-list">
|
|
<view class="dropdown-row" :class="[sortMode == 'default' ? 'selected' : '']" @click="setSortMode('default')">
|
|
<text class="dropdown-text">默认排序</text>
|
|
<text v-if="sortMode == 'default'" class="dropdown-check">✓</text>
|
|
</view>
|
|
<view class="dropdown-row" :class="[sortMode == 'discount_desc' ? 'selected' : '']" @click="setSortMode('discount_desc')">
|
|
<text class="dropdown-text">从高到低</text>
|
|
<text v-if="sortMode == 'discount_desc'" class="dropdown-check">✓</text>
|
|
</view>
|
|
<view class="dropdown-row" :class="[sortMode == 'discount_asc' ? 'selected' : '']" @click="setSortMode('discount_asc')">
|
|
<text class="dropdown-text">从低到高</text>
|
|
<text v-if="sortMode == 'discount_asc'" class="dropdown-check">✓</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="activeFilterPanel != ''" class="filter-mask" @click="closeFilterPanel"></view>
|
|
|
|
<!-- 优惠券列表 -->
|
|
<scroll-view class="coupon-list" :scroll-y="true" :style="{ paddingBottom: manageMode ? '60px' : '0' }">
|
|
<view v-if="loading" class="empty-state">
|
|
<text class="empty-text">加载中...</text>
|
|
</view>
|
|
|
|
<view v-else-if="loadError" class="empty-state">
|
|
<text class="empty-icon">⚠️</text>
|
|
<text class="empty-text">优惠券加载失败</text>
|
|
<button class="retry-btn" @click="loadCoupons">重新加载</button>
|
|
</view>
|
|
|
|
<view v-else-if="storeGroups.length == 0" class="empty-state">
|
|
<text class="empty-icon">🎫</text>
|
|
<text class="empty-text">暂无优惠券</text>
|
|
</view>
|
|
|
|
<view v-else>
|
|
<view v-for="group in storeGroups" :key="group.shopId" class="store-group">
|
|
<!-- 店铺标题 -->
|
|
<view class="store-header">
|
|
<view class="store-logo-wrap">
|
|
<image v-if="group.shopLogo != ''" class="store-logo" :src="group.shopLogo" mode="aspectFill"></image>
|
|
<text v-else class="store-logo-placeholder">🏪</text>
|
|
</view>
|
|
<text class="store-name">{{ group.shopName }}</text>
|
|
</view>
|
|
|
|
<!-- 该店铺下的优惠券 -->
|
|
<view v-for="coupon in group.coupons" :key="coupon.id" class="coupon-card" :class="[coupon.status]">
|
|
<!-- 上下圆形凹槽 -->
|
|
<view class="coupon-notch coupon-notch-top"></view>
|
|
<view class="coupon-notch coupon-notch-bottom"></view>
|
|
|
|
<!-- 管理模式选择圆点 -->
|
|
<view v-if="manageMode" class="select-dot-wrap" @click.stop="toggleSelectCoupon(coupon)">
|
|
<view class="select-dot" :class="[coupon.selected ? 'selected' : '']">
|
|
<text v-if="coupon.selected" class="check-mark">✓</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 左侧金额区 -->
|
|
<view class="coupon-left">
|
|
<text class="coupon-amount">{{ coupon.displayAmount }}</text>
|
|
<text class="coupon-threshold">{{ coupon.thresholdText }}</text>
|
|
</view>
|
|
|
|
<!-- 中间分割线 -->
|
|
<view class="coupon-divider">
|
|
<view class="coupon-dash"></view>
|
|
</view>
|
|
|
|
<!-- 右侧信息区 -->
|
|
<view class="coupon-right">
|
|
<view class="coupon-info-top">
|
|
<text class="coupon-title">{{ coupon.title }}</text>
|
|
<text class="coupon-expiry">{{ coupon.expiryText }}</text>
|
|
<text class="coupon-scope">{{ coupon.scopeText }}</text>
|
|
</view>
|
|
<view class="coupon-info-bottom">
|
|
<button
|
|
v-if="coupon.status == 'unused' && !manageMode"
|
|
class="use-btn"
|
|
@click="useCoupon(coupon)"
|
|
>去使用</button>
|
|
<text v-else-if="coupon.status == 'used'" class="status-text">已使用</text>
|
|
<text v-else-if="coupon.status == 'expired'" class="status-text">已过期</text>
|
|
<text v-else-if="manageMode" class="status-text">-</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 底部安全距离 -->
|
|
<view class="safe-bottom"></view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- 底部管理操作栏 -->
|
|
<view v-if="manageMode" class="manage-bar">
|
|
<view class="manage-left" @click="selectAllCurrent">
|
|
<view class="select-dot" :class="[isAllSelected ? 'selected' : '']">
|
|
<text v-if="isAllSelected" class="check-mark">✓</text>
|
|
</view>
|
|
<text class="manage-bar-text">全选</text>
|
|
</view>
|
|
<text class="manage-count">已选 {{ selectedCount }} 张</text>
|
|
<button class="delete-btn" @click="deleteSelectedCoupons">删除</button>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import supabaseService from '@/utils/supabaseService.uts'
|
|
import type { UserCoupon } from '@/utils/supabaseService.uts'
|
|
|
|
type CouponStatus = string
|
|
type CouponScope = string
|
|
type SortMode = string
|
|
type TypeFilter = string
|
|
|
|
type Coupon = {
|
|
id: string
|
|
templateId: string
|
|
shopId: string
|
|
shopName: string
|
|
shopLogo: string
|
|
title: string
|
|
displayAmount: string
|
|
amount: number
|
|
discountType: number
|
|
discountValue: number
|
|
discountSortValue: number
|
|
thresholdText: string
|
|
scope: CouponScope
|
|
scopeText: string
|
|
validStartAt: string
|
|
expireAt: string
|
|
expiryText: string
|
|
usedAt: string
|
|
receivedAt: string
|
|
status: CouponStatus
|
|
selected: boolean
|
|
}
|
|
|
|
type CouponStoreGroup = {
|
|
shopId: string
|
|
shopName: string
|
|
shopLogo: string
|
|
coupons: Coupon[]
|
|
}
|
|
|
|
const allCoupons = ref<Coupon[]>([])
|
|
const loading = ref<boolean>(true)
|
|
const loadError = ref<boolean>(false)
|
|
const activeStatus = ref<CouponStatus>('unused')
|
|
const typeFilter = ref<TypeFilter>('all')
|
|
const sortMode = ref<SortMode>('default')
|
|
const manageMode = ref<boolean>(false)
|
|
const activeFilterPanel = ref<string>('')
|
|
|
|
const typeFilterOptions = [
|
|
{ value: 'all' as TypeFilter, label: '全部' },
|
|
{ value: 'store' as TypeFilter, label: '店铺券' },
|
|
{ value: 'product' as TypeFilter, label: '商品券' }
|
|
]
|
|
|
|
const sortModeOptions = [
|
|
{ value: 'default' as SortMode, label: '默认排序' },
|
|
{ value: 'discount_desc' as SortMode, label: '从高到低' },
|
|
{ value: 'discount_asc' as SortMode, label: '从低到高' }
|
|
]
|
|
|
|
// 状态数量统计
|
|
const unusedCount = computed((): number => {
|
|
let count = 0
|
|
for (let i = 0; i < allCoupons.value.length; i++) {
|
|
if (allCoupons.value[i].status == 'unused') count++
|
|
}
|
|
return count
|
|
})
|
|
|
|
const usedCount = computed((): number => {
|
|
let count = 0
|
|
for (let i = 0; i < allCoupons.value.length; i++) {
|
|
if (allCoupons.value[i].status == 'used') count++
|
|
}
|
|
return count
|
|
})
|
|
|
|
const expiredCount = computed((): number => {
|
|
let count = 0
|
|
for (let i = 0; i < allCoupons.value.length; i++) {
|
|
if (allCoupons.value[i].status == 'expired') count++
|
|
}
|
|
return count
|
|
})
|
|
|
|
// 过滤后的优惠券
|
|
const filteredCoupons = computed((): Coupon[] => {
|
|
let result: Coupon[] = []
|
|
|
|
// 按状态过滤
|
|
for (let i = 0; i < allCoupons.value.length; i++) {
|
|
const c = allCoupons.value[i]
|
|
if (c.status == activeStatus.value) {
|
|
result.push(c)
|
|
}
|
|
}
|
|
|
|
// 按类型过滤
|
|
if (typeFilter.value != 'all') {
|
|
const filtered: Coupon[] = []
|
|
for (let i = 0; i < result.length; i++) {
|
|
if (result[i].scope == typeFilter.value) {
|
|
filtered.push(result[i])
|
|
}
|
|
}
|
|
result = filtered
|
|
}
|
|
|
|
// 排序
|
|
if (sortMode.value == 'discount_desc') {
|
|
result.sort((a: Coupon, b: Coupon) => {
|
|
return b.discountSortValue - a.discountSortValue
|
|
})
|
|
} else if (sortMode.value == 'discount_asc') {
|
|
result.sort((a: Coupon, b: Coupon) => {
|
|
return a.discountSortValue - b.discountSortValue
|
|
})
|
|
} else {
|
|
// 默认排序规则
|
|
if (activeStatus.value == 'unused') {
|
|
result.sort((a: Coupon, b: Coupon) => {
|
|
if (a.expireAt < b.expireAt) return -1
|
|
if (a.expireAt > b.expireAt) return 1
|
|
if (a.receivedAt > b.receivedAt) return -1
|
|
if (a.receivedAt < b.receivedAt) return 1
|
|
return 0
|
|
})
|
|
} else if (activeStatus.value == 'used') {
|
|
result.sort((a: Coupon, b: Coupon) => {
|
|
if (a.usedAt > b.usedAt) return -1
|
|
if (a.usedAt < b.usedAt) return 1
|
|
return 0
|
|
})
|
|
} else if (activeStatus.value == 'expired') {
|
|
result.sort((a: Coupon, b: Coupon) => {
|
|
if (a.expireAt > b.expireAt) return -1
|
|
if (a.expireAt < b.expireAt) return 1
|
|
return 0
|
|
})
|
|
}
|
|
}
|
|
|
|
return result
|
|
})
|
|
|
|
// 按店铺分组
|
|
const storeGroups = computed((): CouponStoreGroup[] => {
|
|
const groups: CouponStoreGroup[] = []
|
|
const coupons = filteredCoupons.value
|
|
|
|
for (let i = 0; i < coupons.length; i++) {
|
|
const c = coupons[i]
|
|
let found = false
|
|
for (let j = 0; j < groups.length; j++) {
|
|
if (groups[j].shopId == c.shopId) {
|
|
groups[j].coupons.push(c)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if (!found) {
|
|
groups.push({
|
|
shopId: c.shopId,
|
|
shopName: c.shopName,
|
|
shopLogo: c.shopLogo,
|
|
coupons: [c]
|
|
})
|
|
}
|
|
}
|
|
|
|
return groups
|
|
})
|
|
|
|
// 管理模式:已选数量
|
|
const selectedCount = computed((): number => {
|
|
let count = 0
|
|
for (let i = 0; i < allCoupons.value.length; i++) {
|
|
if (allCoupons.value[i].selected) count++
|
|
}
|
|
return count
|
|
})
|
|
|
|
// 管理模式:当前列表是否全选
|
|
const isAllSelected = computed((): boolean => {
|
|
const current = filteredCoupons.value
|
|
if (current.length == 0) return false
|
|
for (let i = 0; i < current.length; i++) {
|
|
if (!current[i].selected) return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
// 将后端 UserCoupon 映射为前端 Coupon
|
|
function mapUserCouponToCoupon(item: UserCoupon): Coupon {
|
|
const discountType = item.discount_type
|
|
const amountVal = item.amount
|
|
let displayAmount = ''
|
|
let discountSortValue = 0
|
|
|
|
if (discountType == 2) {
|
|
displayAmount = Math.round(amountVal * 100) / 10 + '折'
|
|
discountSortValue = 10 - amountVal
|
|
} else {
|
|
displayAmount = '¥' + amountVal.toString()
|
|
discountSortValue = amountVal
|
|
}
|
|
|
|
const thresholdText = item.min_spend > 0
|
|
? '满' + item.min_spend.toString() + '元可用'
|
|
: '无门槛'
|
|
|
|
const scope = item.scope_type == 'product' ? 'product' : 'store'
|
|
const scopeText = scope == 'product' ? '限该店铺内指定商品可用' : '限该店铺内全部商品可用'
|
|
|
|
let status: CouponStatus = 'unused'
|
|
if (item.status == 2) {
|
|
status = 'used'
|
|
} else if (item.status == 3) {
|
|
status = 'expired'
|
|
}
|
|
|
|
let expiryText = '长期有效'
|
|
if (item.expire_at != '' && item.expire_at != null) {
|
|
const tIndex = item.expire_at.indexOf('T')
|
|
if (tIndex > 0) {
|
|
const datePart = item.expire_at.substring(0, tIndex)
|
|
const timePart = item.expire_at.substring(tIndex + 1, tIndex + 6)
|
|
expiryText = datePart + ' ' + timePart + ' 到期'
|
|
} else {
|
|
expiryText = item.expire_at + ' 到期'
|
|
}
|
|
}
|
|
|
|
return {
|
|
id: item.id,
|
|
templateId: item.template_id,
|
|
shopId: item.merchant_id != '' ? item.merchant_id : 'platform',
|
|
shopName: item.shop_name != '' ? item.shop_name : (item.merchant_id != '' ? '未知店铺' : '平台优惠券'),
|
|
shopLogo: item.shop_logo,
|
|
title: item.template_name != '' ? item.template_name : '优惠券',
|
|
displayAmount: displayAmount,
|
|
amount: amountVal,
|
|
discountType: discountType,
|
|
discountValue: amountVal,
|
|
discountSortValue: discountSortValue,
|
|
thresholdText: thresholdText,
|
|
scope: scope,
|
|
scopeText: scopeText,
|
|
validStartAt: item.received_at,
|
|
expireAt: item.expire_at,
|
|
expiryText: expiryText,
|
|
usedAt: item.used_at,
|
|
receivedAt: item.received_at,
|
|
status: status,
|
|
selected: false
|
|
}
|
|
}
|
|
|
|
const loadCoupons = async () => {
|
|
loading.value = true
|
|
loadError.value = false
|
|
try {
|
|
// 不传 status 参数,查询该用户所有优惠券
|
|
const userCoupons = await supabaseService.getUserCoupons()
|
|
const couponList: Coupon[] = []
|
|
for (let i = 0; i < userCoupons.length; i++) {
|
|
const item = userCoupons[i]
|
|
|
|
// 兼容:过滤掉 consumer_deleted_at 不为空的记录(后端补充该字段后生效)
|
|
if (item.consumer_deleted_at != null && item.consumer_deleted_at != '') {
|
|
continue
|
|
}
|
|
|
|
const coupon = mapUserCouponToCoupon(item)
|
|
couponList.push(coupon)
|
|
}
|
|
allCoupons.value = couponList
|
|
} catch (e) {
|
|
console.error('加载优惠券失败', e)
|
|
loadError.value = true
|
|
allCoupons.value = [] as Coupon[]
|
|
uni.showToast({
|
|
title: '优惠券加载失败,请稍后重试',
|
|
icon: 'none'
|
|
})
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadCoupons()
|
|
})
|
|
|
|
const switchStatus = (status: CouponStatus) => {
|
|
activeStatus.value = status
|
|
activeFilterPanel.value = ''
|
|
// 切换 tab 时取消管理模式,避免误操作
|
|
if (manageMode.value) {
|
|
manageMode.value = false
|
|
clearSelection()
|
|
}
|
|
}
|
|
|
|
const toggleFilterPanel = (panel: string) => {
|
|
if (activeFilterPanel.value == panel) {
|
|
activeFilterPanel.value = ''
|
|
} else {
|
|
activeFilterPanel.value = panel
|
|
}
|
|
}
|
|
|
|
const closeFilterPanel = () => {
|
|
activeFilterPanel.value = ''
|
|
}
|
|
|
|
const setTypeFilter = (value: TypeFilter) => {
|
|
typeFilter.value = value
|
|
activeFilterPanel.value = ''
|
|
}
|
|
|
|
const setSortMode = (value: SortMode) => {
|
|
sortMode.value = value
|
|
activeFilterPanel.value = ''
|
|
}
|
|
|
|
const toggleManage = () => {
|
|
manageMode.value = !manageMode.value
|
|
if (!manageMode.value) {
|
|
clearSelection()
|
|
}
|
|
}
|
|
|
|
const clearSelection = () => {
|
|
for (let i = 0; i < allCoupons.value.length; i++) {
|
|
allCoupons.value[i].selected = false
|
|
}
|
|
}
|
|
|
|
const toggleSelectCoupon = (coupon: Coupon) => {
|
|
coupon.selected = !coupon.selected
|
|
}
|
|
|
|
const selectAllCurrent = () => {
|
|
const current = filteredCoupons.value
|
|
const allSelected = isAllSelected.value
|
|
for (let i = 0; i < current.length; i++) {
|
|
current[i].selected = !allSelected
|
|
}
|
|
}
|
|
|
|
const deleteSelectedCoupons = () => {
|
|
const ids: string[] = []
|
|
for (let i = 0; i < allCoupons.value.length; i++) {
|
|
if (allCoupons.value[i].selected) {
|
|
ids.push(allCoupons.value[i].id)
|
|
}
|
|
}
|
|
|
|
if (ids.length == 0) {
|
|
uni.showToast({ title: '请先选择优惠券', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
uni.showModal({
|
|
title: '提示',
|
|
content: '确定删除选中的 ' + ids.length.toString() + ' 张优惠券吗?删除后前端不再展示,但后台仍保留记录。',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
doDelete(ids)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
const doDelete = async (ids: string[]) => {
|
|
const success = await supabaseService.softDeleteUserCoupons(ids)
|
|
if (success) {
|
|
const remaining: Coupon[] = []
|
|
for (let i = 0; i < allCoupons.value.length; i++) {
|
|
if (!allCoupons.value[i].selected) {
|
|
remaining.push(allCoupons.value[i])
|
|
}
|
|
}
|
|
allCoupons.value = remaining
|
|
manageMode.value = false
|
|
uni.showToast({ title: '已删除', icon: 'success' })
|
|
} else {
|
|
uni.showToast({ title: '删除失败,请稍后重试', icon: 'none' })
|
|
}
|
|
}
|
|
|
|
const useCoupon = (coupon: Coupon) => {
|
|
if (manageMode.value) return
|
|
if (coupon.status != 'unused') return
|
|
|
|
// TODO: 根据优惠券类型和店铺跳转到对应页面
|
|
// 如果 coupon.scope == 'product' 且有具体商品,可跳转到商品详情
|
|
// 如果 coupon.shopId != 'platform',可跳转到店铺页
|
|
// 暂时保留首页跳转作为兜底
|
|
|
|
uni.switchTab({
|
|
url: '/pages/main/index'
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.coupons-page {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: #fefefe;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* 顶部 Tabs */
|
|
.tab-bar {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
background-color: #ffffff;
|
|
padding: 0 12px;
|
|
border-bottom: 1px solid #eeeeee;
|
|
}
|
|
|
|
.tab-item {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 12px 0;
|
|
position: relative;
|
|
}
|
|
|
|
.tab-text {
|
|
font-size: 14px;
|
|
color: #666666;
|
|
}
|
|
|
|
.tab-item.active .tab-text {
|
|
color: #ff2b2b;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.tab-line {
|
|
position: absolute;
|
|
bottom: 0;
|
|
width: 40px;
|
|
height: 3px;
|
|
background-color: #ff2b2b;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.tab-manage {
|
|
padding: 12px 8px;
|
|
}
|
|
|
|
.manage-text {
|
|
font-size: 14px;
|
|
color: #666666;
|
|
}
|
|
|
|
/* 筛选 tabs */
|
|
.filter-tabs {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
height: 44px;
|
|
background-color: #ffffff;
|
|
border-bottom: 1px solid #eeeeee;
|
|
position: relative;
|
|
z-index: 20;
|
|
}
|
|
|
|
.filter-tab {
|
|
flex: 1;
|
|
height: 44px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.filter-tab-text {
|
|
font-size: 13px;
|
|
color: #666666;
|
|
}
|
|
|
|
.filter-arrow {
|
|
font-size: 10px;
|
|
color: #999999;
|
|
margin-left: 4px;
|
|
}
|
|
|
|
.filter-tab.active .filter-tab-text,
|
|
.filter-tab.active .filter-arrow {
|
|
color: #ff2b2b;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.filter-collapse {
|
|
flex: 0 0 48px;
|
|
border-left: 1px solid #eeeeee;
|
|
}
|
|
|
|
.filter-arrow-big {
|
|
font-size: 16px;
|
|
color: #999999;
|
|
}
|
|
|
|
/* 筛选下拉面板 */
|
|
.filter-dropdown {
|
|
position: relative;
|
|
z-index: 30;
|
|
background-color: #ffffff;
|
|
border-bottom: 1px solid #eeeeee;
|
|
}
|
|
|
|
.dropdown-list {
|
|
background-color: #ffffff;
|
|
}
|
|
|
|
.dropdown-row {
|
|
height: 44px;
|
|
padding: 0 20px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
border-bottom: 1px solid #f1f1f1;
|
|
}
|
|
|
|
.dropdown-text {
|
|
font-size: 14px;
|
|
color: #333333;
|
|
}
|
|
|
|
.dropdown-row.selected .dropdown-text {
|
|
color: #ff2b2b;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.dropdown-check {
|
|
font-size: 16px;
|
|
color: #ff2b2b;
|
|
}
|
|
|
|
/* 遮罩 */
|
|
.filter-mask {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
top: 132px;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.35);
|
|
z-index: 10;
|
|
}
|
|
|
|
/* 优惠券列表 */
|
|
.coupon-list {
|
|
flex: 1;
|
|
min-height: 0;
|
|
width: 100%;
|
|
padding: 0 24rpx;
|
|
}
|
|
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding-top: 100px;
|
|
}
|
|
|
|
.empty-icon {
|
|
font-size: 60px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.empty-text {
|
|
font-size: 16px;
|
|
color: #999999;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.retry-btn {
|
|
font-size: 14px;
|
|
background-color: #FF5722;
|
|
color: white;
|
|
padding: 6px 16px;
|
|
border-radius: 15px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* 店铺分组 */
|
|
.store-group {
|
|
margin-top: 16px;
|
|
}
|
|
|
|
.store-header {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.store-logo-wrap {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 12px;
|
|
background-color: #eeeeee;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.store-logo {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.store-logo-placeholder {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.store-name {
|
|
font-size: 14px;
|
|
color: #333333;
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* 优惠券卡片 */
|
|
.coupon-card {
|
|
display: flex;
|
|
flex-direction: row;
|
|
height: 220rpx;
|
|
border-radius: 20rpx;
|
|
overflow: hidden;
|
|
background-color: #fef0f3;
|
|
margin-bottom: 20rpx;
|
|
position: relative;
|
|
}
|
|
|
|
.coupon-card.used,
|
|
.coupon-card.expired {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.coupon-card.used .coupon-left,
|
|
.coupon-card.expired .coupon-left {
|
|
background-color: #f0f0f0;
|
|
}
|
|
|
|
.coupon-card.used .coupon-right,
|
|
.coupon-card.expired .coupon-right {
|
|
background-color: #f0f0f0;
|
|
}
|
|
|
|
/* 管理模式选择圆点 */
|
|
.select-dot-wrap {
|
|
position: absolute;
|
|
top: 16rpx;
|
|
left: 16rpx;
|
|
z-index: 2;
|
|
padding: 4rpx;
|
|
}
|
|
|
|
.select-dot {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 10px;
|
|
border: 2px solid #cccccc;
|
|
background-color: #ffffff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.select-dot.selected {
|
|
border-color: #ff2b2b;
|
|
background-color: #ff2b2b;
|
|
}
|
|
|
|
.check-mark {
|
|
font-size: 12px;
|
|
color: #ffffff;
|
|
}
|
|
|
|
/* 左侧金额区 */
|
|
.coupon-left {
|
|
width: 210rpx;
|
|
background-color: #fef0f3;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 16rpx;
|
|
}
|
|
|
|
.coupon-amount {
|
|
font-size: 28px;
|
|
font-weight: bold;
|
|
color: #ff2b2b;
|
|
}
|
|
|
|
.coupon-threshold {
|
|
font-size: 12px;
|
|
color: #ff3b30;
|
|
margin-top: 8rpx;
|
|
}
|
|
|
|
/* 中间分割线 */
|
|
.coupon-divider {
|
|
width: 2rpx;
|
|
height: 100%;
|
|
background-color: #fef0f3;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.coupon-dash {
|
|
width: 0;
|
|
height: 180rpx;
|
|
border-left: 3rpx dashed #f28fa1;
|
|
}
|
|
|
|
/* 上下圆形凹槽 */
|
|
.coupon-notch {
|
|
position: absolute;
|
|
width: 34rpx;
|
|
height: 34rpx;
|
|
border-radius: 50%;
|
|
background-color: #fefefe;
|
|
left: 193rpx;
|
|
z-index: 5;
|
|
}
|
|
|
|
.coupon-notch-top {
|
|
top: -17rpx;
|
|
}
|
|
|
|
.coupon-notch-bottom {
|
|
bottom: -17rpx;
|
|
}
|
|
|
|
/* 右侧信息区 */
|
|
.coupon-right {
|
|
flex: 1;
|
|
background-color: #fef0f3;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 28rpx 24rpx;
|
|
}
|
|
|
|
.coupon-info-top {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
}
|
|
|
|
.coupon-title {
|
|
font-size: 15px;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.coupon-expiry {
|
|
font-size: 11px;
|
|
color: #999999;
|
|
margin-bottom: 4rpx;
|
|
}
|
|
|
|
.coupon-scope {
|
|
font-size: 11px;
|
|
color: #ff6666;
|
|
}
|
|
|
|
.coupon-info-bottom {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
margin-left: 12rpx;
|
|
}
|
|
|
|
.use-btn {
|
|
font-size: 12px;
|
|
background-color: #ff2b2b;
|
|
color: #ffffff;
|
|
padding: 6px 14px;
|
|
border-radius: 12px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.status-text {
|
|
font-size: 12px;
|
|
color: #999999;
|
|
}
|
|
|
|
/* 底部安全距离 */
|
|
.safe-bottom {
|
|
height: 20px;
|
|
}
|
|
|
|
/* 底部管理操作栏 */
|
|
.manage-bar {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
height: 52px;
|
|
background-color: #ffffff;
|
|
border-top: 1px solid #eeeeee;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 16px;
|
|
}
|
|
|
|
.manage-left {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
|
|
.manage-bar-text {
|
|
font-size: 14px;
|
|
color: #666666;
|
|
margin-left: 8px;
|
|
}
|
|
|
|
.manage-count {
|
|
font-size: 14px;
|
|
color: #333333;
|
|
}
|
|
|
|
.delete-btn {
|
|
font-size: 14px;
|
|
background-color: #ff2b2b;
|
|
color: #ffffff;
|
|
padding: 5px 16px;
|
|
border-radius: 16px;
|
|
line-height: 1.5;
|
|
}
|
|
</style>
|