fix: rating

This commit is contained in:
zzc
2026-05-24 23:45:47 +08:00
parent f8f7d80922
commit 8dfe94453b
3 changed files with 268 additions and 146 deletions

View File

@@ -9,7 +9,7 @@ export const topicItemScore = async (data) => {
}); });
}; };
// 获取主题下的评分项 // 获取评分项详情
export const fetchTopicRatingItems = async (itemId) => { export const fetchTopicRatingItems = async (itemId) => {
return request({ return request({
url: `/api/rating/topic/item/detail/${itemId}`, url: `/api/rating/topic/item/detail/${itemId}`,

View File

@@ -0,0 +1,168 @@
<template>
<view class="attitude-panel card">
<text class="section-title">你的态度</text>
<view class="action-group">
<view
class="action-item"
v-for="(action, index) in actions"
:key="index"
:class="{ active: currentAction === action.label }"
@tap="handleAction(action)"
>
<view class="emoji-wrap">
<text class="emoji">{{ action.emoji }}</text>
</view>
<text class="action-name">{{ action.label }}</text>
</view>
</view>
<view class="quick-comment">
<input
class="comment-input"
type="text"
v-model="commentText"
placeholder="说点什么吧..."
placeholder-class="input-placeholder"
/>
<button class="send-btn" @tap="handleSend">发送</button>
</view>
</view>
</template>
<script setup>
import { ref, watch } from 'vue';
const props = defineProps({
initialAction: {
type: String,
default: ''
}
});
const emit = defineEmits(['action-change', 'send-comment']);
const currentAction = ref(props.initialAction);
// 监听父组件传入的 initialAction 的变化
watch(() => props.initialAction, (newVal) => {
currentAction.value = newVal;
});
const commentText = ref('');
const actions = [
{ label: '拉', value: 'terrible', emoji: '👎', score: 2, scoreType:1 },
{ label: 'NPC', value: 'bad', emoji: '😐', score: 4, scoreType: 2},
{ label: '人上人', value: 'ok', emoji: '🙂', score: 6, scoreType: 3},
{ label: '顶级', value: 'good', emoji: '🔥', score: 8, scoreType: 4},
{ label: '夯', value: 'god', emoji: '👑', score: 10, scoreType: 5 }
];
const handleAction = (action) => {
emit('action-change', action);
};
const handleSend = () => {
if (!commentText.value.trim()) {
uni.showToast({
title: '请输入评论内容',
icon: 'none'
});
return;
}
emit('send-comment', {
action: currentAction.value,
content: commentText.value
});
commentText.value = ''; // 发送后清空
};
</script>
<style lang="scss" scoped>
.card {
background-color: #fff;
border-radius: 32rpx;
padding: 32rpx;
margin-bottom: 24rpx;
}
.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;
}
.action-group {
display: flex;
justify-content: space-between;
margin-bottom: 32rpx;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
transition: transform 0.2s;
}
.emoji-wrap {
margin-bottom: 12rpx;
}
.emoji {
font-size: 56rpx;
filter: grayscale(100%);
opacity: 0.6;
}
.action-name {
font-size: 24rpx;
color: #999;
}
.action-item.active .emoji {
filter: grayscale(0%);
opacity: 1;
transform: scale(1.2);
}
.action-item.active .action-name {
color: #2953ff;
font-weight: bold;
}
.quick-comment {
display: flex;
align-items: center;
background-color: #f0f2f7;
border-radius: 16rpx;
padding: 8rpx 8rpx 8rpx 32rpx;
}
.comment-input {
flex: 1;
height: 72rpx;
font-size: 28rpx;
}
.input-placeholder {
color: #999;
}
.send-btn {
background-color: #4d44f1;
color: #fff;
font-size: 26rpx;
border-radius: 12rpx;
padding: 0 40rpx;
height: 64rpx;
line-height: 64rpx;
margin: 0;
}
.send-btn::after {
border: none;
}
</style>

View File

@@ -27,7 +27,8 @@
</view> </view>
<view class="hero-meta"> <view class="hero-meta">
<text class="meta-rank">🥇 No.1</text> <text class="meta-rank">🥇 No.1</text>
<text class="meta-heat">🔥 98.2w 热度</text> <text class="meta-heat">🔥 {{ detailData.hotScore }} 热度</text>
<text class="meta-count" style="margin-left: 20rpx; color: #999;">{{ detailData.ratingCount }} 人评分</text>
</view> </view>
</view> </view>
</view> </view>
@@ -42,27 +43,11 @@
</view> </view>
<!-- 你的态度 --> <!-- 你的态度 -->
<view class="attitude-panel card"> <AttitudePanel
<text class="section-title">你的态度</text> :initial-action="currentAction"
<view class="action-group"> @action-change="handleActionChange"
<view @send-comment="handleSendComment"
class="action-item" />
v-for="(action, index) in actions"
:key="index"
:class="{ active: currentAction === action.value }"
@tap="handleAction(action.value)"
>
<view class="emoji-wrap">
<text class="emoji">{{ action.emoji }}</text>
</view>
<text class="action-name">{{ action.label }}</text>
</view>
</view>
<view class="quick-comment">
<input class="comment-input" type="text" placeholder="说点什么吧..." placeholder-class="input-placeholder" />
<button class="send-btn">发送</button>
</view>
</view>
<!-- 大家都在 PK --> <!-- 大家都在 PK -->
<view class="pk-panel card"> <view class="pk-panel card">
@@ -137,82 +122,122 @@
import { ref } from 'vue'; import { ref } from 'vue';
import { getStatusBarHeight } from "@/utils/system"; import { getStatusBarHeight } from "@/utils/system";
import { onLoad, onPullDownRefresh, onReachBottom } from "@dcloudio/uni-app"; import { onLoad, onPullDownRefresh, onReachBottom } from "@dcloudio/uni-app";
import AttitudePanel from "@/components/AttitudePanel/AttitudePanel.vue";
import { fetchTopicRatingItems } from "@/api/topicItem";
import { FILE_BASE_URL } from "@/utils/constants";
import { topicItemScore } from "@/api/topicItem";
const statusBarHeight = ref(getStatusBarHeight() || 44); const statusBarHeight = ref(getStatusBarHeight() || 44);
const loading = ref(false); const loading = ref(false);
const hasMore = ref(true); const hasMore = ref(true);
const currentAction = ref('god'); const currentAction = ref('');
const goBack = () => { const goBack = () => {
uni.navigateBack(); uni.navigateBack();
}; };
const detailData = ref({ const detailData = ref({
id: 1, id: '',
name: '李世民', name: '',
score: '9.8', score: '0.0',
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=11', avatar: '',
aiSummary: '多数用户认为李世民综合能力最强,尤其在文治武功方面优势明显。关于玄武门事件的争议依然存在。' aiSummary: '加载中...',
userAction: "",
topicId: ''
}); });
const actions = [ const comments = ref([]);
{ label: '离谱', value: 'terrible', emoji: '👎' }, const itemId = ref('');
{ label: '一般', value: 'bad', emoji: '😐' },
{ label: '不错', value: 'ok', emoji: '🙂' },
{ label: '很强', value: 'good', emoji: '🔥' },
{ label: '封神', value: 'god', emoji: '👑' }
];
const mockComments = [ const handleActionChange = async (action) => {
{ const originAction = detailData.value.userAction;
id: 1, const originScore = detailData.value.score;
name: '历史课代表', try {
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=u1', detailData.value.userAction = detailData.value.userAction === action.label ? '' : action.label;
time: '2小时前', currentAction.value = action.label;
content: '李世民唯一缺点是儿子太抽象。',
likes: '2.3k', const res = await topicItemScore({
badge: '封神', topicId: detailData.value.topicId,
badgeIcon: '👑' itemId: itemId.value,
}, scoreType: action.scoreType
{ });
id: 2, detailData.value.score = res?.scoreAvg || originScore;
name: '贞观长歌', } catch (error) {
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=u2', uni.showToast({
time: '5小时前', title: '评分失败',
content: '不管怎么说,六边形战士在历史上是真罕见。', icon: 'none'
likes: '856', });
badge: '很强', detailData.value.userAction = originAction;
badgeIcon: '🔥' detailData.value.score = originScore;
currentAction.value = originAction;
} }
]; };
const comments = ref([...mockComments]); const handleSendComment = (payload) => {
console.log('发送评论:', payload);
uni.showToast({
title: '发送成功',
icon: 'success'
});
// TODO: 调用接口发送评论,然后更新评论列表
};
const handleAction = (val) => { const getDetailData = async () => {
currentAction.value = val; 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 || '',
ratingCount: data.scoreCount || 0,
userAction: data.userAction || ""
};
// 如果接口返回了当前用户的态度,初始化它
if (data.userAction) {
currentAction.value = data.userAction;
}
// 如果有评论数据,更新评论
if (data.comments && Array.isArray(data.comments)) {
comments.value = data.comments;
} else {
comments.value = [];
}
} catch (error) {
console.error('获取详情失败:', error);
uni.showToast({
title: '获取详情失败',
icon: 'none'
});
} finally {
uni.hideLoading();
uni.stopPullDownRefresh();
}
}; };
onLoad((options) => { onLoad((options) => {
console.log('Received itemId:', options.itemId); if (options.itemId) {
itemId.value = options.itemId;
getDetailData();
}
}); });
onPullDownRefresh(() => { onPullDownRefresh(() => {
setTimeout(() => { getDetailData();
comments.value = [...mockComments];
hasMore.value = true;
uni.stopPullDownRefresh();
}, 1000);
}); });
onReachBottom(() => { onReachBottom(() => {
if (!hasMore.value || loading.value) return; if (!hasMore.value || loading.value) return;
loading.value = true; // TODO: 评论分页加载逻辑
setTimeout(() => {
const more = mockComments.map(c => ({...c, id: c.id + comments.value.length}));
comments.value.push(...more);
loading.value = false;
if (comments.value.length >= 10) hasMore.value = false;
}, 1000);
}); });
</script> </script>
@@ -384,6 +409,8 @@ onReachBottom(() => {
} }
/* 你的态度面板 */ /* 你的态度面板 */
/* 大家都在 PK */
.section-title { .section-title {
font-size: 34rpx; font-size: 34rpx;
font-weight: 700; font-weight: 700;
@@ -393,79 +420,6 @@ onReachBottom(() => {
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
} }
.action-group {
display: flex;
justify-content: space-between;
margin-bottom: 32rpx;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
transition: transform 0.2s;
}
.emoji-wrap {
margin-bottom: 12rpx;
}
.emoji {
font-size: 56rpx;
filter: grayscale(100%);
opacity: 0.6;
}
.action-name {
font-size: 24rpx;
color: #999;
}
.action-item.active .emoji {
filter: grayscale(0%);
opacity: 1;
transform: scale(1.2);
}
.action-item.active .action-name {
color: #2953ff;
font-weight: bold;
}
.quick-comment {
display: flex;
align-items: center;
background-color: #f0f2f7;
border-radius: 16rpx;
padding: 8rpx 8rpx 8rpx 32rpx;
}
.comment-input {
flex: 1;
height: 72rpx;
font-size: 28rpx;
}
.input-placeholder {
color: #999;
}
.send-btn {
background-color: #4d44f1;
color: #fff;
font-size: 26rpx;
border-radius: 12rpx;
padding: 0 40rpx;
height: 64rpx;
line-height: 64rpx;
margin: 0;
}
.send-btn::after {
border: none;
}
/* 大家都在 PK */
.pk-header { .pk-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;