770 lines
21 KiB
Vue
770 lines
21 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<!-- 顶部导航栏 -->
|
||
<view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="nav-content">
|
||
<view class="nav-left" @tap="goBack">
|
||
<text class="back-icon"></text>
|
||
<text class="nav-title">夯拉评分</text>
|
||
</view>
|
||
<view class="nav-right">
|
||
<text class="search-icon"></text>
|
||
<image class="nav-avatar" src="/static/images/default-avatar.png" mode="aspectFill" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view :style="{ height: statusBarHeight + 44 + 'px' }"></view>
|
||
|
||
<view class="content-wrap">
|
||
<!-- 头部信息卡片 -->
|
||
<view class="hero-card card">
|
||
<image class="hero-avatar" :src="detailData.avatar" mode="aspectFill" />
|
||
<view class="hero-info">
|
||
<view class="hero-name-row">
|
||
<text class="hero-name">{{ detailData.name }}</text>
|
||
<text class="hero-score">{{ detailData.score }}</text>
|
||
</view>
|
||
<view class="hero-meta">
|
||
<text class="meta-rank">🥇 No.1</text>
|
||
<view class="meta-heat">
|
||
<image class="meta-icon" src="/static/images/icon/fire.png" mode="aspectFit"></image>
|
||
<text>{{ detailData.hotScore }}w 热度</text>
|
||
</view>
|
||
<text class="meta-count" style="margin-left: 20rpx; color: #999;">{{ detailData.ratingCount }} 人评分</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- AI 观察员 -->
|
||
<view class="ai-card">
|
||
<view class="ai-header">
|
||
<image class="ai-icon ai-icon-img" src="/static/images/icon/robot.png" mode="aspectFit"></image>
|
||
<text class="ai-title">AI观察员</text>
|
||
</view>
|
||
<text class="ai-content">{{ detailData.aiSummary }}</text>
|
||
</view>
|
||
|
||
<!-- 你的态度 -->
|
||
<AttitudePanel
|
||
ref="attitudePanelRef"
|
||
:initial-action="currentAction"
|
||
:sending="isSendingComment"
|
||
@action-change="handleActionChange"
|
||
@send-comment="handleSendComment"
|
||
/>
|
||
|
||
<!-- 大家都在 PK -->
|
||
<!-- <view v-if="pkPreview" class="pk-panel card" @tap="goToPk()">
|
||
<view class="pk-header">
|
||
<text class="section-title">大家都在 PK</text>
|
||
<view class="go-compare">去对比<text class="arrow-right"></text></view>
|
||
</view>
|
||
<view class="pk-content">
|
||
<view class="pk-person">
|
||
<image class="pk-avatar blue-border" :src="pkPreview.left.avatar" mode="aspectFill" />
|
||
<text class="pk-name">{{ pkPreview.left.name }}</text>
|
||
<text class="pk-score blue-text">{{ pkPreview.left.scoreText }}</text>
|
||
</view>
|
||
<text class="vs-text">VS</text>
|
||
<view class="pk-person">
|
||
<image class="pk-avatar yellow-border" :src="pkPreview.right.avatar" mode="aspectFill" />
|
||
<text class="pk-name">{{ pkPreview.right.name }}</text>
|
||
<text class="pk-score yellow-text">{{ pkPreview.right.scoreText }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="pk-bar-wrap">
|
||
<view class="pk-bar blue-bar" :style="{ width: `${pkPreview.leftPercent}%` }"></view>
|
||
<view class="pk-bar yellow-bar" :style="{ width: `${pkPreview.rightPercent}%` }"></view>
|
||
</view>
|
||
<view class="pk-data-text">
|
||
<text class="blue-text">{{ pkPreview.leftPercent }}% 看好</text>
|
||
<text class="yellow-text">{{ pkPreview.rightPercent }}% 看好</text>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 评论区 -->
|
||
<CommentSection
|
||
ref="commentSectionRef"
|
||
variant="topic"
|
||
section-title="评论区"
|
||
:request-key="commentRequestKey"
|
||
:visible="!!commentRequestKey"
|
||
:allow-compose="false"
|
||
page-name="rating_detail"
|
||
loading-desc="正在拉取大家的锐评"
|
||
empty-title="还没有评论"
|
||
empty-desc="来发第一条锐评,带动一下讨论气氛"
|
||
empty-tip="你的观点,可能就是这条榜单最有意思的开始"
|
||
:show-empty-visual="true"
|
||
load-more-mode="scroll"
|
||
footer-hint-text="上拉加载更多"
|
||
:show-success-toast="false"
|
||
:list-handler="fetchTopicComments"
|
||
:replies-handler="fetchTopicCommentReplies"
|
||
:publish-handler="publishTopicComment"
|
||
:normalize-comment="normalizeCommentItem"
|
||
@need-login="openLoginPopup"
|
||
@published="handleCommentPublished"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 评分成功特效弹窗 -->
|
||
<SuccessPopup ref="successPopupRef" />
|
||
<LoginPopup ref="loginPopupRef" @logind="handleLoginSuccess" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from 'vue';
|
||
import { getStatusBarHeight } from "@/utils/system";
|
||
import { onLoad, onPullDownRefresh, onReachBottom, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
|
||
import AttitudePanel from "@/components/AttitudePanel/AttitudePanel.vue";
|
||
import SuccessPopup from "@/components/SuccessPopup/SuccessPopup.vue";
|
||
import CommentSection from "@/components/CommentSection/CommentSection.vue";
|
||
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
||
import { FILE_BASE_URL } from "@/utils/constants";
|
||
import { fetchTopicRatingItems as fetchTopicItemList } from "@/api/topic";
|
||
import { fetchTopicRatingItems, topicItemScore, topicItemComment, topicItemCommentList, topicItemCommentReplyList } from "@/api/topicItem";
|
||
import { formatDate } from "@/utils/date";
|
||
import { getShareToken } from "@/utils/common";
|
||
import { getShareReward } from "@/api/system";
|
||
import { useUserStore } from "@/stores/user";
|
||
const userStore = useUserStore();
|
||
const statusBarHeight = ref(getStatusBarHeight() || 44);
|
||
const currentAction = ref('');
|
||
const successPopupRef = ref(null);
|
||
const loginPopupRef = ref(null);
|
||
const attitudePanelRef = ref(null);
|
||
const commentSectionRef = ref(null);
|
||
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
|
||
const isSendingComment = ref(false);
|
||
const pkPreview = ref(null);
|
||
const commentRequestKey = computed(() => {
|
||
if (!detailData.value.topicId || !itemId.value) return '';
|
||
return `${detailData.value.topicId}_${itemId.value}`;
|
||
});
|
||
|
||
const goBack = () => {
|
||
uni.navigateBack();
|
||
};
|
||
|
||
const openLoginPopup = (message = '请先登录后再操作') => {
|
||
loginPopupRef.value?.open();
|
||
if (message) {
|
||
uni.showToast({
|
||
title: message,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
};
|
||
|
||
const detailData = ref({
|
||
id: '',
|
||
name: '',
|
||
score: '0.0',
|
||
avatar: '',
|
||
aiSummary: '加载中...',
|
||
userAction: "",
|
||
topicId: ''
|
||
});
|
||
const itemId = ref('');
|
||
|
||
const buildAvatarUrl = (avatar) => {
|
||
if (!avatar) return 'https://api.dicebear.com/7.x/avataaars/svg?seed=fallback';
|
||
const normalizedAvatar = String(avatar).trim();
|
||
return normalizedAvatar.startsWith('http') ? normalizedAvatar : `${FILE_BASE_URL}${normalizedAvatar}`;
|
||
};
|
||
|
||
const normalizeCommentItem = (item = {}, rootComment = null) => {
|
||
const user = item.user || {};
|
||
const replyToUser = item.replyToUser || {};
|
||
return {
|
||
id: item.id,
|
||
rootCommentId: item.rootCommentId || rootComment?.id || null,
|
||
parentCommentId: item.parentCommentId || rootComment?.id || null,
|
||
replyToCommentId: item.replyToCommentId || null,
|
||
name: user.nickname || '匿名用户',
|
||
avatar: buildAvatarUrl(user.avatar),
|
||
time: formatDate(item.createdAt) || '刚刚',
|
||
content: item.content || '',
|
||
likes: Number(item.likeCount || 0),
|
||
isLiked: !!item.isLiked,
|
||
badge: '',
|
||
badgeIcon: '',
|
||
userAction: item.userAction || '',
|
||
replyCount: Number(item.replyCount || 0),
|
||
replyToName: replyToUser.nickname || '',
|
||
repliesLoading: false,
|
||
repliesLoaded: !!rootComment,
|
||
repliesHasMore: false,
|
||
repliesPage: 1,
|
||
replies: rootComment ? [] : []
|
||
};
|
||
};
|
||
|
||
const calcPkPercent = (leftScore, rightScore) => {
|
||
const left = Number(leftScore) || 0;
|
||
const right = Number(rightScore) || 0;
|
||
if (left <= 0 && right <= 0) {
|
||
return { leftPercent: 50, rightPercent: 50 };
|
||
}
|
||
const total = left + right || 1;
|
||
let leftPercent = Math.round((left / total) * 100);
|
||
leftPercent = Math.min(82, Math.max(18, leftPercent));
|
||
return {
|
||
leftPercent,
|
||
rightPercent: 100 - leftPercent
|
||
};
|
||
};
|
||
|
||
const buildPkPreview = (items) => {
|
||
const sortedItems = [...items].sort((a, b) => (Number(b.scoreAvg) || 0) - (Number(a.scoreAvg) || 0));
|
||
const currentIndex = sortedItems.findIndex((item) => String(item.id) === String(itemId.value));
|
||
if (currentIndex === -1) return null;
|
||
|
||
const currentItem = sortedItems[currentIndex];
|
||
const rival =
|
||
sortedItems[currentIndex === 0 ? 1 : currentIndex - 1] ||
|
||
sortedItems.find((item) => String(item.id) !== String(itemId.value));
|
||
|
||
if (!currentItem || !rival) return null;
|
||
|
||
const { leftPercent, rightPercent } = calcPkPercent(currentItem.scoreAvg, rival.scoreAvg);
|
||
return {
|
||
left: {
|
||
id: currentItem.id,
|
||
name: currentItem.name || '当前对象',
|
||
avatar: buildAvatarUrl(currentItem.avatarUrl),
|
||
scoreText: `${Number(currentItem.scoreAvg || 0).toFixed(1)} 分`
|
||
},
|
||
right: {
|
||
id: rival.id,
|
||
name: rival.name || '对手',
|
||
avatar: buildAvatarUrl(rival.avatarUrl),
|
||
scoreText: `${Number(rival.scoreAvg || 0).toFixed(1)} 分`
|
||
},
|
||
leftPercent,
|
||
rightPercent
|
||
};
|
||
};
|
||
|
||
const getPkPreviewData = async () => {
|
||
if (!detailData.value.topicId || !itemId.value) {
|
||
pkPreview.value = null;
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const res = await fetchTopicItemList(detailData.value.topicId, 1);
|
||
const rawList = res?.data?.records || res?.records || res?.data?.list || res?.list || [];
|
||
const list = Array.isArray(rawList) ? rawList : [];
|
||
pkPreview.value = list.length >= 2 ? buildPkPreview(list) : null;
|
||
} catch (error) {
|
||
pkPreview.value = null;
|
||
}
|
||
};
|
||
|
||
const goToPk = () => {
|
||
if (!detailData.value.topicId || !itemId.value) return;
|
||
uni.navigateTo({
|
||
url: `/pages/rating/pk?topicId=${detailData.value.topicId}&itemId=${itemId.value}`
|
||
});
|
||
};
|
||
|
||
const handleActionChange = async (action) => {
|
||
if (!isLoggedIn.value) {
|
||
openLoginPopup();
|
||
return;
|
||
}
|
||
|
||
const originAction = detailData.value.userAction;
|
||
const originScore = detailData.value.score;
|
||
const isCancel = detailData.value.userAction === action.label;
|
||
|
||
try {
|
||
detailData.value.userAction = isCancel ? '' : action.label;
|
||
currentAction.value = detailData.value.userAction;
|
||
|
||
const res = await topicItemScore({
|
||
topicId: detailData.value.topicId,
|
||
itemId: itemId.value,
|
||
scoreType: action.scoreType
|
||
});
|
||
detailData.value.score = res?.scoreAvg || originScore;
|
||
|
||
// 如果是成功评分(非取消),展示特效弹窗
|
||
if (!isCancel && successPopupRef.value) {
|
||
successPopupRef.value.show(action);
|
||
}
|
||
uni.$trackRecord({
|
||
eventName: 'rating',
|
||
eventType: 'rating',
|
||
elementId: `item_${itemId.value}`,
|
||
elementContent: `评分项_${detailData.value.name}`,
|
||
customParams: {
|
||
scoreType: action.scoreType,
|
||
action: action.label,
|
||
topicId: detailData.value.topicId,
|
||
topicTitle: detailData.value.name,
|
||
score: detailData.value.score,
|
||
page: 'rating_detail'
|
||
}
|
||
});
|
||
} catch (error) {
|
||
detailData.value.userAction = originAction;
|
||
detailData.value.score = originScore;
|
||
currentAction.value = originAction;
|
||
}
|
||
};
|
||
|
||
const publishTopicComment = ({ content, replyTarget, mode }) => {
|
||
return topicItemComment({
|
||
topicId: detailData.value.topicId,
|
||
itemId: itemId.value,
|
||
content,
|
||
parentCommentId: mode === 'reply' ? replyTarget?.parentCommentId : undefined,
|
||
replyToCommentId: mode === 'reply' ? replyTarget?.replyToCommentId : undefined
|
||
});
|
||
};
|
||
|
||
const fetchTopicComments = ({ page, orderBy }) => {
|
||
return topicItemCommentList(detailData.value.topicId, itemId.value, page, orderBy);
|
||
};
|
||
|
||
const fetchTopicCommentReplies = ({ rootCommentId, page }) => {
|
||
return topicItemCommentReplyList({
|
||
rootCommentId,
|
||
page
|
||
});
|
||
};
|
||
|
||
const handleCommentPublished = ({ mode, content, replyTarget }) => {
|
||
if (successPopupRef.value) {
|
||
successPopupRef.value.show({
|
||
emoji: '💬',
|
||
label: mode === 'reply' ? '回复已发送' : '你的锐评已发布'
|
||
}, 'comment');
|
||
}
|
||
|
||
uni.$trackRecord({
|
||
eventName: 'comment',
|
||
eventType: 'comment',
|
||
elementId: `item_${itemId.value}`,
|
||
elementContent: `评论项_${detailData.value.name}`,
|
||
customParams: {
|
||
comment: content,
|
||
replyToCommentId: replyTarget?.replyToCommentId || '',
|
||
page: 'rating_detail'
|
||
}
|
||
});
|
||
};
|
||
|
||
const handleSendComment = async (payload) => {
|
||
if (!isLoggedIn.value) {
|
||
openLoginPopup();
|
||
return;
|
||
}
|
||
|
||
if (!detailData.value.topicId || !itemId.value || isSendingComment.value) return;
|
||
|
||
try {
|
||
isSendingComment.value = true;
|
||
uni.showLoading({ title: '发送中...' });
|
||
const res = await publishTopicComment({
|
||
content: payload.content,
|
||
mode: 'comment',
|
||
replyTarget: null
|
||
});
|
||
uni.hideLoading();
|
||
if(res?.success === false) {
|
||
uni.showToast({
|
||
title: res?.message || '发送失败',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
handleCommentPublished({
|
||
mode: 'comment',
|
||
content: payload.content,
|
||
replyTarget: null
|
||
});
|
||
attitudePanelRef.value?.resetComment?.();
|
||
await commentSectionRef.value?.refreshComments?.();
|
||
} catch (error) {
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: '发送失败',
|
||
icon: 'none'
|
||
});
|
||
} finally {
|
||
isSendingComment.value = false;
|
||
}
|
||
};
|
||
|
||
const getDetailData = async () => {
|
||
if (!itemId.value) return;
|
||
try {
|
||
uni.showLoading({ title: '加载中' });
|
||
const res = await fetchTopicRatingItems(itemId.value);
|
||
const data = res?.data || res || {};
|
||
|
||
detailData.value = {
|
||
id: itemId.value,
|
||
name: data.name || '未知',
|
||
score: data.scoreAvg ? Number(data.scoreAvg).toFixed(1) : '0.0',
|
||
avatar: data.avatarUrl ? `${FILE_BASE_URL}${data.avatarUrl}` : '',
|
||
aiSummary: data.description || '暂无总结',
|
||
hotScore: data.hotScore || '0.00',
|
||
topicId: data.topicId || detailData.value.topicId || '',
|
||
ratingCount: data.scoreCount || 0,
|
||
userAction: data.userAction || ""
|
||
};
|
||
|
||
currentAction.value = data.userAction || '';
|
||
|
||
await commentSectionRef.value?.refreshComments?.();
|
||
getPkPreviewData();
|
||
} catch (error) {
|
||
console.error('获取详情失败:', error);
|
||
uni.showToast({
|
||
title: '获取详情失败',
|
||
icon: 'none'
|
||
});
|
||
} finally {
|
||
uni.hideLoading();
|
||
uni.stopPullDownRefresh();
|
||
}
|
||
};
|
||
|
||
onLoad((options) => {
|
||
uni.$trackRecord({
|
||
eventName: "rating_item_detail_page_visit",
|
||
eventType: `visit`,
|
||
elementId: options.itemId
|
||
})
|
||
if (options.topicId) {
|
||
detailData.value.topicId = options.topicId;
|
||
}
|
||
if (options.itemId) {
|
||
itemId.value = options.itemId;
|
||
getDetailData();
|
||
}
|
||
});
|
||
|
||
const handleLoginSuccess = () => {
|
||
getDetailData();
|
||
};
|
||
|
||
onShareAppMessage(async () => {
|
||
const shareToken = await getShareToken("rating_item");
|
||
getShareReward({ scene: "rating_item" });
|
||
const itemName = detailData.value.name || "这个评分对象";
|
||
return {
|
||
title: `「${itemName}」在夯拉评分里是什么段位?`,
|
||
path: `/pages/rating/detail?itemId=${itemId.value}&topicId=${detailData.value.topicId}&shareToken=${shareToken}`,
|
||
};
|
||
});
|
||
|
||
onShareTimeline(async () => {
|
||
const shareToken = await getShareToken("rating_item");
|
||
getShareReward({ scene: "rating_item_timeline" });
|
||
const itemName = detailData.value.name || "这个评分对象";
|
||
const score = detailData.value.score || "0.0";
|
||
return {
|
||
title: `${itemName}当前评分 ${score},来看看大家怎么评`,
|
||
query: `itemId=${itemId.value}&topicId=${detailData.value.topicId}&shareToken=${shareToken}`,
|
||
};
|
||
});
|
||
|
||
onPullDownRefresh(() => {
|
||
getDetailData();
|
||
});
|
||
|
||
onReachBottom(() => {
|
||
commentSectionRef.value?.loadMoreComments?.();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page-container {
|
||
min-height: 100vh;
|
||
background-color: #f5f6fa;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
/* 导航栏 */
|
||
.nav-bar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
background-color: #f5f6fa;
|
||
}
|
||
|
||
.nav-content {
|
||
height: 44px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0 32rpx;
|
||
}
|
||
|
||
.nav-left {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.back-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='19' y1='12' x2='5' y2='12'%3E%3C/line%3E%3Cpolyline points='12 19 5 12 12 5'%3E%3C/polyline%3E%3C/svg%3E");
|
||
background-size: cover;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 34rpx;
|
||
font-weight: 700;
|
||
color: #111;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
|
||
}
|
||
|
||
.nav-right {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.search-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232953ff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");
|
||
background-size: cover;
|
||
margin-right: 24rpx;
|
||
}
|
||
|
||
.nav-avatar {
|
||
width: 52rpx;
|
||
height: 52rpx;
|
||
border-radius: 8rpx;
|
||
background-color: #eee;
|
||
}
|
||
|
||
.content-wrap {
|
||
padding: 24rpx 32rpx;
|
||
}
|
||
|
||
.card {
|
||
background-color: #fff;
|
||
border-radius: 32rpx;
|
||
padding: 32rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
/* 英雄卡片 */
|
||
.hero-card {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.hero-avatar {
|
||
width: 140rpx;
|
||
height: 140rpx;
|
||
border-radius: 50%;
|
||
margin-right: 32rpx;
|
||
border: 4rpx solid #fff;
|
||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.hero-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.hero-name-row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.hero-name {
|
||
font-size: 44rpx;
|
||
font-weight: bold;
|
||
color: #111;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.hero-score {
|
||
font-size: 44rpx;
|
||
font-weight: 900;
|
||
color: #2953ff;
|
||
font-family: 'DIN Alternate', sans-serif;
|
||
}
|
||
|
||
.hero-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.meta-rank {
|
||
margin-right: 24rpx;
|
||
font-weight: bold;
|
||
color: #d9a000;
|
||
}
|
||
|
||
.meta-heat {
|
||
display: flex;
|
||
align-items: center;
|
||
color: #666;
|
||
}
|
||
|
||
.meta-icon {
|
||
width: 24rpx;
|
||
height: 24rpx;
|
||
margin-right: 8rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* AI 观察员 */
|
||
.ai-card {
|
||
background: #ffffff;
|
||
border: 2rpx solid #e0d6ff;
|
||
border-radius: 32rpx;
|
||
padding: 32rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.ai-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.ai-icon {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.ai-icon-img {
|
||
display: block;
|
||
}
|
||
|
||
.ai-title {
|
||
font-size: 28rpx;
|
||
font-weight: 800;
|
||
color: #7b46f1;
|
||
}
|
||
|
||
.ai-content {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* 你的态度面板 */
|
||
|
||
/* 大家都在 PK */
|
||
.section-title {
|
||
font-size: 34rpx;
|
||
font-weight: 700;
|
||
color: #111;
|
||
margin-bottom: 32rpx;
|
||
display: block;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
|
||
}
|
||
|
||
.pk-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.pk-header .section-title {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.go-compare {
|
||
font-size: 24rpx;
|
||
color: #2953ff;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.arrow-right {
|
||
width: 24rpx;
|
||
height: 24rpx;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232953ff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 18 15 12 9 6'%3E%3C/polyline%3E%3C/svg%3E");
|
||
background-size: cover;
|
||
}
|
||
|
||
.pk-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24rpx;
|
||
padding: 0 40rpx;
|
||
}
|
||
|
||
.pk-person {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.pk-avatar {
|
||
width: 110rpx;
|
||
height: 110rpx;
|
||
border-radius: 50%;
|
||
border: 4rpx solid transparent;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.blue-border { border-color: #2953ff; }
|
||
.yellow-border { border-color: #ffc107; }
|
||
|
||
.pk-name {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.pk-score {
|
||
margin-top: 8rpx;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.vs-text {
|
||
font-size: 40rpx;
|
||
font-weight: 900;
|
||
color: #a1a1a1;
|
||
}
|
||
|
||
.pk-bar-wrap {
|
||
height: 16rpx;
|
||
border-radius: 100rpx;
|
||
display: flex;
|
||
overflow: hidden;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.pk-bar { height: 100%; }
|
||
.blue-bar { background-color: #4d44f1; }
|
||
.yellow-bar { background-color: #ffe066; }
|
||
|
||
.pk-data-text {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 24rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.blue-text { color: #4d44f1; }
|
||
.yellow-text { color: #333; }
|
||
|
||
</style>
|