feat(expert): adapt expert detail page to dual-theme system with theme-warm support
这个提交包含在:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="expert-detail-container">
|
||||
<view class="expert-detail-container" :class="{ 'theme-warm': currentThemeKey === 'warm' }">
|
||||
<!-- 大神主页骨架屏 (首屏加载中流光占位,杜绝视觉空白) -->
|
||||
<skeleton-screen v-if="isLoading" type="expert" />
|
||||
|
||||
@@ -240,11 +240,29 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import SkeletonScreen from '@/components/SkeletonScreen.vue';
|
||||
import { getCompanionDetail, getExpertReviews, replyOrderReview, submitReport as submitReportApi, checkFavorite, addFavorite as addFavApi, removeFavorite as removeFavApi } from '@/common/api.js';
|
||||
import { ICONS } from '@/common/icons.js';
|
||||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||||
|
||||
const currentThemeKey = ref(getCurrentThemeKey());
|
||||
|
||||
const onThemeChanged = (key) => {
|
||||
currentThemeKey.value = key;
|
||||
applyThemeToNativeUI(key);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
currentThemeKey.value = getCurrentThemeKey();
|
||||
applyThemeToNativeUI(currentThemeKey.value);
|
||||
uni.$on('theme-change', onThemeChanged);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off('theme-change', onThemeChanged);
|
||||
});
|
||||
|
||||
const isLoading = ref(true);
|
||||
const isFav = ref(false);
|
||||
@@ -279,6 +297,8 @@ const refreshFavoriteState = async () => {
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
currentThemeKey.value = getCurrentThemeKey();
|
||||
applyThemeToNativeUI(currentThemeKey.value);
|
||||
refreshFavoriteState();
|
||||
});
|
||||
|
||||
@@ -1185,4 +1205,304 @@ const submitReportTicket = async () => {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 暖阳晨曦浅色主题适配 (theme-warm) */
|
||||
.expert-detail-container.theme-warm {
|
||||
background-color: #FDFBF7;
|
||||
|
||||
.hero-header {
|
||||
.cover-mask {
|
||||
background: linear-gradient(180deg, rgba(253, 251, 247, 0.45) 0%, #FDFBF7 100%);
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
.avatar-wrap {
|
||||
.avatar-img {
|
||||
border-color: #EA580C;
|
||||
}
|
||||
}
|
||||
|
||||
.info-wrap {
|
||||
.name-row {
|
||||
.nickname {
|
||||
color: #1C1917;
|
||||
}
|
||||
}
|
||||
|
||||
.rank-row {
|
||||
.game-tag {
|
||||
background: #F5F5F4;
|
||||
color: #57534E;
|
||||
}
|
||||
.rank-tag {
|
||||
background: #FFF7ED;
|
||||
color: #EA580C;
|
||||
}
|
||||
}
|
||||
|
||||
.city-row {
|
||||
.city-text {
|
||||
color: #EA580C;
|
||||
}
|
||||
}
|
||||
|
||||
.data-row {
|
||||
color: #78716C;
|
||||
|
||||
.rating-box {
|
||||
.rating-val {
|
||||
color: #D97706;
|
||||
}
|
||||
}
|
||||
.order-val {
|
||||
color: #78716C;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.voice-card {
|
||||
background: #FFFFFF;
|
||||
border-color: #FED7AA;
|
||||
box-shadow: 0 4rpx 16rpx rgba(234, 88, 12, 0.08);
|
||||
|
||||
.voice-icon-wrap {
|
||||
background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
|
||||
box-shadow: 0 4rpx 12rpx rgba(234, 88, 12, 0.3);
|
||||
}
|
||||
|
||||
.voice-info {
|
||||
.voice-title {
|
||||
color: #1C1917;
|
||||
}
|
||||
.voice-dur {
|
||||
color: #78716C;
|
||||
}
|
||||
.voice-wave .bar {
|
||||
background: #EA580C;
|
||||
}
|
||||
}
|
||||
|
||||
.listen-tag {
|
||||
background: #FFF7ED;
|
||||
color: #EA580C;
|
||||
border: 1px solid rgba(234, 88, 12, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.section-card {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #F0ECE4;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
|
||||
|
||||
.sec-title {
|
||||
color: #1C1917;
|
||||
}
|
||||
|
||||
.bio-text {
|
||||
color: #57534E;
|
||||
}
|
||||
|
||||
.rate-percent {
|
||||
color: #EA580C;
|
||||
}
|
||||
|
||||
.empty-reviews {
|
||||
color: #A8A29E;
|
||||
}
|
||||
}
|
||||
|
||||
.service-list {
|
||||
.service-item {
|
||||
background: #FDFBF7;
|
||||
border-color: #E7E5E4;
|
||||
|
||||
.srv-left {
|
||||
.srv-title {
|
||||
color: #1C1917;
|
||||
}
|
||||
.srv-desc {
|
||||
color: #78716C;
|
||||
}
|
||||
}
|
||||
|
||||
.srv-right {
|
||||
.price-sym, .price-num {
|
||||
color: #EA580C;
|
||||
}
|
||||
.price-unit {
|
||||
color: #A8A29E;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: #EA580C;
|
||||
background: #FFF7ED;
|
||||
box-shadow: 0 4rpx 16rpx rgba(234, 88, 12, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.offline-safety-card {
|
||||
background: #FFFBEB;
|
||||
border-color: #FDE68A;
|
||||
|
||||
.safety-content {
|
||||
.safety-title {
|
||||
color: #B45309;
|
||||
}
|
||||
.safety-desc {
|
||||
color: #78716C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gallery-list {
|
||||
.gallery-item {
|
||||
border-color: #E7E5E4;
|
||||
}
|
||||
}
|
||||
|
||||
.review-list {
|
||||
.review-item {
|
||||
border-bottom-color: #F5F5F4;
|
||||
|
||||
.rev-body {
|
||||
.rev-top {
|
||||
.rev-name {
|
||||
color: #1C1917;
|
||||
}
|
||||
.rev-time {
|
||||
color: #A8A29E;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-row {
|
||||
.rev-tag {
|
||||
background: #F5F5F4;
|
||||
color: #57534E;
|
||||
}
|
||||
}
|
||||
|
||||
.rev-content {
|
||||
color: #44403C;
|
||||
}
|
||||
|
||||
.expert-reply-bubble {
|
||||
background: #FDFBF7;
|
||||
border-left-color: #EA580C;
|
||||
|
||||
.reply-badge-row {
|
||||
.reply-badge {
|
||||
background: #FFF7ED;
|
||||
color: #EA580C;
|
||||
}
|
||||
.reply-time {
|
||||
color: #A8A29E;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-text {
|
||||
color: #57534E;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-action-row {
|
||||
.btn-quick-reply {
|
||||
background: #FFF7ED;
|
||||
color: #EA580C;
|
||||
border-color: rgba(234, 88, 12, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-top-color: #F0ECE4;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.icon-action {
|
||||
.action-text {
|
||||
color: #57534E;
|
||||
|
||||
&.fav-active {
|
||||
color: #EA580C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.price-summary {
|
||||
.summary-label {
|
||||
color: #78716C;
|
||||
}
|
||||
.summary-price {
|
||||
color: #EA580C;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-book {
|
||||
background: linear-gradient(135deg, #EA580C 0%, #C2410C 100%);
|
||||
box-shadow: 0 4rpx 20rpx rgba(234, 88, 12, 0.35);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.review-modal-content {
|
||||
background: #FFFFFF;
|
||||
border-color: #E7E5E4;
|
||||
box-shadow: 0 -8rpx 32rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
.modal-header {
|
||||
.modal-title {
|
||||
color: #1C1917;
|
||||
}
|
||||
.modal-subtitle {
|
||||
color: #78716C;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-section {
|
||||
.tags-label {
|
||||
color: #1C1917;
|
||||
}
|
||||
.tags-wrap {
|
||||
.review-tag {
|
||||
background: #F5F5F4;
|
||||
color: #57534E;
|
||||
border-color: #E7E5E4;
|
||||
|
||||
&.active {
|
||||
color: #EF4444;
|
||||
background: #FEF2F2;
|
||||
border-color: #EF4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-section {
|
||||
.textarea-label {
|
||||
color: #1C1917;
|
||||
}
|
||||
.review-textarea {
|
||||
background: #FDFBF7;
|
||||
color: #1C1917;
|
||||
border-color: #E7E5E4;
|
||||
}
|
||||
.char-count {
|
||||
color: #A8A29E;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
.modal-btn.cancel-btn {
|
||||
background: #F5F5F4;
|
||||
color: #57534E;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1051,6 +1051,16 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安
|
||||
expect(content).toContain('syncInitialPostStates');
|
||||
});
|
||||
|
||||
it('大神主页必须接入全局双主题联动并在暖阳晨曦模式下适配 theme-warm', () => {
|
||||
const expertDetailPath = path.join(rootDir, 'pwuniapp', 'pages', 'expert', 'detail.vue');
|
||||
const content = fs.readFileSync(expertDetailPath, 'utf-8');
|
||||
expect(content).toContain("theme-warm': currentThemeKey === 'warm'");
|
||||
expect(content).toContain('getCurrentThemeKey');
|
||||
expect(content).toContain('applyThemeToNativeUI');
|
||||
expect(content).toContain('theme-change');
|
||||
expect(content).toContain('.expert-detail-container.theme-warm');
|
||||
});
|
||||
|
||||
it('所有状态持久化修复代码与测试严禁包含任何系统 Emoji 违规字符', () => {
|
||||
const actDetailPath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'detail.vue');
|
||||
const ticketPath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'my-ticket.vue');
|
||||
|
||||
在新工单中引用
屏蔽一个用户