feat(coupon): 优惠券中心(领取)+ 我的券 + 下单核销/回退
- 新表 user_coupons(快照面额/门槛/类目/有效期)+ orders.user_coupon_id - 仓储:getCouponsWithClaimed/claimCoupon/getUserCoupons/resolveUserCoupon/consumeUserCoupon/releaseUserCouponByOrder - 接口:POST /api/marketing/coupons/:id/claim(同券仅一次、受总量限制)、GET /api/marketing/my-coupons(自动标记过期)、券中心带 claimed 标识 - 下单:createOrder 支持 userCouponId(校验归属/门槛/类目/有效期,服务端算抵扣,与订单同事务占用) - 回退:买家取消/大神拒单/超时退款/仲裁退款 均把券退回为 UNUSED - 小程序:新增 pages/user/coupons(券中心+我的券)、个人中心入口(可用张数)、下单改为选择"我的券"并传 userCouponId - 回归 94 套件/666 用例全绿;冒烟 9/9(领取/重复领取409/不存在404/下单抵扣100-10=90/取消回退)
这个提交包含在:
@@ -759,13 +759,17 @@ export async function getRechargeTiers() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用优惠券列表 (支持品类过滤)
|
||||
* 获取可用优惠券列表 (支持品类过滤;带 userId 时会返回 claimed 标识)
|
||||
*/
|
||||
export async function getMarketingCoupons(category) {
|
||||
try {
|
||||
const query = category ? `?category=${category}` : '';
|
||||
const uid = normalizeUserId();
|
||||
const qs = [];
|
||||
if (category) qs.push(`category=${category}`);
|
||||
if (uid) qs.push(`userId=${uid}`);
|
||||
const query = qs.length ? `?${qs.join('&')}` : '';
|
||||
const res = await http.get(`/marketing/coupons${query}`);
|
||||
if (Array.isArray(res) && res.length > 0) {
|
||||
if (Array.isArray(res)) {
|
||||
return res;
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -774,6 +778,25 @@ export async function getMarketingCoupons(category) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 领取优惠券
|
||||
*/
|
||||
export async function claimCoupon(couponId, userId) {
|
||||
const uid = normalizeUserId(userId);
|
||||
const res = await http.post(`/marketing/coupons/${Number(couponId)}/claim`, { userId: uid });
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的优惠券(status: UNUSED | USED | EXPIRED,缺省返回全部)
|
||||
*/
|
||||
export async function getMyCoupons(userId, status) {
|
||||
const uid = normalizeUserId(userId);
|
||||
const query = status ? `?userId=${uid}&status=${status}` : `?userId=${uid}`;
|
||||
const res = await http.get(`/marketing/my-coupons${query}`);
|
||||
return res || { list: [], usable: 0 };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -211,6 +211,16 @@
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/coupons",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "优惠券",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/create",
|
||||
"style": {
|
||||
|
||||
@@ -239,7 +239,7 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import { createOrder, payOrder as payOrderApi, requestWechatPayment, getCompanionDetail, getUserProfile, getMarketingCoupons } from '@/common/api.js';
|
||||
import { createOrder, payOrder as payOrderApi, requestWechatPayment, getCompanionDetail, getUserProfile, getMyCoupons } from '@/common/api.js';
|
||||
import { preloadSubscribeTemplates, requestSubscribeFor } from '@/common/subscribe.js';
|
||||
import { ICONS } from '@/common/icons.js';
|
||||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||||
@@ -381,21 +381,20 @@ const currentCoupon = ref(availableCoupons.value[0]);
|
||||
|
||||
const loadLiveCoupons = async () => {
|
||||
try {
|
||||
const list = await getMarketingCoupons();
|
||||
if (Array.isArray(list) && list.length > 0) {
|
||||
const activeCoupons = list
|
||||
.filter(c => c.status === 1)
|
||||
.map(c => ({
|
||||
id: String(c.id),
|
||||
title: c.name,
|
||||
amount: Number(c.discountAmount || 0),
|
||||
minAmount: Number(c.minSpend || 0),
|
||||
tag: c.minSpend > 0 ? `满¥${c.minSpend}减¥${c.discountAmount}` : `立减¥${c.discountAmount}`
|
||||
}));
|
||||
activeCoupons.push({ id: 'c_none', title: '不使用优惠券', amount: 0, minAmount: 0, tag: '不使用' });
|
||||
availableCoupons.value = activeCoupons;
|
||||
autoSelectBestCoupon();
|
||||
}
|
||||
const uid = uni.getStorageSync('userId') || 0;
|
||||
const res = uid ? await getMyCoupons(uid, 'UNUSED') : null;
|
||||
const mine = Array.isArray(res?.list) ? res.list : [];
|
||||
const activeCoupons = mine.map(c => ({
|
||||
id: String(c.id),
|
||||
title: c.name,
|
||||
amount: Number(c.discountAmount || 0),
|
||||
minAmount: Number(c.minSpend || 0),
|
||||
categoryLimit: c.categoryLimit || 'ALL',
|
||||
tag: c.minSpend > 0 ? `满¥${c.minSpend}减¥${c.discountAmount}` : `立减¥${c.discountAmount}`
|
||||
}));
|
||||
activeCoupons.push({ id: 'c_none', title: '不使用优惠券', amount: 0, minAmount: 0, tag: '不使用', categoryLimit: 'ALL' });
|
||||
availableCoupons.value = activeCoupons;
|
||||
autoSelectBestCoupon();
|
||||
} catch (e) {
|
||||
console.warn('[OrderCreate] 加载优惠券异常:', e);
|
||||
}
|
||||
@@ -474,6 +473,7 @@ const handlePay = async () => {
|
||||
|
||||
const uid = Number(uni.getStorageSync('userId') || currentUserId.value || 1001);
|
||||
const serviceTitle = selectedService.value ? selectedService.value.title : `${expert.value.game}专属服务`;
|
||||
const usingUserCoupon = currentCoupon.value && currentCoupon.value.id && currentCoupon.value.id !== 'c_none';
|
||||
const basePayload = {
|
||||
buyerId: uid,
|
||||
clientName: currentUserName.value || '超能电竞玩家',
|
||||
@@ -485,7 +485,8 @@ const handlePay = async () => {
|
||||
hourlyRate: selectedService.value ? selectedService.value.price : (expert.value.price || 35),
|
||||
durationHours: hours.value,
|
||||
amount: finalTotal.value,
|
||||
couponDiscount: discount.value,
|
||||
couponDiscount: usingUserCoupon ? 0 : discount.value,
|
||||
userCouponId: usingUserCoupon ? String(currentCoupon.value.id) : '',
|
||||
payType: payMethod.value,
|
||||
location: isOffline.value ? meetVenue.value : (remark.value || '线上语音开黑'),
|
||||
expenseBurden: expenseBurden.value === 'AA' ? 'AA制各自付' : (expenseBurden.value === 'CLIENT' ? '客户全包' : '大神自理'),
|
||||
|
||||
400
pwuniapp/pages/user/coupons.vue
普通文件
400
pwuniapp/pages/user/coupons.vue
普通文件
@@ -0,0 +1,400 @@
|
||||
<template>
|
||||
<view class="coupon-page" :class="['theme-' + currentThemeKey]" :style="navStyle">
|
||||
<view class="custom-nav-bar">
|
||||
<view class="nav-content">
|
||||
<view class="btn-nav-back btn-active" @click="handleBack">
|
||||
<image class="nav-back-icon" :src="ICONS.chevronLeft" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="nav-title-wrap">
|
||||
<text class="nav-title">优惠券</text>
|
||||
</view>
|
||||
<view class="nav-capsule-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 顶部切换:券中心 / 我的券 -->
|
||||
<view class="tab-bar">
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: activeTab === 'center' }"
|
||||
@click="switchTab('center')"
|
||||
>
|
||||
<text class="tab-text">券中心</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: activeTab === 'mine' }"
|
||||
@click="switchTab('mine')"
|
||||
>
|
||||
<text class="tab-text">我的券{{ usableCount > 0 ? ` (${usableCount})` : '' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-y class="coupon-scroll" refresher-enabled :refresher-triggered="isRefreshing" @refresherrefresh="onRefresh">
|
||||
<view class="coupon-body">
|
||||
<!-- 券中心 -->
|
||||
<block v-if="activeTab === 'center'">
|
||||
<view v-if="isLoading" class="empty-tip"><text>加载中…</text></view>
|
||||
<view v-else-if="centerList.length === 0" class="empty-tip">
|
||||
<text class="empty-title">暂无可领取优惠券</text>
|
||||
<text>平台会不定期发放新人券与品类券,敬请关注</text>
|
||||
</view>
|
||||
<view v-else class="coupon-list">
|
||||
<view v-for="c in centerList" :key="c.id" class="coupon-card">
|
||||
<view class="coupon-amount">
|
||||
<text class="amount-num">¥{{ c.discountAmount }}</text>
|
||||
<text class="amount-sub">{{ c.minSpend > 0 ? `满${c.minSpend}可用` : '无门槛' }}</text>
|
||||
</view>
|
||||
<view class="coupon-info">
|
||||
<text class="coupon-name">{{ c.name }}</text>
|
||||
<text class="coupon-desc">{{ c.description || '全平台通用' }}</text>
|
||||
<text class="coupon-extra">{{ c.categoryLimit && c.categoryLimit !== 'ALL' ? `限${c.categoryLimit} · ` : '全品类 · ' }}有效期 {{ c.validDays }} 天</text>
|
||||
</view>
|
||||
<view
|
||||
class="coupon-btn btn-active"
|
||||
:class="{ disabled: c.claimed || c.receivedCount >= c.totalCount }"
|
||||
@click="handleClaim(c)"
|
||||
>
|
||||
<text>{{ c.claimed ? '已领取' : (c.receivedCount >= c.totalCount ? '已领完' : '立即领取') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 我的券 -->
|
||||
<block v-else>
|
||||
<view class="filter-bar">
|
||||
<text
|
||||
v-for="f in statusFilters"
|
||||
:key="f.key"
|
||||
class="filter-item btn-active"
|
||||
:class="{ active: mineFilter === f.key }"
|
||||
@click="mineFilter = f.key"
|
||||
>{{ f.label }}</text>
|
||||
</view>
|
||||
<view v-if="mineList.length === 0" class="empty-tip">
|
||||
<text class="empty-title">暂无优惠券</text>
|
||||
<text>去「券中心」领取平台优惠券吧</text>
|
||||
</view>
|
||||
<view v-else class="coupon-list">
|
||||
<view
|
||||
v-for="c in mineList"
|
||||
:key="c.id"
|
||||
class="coupon-card"
|
||||
:class="{ used: c.status !== 'UNUSED' }"
|
||||
>
|
||||
<view class="coupon-amount">
|
||||
<text class="amount-num">¥{{ c.discountAmount }}</text>
|
||||
<text class="amount-sub">{{ c.minSpend > 0 ? `满${c.minSpend}可用` : '无门槛' }}</text>
|
||||
</view>
|
||||
<view class="coupon-info">
|
||||
<text class="coupon-name">{{ c.name }}</text>
|
||||
<text class="coupon-extra">{{ c.categoryLimit && c.categoryLimit !== 'ALL' ? `限${c.categoryLimit} · ` : '全品类 · ' }}有效期至 {{ formatDate(c.expiredAt) }}</text>
|
||||
</view>
|
||||
<view class="coupon-state">
|
||||
<text>{{ c.status === 'UNUSED' ? '未使用' : (c.status === 'USED' ? '已使用' : '已过期') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import { ICONS } from '@/common/icons.js';
|
||||
import { getCurrentThemeKey } from '@/common/theme.js';
|
||||
import { getNavBarInfo } from '@/common/nav.js';
|
||||
import { getMarketingCoupons, claimCoupon, getMyCoupons } from '@/common/api.js';
|
||||
|
||||
const currentThemeKey = ref(getCurrentThemeKey());
|
||||
const navInfo = ref(getNavBarInfo());
|
||||
const activeTab = ref('center');
|
||||
const isLoading = ref(true);
|
||||
const isRefreshing = ref(false);
|
||||
const centerList = ref([]);
|
||||
const mineList = ref([]);
|
||||
const mineFilter = ref('UNUSED');
|
||||
|
||||
const statusFilters = [
|
||||
{ key: 'UNUSED', label: '未使用' },
|
||||
{ key: 'USED', label: '已使用' },
|
||||
{ key: 'EXPIRED', label: '已过期' }
|
||||
];
|
||||
|
||||
const navStyle = computed(() => ({
|
||||
'--safe-header-gap': `${navInfo.value.totalHeaderHeight}px`,
|
||||
'--capsule-right-space': `${navInfo.value.capsuleRightSpace}px`,
|
||||
'--capsule-height': `${navInfo.value.capsuleHeight}px`
|
||||
}));
|
||||
|
||||
const usableCount = computed(() => mineList.value.filter((x) => x.status === 'UNUSED').length);
|
||||
|
||||
const currentUserId = () => uni.getStorageSync('userId') || 0;
|
||||
|
||||
const formatDate = (v) => {
|
||||
if (!v) return '长期有效';
|
||||
const s = String(v).replace('T', ' ');
|
||||
return s.slice(0, 10);
|
||||
};
|
||||
|
||||
const loadCenter = async () => {
|
||||
const list = await getMarketingCoupons();
|
||||
centerList.value = Array.isArray(list) ? list : [];
|
||||
};
|
||||
|
||||
const loadMine = async () => {
|
||||
const uid = currentUserId();
|
||||
if (!uid) {
|
||||
mineList.value = [];
|
||||
return;
|
||||
}
|
||||
const res = await getMyCoupons(uid);
|
||||
const all = Array.isArray(res?.list) ? res.list : [];
|
||||
mineList.value = all.filter((x) => x.status === mineFilter.value);
|
||||
};
|
||||
|
||||
const reloadAll = async () => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await Promise.all([loadCenter(), loadMine()]);
|
||||
} catch (_) {
|
||||
// 忽略网络异常
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
isRefreshing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const switchTab = async (tab) => {
|
||||
activeTab.value = tab;
|
||||
await reloadAll();
|
||||
};
|
||||
|
||||
const onRefresh = () => {
|
||||
isRefreshing.value = true;
|
||||
reloadAll();
|
||||
};
|
||||
|
||||
const handleClaim = async (c) => {
|
||||
if (c.claimed || c.receivedCount >= c.totalCount) return;
|
||||
const uid = currentUserId();
|
||||
if (!uid) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await claimCoupon(c.id, uid);
|
||||
c.claimed = true;
|
||||
c.receivedCount = Number(c.receivedCount || 0) + 1;
|
||||
uni.showToast({ title: '领取成功', icon: 'success' });
|
||||
await loadMine();
|
||||
} catch (err) {
|
||||
uni.showToast({ title: err.message || '领取失败', icon: 'none' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
const pages = getCurrentPages();
|
||||
if (pages && pages.length > 1) {
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.switchTab({ url: '/pages/user/index' });
|
||||
}
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
navInfo.value = getNavBarInfo();
|
||||
currentThemeKey.value = getCurrentThemeKey();
|
||||
reloadAll();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.coupon-page {
|
||||
min-height: 100vh;
|
||||
background: #0a0f1d;
|
||||
}
|
||||
.custom-nav-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
background: rgba(10, 15, 29, 0.92);
|
||||
padding-top: var(--safe-header-gap, 0);
|
||||
}
|
||||
.nav-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
.btn-nav-back {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.nav-back-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
.nav-title-wrap {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
.nav-title {
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.nav-capsule-placeholder {
|
||||
width: 32px;
|
||||
}
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 8px 16px 0;
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
height: 36px;
|
||||
border-radius: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
.tab-item.active {
|
||||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||||
}
|
||||
.tab-text {
|
||||
color: #cbd5e1;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tab-item.active .tab-text {
|
||||
color: #fff;
|
||||
}
|
||||
.coupon-scroll {
|
||||
height: calc(100vh - 88px - var(--safe-header-gap, 0px));
|
||||
}
|
||||
.coupon-body {
|
||||
padding: 12px 16px 40px;
|
||||
}
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.filter-item {
|
||||
color: #7d89a8;
|
||||
font-size: 13px;
|
||||
}
|
||||
.filter-item.active {
|
||||
color: #8b9cff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.coupon-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.coupon-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(135deg, #1b2340 0%, #141a30 100%);
|
||||
}
|
||||
.coupon-card.used {
|
||||
opacity: 0.55;
|
||||
}
|
||||
.coupon-amount {
|
||||
width: 84px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.amount-num {
|
||||
color: #ffd166;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.amount-sub {
|
||||
color: #8d99b8;
|
||||
font-size: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.coupon-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.coupon-name {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.coupon-desc {
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
}
|
||||
.coupon-extra {
|
||||
color: #6d7997;
|
||||
font-size: 11px;
|
||||
}
|
||||
.coupon-btn {
|
||||
flex-shrink: 0;
|
||||
padding: 7px 12px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||||
}
|
||||
.coupon-btn text {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.coupon-btn.disabled {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
.coupon-btn.disabled text {
|
||||
color: #8d99b8;
|
||||
}
|
||||
.coupon-state {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.coupon-state text {
|
||||
color: #8d99b8;
|
||||
font-size: 12px;
|
||||
}
|
||||
.empty-tip {
|
||||
padding: 60px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.empty-title {
|
||||
color: #e6eaf5;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.empty-tip text {
|
||||
color: #7d89a8;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.btn-active:active {
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
@@ -149,6 +149,12 @@
|
||||
<text class="row-label">官方在线客服</text>
|
||||
<image class="row-arrow-svg" :src="ICONS.chevronRight" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="list-row btn-active" @click="openCoupons">
|
||||
<image class="row-svg" :src="ICONS.ticket" mode="aspectFit" />
|
||||
<text class="row-label">我的优惠券</text>
|
||||
<text class="row-sub-tag" v-if="couponUsable > 0">{{ couponUsable }}张可用</text>
|
||||
<image class="row-arrow-svg" :src="ICONS.chevronRight" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="list-row btn-active" @click="openNotifications">
|
||||
<image class="row-svg" :src="ICONS.chat" mode="aspectFit" />
|
||||
<text class="row-label">消息通知</text>
|
||||
@@ -357,7 +363,7 @@ import { ICONS } from '@/common/icons.js';
|
||||
import { getCurrentThemeKey, setTheme, THEME_KEYS, applyThemeToNativeUI } from '@/common/theme.js';
|
||||
import { getCurrentRole, setCurrentRole, ROLE_TYPES, toggleRole as toggleUserRole, isExpertCertified, getExpertCertStatus, setExpertCertStatus, CERT_STATUS } from '@/common/role.js';
|
||||
import { getNavBarInfo } from '@/common/nav.js';
|
||||
import { getUserProfile, rechargeBalance, withdrawCommission, getUserFinanceRecords, updateUserProfile, uploadImage, getRechargeTiers, getPlatformConfigs, applyExpertCert, getUserWithdrawals, getFavorites, removeFavorite, getDistributionTeam, getDistributionRecords, getMyReports, bindInviteCode, getNotifications } from '@/common/api.js';
|
||||
import { getUserProfile, rechargeBalance, withdrawCommission, getUserFinanceRecords, updateUserProfile, uploadImage, getRechargeTiers, getPlatformConfigs, applyExpertCert, getUserWithdrawals, getFavorites, removeFavorite, getDistributionTeam, getDistributionRecords, getMyReports, bindInviteCode, getNotifications, getMyCoupons } from '@/common/api.js';
|
||||
import { preloadSubscribeTemplates, requestSubscribeAll } from '@/common/subscribe.js';
|
||||
|
||||
const DEFAULT_GUEST_AVATAR = 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=200';
|
||||
@@ -391,6 +397,28 @@ const openMyReportsModal = () => {
|
||||
const privacyHidePhone = ref(uni.getStorageSync('privacy_hide_phone') === '1');
|
||||
const noticeSubscribe = ref(uni.getStorageSync('notice_subscribe') === '1');
|
||||
const noticeUnread = ref(0);
|
||||
const couponUsable = ref(0);
|
||||
|
||||
const openCoupons = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/coupons',
|
||||
fail: () => {
|
||||
uni.showToast({ title: '页面打开失败,请稍后重试', icon: 'none' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const loadCouponUsable = async () => {
|
||||
const uid = uni.getStorageSync('userId') || 0;
|
||||
if (!uid) return;
|
||||
try {
|
||||
const res = await getMyCoupons(uid, 'UNUSED');
|
||||
const list = Array.isArray(res?.list) ? res.list : [];
|
||||
couponUsable.value = Number(res?.usable ?? list.length) || 0;
|
||||
} catch (_) {
|
||||
couponUsable.value = 0;
|
||||
}
|
||||
};
|
||||
const cacheSizeText = ref('1.25 MB');
|
||||
|
||||
// 分销推广管理
|
||||
@@ -651,6 +679,7 @@ onShow(() => {
|
||||
privacyHidePhone.value = uni.getStorageSync('privacy_hide_phone') === '1';
|
||||
noticeSubscribe.value = uni.getStorageSync('notice_subscribe') === '1';
|
||||
loadNoticeUnread();
|
||||
loadCouponUsable();
|
||||
if (isLoggedIn.value) {
|
||||
loadFavorites();
|
||||
loadMyReports();
|
||||
|
||||
在新工单中引用
屏蔽一个用户