fix(withdraw): remove alipay/bank channels and enhance rules card text contrast in warm theme

这个提交包含在:
xcbyy
2026-10-13 09:58:16 +08:00
父节点 d93a68d47c
当前提交 cf11d5654b
共有 7 个文件被更改,包括 954 次插入 和 691 次删除

查看文件

@@ -50,7 +50,7 @@
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "",
"appid" : null,
"setting" : {
"urlCheck" : false
},

查看文件

@@ -122,6 +122,16 @@
"backgroundColor": "#0A0F1D"
}
},
{
"path": "pages/user/favorites",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "我的收藏",
"navigationBarBackgroundColor": "#0A0F1D",
"navigationBarTextStyle": "white",
"backgroundColor": "#0A0F1D"
}
},
{
"path": "pages/activity/create",
"style": {

查看文件

@@ -0,0 +1,745 @@
<template>
<view class="favorites-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="filter-header-bar" v-if="!isLoading && allFavorites.length > 0">
<view class="header-stat-wrap">
<text class="stat-count-txt">已收藏 {{ allFavorites.length }} 位陪伴师</text>
</view>
<view
class="online-filter-pill btn-active"
:class="{ active: filterOnlyOnline }"
@click="filterOnlyOnline = !filterOnlyOnline"
>
<view class="filter-dot"></view>
<text class="filter-txt">仅看接单中</text>
</view>
</view>
<!-- 骨架加载屏 -->
<view class="favorites-loading" v-if="isLoading">
<view class="skeleton-card" v-for="n in 3" :key="n"></view>
</view>
<!-- 收藏列表内容区 -->
<scroll-view
v-else
scroll-y
class="favorites-scroll-view"
refresher-enabled
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
>
<view class="favorites-list-wrap" v-if="displayedFavorites.length > 0">
<view
class="expert-fav-card card-active"
v-for="item in displayedFavorites"
:key="item.id"
@click="goToExpertDetail(item.id)"
>
<!-- 头像与在线徽章 -->
<view class="card-avatar-col">
<image class="expert-avatar" :src="item.avatar" mode="aspectFill" />
<view class="online-status-badge" :class="{ online: item.isOnline }">
<view class="status-dot"></view>
<text class="status-txt">{{ item.isOnline ? '接单中' : '休息中' }}</text>
</view>
</view>
<!-- 大神核心信息 -->
<view class="card-info-col">
<view class="name-row">
<text class="expert-nickname">{{ item.nickname }}</text>
<view class="gender-pill" :class="item.gender" v-if="item.gender && item.gender !== '保密'">
<image class="gender-svg" :src="item.gender === 'female' ? ICONS.female : ICONS.male" mode="aspectFit" />
</view>
<text class="track-tag">{{ item.track }}</text>
</view>
<view class="rank-row">
<text class="rank-tag">{{ item.rankName || '平台认证' }}</text>
<text class="rating-text">评分 {{ item.rating || '5.0' }}</text>
<text class="order-count-text">已接 {{ item.orderCount || 0 }} 单</text>
</view>
<!-- 技能标签胶囊 -->
<view class="tags-row" v-if="item.tags && item.tags.length > 0">
<text class="skill-tag" v-for="(t, idx) in item.tags.slice(0, 3)" :key="idx">{{ t }}</text>
</view>
<!-- 价格与操作栏 -->
<view class="card-bottom-row">
<view class="price-wrap">
<text class="price-symbol">¥</text>
<text class="price-value">{{ item.hourlyRate }}</text>
<text class="price-unit">/小时</text>
</view>
<view class="actions-group" @click.stop>
<button class="btn-unfav btn-active" @click="handleCancelFavorite(item)">
移除
</button>
<button class="btn-book-now btn-active" @click="goToExpertDetail(item.id)">
立即约玩
</button>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态展示 -->
<view class="empty-state-card" v-else>
<view class="empty-icon-wrap">
<image class="empty-star-svg" :src="ICONS.starOutline" mode="aspectFit" />
</view>
<text class="empty-title">{{ filterOnlyOnline ? '暂无接单中的在线陪伴师' : '暂未收藏心仪的陪伴师' }}</text>
<text class="empty-desc">前往首页大厅浏览各领域顶尖大神,点击收藏即可在这里快速找到他们</text>
<button class="btn-go-home btn-active" @click="goToHomePage">前往大厅发现</button>
</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, applyThemeToNativeUI } from '@/common/theme.js';
import { getNavBarInfo } from '@/common/nav.js';
import { getFavorites, removeFavorite } from '@/common/api.js';
const currentThemeKey = ref(getCurrentThemeKey());
const navInfo = ref(getNavBarInfo());
const isLoading = ref(true);
const isRefreshing = ref(false);
const allFavorites = ref([]);
const filterOnlyOnline = ref(false);
const navStyle = computed(() => {
return {
'--safe-header-gap': `${navInfo.value.totalHeaderHeight}px`,
'--capsule-right-space': `${navInfo.value.capsuleRightSpace}px`,
'--capsule-height': `${navInfo.value.capsuleHeight}px`
};
});
// 根据“仅看在线”进行过滤
const displayedFavorites = computed(() => {
if (filterOnlyOnline.value) {
return allFavorites.value.filter(item => item.isOnline);
}
return allFavorites.value;
});
const loadFavoritesList = async (showLoadingTip = false) => {
if (showLoadingTip) {
isLoading.value = true;
}
try {
const userId = uni.getStorageSync('userId') || 1001;
const list = await getFavorites(userId);
allFavorites.value = Array.isArray(list) ? list : [];
} catch (err) {
console.warn('[Favorites] 加载收藏列表异常:', err.message);
allFavorites.value = [];
} finally {
isLoading.value = false;
isRefreshing.value = false;
}
};
const onRefresh = async () => {
isRefreshing.value = true;
await loadFavoritesList(false);
};
const handleBack = () => {
const pages = getCurrentPages();
if (pages.length > 1) {
uni.navigateBack();
} else {
uni.switchTab({
url: '/pages/user/index'
});
}
};
const goToHomePage = () => {
uni.switchTab({
url: '/pages/index/index'
});
};
const goToExpertDetail = (expertId) => {
uni.navigateTo({
url: `/pages/expert/detail?id=${expertId}`
});
};
const handleCancelFavorite = (item) => {
uni.showModal({
title: '取消收藏',
content: `确定取消收藏陪伴师「${item.nickname}」吗?`,
confirmText: '确定移除',
confirmColor: '#EF4444',
cancelText: '保留',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({ title: '正在移除...' });
const userId = uni.getStorageSync('userId') || 1001;
await removeFavorite(item.id, userId);
allFavorites.value = allFavorites.value.filter(fav => fav.id !== item.id);
uni.hideLoading();
uni.showToast({ title: '已取消收藏', icon: 'success' });
} catch (err) {
uni.hideLoading();
uni.showToast({ title: err.message || '操作失败', icon: 'none' });
}
}
}
});
};
onShow(() => {
navInfo.value = getNavBarInfo();
currentThemeKey.value = getCurrentThemeKey();
applyThemeToNativeUI(currentThemeKey.value);
loadFavoritesList(true);
});
</script>
<style lang="scss" scoped>
.favorites-page {
min-height: 100vh;
background-color: #0A0F1D;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
// 顶部自定义沉浸式导航栏
.custom-nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--safe-header-gap);
background: rgba(10, 15, 29, 0.9);
backdrop-filter: blur(12px);
z-index: 1000;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
.nav-content {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: var(--capsule-height);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
.btn-nav-back {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.06);
border-radius: 32rpx;
.nav-back-icon {
width: 36rpx;
height: 36rpx;
}
}
.nav-title-wrap {
flex: 1;
text-align: center;
.nav-title {
font-size: 32rpx;
font-weight: 700;
color: #F8FAFC;
letter-spacing: 1rpx;
}
}
.nav-capsule-placeholder {
width: var(--capsule-right-space);
}
}
}
// 顶部统计与过滤栏
.filter-header-bar {
margin-top: calc(var(--safe-header-gap) + 16rpx);
padding: 0 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
.header-stat-wrap {
.stat-count-txt {
font-size: 24rpx;
color: #94A3B8;
font-weight: 500;
}
}
.online-filter-pill {
display: flex;
align-items: center;
gap: 8rpx;
padding: 8rpx 20rpx;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 24rpx;
transition: all 0.2s ease;
.filter-dot {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: #64748B;
}
.filter-txt {
font-size: 22rpx;
color: #94A3B8;
}
&.active {
background: rgba(16, 185, 129, 0.15);
border-color: rgba(16, 185, 129, 0.4);
.filter-dot {
background: #10B981;
box-shadow: 0 0 8rpx #10B981;
}
.filter-txt {
color: #10B981;
font-weight: 600;
}
}
}
}
// 骨架屏
.favorites-loading {
margin-top: calc(var(--safe-header-gap) + 24rpx);
padding: 0 32rpx;
.skeleton-card {
height: 240rpx;
background: rgba(255, 255, 255, 0.04);
border-radius: 24rpx;
margin-bottom: 24rpx;
animation: pulse 1.5s infinite;
}
}
// 页面滚动区
.favorites-scroll-view {
flex: 1;
padding: 0 32rpx;
box-sizing: border-box;
.favorites-list-wrap {
padding-bottom: calc(48rpx + env(safe-area-inset-bottom));
}
}
// 收藏大神卡片
.expert-fav-card {
display: flex;
gap: 24rpx;
background: #151D30;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 24rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.2);
transition: all 0.2s ease;
.card-avatar-col {
position: relative;
width: 140rpx;
height: 140rpx;
flex-shrink: 0;
.expert-avatar {
width: 100%;
height: 100%;
border-radius: 20rpx;
border: 2rpx solid rgba(255, 255, 255, 0.1);
}
.online-status-badge {
position: absolute;
bottom: -8rpx;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 6rpx;
background: rgba(100, 116, 139, 0.9);
padding: 2rpx 10rpx;
border-radius: 12rpx;
white-space: nowrap;
.status-dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #CBD5E1;
}
.status-txt {
font-size: 18rpx;
color: #FFFFFF;
}
&.online {
background: rgba(5, 150, 105, 0.95);
.status-dot {
background: #34D399;
box-shadow: 0 0 6rpx #34D399;
}
}
}
}
.card-info-col {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.name-row {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 8rpx;
.expert-nickname {
font-size: 30rpx;
font-weight: 700;
color: #F8FAFC;
}
.gender-pill {
display: flex;
align-items: center;
justify-content: center;
width: 28rpx;
height: 28rpx;
border-radius: 14rpx;
.gender-svg {
width: 20rpx;
height: 20rpx;
}
&.female {
background: rgba(244, 63, 94, 0.15);
}
&.male {
background: rgba(59, 130, 246, 0.15);
}
}
.track-tag {
font-size: 20rpx;
color: #818CF8;
background: rgba(99, 102, 241, 0.15);
padding: 2rpx 12rpx;
border-radius: 8rpx;
font-weight: 500;
}
}
.rank-row {
display: flex;
align-items: center;
gap: 14rpx;
margin-bottom: 12rpx;
.rank-tag {
font-size: 22rpx;
color: #F59E0B;
font-weight: 600;
}
.rating-text {
font-size: 22rpx;
color: #FBBF24;
}
.order-count-text {
font-size: 22rpx;
color: #64748B;
}
}
.tags-row {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
margin-bottom: 16rpx;
.skill-tag {
font-size: 20rpx;
color: #94A3B8;
background: rgba(255, 255, 255, 0.05);
padding: 4rpx 12rpx;
border-radius: 8rpx;
}
}
.card-bottom-row {
display: flex;
align-items: flex-end;
justify-content: space-between;
.price-wrap {
display: flex;
align-items: baseline;
.price-symbol {
font-size: 26rpx;
font-weight: 700;
color: #10B981;
}
.price-value {
font-size: 38rpx;
font-weight: 700;
color: #10B981;
margin: 0 4rpx;
font-variant-numeric: tabular-nums;
}
.price-unit {
font-size: 20rpx;
color: #64748B;
}
}
.actions-group {
display: flex;
gap: 14rpx;
.btn-unfav {
height: 60rpx;
line-height: 58rpx;
font-size: 22rpx;
color: #94A3B8;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 30rpx;
padding: 0 24rpx;
margin: 0;
}
.btn-book-now {
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
font-weight: 600;
color: #FFFFFF;
background: linear-gradient(135deg, #6366F1, #8B5CF6);
border-radius: 30rpx;
padding: 0 28rpx;
margin: 0;
box-shadow: 0 4rpx 12rpx rgba(99, 102, 241, 0.35);
}
}
}
}
}
// 空状态卡片
.empty-state-card {
margin-top: 120rpx;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 40rpx;
.empty-icon-wrap {
width: 140rpx;
height: 140rpx;
border-radius: 70rpx;
background: rgba(255, 255, 255, 0.03);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 28rpx;
.empty-star-svg {
width: 72rpx;
height: 72rpx;
opacity: 0.4;
}
}
.empty-title {
font-size: 30rpx;
font-weight: 600;
color: #F8FAFC;
margin-bottom: 12rpx;
}
.empty-desc {
font-size: 24rpx;
color: #64748B;
line-height: 1.5;
max-width: 480rpx;
margin-bottom: 40rpx;
}
.btn-go-home {
background: linear-gradient(135deg, #6366F1, #4F46E5);
color: #FFFFFF;
font-size: 26rpx;
font-weight: 600;
height: 72rpx;
line-height: 72rpx;
padding: 0 48rpx;
border-radius: 36rpx;
box-shadow: 0 4rpx 16rpx rgba(99, 102, 241, 0.35);
}
}
// 暖阳晨曦模式适配 (theme-warm)
.favorites-page.theme-warm {
background-color: #F8F6F0;
.custom-nav-bar {
background: rgba(248, 246, 240, 0.95);
border-color: #E7E5E4;
.nav-content {
.btn-nav-back {
background: #FFFFFF;
border: 1px solid #E7E5E4;
}
.nav-title-wrap .nav-title {
color: #1C1917;
}
}
}
.filter-header-bar {
.header-stat-wrap .stat-count-txt {
color: #78716C;
}
.online-filter-pill {
background: #FFFFFF;
border-color: #E7E5E4;
.filter-txt {
color: #78716C;
}
&.active {
background: #FFF7ED;
border-color: #EA580C;
.filter-dot {
background: #EA580C;
box-shadow: 0 0 8rpx #EA580C;
}
.filter-txt {
color: #EA580C;
}
}
}
}
.expert-fav-card {
background: #FFFFFF;
border-color: #F0ECE4;
box-shadow: 0 4rpx 20rpx rgba(28, 25, 23, 0.06);
.card-info-col {
.name-row .expert-nickname {
color: #1C1917;
}
.rank-row .order-count-text {
color: #A8A29E;
}
.tags-row .skill-tag {
background: #F5F2EC;
color: #78716C;
}
.card-bottom-row .actions-group {
.btn-unfav {
background: #F5F2EC;
border-color: #E7E5E4;
color: #78716C;
}
.btn-book-now {
background: linear-gradient(135deg, #F97316, #EA580C);
box-shadow: 0 4rpx 12rpx rgba(234, 88, 12, 0.35);
}
}
}
}
.empty-state-card {
.empty-icon-wrap {
background: #FFFFFF;
border: 1px solid #E7E5E4;
}
.empty-title {
color: #1C1917;
}
.empty-desc {
color: #78716C;
}
.btn-go-home {
background: linear-gradient(135deg, #F97316, #EA580C);
box-shadow: 0 4rpx 16rpx rgba(234, 88, 12, 0.35);
}
}
}
@keyframes pulse {
0% { opacity: 0.6; }
50% { opacity: 0.3; }
100% { opacity: 0.6; }
}
</style>

查看文件

@@ -320,58 +320,6 @@
</view>
</view>
<!-- 1. 我的收藏弹窗 -->
<view class="profile-edit-modal-mask" v-if="showFavoritesModal" @click="showFavoritesModal = false">
<view class="profile-edit-modal fav-modal-box" @click.stop>
<view class="modal-header">
<view class="modal-title-wrap">
<text class="modal-title">我的收藏陪玩</text>
<text class="modal-sub">实时高亮接单在线状态</text>
</view>
<view class="btn-close btn-active" @click="showFavoritesModal = false">
<image class="close-svg" :src="ICONS.close" mode="aspectFit" />
</view>
</view>
<view class="modal-body fav-modal-body">
<scroll-view scroll-y class="fav-list-scroll" v-if="favoriteList.length > 0">
<view class="fav-item-card" v-for="item in favoriteList" :key="item.id">
<view class="fav-item-left" @click="goToExpertDetail(item.id)">
<view class="fav-avatar-wrap">
<image class="fav-avatar" :src="item.avatar" mode="aspectFill" />
<view class="status-indicator" :class="{ online: item.isOnline }">
<view class="dot"></view>
<text class="status-lbl">{{ item.isOnline ? '接单中' : '休息中' }}</text>
</view>
</view>
<view class="fav-meta">
<view class="fav-name-row">
<text class="fav-name">{{ item.nickname }}</text>
<text class="fav-track-tag">{{ item.track }}</text>
</view>
<text class="fav-rank">{{ item.rankName || '金牌陪伴师' }}</text>
<view class="fav-price-row">
<text class="fav-price">¥{{ item.hourlyRate }}</text>
<text class="fav-unit">/小时</text>
<text class="fav-rating">评分 {{ item.rating || '5.0' }}</text>
</view>
</view>
</view>
<view class="fav-item-actions">
<button class="btn-action-book btn-active" @click="goToExpertDetail(item.id)">立即约玩</button>
<button class="btn-action-unfav btn-active" @click="handleRemoveFavorite(item.id)">移除</button>
</view>
</view>
</scroll-view>
<view class="empty-fav-box" v-else>
<image class="empty-icon-svg" :src="ICONS.starOutline" mode="aspectFit" />
<text class="empty-fav-txt">暂未收藏心仪的陪玩大神</text>
<button class="btn-go-discover btn-active" @click="goToDiscover">去大厅发现</button>
</view>
</view>
</view>
</view>
<!-- 2. 系统与隐私设置弹窗 -->
<view class="profile-edit-modal-mask" v-if="showSettingsModal" @click="showSettingsModal = false">
<view class="profile-edit-modal settings-modal-box" @click.stop>
@@ -650,7 +598,6 @@ const isLoggedIn = ref(true);
const showLoginModal = ref(false);
// 我的收藏
const showFavoritesModal = ref(false);
const favoriteList = ref([]);
const loadingFavorites = ref(false);
@@ -1208,11 +1155,12 @@ const displayPhone = computed(() => {
return phone;
});
// 1. 我的收藏业务逻辑
const openFavoritesModal = async () => {
// 1. 我的收藏业务逻辑(跳转独立页面)
const openFavoritesModal = () => {
if (!ensureLoggedIn()) return;
showFavoritesModal.value = true;
await loadFavorites();
uni.navigateTo({
url: '/pages/user/favorites'
});
};
const loadFavorites = async () => {
@@ -1227,30 +1175,6 @@ const loadFavorites = async () => {
}
};
const handleRemoveFavorite = async (expertId) => {
try {
await removeFavorite(expertId, user.value.id || 1001);
favoriteList.value = favoriteList.value.filter(item => item.id !== expertId);
uni.showToast({ title: '已取消收藏', icon: 'none' });
} catch (err) {
uni.showToast({ title: err.message || '操作失败', icon: 'none' });
}
};
const goToExpertDetail = (expertId) => {
showFavoritesModal.value = false;
uni.navigateTo({
url: `/pages/expert/detail?id=${expertId}`
});
};
const goToDiscover = () => {
showFavoritesModal.value = false;
uni.switchTab({
url: '/pages/index/index'
});
};
// 2. 系统与隐私设置业务逻辑
const updateCacheSize = () => {
try {
@@ -2686,204 +2610,6 @@ const navTo = (url) => {
font-weight: 600;
}
// 1. 我的收藏弹窗样式
.fav-modal-box {
max-width: 680rpx;
max-height: 85vh;
.fav-modal-body {
padding: 10rpx 0 20rpx;
}
.fav-list-scroll {
max-height: 600rpx;
.fav-item-card {
display: flex;
justify-content: space-between;
align-items: center;
background: #0F172A;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16rpx;
padding: 16rpx 20rpx;
margin-bottom: 16rpx;
.fav-item-left {
flex: 1;
display: flex;
gap: 16rpx;
align-items: center;
.fav-avatar-wrap {
position: relative;
width: 90rpx;
height: 90rpx;
flex-shrink: 0;
.fav-avatar {
width: 100%;
height: 100%;
border-radius: 45rpx;
}
.status-indicator {
position: absolute;
bottom: -4rpx;
right: -4rpx;
display: flex;
align-items: center;
gap: 4rpx;
background: #475569;
padding: 2rpx 8rpx;
border-radius: 10rpx;
.dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #94A3B8;
}
.status-lbl {
font-size: 16rpx;
color: #FFFFFF;
}
&.online {
background: #059669;
.dot {
background: #34D399;
box-shadow: 0 0 6rpx #34D399;
}
}
}
}
.fav-meta {
flex: 1;
.fav-name-row {
display: flex;
align-items: center;
gap: 8rpx;
margin-bottom: 4rpx;
.fav-name {
font-size: 26rpx;
font-weight: 700;
color: #F8FAFC;
}
.fav-track-tag {
font-size: 18rpx;
color: #818CF8;
background: rgba(99, 102, 241, 0.15);
padding: 2rpx 8rpx;
border-radius: 6rpx;
}
}
.fav-rank {
font-size: 20rpx;
color: #94A3B8;
display: block;
margin-bottom: 4rpx;
}
.fav-price-row {
display: flex;
align-items: baseline;
gap: 4rpx;
.fav-price {
font-size: 28rpx;
font-weight: 700;
color: #10B981;
}
.fav-unit {
font-size: 18rpx;
color: #64748B;
}
.fav-rating {
font-size: 20rpx;
color: #F59E0B;
font-weight: 600;
margin-left: 12rpx;
}
}
}
}
.fav-item-actions {
display: flex;
flex-direction: column;
gap: 10rpx;
flex-shrink: 0;
.btn-action-book {
height: 52rpx;
line-height: 50rpx;
font-size: 22rpx;
font-weight: 600;
color: #FFFFFF;
background: linear-gradient(135deg, #6366F1, #4F46E5);
border-radius: 26rpx;
padding: 0 20rpx;
margin: 0;
}
.btn-action-unfav {
height: 48rpx;
line-height: 46rpx;
font-size: 20rpx;
color: #94A3B8;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 24rpx;
padding: 0 16rpx;
margin: 0;
}
}
}
}
.empty-fav-box {
padding: 60rpx 0;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
.empty-icon-svg {
width: 64rpx;
height: 64rpx;
margin-bottom: 16rpx;
opacity: 0.5;
}
.empty-fav-txt {
font-size: 24rpx;
color: #64748B;
margin-bottom: 24rpx;
}
.btn-go-discover {
font-size: 24rpx;
font-weight: 600;
color: #6366F1;
background: rgba(99, 102, 241, 0.12);
border: 1px solid rgba(99, 102, 241, 0.3);
border-radius: 30rpx;
height: 60rpx;
line-height: 58rpx;
padding: 0 32rpx;
}
}
}
// 2. 系统设置弹窗样式
.settings-modal-box {
max-width: 660rpx;
@@ -3439,20 +3165,6 @@ const navTo = (url) => {
}
}
.fav-modal-box {
.fav-list-scroll .fav-item-card {
background: #FAFAF9;
border-color: #E7E5E4;
.fav-item-left .fav-meta .fav-name-row .fav-name {
color: #1C1917;
}
.fav-item-actions .btn-action-book {
background: linear-gradient(135deg, #F97316, #EA580C);
}
}
}
.settings-modal-box {
.settings-modal-body .settings-group {
.settings-row, .settings-info-row {

查看文件

@@ -57,64 +57,31 @@
</view>
</view>
<!-- 卡片 2: 到账渠道选择矩阵 -->
<view class="content-card">
<!-- 卡片 2: 提现到账方式 (微信零钱专属极速直达通道) -->
<view class="content-card channel-card">
<view class="card-section-head">
<text class="section-title">提现到账方式</text>
<text class="section-sub">支持三种主流渠道结算</text>
<view class="section-badge-green">极速免审核</view>
</view>
<view class="channel-grid">
<!-- 微信零钱 -->
<view
class="channel-item btn-active"
:class="{ active: currentChannel === 'WECHAT' }"
@click="selectChannel('WECHAT')"
>
<view class="channel-badge" v-if="currentChannel === 'WECHAT'">推荐</view>
<view class="channel-icon-wrap wechat">
<image class="channel-svg" :src="ICONS.wechat" mode="aspectFit" />
</view>
<text class="channel-name">微信零钱</text>
<text class="channel-desc">极速到账</text>
<view class="channel-select-mark" v-if="currentChannel === 'WECHAT'">
<image class="check-svg" :src="ICONS.checkCircle" mode="aspectFit" />
</view>
<view class="wechat-channel-box">
<view class="channel-icon-wrap wechat">
<image class="channel-svg" :src="ICONS.wechat" mode="aspectFit" />
</view>
<view class="channel-info-col">
<view class="channel-line-1">
<text class="channel-name">微信零钱钱包</text>
<text class="channel-tag">推荐首选</text>
</view>
<text class="channel-desc">企业付款直达当前微信零钱 · 免手续费</text>
</view>
<view class="channel-check-badge">
<image class="check-svg" :src="ICONS.checkCircle" mode="aspectFit" />
</view>
</view>
<!-- 支付宝 -->
<view
class="channel-item btn-active"
:class="{ active: currentChannel === 'ALIPAY' }"
@click="selectChannel('ALIPAY')"
>
<view class="channel-badge blue" v-if="currentChannel === 'ALIPAY'">大额首选</view>
<view class="channel-icon-wrap alipay">
<image class="channel-svg" :src="ICONS.alipay" mode="aspectFit" />
</view>
<text class="channel-name">支付宝</text>
<text class="channel-desc">支持大额</text>
<view class="channel-select-mark" v-if="currentChannel === 'ALIPAY'">
<image class="check-svg" :src="ICONS.checkCircle" mode="aspectFit" />
</view>
</view>
<!-- 银行卡 -->
<view
class="channel-item btn-active"
:class="{ active: currentChannel === 'BANK' }"
@click="selectChannel('BANK')"
>
<view class="channel-badge gold" v-if="currentChannel === 'BANK'">主流银行</view>
<view class="channel-icon-wrap bank">
<image class="channel-svg" :src="ICONS.bankCard" mode="aspectFit" />
</view>
<text class="channel-name">银行储蓄卡</text>
<text class="channel-desc">全国通用</text>
<view class="channel-select-mark" v-if="currentChannel === 'BANK'">
<image class="check-svg" :src="ICONS.checkCircle" mode="aspectFit" />
</view>
</view>
<view class="wechat-safe-notice">
<text class="notice-desc">系统将通过微信商户企业付款打款到您当前登录小程序的微信零钱账户中,无需额外录入银行卡号,提交申请后预计 1 至 24 小时内由系统自动发放至微信零钱钱包。</text>
</view>
</view>
@@ -154,85 +121,7 @@
</view>
</view>
<!-- 卡片 4: 收款账号资料区 -->
<view class="content-card">
<view class="card-section-head">
<text class="section-title">收款账户资料</text>
<text class="section-sub">请确保实名信息完全一致</text>
</view>
<!-- 微信零钱提示卡 -->
<view v-if="currentChannel === 'WECHAT'" class="wechat-bind-notice">
<view class="notice-head">
<image class="notice-icon" :src="ICONS.wechat" mode="aspectFit" />
<text class="notice-title">自动直达当前微信零钱钱包</text>
</view>
<text class="notice-body">
系统将通过微信商户企业付款打款到您当前登录小程序的微信零钱账户中,无需额外录入卡号,提交申请后预计 1 至 24 小时内由系统自动发放。
</text>
</view>
<!-- 支付宝输入区 -->
<view v-if="currentChannel === 'ALIPAY'" class="form-inputs-wrap">
<view class="form-group">
<text class="form-label">支付宝账号</text>
<input
class="form-input"
v-model="accountForm.alipayAccount"
placeholder="请输入支付宝登录账号 / 手机号 / 邮箱"
placeholder-class="form-ph"
maxlength="50"
/>
</view>
<view class="form-group">
<text class="form-label">收款人真实姓名</text>
<input
class="form-input"
v-model="accountForm.realName"
placeholder="需与支付宝实名认证姓名严格一致"
placeholder-class="form-ph"
maxlength="20"
/>
</view>
</view>
<!-- 银行卡输入区 -->
<view v-if="currentChannel === 'BANK'" class="form-inputs-wrap">
<view class="form-group">
<text class="form-label">储蓄卡卡号</text>
<input
class="form-input"
type="number"
v-model="accountForm.bankCard"
placeholder="请输入收款人储蓄卡卡号"
placeholder-class="form-ph"
maxlength="30"
/>
</view>
<view class="form-group">
<text class="form-label">开户银行名称</text>
<input
class="form-input"
v-model="accountForm.bankName"
placeholder="例如:中国工商银行 / 招商银行 / 建设银行"
placeholder-class="form-ph"
maxlength="40"
/>
</view>
<view class="form-group">
<text class="form-label">持卡人真实姓名</text>
<input
class="form-input"
v-model="accountForm.realName"
placeholder="需与银行卡开户姓名严格一致"
placeholder-class="form-ph"
maxlength="20"
/>
</view>
</view>
</view>
<!-- 卡片 5: 温馨提示与财务规范 -->
<!-- 卡片 4: 温馨提示与财务规范 -->
<view class="rules-card">
<view class="rules-head">
<image class="rules-icon" :src="ICONS.shield" mode="aspectFit" />
@@ -249,7 +138,7 @@
</view>
<view class="rule-line">
<text class="dot">·</text>
<text class="text">请仔细核对支付宝账号、银行卡号与收款人姓名,若信息有误将导致打款驳回原路退回;</text>
<text class="text">资金通过微信商户官方企业付款通道结算,直达您的微信零钱钱包,平台免收任何手续费;</text>
</view>
<view class="rule-line">
<text class="dot">·</text>
@@ -482,41 +371,12 @@ const handleSubmitWithdraw = async () => {
return;
}
// 渠道表单必填项校验
if (currentChannel.value === 'ALIPAY') {
if (!accountForm.value.alipayAccount.trim()) {
uni.showToast({ title: '请输入支付宝账号', icon: 'none' });
return;
}
if (!accountForm.value.realName.trim()) {
uni.showToast({ title: '请输入收款人姓名', icon: 'none' });
return;
}
} else if (currentChannel.value === 'BANK') {
if (!accountForm.value.bankCard.trim()) {
uni.showToast({ title: '请输入银行卡卡号', icon: 'none' });
return;
}
if (!accountForm.value.bankName.trim()) {
uni.showToast({ title: '请输入开户行名称', icon: 'none' });
return;
}
if (!accountForm.value.realName.trim()) {
uni.showToast({ title: '请输入持卡人姓名', icon: 'none' });
return;
}
}
isSubmitting.value = true;
uni.showLoading({ title: '正在提交申请...' });
try {
const payload = {
channel: currentChannel.value,
alipayAccount: accountForm.value.alipayAccount.trim(),
bankCard: accountForm.value.bankCard.trim(),
bankName: accountForm.value.bankName.trim(),
realName: accountForm.value.realName.trim()
channel: 'WECHAT'
};
const res = await withdrawCommission(1001, amt, payload);
@@ -824,275 +684,159 @@ onShow(() => {
}
}
/* 渠道选择九宫格 */
.channel-grid {
display: flex;
gap: 16rpx;
.channel-item {
flex: 1;
position: relative;
background: #0F172A;
border: 1.5px solid rgba(255, 255, 255, 0.08);
/* 微信零钱单渠道专享卡 */
.channel-card {
.section-badge-green {
font-size: 20rpx;
font-weight: 600;
color: #10B981;
background: rgba(16, 185, 129, 0.12);
border: 1px solid rgba(16, 185, 129, 0.3);
padding: 2rpx 14rpx;
border-radius: 20rpx;
padding: 24rpx 12rpx;
}
.wechat-channel-box {
display: flex;
flex-direction: column;
align-items: center;
transition: all 0.2s ease;
border: 1.5px solid rgba(16, 185, 129, 0.4);
background: rgba(16, 185, 129, 0.06);
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(16, 185, 129, 0.12);
&.active {
background: rgba(16, 185, 129, 0.08);
border-color: #10B981;
box-shadow: 0 4rpx 20rpx rgba(16, 185, 129, 0.25);
}
.channel-badge {
position: absolute;
top: -12rpx;
right: 12rpx;
font-size: 18rpx;
background: #10B981;
color: #FFFFFF;
padding: 2rpx 10rpx;
border-radius: 12rpx;
font-weight: 600;
&.blue { background: #0284C7; }
&.gold { background: #D97706; }
}
.channel-icon-wrap {
width: 72rpx;
height: 72rpx;
.channel-icon-wrap.wechat {
width: 80rpx;
height: 80rpx;
border-radius: 20rpx;
background: rgba(16, 185, 129, 0.18);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12rpx;
&.wechat { background: rgba(16, 185, 129, 0.15); }
&.alipay { background: rgba(2, 132, 199, 0.15); }
&.bank { background: rgba(245, 158, 11, 0.15); }
margin-right: 20rpx;
flex-shrink: 0;
.channel-svg {
width: 44rpx;
height: 44rpx;
width: 48rpx;
height: 48rpx;
}
}
.channel-name {
font-size: 24rpx;
font-weight: 700;
color: #F8FAFC;
margin-bottom: 4rpx;
.channel-info-col {
flex: 1;
.channel-line-1 {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 6rpx;
.channel-name {
font-size: 30rpx;
font-weight: 700;
color: #F8FAFC;
}
.channel-tag {
font-size: 18rpx;
color: #FFFFFF;
background: linear-gradient(135deg, #10B981, #059669);
padding: 2rpx 12rpx;
border-radius: 12rpx;
font-weight: 600;
}
}
.channel-desc {
font-size: 22rpx;
color: #94A3B8;
line-height: 1.4;
display: block;
}
}
.channel-desc {
font-size: 20rpx;
color: #64748B;
}
.channel-select-mark {
position: absolute;
bottom: 8rpx;
right: 8rpx;
.channel-check-badge {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
margin-left: 16rpx;
flex-shrink: 0;
.check-svg {
width: 28rpx;
height: 28rpx;
width: 36rpx;
height: 36rpx;
}
}
}
}
/* 提现金额大字输入框 */
.amount-input-box {
display: flex;
align-items: center;
background: #0F172A;
border: 1.5px solid rgba(255, 255, 255, 0.1);
border-radius: 20rpx;
padding: 0 24rpx;
height: 116rpx;
margin-bottom: 14rpx;
transition: all 0.25s ease;
.wechat-safe-notice {
background: rgba(16, 185, 129, 0.05);
border: 1px dashed rgba(16, 185, 129, 0.25);
border-radius: 16rpx;
padding: 18rpx 20rpx;
&:focus-within {
border-color: #10B981;
box-shadow: 0 0 16rpx rgba(16, 185, 129, 0.2);
}
.currency-prefix-box {
display: flex;
align-items: center;
justify-content: center;
padding-right: 12rpx;
flex-shrink: 0;
.input-currency-symbol {
font-size: 50rpx;
font-weight: 800;
color: #10B981;
line-height: 1;
}
}
.amount-field-input {
flex: 1;
height: 100%;
font-size: 50rpx;
font-weight: 800;
color: #FFFFFF;
font-family: -apple-system, BlinkMacSystemFont, 'DIN Alternate', Roboto, sans-serif;
font-variant-numeric: tabular-nums;
padding: 0 10rpx;
line-height: 116rpx;
}
.amount-input-ph {
font-size: 38rpx;
font-weight: 500;
color: #475569;
}
.btn-all-in {
font-size: 24rpx;
font-weight: 700;
color: #10B981;
background: rgba(16, 185, 129, 0.15);
border: 1px solid rgba(16, 185, 129, 0.35);
padding: 10rpx 22rpx;
border-radius: 14rpx;
margin: 0;
line-height: 1.4;
white-space: nowrap;
flex-shrink: 0;
}
}
.amount-tips-row {
.tip-msg {
font-size: 22rpx;
line-height: 1.4;
&.normal { color: #94A3B8; }
&.warning { color: #F59E0B; }
&.error { color: #EF4444; font-weight: 600; }
}
}
/* 账户资料录入与通知卡 */
.wechat-bind-notice {
background: rgba(16, 185, 129, 0.08);
border: 1px dashed rgba(16, 185, 129, 0.3);
border-radius: 18rpx;
padding: 22rpx 24rpx;
.notice-head {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 8rpx;
.notice-icon {
width: 32rpx;
height: 32rpx;
}
.notice-title {
font-size: 24rpx;
font-weight: 700;
color: #34D399;
}
}
.notice-body {
font-size: 22rpx;
color: #94A3B8;
line-height: 1.5;
}
}
.form-inputs-wrap {
display: flex;
flex-direction: column;
gap: 20rpx;
.form-group {
display: flex;
flex-direction: column;
gap: 10rpx;
.form-label {
font-size: 24rpx;
font-weight: 600;
color: #CBD5E1;
}
.form-input {
background: #0F172A;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16rpx;
padding: 0 20rpx;
height: 84rpx;
font-size: 26rpx;
color: #FFFFFF;
}
.form-ph {
font-size: 24rpx;
color: #64748B;
.notice-desc {
font-size: 22rpx;
color: #94A3B8;
line-height: 1.5;
display: block;
}
}
}
/* 规范说明卡片 */
/* 规范说明卡片 (高对比度深色模式) */
.rules-card {
background: rgba(21, 29, 48, 0.6);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 20rpx;
padding: 24rpx;
background: rgba(30, 41, 59, 0.65);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 24rpx;
padding: 28rpx 24rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.2);
.rules-head {
display: flex;
align-items: center;
gap: 10rpx;
margin-bottom: 14rpx;
gap: 12rpx;
margin-bottom: 18rpx;
.rules-icon {
width: 28rpx;
height: 28rpx;
width: 32rpx;
height: 32rpx;
}
.rules-title {
font-size: 24rpx;
font-weight: 600;
color: #94A3B8;
font-size: 26rpx;
font-weight: 700;
color: #F8FAFC;
letter-spacing: 0.5rpx;
}
}
.rules-list {
display: flex;
flex-direction: column;
gap: 10rpx;
gap: 14rpx;
.rule-line {
display: flex;
align-items: flex-start;
gap: 8rpx;
gap: 10rpx;
.dot {
font-size: 24rpx;
color: #64748B;
font-size: 28rpx;
font-weight: 800;
color: #10B981;
line-height: 1.4;
}
.text {
font-size: 22rpx;
color: #64748B;
line-height: 1.5;
font-size: 23rpx;
color: #CBD5E1;
line-height: 1.6;
font-weight: 400;
flex: 1;
}
}
@@ -1364,16 +1108,34 @@ onShow(() => {
}
}
.channel-grid .channel-item {
background: #FAFAF9;
border-color: #E7E5E4;
&.active {
.channel-card {
.section-badge-green {
color: #EA580C;
background: #FFF7ED;
border-color: #EA580C;
border-color: #FDBA74;
}
.channel-name { color: #1C1917; }
.wechat-channel-box {
background: #FFF7ED;
border: 1.5px solid #FDBA74;
box-shadow: 0 4rpx 16rpx rgba(234, 88, 12, 0.08);
.channel-info-col .channel-line-1 .channel-name {
color: #1C1917;
}
.channel-info-col .channel-desc {
color: #78716C;
}
}
.wechat-safe-notice {
background: #FAFAF9;
border-color: #E7E5E4;
.notice-desc {
color: #78716C;
}
}
}
.amount-input-box {
@@ -1395,12 +1157,38 @@ onShow(() => {
}
}
.form-inputs-wrap .form-group {
.form-label { color: #44403C; }
.form-input {
background: #FAFAF9;
border-color: #E7E5E4;
color: #1C1917;
/* 规范说明卡片浅色暖阳专属高清晰度适配 (彻底解决图2红圈看不清问题) */
.rules-card {
background: #FFFFFF;
border: 1px solid #FED7AA;
box-shadow: 0 4rpx 20rpx rgba(234, 88, 12, 0.06);
.rules-head {
.rules-icon {
filter: brightness(0) saturate(100%) invert(44%) sepia(76%) saturate(2227%) hue-rotate(357deg) brightness(97%) contrast(93%);
}
.rules-title {
color: #9A3412;
font-weight: 700;
font-size: 26rpx;
}
}
.rules-list {
.rule-line {
.dot {
color: #EA580C;
font-weight: 800;
font-size: 28rpx;
}
.text {
color: #44403C;
font-size: 24rpx;
line-height: 1.6;
font-weight: 500;
}
}
}
}

查看文件

@@ -147,17 +147,27 @@ describe('ClientFeatures (TDD 测试套件: 用户端功能闭环)', () => {
expect(content).toContain("app.get('/api/favorites/check'");
});
it('陪玩详情页与个人中心必须实现收藏状态联动与列表渲染', () => {
it('陪玩详情页、个人中心与独立收藏页面必须实现收藏状态联动与列表渲染', () => {
const detailVuePath = path.join(uniPagesDir, 'expert', 'detail.vue');
const detailContent = fs.readFileSync(detailVuePath, 'utf-8');
expect(detailContent).toContain('toggleFavorite');
expect(detailContent).toContain('isFav');
// 个人中心点击我的收藏直接跳转独立页面
const userVuePath = path.join(uniPagesDir, 'user', 'index.vue');
const userContent = fs.readFileSync(userVuePath, 'utf-8');
expect(userContent).toContain('openFavoritesModal');
expect(userContent).toContain('favoriteList');
expect(userContent).toContain('handleRemoveFavorite');
expect(userContent).toContain('/pages/user/favorites');
// 独立收藏页面完整闭环与能力
const favVuePath = path.join(uniPagesDir, 'user', 'favorites.vue');
expect(fs.existsSync(favVuePath)).toBe(true);
const favContent = fs.readFileSync(favVuePath, 'utf-8');
expect(favContent).toContain('removeFavorite');
expect(favContent).toContain('handleCancelFavorite');
expect(favContent).toContain('filterOnlyOnline');
expect(favContent).toContain('isOnline');
});
});

查看文件

@@ -130,7 +130,7 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安
const raw = fs.readFileSync(pagesJsonPath, 'utf-8');
const config = JSON.parse(raw);
expect(Array.isArray(config.pages)).toBe(true);
expect(config.pages.length).toBe(17);
expect(config.pages.length).toBe(18);
for (const pageItem of config.pages) {
const vueFilePath = path.join(rootDir, 'pwuniapp', `${pageItem.path}.vue`);
@@ -685,13 +685,11 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安
expect(pagesJson).toContain('收益提现');
});
it('独立提现页面必须支持微信零钱、支付宝、银行卡三渠道与金额输入校验', () => {
it('独立提现页面必须支持微信零钱官方极速直达渠道与金额输入校验', () => {
const wdPath = path.join(rootDir, 'pwuniapp', 'pages', 'user', 'withdraw.vue');
const content = fs.readFileSync(wdPath, 'utf-8');
expect(content).toContain('currentChannel');
expect(content).toContain('WECHAT');
expect(content).toContain('ALIPAY');
expect(content).toContain('BANK');
expect(content).toContain('withdrawAmount');
expect(content).toContain('handleFillAllCommission');
expect(content).toContain('withdrawCommission');