diff --git a/pwuniapp/manifest.json b/pwuniapp/manifest.json index 9d32815..92fa3bd 100644 --- a/pwuniapp/manifest.json +++ b/pwuniapp/manifest.json @@ -50,7 +50,7 @@ "quickapp" : {}, /* 小程序特有相关 */ "mp-weixin" : { - "appid" : "", + "appid" : null, "setting" : { "urlCheck" : false }, diff --git a/pwuniapp/pages.json b/pwuniapp/pages.json index 94b0721..2d1d4e7 100644 --- a/pwuniapp/pages.json +++ b/pwuniapp/pages.json @@ -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": { diff --git a/pwuniapp/pages/user/favorites.vue b/pwuniapp/pages/user/favorites.vue new file mode 100644 index 0000000..036a2dd --- /dev/null +++ b/pwuniapp/pages/user/favorites.vue @@ -0,0 +1,745 @@ + + + + + diff --git a/pwuniapp/pages/user/index.vue b/pwuniapp/pages/user/index.vue index 6037daa..76d8b60 100644 --- a/pwuniapp/pages/user/index.vue +++ b/pwuniapp/pages/user/index.vue @@ -320,58 +320,6 @@ - - - - - - 我的收藏陪玩 - 实时高亮接单在线状态 - - - - - - - - - - - - - - - {{ item.isOnline ? '接单中' : '休息中' }} - - - - - {{ item.nickname }} - {{ item.track }} - - {{ item.rankName || '金牌陪伴师' }} - - ¥{{ item.hourlyRate }} - /小时 - 评分 {{ item.rating || '5.0' }} - - - - - - - - - - - - 暂未收藏心仪的陪玩大神 - - - - - - @@ -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 { diff --git a/pwuniapp/pages/user/withdraw.vue b/pwuniapp/pages/user/withdraw.vue index 950f462..9ba03f4 100644 --- a/pwuniapp/pages/user/withdraw.vue +++ b/pwuniapp/pages/user/withdraw.vue @@ -57,64 +57,31 @@ - - + + 提现到账方式 - 支持三种主流渠道结算 + 极速免审核 - - - - 推荐 - - - - 微信零钱 - 极速到账 - - - + + + + + + 微信零钱钱包 + 推荐首选 + + 企业付款直达当前微信零钱 · 免手续费 + + + + + - - - 大额首选 - - - - 支付宝 - 支持大额 - - - - - - - - 主流银行 - - - - 银行储蓄卡 - 全国通用 - - - - + + 系统将通过微信商户企业付款打款到您当前登录小程序的微信零钱账户中,无需额外录入银行卡号,提交申请后预计 1 至 24 小时内由系统自动发放至微信零钱钱包。 @@ -154,85 +121,7 @@ - - - - 收款账户资料 - 请确保实名信息完全一致 - - - - - - - 自动直达当前微信零钱钱包 - - - 系统将通过微信商户企业付款打款到您当前登录小程序的微信零钱账户中,无需额外录入卡号,提交申请后预计 1 至 24 小时内由系统自动发放。 - - - - - - - 支付宝账号 - - - - 收款人真实姓名 - - - - - - - - 储蓄卡卡号 - - - - 开户银行名称 - - - - 持卡人真实姓名 - - - - - - + @@ -249,7 +138,7 @@ · - 请仔细核对支付宝账号、银行卡号与收款人姓名,若信息有误将导致打款驳回原路退回; + 资金通过微信商户官方企业付款通道结算,直达您的微信零钱钱包,平台免收任何手续费; · @@ -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; + } + } } } diff --git a/server/tests/client-features.spec.ts b/server/tests/client-features.spec.ts index f4fe969..117cf9a 100644 --- a/server/tests/client-features.spec.ts +++ b/server/tests/client-features.spec.ts @@ -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'); }); }); diff --git a/server/tests/content-security.spec.ts b/server/tests/content-security.spec.ts index 938fdd9..0aa2284 100644 --- a/server/tests/content-security.spec.ts +++ b/server/tests/content-security.spec.ts @@ -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');