1088 lines
26 KiB
Vue
1088 lines
26 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<!-- 自定义导航栏 -->
|
||
<view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="nav-content">
|
||
<view class="nav-back" @tap="goBack">
|
||
<text class="back-icon"></text>
|
||
</view>
|
||
<view class="nav-logo-wrap">
|
||
<image class="nav-logo" src="/static/images/logo.jpg" mode="aspectFill" />
|
||
</view>
|
||
<text class="nav-title">夯拉评分</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 顶部占位 -->
|
||
<view :style="{ height: statusBarHeight + 44 + 'px' }"></view>
|
||
|
||
<view class="content-wrap">
|
||
<!-- 头部信息 -->
|
||
<view class="topic-header">
|
||
<text class="topic-title">{{ topicData.title }}</text>
|
||
<view class="topic-stats">
|
||
<image class="stat-icon stat-icon-img" src="/static/images/icon/fire.png" mode="aspectFit"></image>
|
||
<text class="stat-text">热度 {{ topicData.hotScore }} w</text>
|
||
<image class="stat-icon stat-icon-img stat-icon-gap" src="/static/images/icon/group.png" mode="aspectFit"></image>
|
||
<text class="stat-text stat-text-participant">{{ topicData.participantCount }}人参与</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- AI 实时观察 -->
|
||
<view class="ai-observe">
|
||
<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">{{ topicData.aiSummary }}</text>
|
||
</view>
|
||
|
||
<!-- <view v-if="pkPair.left && pkPair.right" class="pk-entry-card" @tap="goToPk()">
|
||
<view class="pk-entry-header">
|
||
<view class="pk-entry-title-wrap">
|
||
<text class="pk-entry-title">PK 对决模式</text>
|
||
<text class="pk-entry-subtitle">快速站队,看谁更能打</text>
|
||
</view>
|
||
<view class="pk-entry-action">
|
||
<text>进入 PK</text>
|
||
<text class="pk-entry-arrow"></text>
|
||
</view>
|
||
</view>
|
||
<view class="pk-entry-battle">
|
||
<view class="pk-entry-user left">
|
||
<image class="pk-entry-avatar" :src="FILE_BASE_URL + pkPair.left.avatarUrl" mode="aspectFill" />
|
||
<text class="pk-entry-name">{{ pkPair.left.name }}</text>
|
||
</view>
|
||
<view class="pk-entry-vs">VS</view>
|
||
<view class="pk-entry-user right">
|
||
<image class="pk-entry-avatar" :src="FILE_BASE_URL + pkPair.right.avatarUrl" mode="aspectFill" />
|
||
<text class="pk-entry-name">{{ pkPair.right.name }}</text>
|
||
</view>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 领奖台 (Top 3) -->
|
||
<view class="podium-section" v-if="ratingItems && ratingItems.length >= 3">
|
||
<!-- 第二名 -->
|
||
<view class="podium-item rank-2" @tap="goToDetail(ratingItems[1].id)">
|
||
<view class="avatar-wrap">
|
||
<image class="avatar" :src="FILE_BASE_URL + ratingItems[1].avatarUrl" mode="aspectFill" />
|
||
<text class="rank-badge badge-2">2</text>
|
||
</view>
|
||
<view class="info-box box-2">
|
||
<text class="name">{{ ratingItems[1].name }}</text>
|
||
<text class="score">分数 {{ ratingItems[1].scoreAvg }}</text>
|
||
</view>
|
||
</view>
|
||
<!-- 第一名 -->
|
||
<view class="podium-item rank-1" @tap="goToDetail(ratingItems[0].id)">
|
||
<view class="avatar-wrap">
|
||
<image class="avatar avatar-1" :src="FILE_BASE_URL + ratingItems[0].avatarUrl" mode="aspectFill" />
|
||
<text class="rank-badge badge-1">1</text>
|
||
</view>
|
||
<view class="info-box box-1">
|
||
<text class="name name-1">{{ ratingItems[0].name }}</text>
|
||
<text class="score score-1">分数 {{ ratingItems[0].scoreAvg }}</text>
|
||
</view>
|
||
</view>
|
||
<!-- 第三名 -->
|
||
<view class="podium-item rank-3" @tap="goToDetail(ratingItems[2].id)">
|
||
<view class="avatar-wrap">
|
||
<image class="avatar" :src="FILE_BASE_URL + ratingItems[2].avatarUrl" mode="aspectFill" />
|
||
<text class="rank-badge badge-3">3</text>
|
||
</view>
|
||
<view class="info-box box-3">
|
||
<text class="name">{{ ratingItems[2].name }}</text>
|
||
<text class="score">分数 {{ ratingItems[2].scoreAvg }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 评分项列表 -->
|
||
<view class="rating-list">
|
||
<RatingCard
|
||
v-for="(item, index) in ratingItems"
|
||
:key="item.id"
|
||
:item="item"
|
||
@item-click="goToDetail"
|
||
@action-click="handleAction"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 热门锐评 -->
|
||
<!-- <view class="comments-section">
|
||
<view class="section-header">
|
||
<text class="comment-icon"></text>
|
||
<text class="section-title">热门锐评</text>
|
||
</view>
|
||
|
||
<view class="comment-input-box">
|
||
<text class="edit-icon"></text>
|
||
<text class="placeholder">发表你的锐评...</text>
|
||
</view>
|
||
|
||
<view class="comment-list">
|
||
<view class="comment-item" v-for="comment in comments" :key="comment.id">
|
||
<view class="comment-user">
|
||
<image class="comment-avatar" :src="comment.avatar" mode="aspectFill" />
|
||
<text class="comment-name">{{ comment.name }}</text>
|
||
<text class="comment-time">{{ comment.time }}</text>
|
||
</view>
|
||
<view class="comment-content">
|
||
<text>{{ comment.content }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 加载更多 -->
|
||
<view class="load-more">
|
||
<text>{{ loading ? '加载中...' : (hasMore ? '上拉加载更多' : '没有更多了') }}</text>
|
||
</view>
|
||
|
||
</view>
|
||
|
||
<!-- 右下角悬浮添加按钮 -->
|
||
<view class="fab-btn" @tap="handleAddClick">
|
||
<text class="plus-icon">+</text>
|
||
</view>
|
||
|
||
<!-- 添加评分项弹窗 -->
|
||
<view class="popup-mask" v-if="showAddPopup" @tap="showAddPopup = false">
|
||
<view class="popup-content" @tap.stop>
|
||
<text class="popup-title">添加评分项</text>
|
||
<view class="form-item">
|
||
<text class="form-label">上传</text>
|
||
<view class="avatar-uploader" @tap="chooseAvatar">
|
||
<image v-if="newItem.avatar" :src="newItem.avatar" class="preview-avatar" mode="aspectFill" />
|
||
<view v-else class="upload-placeholder">
|
||
<text class="camera-icon"></text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label">名称</text>
|
||
<input class="form-input" type="text" v-model="newItem.name" placeholder="请输入名称" placeholder-class="input-placeholder" />
|
||
</view>
|
||
|
||
<view class="popup-actions">
|
||
<button class="btn-cancel" @tap="showAddPopup = false">取消</button>
|
||
<button class="btn-confirm" @tap="confirmAddItem">确定</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 评分成功特效弹窗 -->
|
||
<SuccessPopup ref="successPopupRef" />
|
||
<LoginPopup ref="loginPopupRef" @logind="handleLoginSuccess" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from 'vue';
|
||
import { getStatusBarHeight } from "@/utils/system";
|
||
import { onPullDownRefresh, onReachBottom, onLoad, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
|
||
import { fetchTopicDetail, fetchTopicRatingItems } from "@/api/topic";
|
||
import { FILE_BASE_URL } from "@/utils/constants";
|
||
import RatingCard from "@/components/RatingCard/RatingCard.vue";
|
||
import SuccessPopup from "@/components/SuccessPopup/SuccessPopup.vue";
|
||
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
||
import { topicItemScore, topicItemAdd } from "@/api/topicItem";
|
||
import { uploadImage } from "@/utils/common";
|
||
import { getShareToken } from "@/utils/common";
|
||
import { getShareReward } from "@/api/system";
|
||
import { useUserStore } from "@/stores/user";
|
||
// 状态栏高度处理
|
||
const userStore = useUserStore();
|
||
const statusBarHeight = ref(getStatusBarHeight() || 44);
|
||
|
||
const loading = ref(false);
|
||
const hasMore = ref(true);
|
||
const currentPage = ref(1);
|
||
const currentTopicId = ref(null);
|
||
const successPopupRef = ref(null);
|
||
const loginPopupRef = ref(null);
|
||
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
|
||
const pkPair = computed(() => ({
|
||
left: ratingItems.value?.[0] || null,
|
||
right: ratingItems.value?.[1] || null
|
||
}));
|
||
|
||
const goBack = () => {
|
||
uni.navigateBack({
|
||
delta: 1
|
||
});
|
||
};
|
||
|
||
const openLoginPopup = (message = '请先登录后再操作') => {
|
||
loginPopupRef.value?.open();
|
||
if (message) {
|
||
uni.showToast({ title: message, icon: 'none' });
|
||
}
|
||
};
|
||
|
||
const topicData = ref({
|
||
title: '',
|
||
heat: '0',
|
||
participants: '0',
|
||
aiSummary: ''
|
||
});
|
||
|
||
const loadDetail = async (topicId) => {
|
||
try {
|
||
const res = await fetchTopicDetail(topicId);
|
||
const data = res?.data || res || {};
|
||
topicData.value = {
|
||
title: data.title || '',
|
||
heat: data.heat || '0',
|
||
participants: data.participants || '0',
|
||
aiSummary: data.aiSummary || '暂无总结',
|
||
participantCount: data.participantCount || '0',
|
||
hotScore: data.hotScore || '0'
|
||
};
|
||
} catch (e) {
|
||
console.error("获取主题详情失败:", e);
|
||
}
|
||
};
|
||
|
||
const showAddPopup = ref(false);
|
||
const newItem = ref({
|
||
name: '',
|
||
avatar: ''
|
||
});
|
||
|
||
const chooseAvatar = () => {
|
||
if (!isLoggedIn.value) {
|
||
openLoginPopup();
|
||
return;
|
||
}
|
||
|
||
uni.chooseImage({
|
||
count: 1,
|
||
success: (res) => {
|
||
newItem.value.avatar = res.tempFilePaths[0];
|
||
}
|
||
});
|
||
};
|
||
|
||
const confirmAddItem = async () => {
|
||
if (!isLoggedIn.value) {
|
||
openLoginPopup();
|
||
return;
|
||
}
|
||
|
||
if (!newItem.value.name.trim()) {
|
||
uni.showToast({ title: '请输入名称', icon: 'none' });
|
||
return;
|
||
}
|
||
if (!newItem.value.avatar) {
|
||
uni.showToast({ title: '请上传头像', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
try {
|
||
uni.showLoading({ title: '添加中...' });
|
||
|
||
// 1. 先通过 uni.uploadFile 上传头像图片
|
||
const uploadRes = await uploadImage(newItem.value.avatar);
|
||
|
||
// 2. 将上传成功后的图片 URL 和其他数据一起提交给添加接口
|
||
await topicItemAdd({
|
||
topicId: currentTopicId.value,
|
||
name: newItem.value.name,
|
||
avatarUrl: uploadRes
|
||
});
|
||
|
||
uni.hideLoading();
|
||
// uni.showToast({ title: '添加成功', icon: 'success' });
|
||
if (successPopupRef.value) {
|
||
successPopupRef.value.show({
|
||
emoji: '🎉',
|
||
label: '请耐心等待管理员审核'
|
||
}, 'release');
|
||
}
|
||
|
||
// 重置表单并关闭弹窗
|
||
newItem.value = { name: '', avatar: '' };
|
||
showAddPopup.value = false;
|
||
|
||
// 重新加载列表数据
|
||
currentPage.value = 1;
|
||
hasMore.value = true;
|
||
loadItems(currentTopicId.value, 1);
|
||
|
||
} catch (error) {
|
||
uni.hideLoading();
|
||
uni.showToast({ title: '添加失败,请重试', icon: 'none' });
|
||
console.error('添加评分项失败:', error);
|
||
}
|
||
};
|
||
|
||
const ratingItems = ref([]);
|
||
|
||
const loadItems = async (topicId, page = 1) => {
|
||
if (page === 1) {
|
||
ratingItems.value = [];
|
||
}
|
||
try {
|
||
loading.value = true;
|
||
const res = await fetchTopicRatingItems(topicId, page);
|
||
const list = res?.data?.records || res?.records || res?.list || [];
|
||
|
||
ratingItems.value = [...ratingItems.value, ...list];
|
||
hasMore.value = list.length >= 10; // 假设 pageSize 是 10
|
||
} catch (error) {
|
||
hasMore.value = false;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
onLoad((options) => {
|
||
uni.$trackRecord({
|
||
eventName: "rating_topic_detail_page_visit",
|
||
eventType: `visit`,
|
||
elementId: options.topicId
|
||
})
|
||
if (options.topicId) {
|
||
currentTopicId.value = options.topicId;
|
||
loadDetail(options.topicId);
|
||
loadItems(options.topicId, 1);
|
||
}
|
||
});
|
||
|
||
const handleAction = async (item, action) => {
|
||
if (!isLoggedIn.value) {
|
||
openLoginPopup();
|
||
return;
|
||
}
|
||
|
||
const originAction = item.userAction;
|
||
const originScore = item.scoreAvg;
|
||
const isCancel = item.userAction === action.label;
|
||
|
||
try {
|
||
item.userAction = isCancel ? '' : action.label;
|
||
const res = await topicItemScore({
|
||
topicId: currentTopicId.value,
|
||
itemId: item.id,
|
||
scoreType: action.scoreType
|
||
});
|
||
item.scoreAvg = res?.scoreAvg || originScore;
|
||
|
||
// 如果是成功评分(非取消),展示特效弹窗
|
||
if (!isCancel && successPopupRef.value) {
|
||
successPopupRef.value.show(action);
|
||
}
|
||
uni.$trackRecord({
|
||
eventName: 'rating',
|
||
eventType: 'rating',
|
||
elementId: `item_${item.id}`,
|
||
elementContent: `评分项_${item.name}`,
|
||
customParams: {
|
||
scoreType: action.scoreType,
|
||
action: action.label,
|
||
topicId: currentTopicId.value,
|
||
topicTitle: topicData.value.title,
|
||
score: item.scoreAvg,
|
||
page: 'rating'
|
||
}
|
||
});
|
||
} catch (error) {
|
||
item.userAction = originAction;
|
||
item.scoreAvg = originScore;
|
||
}
|
||
};
|
||
|
||
const goToDetail = (itemId) => {
|
||
uni.$trackRecord({
|
||
eventName: 'jumpto_detail',
|
||
eventType: 'jump',
|
||
elementId: `item_${itemId}`,
|
||
elementContent: `评分项${itemId}`,
|
||
});
|
||
uni.navigateTo({
|
||
url: `/pages/rating/detail?itemId=${itemId}&topicId=${currentTopicId.value}`
|
||
});
|
||
};
|
||
|
||
const goToPk = (itemId = '') => {
|
||
if (!currentTopicId.value) return;
|
||
uni.navigateTo({
|
||
url: `/pages/rating/pk?topicId=${currentTopicId.value}${itemId ? `&itemId=${itemId}` : ''}`
|
||
});
|
||
};
|
||
|
||
const handleAddClick = () => {
|
||
if (!isLoggedIn.value) {
|
||
openLoginPopup();
|
||
return;
|
||
}
|
||
showAddPopup.value = true;
|
||
};
|
||
|
||
const handleLoginSuccess = async () => {
|
||
if (!currentTopicId.value) return;
|
||
currentPage.value = 1;
|
||
hasMore.value = true;
|
||
await Promise.all([
|
||
loadDetail(currentTopicId.value),
|
||
loadItems(currentTopicId.value, 1)
|
||
]);
|
||
};
|
||
|
||
onShareAppMessage(async () => {
|
||
const shareToken = await getShareToken("rating_topic");
|
||
getShareReward({ scene: "rating_topic" });
|
||
const topName = ratingItems.value?.[0]?.name;
|
||
return {
|
||
title: topName
|
||
? `「${topicData.value.title || "夯拉评分"}」正在热评,${topName}暂时领先`
|
||
: `「${topicData.value.title || "夯拉评分"}」正在热评,快来参与打分`,
|
||
path: `/pages/rating/index?topicId=${currentTopicId.value}&shareToken=${shareToken}`,
|
||
};
|
||
});
|
||
|
||
onShareTimeline(async () => {
|
||
const shareToken = await getShareToken("rating_topic");
|
||
getShareReward({ scene: "rating_topic_timeline" });
|
||
const topName = ratingItems.value?.[0]?.name;
|
||
return {
|
||
title: topName
|
||
? `${topName}在「${topicData.value.title || "夯拉评分"}」里冲到第一了`
|
||
: `「${topicData.value.title || "夯拉评分"}」榜单已开启,来看看大家怎么评`,
|
||
query: `topicId=${currentTopicId.value}&shareToken=${shareToken}`,
|
||
};
|
||
});
|
||
|
||
// 下拉刷新
|
||
onPullDownRefresh(async () => {
|
||
if (currentTopicId.value) {
|
||
currentPage.value = 1;
|
||
hasMore.value = true;
|
||
await Promise.all([
|
||
loadDetail(currentTopicId.value),
|
||
loadItems(currentTopicId.value, currentPage.value)
|
||
]);
|
||
}
|
||
uni.stopPullDownRefresh();
|
||
});
|
||
|
||
// 上拉加载更多
|
||
onReachBottom(() => {
|
||
if (!hasMore.value || loading.value) return;
|
||
if (currentTopicId.value) {
|
||
currentPage.value += 1;
|
||
loadItems(currentTopicId.value, currentPage.value);
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page-container {
|
||
min-height: 100vh;
|
||
background-color: #fafbfe; /* 浅蓝灰色背景 */
|
||
padding-bottom: 120rpx; /* 留出底部 tabBar 空间 */
|
||
}
|
||
|
||
/* 导航栏 */
|
||
.nav-bar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
background-color: rgba(250, 251, 254, 0.95);
|
||
backdrop-filter: blur(20rpx);
|
||
}
|
||
|
||
.nav-content {
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 32rpx;
|
||
}
|
||
|
||
.nav-back {
|
||
padding: 10rpx 24rpx 10rpx 0;
|
||
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%3Cpolyline points='15 18 9 12 15 6'%3E%3C/polyline%3E%3C/svg%3E");
|
||
background-size: cover;
|
||
}
|
||
|
||
.nav-logo-wrap {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.nav-logo {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #2953ff;
|
||
}
|
||
|
||
.content-wrap {
|
||
padding: 24rpx 32rpx;
|
||
}
|
||
|
||
/* 头部信息 */
|
||
.topic-header {
|
||
margin-bottom: 32rpx;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.topic-title {
|
||
font-size: 46rpx;
|
||
font-weight: 800;
|
||
color: #111;
|
||
display: block;
|
||
margin-bottom: 20rpx;
|
||
letter-spacing: 2rpx;
|
||
}
|
||
|
||
.topic-stats {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.stat-icon {
|
||
width: 26rpx;
|
||
height: 26rpx;
|
||
margin-right: 12rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.stat-icon-gap {
|
||
margin-left: 32rpx;
|
||
}
|
||
|
||
.stat-text {
|
||
font-size: 24rpx;
|
||
color: #FF5A5F;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.stat-text-participant {
|
||
color: #555;
|
||
font-weight: normal;
|
||
}
|
||
|
||
/* AI 实时观察 */
|
||
.ai-observe {
|
||
background: #ffffff;
|
||
border: 2rpx solid rgba(77, 68, 241, 0.2);
|
||
border-radius: 24rpx;
|
||
padding: 28rpx;
|
||
margin-bottom: 50rpx;
|
||
box-shadow: 0 8rpx 24rpx rgba(77, 68, 241, 0.03);
|
||
}
|
||
|
||
.ai-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.ai-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.ai-icon-img {
|
||
display: block;
|
||
}
|
||
|
||
.ai-title {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #4d44f1;
|
||
}
|
||
|
||
.ai-content {
|
||
font-size: 26rpx;
|
||
color: #444;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.pk-entry-card {
|
||
background: linear-gradient(135deg, #ffffff 0%, #f7f4ff 52%, #eef1ff 100%);
|
||
border: 2rpx solid rgba(99, 88, 255, 0.14);
|
||
border-radius: 28rpx;
|
||
padding: 28rpx;
|
||
margin-bottom: 42rpx;
|
||
box-shadow: 0 12rpx 30rpx rgba(91, 84, 223, 0.08);
|
||
}
|
||
|
||
.pk-entry-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.pk-entry-title {
|
||
display: block;
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
color: #1d2233;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.pk-entry-subtitle {
|
||
font-size: 24rpx;
|
||
color: #7d8495;
|
||
}
|
||
|
||
.pk-entry-action {
|
||
display: flex;
|
||
align-items: center;
|
||
color: #5c43f5;
|
||
font-size: 24rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.pk-entry-arrow {
|
||
width: 24rpx;
|
||
height: 24rpx;
|
||
margin-left: 6rpx;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c43f5' 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-entry-battle {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.pk-entry-user {
|
||
width: 220rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.pk-entry-avatar {
|
||
width: 104rpx;
|
||
height: 104rpx;
|
||
border-radius: 50%;
|
||
border: 4rpx solid #fff;
|
||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
margin-bottom: 14rpx;
|
||
}
|
||
|
||
.pk-entry-user.left .pk-entry-avatar {
|
||
border-color: rgba(92, 67, 245, 0.22);
|
||
}
|
||
|
||
.pk-entry-user.right .pk-entry-avatar {
|
||
border-color: rgba(255, 154, 76, 0.22);
|
||
}
|
||
|
||
.pk-entry-name {
|
||
font-size: 26rpx;
|
||
color: #2a2f3c;
|
||
font-weight: 700;
|
||
text-align: center;
|
||
}
|
||
|
||
.pk-entry-vs {
|
||
width: 88rpx;
|
||
height: 88rpx;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: linear-gradient(135deg, #6f5aff, #4d44f1);
|
||
color: #fff;
|
||
font-size: 30rpx;
|
||
font-weight: 900;
|
||
letter-spacing: 1rpx;
|
||
box-shadow: 0 14rpx 28rpx rgba(92, 67, 245, 0.22);
|
||
}
|
||
|
||
/* 领奖台 (Top 3) */
|
||
.podium-section {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: flex-end;
|
||
height: 360rpx;
|
||
margin-bottom: 50rpx;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.podium-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
position: relative;
|
||
}
|
||
|
||
.avatar-wrap {
|
||
position: relative;
|
||
z-index: 2;
|
||
margin-bottom: -44rpx; /* 将头像下移,覆盖在底座上 */
|
||
}
|
||
|
||
.avatar {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 50%;
|
||
border: 4rpx solid #fff;
|
||
box-shadow: 0 8rpx 16rpx rgba(0,0,0,0.1);
|
||
background-color: #eee;
|
||
}
|
||
|
||
.avatar-1 {
|
||
width: 140rpx;
|
||
height: 140rpx;
|
||
border-color: #ffd700; /* 冠军金边 */
|
||
border-width: 6rpx;
|
||
}
|
||
|
||
.rank-badge {
|
||
position: absolute;
|
||
bottom: 4rpx;
|
||
right: -8rpx;
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
border-radius: 50%;
|
||
color: #111;
|
||
font-size: 22rpx;
|
||
font-weight: 900;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 4rpx solid #fff;
|
||
}
|
||
|
||
.badge-1 { background-color: #ffd700; width: 44rpx; height: 44rpx; right: -10rpx; bottom: 0; font-size: 26rpx; }
|
||
.badge-2 { background-color: #e2e8f0; }
|
||
.badge-3 { background-color: #e2e8f0; }
|
||
|
||
.info-box {
|
||
width: 200rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding-top: 56rpx;
|
||
padding-bottom: 24rpx;
|
||
border-radius: 20rpx;
|
||
}
|
||
|
||
.box-1 {
|
||
width: 220rpx;
|
||
height: 190rpx;
|
||
background: linear-gradient(180deg, #6c5ce7 0%, #5a48eb 100%);
|
||
color: #fff;
|
||
box-shadow: 0 12rpx 32rpx rgba(108, 92, 231, 0.3);
|
||
}
|
||
|
||
.box-2 {
|
||
height: 150rpx;
|
||
background-color: #e4e7f3;
|
||
}
|
||
|
||
.box-3 {
|
||
height: 150rpx; /* 从 130rpx 调整到 150rpx */
|
||
background-color: #eef1f8;
|
||
}
|
||
|
||
.name {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.name-1 {
|
||
color: #fff;
|
||
font-size: 34rpx;
|
||
}
|
||
|
||
.score {
|
||
font-size: 24rpx;
|
||
color: #888;
|
||
}
|
||
|
||
.score-1 {
|
||
color: rgba(255,255,255,0.8);
|
||
}
|
||
|
||
/* 评分项列表 */
|
||
.rating-list {
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
/* 热门锐评 */
|
||
.comments-section {
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.comment-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%3Cpath d='M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z'%3E%3C/path%3E%3C/svg%3E");
|
||
background-size: cover;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #111;
|
||
}
|
||
|
||
.comment-input-box {
|
||
background-color: #f0f2f7;
|
||
border-radius: 20rpx;
|
||
padding: 24rpx 32rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.edit-icon {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23666'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
|
||
background-size: cover;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.placeholder {
|
||
font-size: 28rpx;
|
||
color: #888;
|
||
}
|
||
|
||
.comment-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.comment-item {
|
||
background-color: #f5f7fb;
|
||
border-radius: 28rpx;
|
||
padding: 28rpx;
|
||
}
|
||
|
||
.comment-user {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.comment-avatar {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
border-radius: 50%;
|
||
margin-right: 16rpx;
|
||
background-color: #eee;
|
||
}
|
||
|
||
.comment-name {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.comment-time {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.comment-content {
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.load-more {
|
||
text-align: center;
|
||
padding: 30rpx 0;
|
||
color: #999;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
/* 悬浮添加按钮 */
|
||
.fab-btn {
|
||
position: fixed;
|
||
right: 40rpx;
|
||
bottom: 160rpx; /* 留出 tabBar 的距离 */
|
||
width: 100rpx;
|
||
height: 100rpx;
|
||
background: linear-gradient(135deg, #4d44f1, #2953ff);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(41, 83, 255, 0.4);
|
||
z-index: 99;
|
||
transition: transform 0.2s;
|
||
}
|
||
|
||
.fab-btn:active {
|
||
transform: scale(0.9);
|
||
}
|
||
|
||
.plus-icon {
|
||
color: #fff;
|
||
font-size: 60rpx;
|
||
font-weight: 300;
|
||
line-height: 1;
|
||
margin-top: -8rpx;
|
||
}
|
||
|
||
/* 弹窗样式 */
|
||
.popup-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
z-index: 1000;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
backdrop-filter: blur(4px);
|
||
}
|
||
|
||
.popup-content {
|
||
width: 600rpx;
|
||
background-color: #fff;
|
||
border-radius: 32rpx;
|
||
padding: 40rpx;
|
||
box-sizing: border-box;
|
||
animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||
}
|
||
|
||
@keyframes popIn {
|
||
from { transform: scale(0.8); opacity: 0; }
|
||
to { transform: scale(1); opacity: 1; }
|
||
}
|
||
|
||
.popup-title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #111;
|
||
text-align: center;
|
||
display: block;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.form-item {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-bottom: 16rpx;
|
||
display: block;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.avatar-uploader {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 20rpx;
|
||
background-color: #f5f6fa;
|
||
border: 2rpx dashed #d1d5db;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.preview-avatar {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.upload-placeholder {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.camera-icon {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z'%3E%3C/path%3E%3Ccircle cx='12' cy='13' r='4'%3E%3C/circle%3E%3C/svg%3E");
|
||
background-size: cover;
|
||
}
|
||
|
||
.form-input {
|
||
height: 80rpx;
|
||
background-color: #f5f6fa;
|
||
border-radius: 16rpx;
|
||
padding: 0 24rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.input-placeholder {
|
||
color: #aaa;
|
||
}
|
||
|
||
.popup-actions {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 48rpx;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.btn-cancel, .btn-confirm {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
border-radius: 100rpx;
|
||
font-size: 30rpx;
|
||
margin: 0;
|
||
}
|
||
|
||
.btn-cancel::after, .btn-confirm::after {
|
||
border: none;
|
||
}
|
||
|
||
.btn-cancel {
|
||
background-color: #f0f2f7;
|
||
color: #666;
|
||
}
|
||
|
||
.btn-confirm {
|
||
background: linear-gradient(135deg, #4d44f1, #2953ff);
|
||
color: #fff;
|
||
font-weight: bold;
|
||
}
|
||
</style>
|