Compare commits
20 Commits
ada774a142
...
628c02752c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
628c02752c | ||
|
|
f3baf1447a | ||
|
|
aa88eef08e | ||
|
|
1e5bbbbf9e | ||
|
|
f95fe36b5a | ||
|
|
8f11ed9333 | ||
|
|
3ae62897b6 | ||
|
|
21bfa3efd8 | ||
|
|
d325329af4 | ||
|
|
0383ad6be2 | ||
|
|
d129f1d38e | ||
|
|
829f9a07d4 | ||
|
|
1173a3e3a1 | ||
|
|
a98f82c113 | ||
|
|
0e0d9604e1 | ||
|
|
d4478cccd8 | ||
|
|
36dbf05c11 | ||
|
|
03cb2edf5e | ||
|
|
65cef99610 | ||
|
|
27c244f3cd |
11
api/mine.js
@@ -1,5 +1,16 @@
|
|||||||
import { request } from "@/utils/request.js";
|
import { request } from "@/utils/request.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 获取当前用户参与的话题数据
|
||||||
|
* @return {participant: number, comment: number, liked: number, label: string[]}
|
||||||
|
*/
|
||||||
|
export const fetchUserNums = async () => {
|
||||||
|
return request({
|
||||||
|
url: "/api/rating/personal/nums",
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const sendFeedback = async (data) => {
|
export const sendFeedback = async (data) => {
|
||||||
return request({
|
return request({
|
||||||
url: "/api/common/feedback",
|
url: "/api/common/feedback",
|
||||||
|
|||||||
@@ -40,3 +40,11 @@ export const fetchTopicRatingItems = async (topicId, page = 1) => {
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取当前用户参与的话题列表
|
||||||
|
export const fetchUserTopics = async (page = 1) => {
|
||||||
|
return request({
|
||||||
|
url: `/api/rating/personal/topic/list?page=${page}`,
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -43,3 +43,13 @@ export const fetchTopicRatingItems = async (itemId) => {
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 添加评分项
|
||||||
|
// data = { topicId: 'xxx', name: 'xxx', avatarUrl: 'xxx' }
|
||||||
|
export const topicItemAdd = async (data) => {
|
||||||
|
return request({
|
||||||
|
url: "/api/rating/topic/item/add",
|
||||||
|
method: "POST",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -20,10 +20,13 @@
|
|||||||
class="comment-input"
|
class="comment-input"
|
||||||
type="text"
|
type="text"
|
||||||
v-model="commentText"
|
v-model="commentText"
|
||||||
|
:disabled="sending"
|
||||||
placeholder="说点什么吧..."
|
placeholder="说点什么吧..."
|
||||||
placeholder-class="input-placeholder"
|
placeholder-class="input-placeholder"
|
||||||
/>
|
/>
|
||||||
<button class="send-btn" @tap="handleSend">发送</button>
|
<button class="send-btn" :class="{ 'is-disabled': sending }" :disabled="sending" @tap="handleSend">
|
||||||
|
{{ sending ? '发送中...' : '发送' }}
|
||||||
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -35,6 +38,10 @@ const props = defineProps({
|
|||||||
initialAction: {
|
initialAction: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
sending: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -62,19 +69,28 @@ const handleAction = (action) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSend = () => {
|
const handleSend = () => {
|
||||||
if (!commentText.value.trim()) {
|
const content = commentText.value.trim();
|
||||||
|
if (!content) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请输入评论内容',
|
title: '请输入评论内容',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (props.sending) return;
|
||||||
emit('send-comment', {
|
emit('send-comment', {
|
||||||
action: currentAction.value,
|
action: currentAction.value,
|
||||||
content: commentText.value
|
content
|
||||||
});
|
});
|
||||||
commentText.value = ''; // 发送后清空
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resetComment = () => {
|
||||||
|
commentText.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
resetComment
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -162,6 +178,10 @@ const handleSend = () => {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.send-btn.is-disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
.send-btn::after {
|
.send-btn::after {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import ActionBadge from "@/components/ActionBadge/ActionBadge.vue";
|
import ActionBadge from "@/components/ActionBadge/ActionBadge.vue";
|
||||||
import { topicItemCommentLike } from "@/api/topicItem";
|
import { topicItemCommentLike } from "@/api/topicItem";
|
||||||
|
import { useUserStore } from "@/stores/user";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
comment: {
|
comment: {
|
||||||
@@ -36,6 +37,10 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['need-login', 'like-change']);
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
|
||||||
|
|
||||||
const isLiked = ref(props.comment.isLiked || false);
|
const isLiked = ref(props.comment.isLiked || false);
|
||||||
const currentLikes = ref(props.comment.likes || 0);
|
const currentLikes = ref(props.comment.likes || 0);
|
||||||
let isLiking = false; // 防抖标志
|
let isLiking = false; // 防抖标志
|
||||||
@@ -49,6 +54,11 @@ watch(() => props.comment.isLiked, (newVal) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleLike = async () => {
|
const handleLike = async () => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
emit('need-login');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isLiking) return;
|
if (isLiking) return;
|
||||||
isLiking = true;
|
isLiking = true;
|
||||||
|
|
||||||
@@ -60,10 +70,26 @@ const handleLike = async () => {
|
|||||||
} else {
|
} else {
|
||||||
currentLikes.value -= 1;
|
currentLikes.value -= 1;
|
||||||
}
|
}
|
||||||
|
emit('like-change', {
|
||||||
|
id: props.comment.id,
|
||||||
|
isLiked: isLiked.value,
|
||||||
|
likes: currentLikes.value
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 调用点赞接口 (点赞和取消点赞为同一个接口)
|
// 调用点赞接口 (点赞和取消点赞为同一个接口)
|
||||||
await topicItemCommentLike({ commentId: props.comment.id });
|
await topicItemCommentLike({ commentId: props.comment.id });
|
||||||
|
// 记录点赞事件
|
||||||
|
uni.$trackRecord({
|
||||||
|
eventName: 'like_comment',
|
||||||
|
eventType: 'like',
|
||||||
|
elementId: `comment_${props.comment.id}`,
|
||||||
|
elementContent: `评论项_${props.comment.name}`,
|
||||||
|
customParams: {
|
||||||
|
comment: props.comment.content,
|
||||||
|
page: 'rating_detail'
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 接口调用失败,回滚 UI 状态
|
// 接口调用失败,回滚 UI 状态
|
||||||
isLiked.value = !isLiked.value;
|
isLiked.value = !isLiked.value;
|
||||||
@@ -72,6 +98,11 @@ const handleLike = async () => {
|
|||||||
} else {
|
} else {
|
||||||
currentLikes.value -= 1;
|
currentLikes.value -= 1;
|
||||||
}
|
}
|
||||||
|
emit('like-change', {
|
||||||
|
id: props.comment.id,
|
||||||
|
isLiked: isLiked.value,
|
||||||
|
likes: currentLikes.value
|
||||||
|
});
|
||||||
uni.showToast({ title: '操作失败', icon: 'none' });
|
uni.showToast({ title: '操作失败', icon: 'none' });
|
||||||
} finally {
|
} finally {
|
||||||
isLiking = false;
|
isLiking = false;
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
<view class="sparkle s-2">✨</view>
|
<view class="sparkle s-2">✨</view>
|
||||||
<view class="sparkle s-3">✨</view>
|
<view class="sparkle s-3">✨</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="title">{{ type === 'comment' ? '评论成功' : '评分成功' }}</text>
|
<text class="title">{{ type === 'comment' ? '评论成功' : type === 'release' ? '已提交审核' : '评分成功' }}</text>
|
||||||
<text class="subtitle" v-if="type === 'comment'">{{ actionData?.label || '你的锐评已发布' }}</text>
|
<text class="subtitle" v-if="type === 'comment'">{{ actionData?.label || '你的锐评已发布' }}</text>
|
||||||
|
<text class="subtitle" v-else-if="type === 'release'">{{ actionData?.label || '请耐心等待管理员审核' }}</text>
|
||||||
<text class="subtitle" v-else>你已将TA评价为「{{ actionData?.label || '...' }}」</text>
|
<text class="subtitle" v-else>你已将TA评价为「{{ actionData?.label || '...' }}」</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
<text class="topic-title">{{ topic.title }}</text>
|
<text class="topic-title">{{ topic.title }}</text>
|
||||||
<view class="topic-stats">
|
<view class="topic-stats">
|
||||||
<view class="stat-item">
|
<view class="stat-item">
|
||||||
<text class="stat-icon icon-group"></text>
|
<image class="stat-icon stat-icon-img" src="/static/images/icon/group.png" mode="aspectFit"></image>
|
||||||
<text>{{ topic.participantCount }}人参与</text>
|
<text>{{ topic.participantCount }}人参与</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item heat">
|
<view class="stat-item heat">
|
||||||
<text class="stat-icon icon-fire"></text>
|
<image class="stat-icon stat-icon-img" src="/static/images/icon/fire.png" mode="aspectFit"></image>
|
||||||
<text>热度 {{ topic.hotScore }} w</text>
|
<text>热度 {{ topic.hotScore }} w</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -23,7 +23,13 @@
|
|||||||
:key="item.id"
|
:key="item.id"
|
||||||
@tap="handleItemTap(item.id)"
|
@tap="handleItemTap(item.id)"
|
||||||
>
|
>
|
||||||
<text class="rank-num" :class="'rank-' + (index + 1)">{{ index + 1 }}</text>
|
<image
|
||||||
|
v-if="index < 3"
|
||||||
|
class="rank-icon"
|
||||||
|
:src="`/static/images/icon/${index + 1}.png`"
|
||||||
|
mode="aspectFit"
|
||||||
|
/>
|
||||||
|
<text v-else class="rank-num" :class="'rank-' + (index + 1)">{{ index + 1 }}</text>
|
||||||
<image class="item-avatar" :src="FILE_BASE_URL + item.avatarUrl" mode="aspectFill"></image>
|
<image class="item-avatar" :src="FILE_BASE_URL + item.avatarUrl" mode="aspectFill"></image>
|
||||||
<text class="item-name">{{ item.name }}</text>
|
<text class="item-name">{{ item.name }}</text>
|
||||||
<text class="item-score" :class="'score-' + (index + 1)">{{ item.scoreAvg }}</text>
|
<text class="item-score" :class="'score-' + (index + 1)">{{ item.scoreAvg }}</text>
|
||||||
@@ -32,7 +38,7 @@
|
|||||||
|
|
||||||
<!-- AI 总结 -->
|
<!-- AI 总结 -->
|
||||||
<view class="ai-summary" v-if="topic.aiSummary">
|
<view class="ai-summary" v-if="topic.aiSummary">
|
||||||
<text class="ai-icon icon-robot"></text>
|
<image class="ai-icon ai-icon-img" src="/static/images/icon/robot.png" mode="aspectFit"></image>
|
||||||
<view class="summary-text">
|
<view class="summary-text">
|
||||||
<text class="summary-label">AI总结:</text>
|
<text class="summary-label">AI总结:</text>
|
||||||
<text class="summary-content">{{ topic.aiSummary }}</text>
|
<text class="summary-content">{{ topic.aiSummary }}</text>
|
||||||
@@ -117,12 +123,8 @@ const handleItemTap = (itemId) => {
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-group {
|
.stat-icon-img {
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23999999'%3E%3Cpath d='M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z'/%3E%3C/svg%3E");
|
flex-shrink: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.icon-fire {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ff4d4f'%3E%3Cpath d='M19.48 13.03c-.02-.19-.13-.36-.29-.46-.17-.11-.38-.13-.56-.05-.98.42-2.12.3-2.92-.31-1.07-.81-1.55-2.22-1.28-3.75.12-.66.1-1.32-.06-1.97-.24-.95-1.1-1.6-2.09-1.58-1 .02-1.83.74-2 1.72-.25 1.55-.95 2.94-2 4.02-1.38 1.41-2.05 3.39-1.85 5.4.2 2.05 1.42 3.86 3.25 4.82 1.37.72 2.94 1.01 4.5.83 2.74-.32 5.06-2.28 5.86-4.95.23-.75.29-1.52.19-2.28-.06-.5-.21-.99-.41-1.45l-.34-.99zm-4.98 6.47c-1.37 0-2.5-1.13-2.5-2.5s1.13-2.5 2.5-2.5 2.5 1.13 2.5 2.5-1.13 2.5-2.5 2.5z'/%3E%3C/svg%3E");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.rank-list {
|
.rank-list {
|
||||||
@@ -152,6 +154,13 @@ const handleItemTap = (itemId) => {
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rank-icon {
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.rank-1 {
|
.rank-1 {
|
||||||
color: #2953ff;
|
color: #2953ff;
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
@@ -209,7 +218,10 @@ const handleItemTap = (itemId) => {
|
|||||||
margin-right: 12rpx;
|
margin-right: 12rpx;
|
||||||
margin-top: 4rpx;
|
margin-top: 4rpx;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%237b46f1'%3E%3Cpath d='M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h5a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h5V5.73A2 2 0 0 1 10 4a2 2 0 0 1 2-2zm-3 8a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm6 0a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z'/%3E%3C/svg%3E");
|
}
|
||||||
|
|
||||||
|
.ai-icon-img {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-text {
|
.summary-text {
|
||||||
|
|||||||
13
pages.json
@@ -3,7 +3,7 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/index/index",
|
"path": "pages/index/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "全民评分",
|
"navigationBarTitleText": "夯拉评分",
|
||||||
"enablePullDownRefresh": true,
|
"enablePullDownRefresh": true,
|
||||||
"navigationStyle": "custom",
|
"navigationStyle": "custom",
|
||||||
"backgroundColor": "#FFFFFF"
|
"backgroundColor": "#FFFFFF"
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/rating/index",
|
"path": "pages/rating/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "全民评分",
|
"navigationBarTitleText": "夯拉评分",
|
||||||
"navigationStyle": "custom",
|
"navigationStyle": "custom",
|
||||||
"enablePullDownRefresh": true
|
"enablePullDownRefresh": true
|
||||||
}
|
}
|
||||||
@@ -79,6 +79,15 @@
|
|||||||
"navigationStyle": "custom",
|
"navigationStyle": "custom",
|
||||||
"enablePullDownRefresh": true
|
"enablePullDownRefresh": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/mine/topics",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "我参与的话题",
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"enablePullDownRefresh": true,
|
||||||
|
"onReachBottomDistance": 50
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
@tap="handleLogin"
|
@tap="handleLogin"
|
||||||
/>
|
/>
|
||||||
<view class="header-texts">
|
<view class="header-texts">
|
||||||
<text class="title">全民评分</text>
|
<text class="title">夯拉评分</text>
|
||||||
<text class="subtitle">看看全国网友怎么评分</text>
|
<text class="subtitle">看看全国网友怎么评分</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -120,8 +120,13 @@ const loadTopicList = async (categoryId, page = 1) => {
|
|||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await fetchTopicList(categoryId, page);
|
const res = await fetchTopicList(categoryId, page);
|
||||||
// 提取真实数据数组
|
const rawList =
|
||||||
const list = res?.list || [];
|
res?.data?.records ||
|
||||||
|
res?.data?.list ||
|
||||||
|
res?.records ||
|
||||||
|
res?.list ||
|
||||||
|
(Array.isArray(res?.data) ? res.data : []);
|
||||||
|
const list = Array.isArray(rawList) ? rawList : [];
|
||||||
// 合并数据
|
// 合并数据
|
||||||
topicList.value = [...topicList.value, ...list];
|
topicList.value = [...topicList.value, ...list];
|
||||||
hasMore.value = list.length >= 5;
|
hasMore.value = list.length >= 5;
|
||||||
@@ -136,58 +141,31 @@ const loadTopicList = async (categoryId, page = 1) => {
|
|||||||
const switchCategory = (categoryId) => {
|
const switchCategory = (categoryId) => {
|
||||||
if (currentCategoryId.value === categoryId) return;
|
if (currentCategoryId.value === categoryId) return;
|
||||||
currentCategoryId.value = categoryId;
|
currentCategoryId.value = categoryId;
|
||||||
|
uni.$trackRecord({
|
||||||
// 切换分类时重新加载数据
|
eventName: 'switch_category',
|
||||||
|
eventType: 'click',
|
||||||
|
elementId: `category_${categoryId}`,
|
||||||
|
elementContent: categories.value.find(c => c.id === categoryId)?.title || '未知分类',
|
||||||
|
});
|
||||||
|
// 切换分类时重新加载数据
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
hasMore.value = true;
|
hasMore.value = true;
|
||||||
loadTopicList(categoryId, currentPage.value);
|
loadTopicList(categoryId, currentPage.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockData = [
|
const topicList = ref([]);
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: '你心中的千古一帝',
|
|
||||||
participants: '23.5万',
|
|
||||||
heat: '98k',
|
|
||||||
items: [
|
|
||||||
{ id: 1, name: '李世民', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=1', score: '9.8' },
|
|
||||||
{ id: 2, name: '秦始皇', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=2', score: '9.5' },
|
|
||||||
{ id: 3, name: '康熙', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=3', score: '9.2' },
|
|
||||||
],
|
|
||||||
aiSummary: '多数用户认为李世民综合能力最强,兼具文治武功。',
|
|
||||||
tags: ['历史', '皇帝', '争议']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: '国产手机系统谁最强',
|
|
||||||
participants: '15万',
|
|
||||||
heat: '76k',
|
|
||||||
items: [
|
|
||||||
{ id: 1, name: 'iOS', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=4', score: '8.5' },
|
|
||||||
{ id: 2, name: 'HyperOS', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=5', score: '8.2' },
|
|
||||||
{ id: 3, name: 'OriginOS', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=6', score: '8.0' },
|
|
||||||
],
|
|
||||||
aiSummary: '系统流畅度与动效是用户关注的核心,底层优化成为评分关键。',
|
|
||||||
tags: ['数码', '手机', '测评']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: '最值得定居的城市',
|
|
||||||
participants: '12.8万',
|
|
||||||
heat: '62k',
|
|
||||||
items: [
|
|
||||||
{ id: 1, name: '成都', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=7', score: '9.2' },
|
|
||||||
{ id: 2, name: '杭州', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=8', score: '8.9' },
|
|
||||||
{ id: 3, name: '厦门', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=9', score: '8.7' },
|
|
||||||
],
|
|
||||||
aiSummary: '宜居性与就业机会是主要博弈点,慢节奏生活深受青睐。',
|
|
||||||
tags: ['城市', '生活', '推荐']
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const topicList = ref([...mockData]);
|
const getCurrentCategoryTitle = () => {
|
||||||
|
return categories.value.find((category) => category.id === currentCategoryId.value)?.title || "推荐";
|
||||||
|
};
|
||||||
|
|
||||||
const goToRating = (topicId, itemId) => {
|
const goToRating = (topicId, itemId) => {
|
||||||
|
uni.$trackRecord({
|
||||||
|
eventName: 'jumpto_rating',
|
||||||
|
eventType: 'jump',
|
||||||
|
elementId: `topic_${topicId}`,
|
||||||
|
elementContent: `主题${topicId}`,
|
||||||
|
});
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/rating/index?topicId=${topicId}&itemId=${itemId}`
|
url: `/pages/rating/index?topicId=${topicId}&itemId=${itemId}`
|
||||||
});
|
});
|
||||||
@@ -226,7 +204,11 @@ onShow(() => {
|
|||||||
initData();
|
initData();
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleLoginSuccess = () => {
|
const handleLoginSuccess = async () => {
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMore.value = true;
|
||||||
|
await loadCategories();
|
||||||
|
await loadTopicList(currentCategoryId.value, currentPage.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 下拉刷新
|
// 下拉刷新
|
||||||
@@ -247,8 +229,9 @@ onReachBottom(() => {
|
|||||||
onShareAppMessage(async () => {
|
onShareAppMessage(async () => {
|
||||||
const shareToken = await getShareToken("index");
|
const shareToken = await getShareToken("index");
|
||||||
getShareReward();
|
getShareReward();
|
||||||
|
const categoryTitle = getCurrentCategoryTitle();
|
||||||
return {
|
return {
|
||||||
title: "全民评分",
|
title: `夯拉评分 | ${categoryTitle}榜单正在热议`,
|
||||||
path: "/pages/index/index?shareToken=" + shareToken,
|
path: "/pages/index/index?shareToken=" + shareToken,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -256,8 +239,9 @@ onShareAppMessage(async () => {
|
|||||||
onShareTimeline(async () => {
|
onShareTimeline(async () => {
|
||||||
const shareToken = await getShareToken("index");
|
const shareToken = await getShareToken("index");
|
||||||
getShareReward();
|
getShareReward();
|
||||||
|
const categoryTitle = getCurrentCategoryTitle();
|
||||||
return {
|
return {
|
||||||
title: "全民评分",
|
title: `${categoryTitle}圈都在玩夯拉评分,来看看谁最夯`,
|
||||||
query: `shareToken=${shareToken}`,
|
query: `shareToken=${shareToken}`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
class="nav-bar"
|
class="nav-bar"
|
||||||
:style="{ paddingTop: navBarTop + 'px', height: navBarHeight + 'px' }"
|
:style="{ paddingTop: navBarTop + 'px', height: navBarHeight + 'px' }"
|
||||||
>
|
>
|
||||||
<!-- <text class="nav-title">我的</text> -->
|
<!-- Optional title -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view
|
<scroll-view
|
||||||
@@ -14,85 +14,76 @@
|
|||||||
:style="{ paddingTop: navBarTop + navBarHeight + 'px' }"
|
:style="{ paddingTop: navBarTop + navBarHeight + 'px' }"
|
||||||
>
|
>
|
||||||
<view class="content-wrap">
|
<view class="content-wrap">
|
||||||
|
|
||||||
<!-- User Card -->
|
<!-- User Card -->
|
||||||
<view class="user-card" @tap="handleUserClick">
|
<view class="user-card">
|
||||||
<view class="avatar-box">
|
<view class="avatar-container" @tap="handleUserClick">
|
||||||
<image :src="userInfo.avatarUrl" class="avatar" mode="aspectFill" />
|
<view class="avatar-ring">
|
||||||
<!-- <view class="red-badge"><text class="fire">🔥</text></view> -->
|
<image :src="userInfo.avatarUrl" class="avatar" mode="aspectFill" />
|
||||||
</view>
|
|
||||||
<view class="user-info">
|
|
||||||
<view class="row-1">
|
|
||||||
<text class="nickname">{{ userInfo.nickName }}</text>
|
|
||||||
<view class="vip-tag" v-if="isLoggedIn && userInfo.isVip">
|
|
||||||
<text class="vip-text">VIP 祥瑞会员</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="row-2" v-if="isLoggedIn">
|
|
||||||
<!-- <text class="arrow-icon">➤</text> -->
|
|
||||||
<!-- <text class="stats-text"
|
|
||||||
>已发送 <text class="num">3</text> 条新春祝福</text
|
|
||||||
> -->
|
|
||||||
</view>
|
|
||||||
<view class="row-2" v-else>
|
|
||||||
<text class="stats-text">点击登录解锁更多功能</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-arrow">›</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- VIP Banner -->
|
<text class="nickname" @tap="handleUserClick">{{ userInfo.nickName }}</text>
|
||||||
<view v-if="!isIos" class="vip-banner" @tap="navTo('vip')">
|
|
||||||
<view class="vip-left">
|
<view class="tags-container" v-if="isLoggedIn && userStats.label && userStats.label.length">
|
||||||
<view class="vip-icon-box">
|
<text class="sparkle-icon">✨</text>
|
||||||
<uni-icons type="vip-filled" size="32" color="#5d4037" />
|
<text class="tags-text">{{ userStats.label.join(' | ') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="tags-container" v-else-if="!isLoggedIn">
|
||||||
|
<text class="tags-text">点击登录解锁更多功能</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="stats-container">
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-num">{{ isLoggedIn ? userStats.participant : '-' }}</text>
|
||||||
|
<text class="stat-label">参与话题</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="vip-content">
|
<view class="stat-divider"></view>
|
||||||
<view class="vip-title-row">
|
<view class="stat-item">
|
||||||
<text class="vip-title">祥瑞会员中心</text>
|
<text class="stat-num">{{ isLoggedIn ? userStats.comment : '-' }}</text>
|
||||||
<view class="diamond-tag">DIAMOND</view>
|
<text class="stat-label">评论数</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="vip-subtitle">开通会员解锁全部特权</text>
|
<view class="stat-divider"></view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-num">{{ isLoggedIn ? userStats.liked : '-' }}</text>
|
||||||
|
<text class="stat-label">获赞</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="vip-right">
|
|
||||||
<text class="open-text">立即开通</text>
|
<view class="action-buttons">
|
||||||
<text class="arrow">›</text>
|
<button class="btn-primary" @tap="navTo('profile')">编辑资料</button>
|
||||||
|
<button v-if="isLoggedIn" class="btn-secondary" open-type="share">
|
||||||
|
<text class="share-icon-btn"></text> 分享主页
|
||||||
|
</button>
|
||||||
|
<button v-else class="btn-secondary" @tap="openLoginPopup('请先登录后再分享主页')">
|
||||||
|
<text class="share-icon-btn"></text> 分享主页
|
||||||
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- My Creations -->
|
<!-- Menu Group 1 -->
|
||||||
<view class="section-title">我的xx</view>
|
<view class="menu-card">
|
||||||
<view class="menu-group">
|
<view class="menu-item" @tap="navTo('/pages/mine/topics')">
|
||||||
<view class="menu-item" @tap="navTo('avatar')">
|
<view class="icon-left"><text class="icon-bookmark"></text></view>
|
||||||
<view class="icon-box pink-bg"><text>☺</text></view>
|
<text class="menu-text">我的参与</text>
|
||||||
<text class="menu-text">我的专属头像</text>
|
<text class="menu-value" v-if="isLoggedIn">{{ userStats.participant }}</text>
|
||||||
<text class="arrow">›</text>
|
</view>
|
||||||
|
<view class="menu-item" @tap="navTo('comments')">
|
||||||
|
<view class="icon-left"><text class="icon-document"></text></view>
|
||||||
|
<text class="menu-text">我的评论</text>
|
||||||
|
<text class="menu-value" v-if="isLoggedIn">{{ userStats.comment }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Other Actions -->
|
<!-- Menu Group 2 -->
|
||||||
<view class="menu-group mt-30">
|
<view class="menu-card">
|
||||||
<button class="menu-item share-btn" open-type="share">
|
|
||||||
<view class="icon-left"><text class="share-icon">🔗</text></view>
|
|
||||||
<text class="menu-text">分享给好友</text>
|
|
||||||
<text class="arrow">›</text>
|
|
||||||
</button>
|
|
||||||
<view class="menu-item" @tap="navTo('feedback')">
|
<view class="menu-item" @tap="navTo('feedback')">
|
||||||
<view class="icon-left"><text class="feedback-icon">📝</text></view>
|
<view class="icon-left"><text class="icon-feedback"></text></view>
|
||||||
<text class="menu-text">意见反馈</text>
|
<text class="menu-text">意见反馈</text>
|
||||||
<text class="arrow">›</text>
|
<text class="arrow">›</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-item" @tap="navTo('help')">
|
|
||||||
<view class="icon-left"><text class="help-icon">❓</text></view>
|
|
||||||
<text class="menu-text">使用说明 / 帮助</text>
|
|
||||||
<text class="arrow">›</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<!-- <view class="version-info">2026 丙午马年 · 精美助手 v1.0.2</view> -->
|
|
||||||
|
|
||||||
|
|
||||||
<view style="height: 120rpx"></view>
|
<view style="height: 120rpx"></view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
@@ -105,9 +96,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import { onShareAppMessage, onLoad } from "@dcloudio/uni-app";
|
import { onShareAppMessage, onShareTimeline, onLoad, onShow } from "@dcloudio/uni-app";
|
||||||
import { getShareToken } from "@/utils/common";
|
import { getShareToken } from "@/utils/common";
|
||||||
import { getShareReward } from "@/api/system";
|
import { getShareReward } from "@/api/system";
|
||||||
|
import { fetchUserNums } from "@/api/mine";
|
||||||
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@@ -118,8 +110,7 @@ const navBarTop = ref(20);
|
|||||||
const navBarHeight = ref(44);
|
const navBarHeight = ref(44);
|
||||||
|
|
||||||
// User Info
|
// User Info
|
||||||
const defaultAvatarUrl =
|
const defaultAvatarUrl = "https://api.dicebear.com/7.x/avataaars/svg?seed=fallback";
|
||||||
"https://file.lihailezzc.com/resource/d9b329082b32f8305101f708593a4882.png";
|
|
||||||
|
|
||||||
const userInfo = computed(() => ({
|
const userInfo = computed(() => ({
|
||||||
nickName: userStore.userInfo.nickName || "点击登录",
|
nickName: userStore.userInfo.nickName || "点击登录",
|
||||||
@@ -127,32 +118,76 @@ const userInfo = computed(() => ({
|
|||||||
isVip: userStore.userInfo.isVip || false,
|
isVip: userStore.userInfo.isVip || false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const isLoggedIn = computed(() => !!userStore.userInfo.nickName);
|
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo.nickName);
|
||||||
|
|
||||||
const isIos = computed(() => uni.getSystemInfoSync().osName === "ios");
|
// User Stats
|
||||||
|
const userStats = ref({
|
||||||
|
participant: 0,
|
||||||
|
comment: 0,
|
||||||
|
liked: 0,
|
||||||
|
label: []
|
||||||
|
});
|
||||||
|
|
||||||
onLoad((options) => {
|
const loadUserStats = async () => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
userStats.value = {
|
||||||
|
participant: 0,
|
||||||
|
comment: 0,
|
||||||
|
liked: 0,
|
||||||
|
label: []
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await fetchUserNums();
|
||||||
|
const data = res?.data || res || {};
|
||||||
|
userStats.value = {
|
||||||
|
participant: data.participant || 0,
|
||||||
|
comment: data.comment || 0,
|
||||||
|
liked: data.liked || 0,
|
||||||
|
label: data.label || []
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取用户数据失败:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
const sysInfo = uni.getSystemInfoSync();
|
const sysInfo = uni.getSystemInfoSync();
|
||||||
navBarTop.value = sysInfo.statusBarHeight;
|
navBarTop.value = sysInfo.statusBarHeight;
|
||||||
navBarHeight.value = 44;
|
navBarHeight.value = 44;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
loadUserStats();
|
||||||
|
});
|
||||||
|
|
||||||
onShareAppMessage(async () => {
|
onShareAppMessage(async () => {
|
||||||
const shareToken = await getShareToken("mine");
|
const shareToken = await getShareToken("mine");
|
||||||
getShareReward({ scene: "mine" });
|
getShareReward({ scene: "mine" });
|
||||||
|
const nickName = isLoggedIn.value ? userInfo.value.nickName : "我";
|
||||||
return {
|
return {
|
||||||
title: "分享",
|
title: `${nickName}也在玩夯拉评分,来看看TA的主页`,
|
||||||
path: "/pages/index/index?shareToken=" + shareToken,
|
path: "/pages/mine/mine?shareToken=" + shareToken,
|
||||||
imageUrl:
|
imageUrl: "https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png",
|
||||||
"https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png",
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
onShareTimeline(async () => {
|
||||||
|
const shareToken = await getShareToken("mine");
|
||||||
|
getShareReward({ scene: "mine_timeline" });
|
||||||
|
const nickName = isLoggedIn.value ? userInfo.value.nickName : "我";
|
||||||
|
const participant = userStats.value.participant || 0;
|
||||||
|
return {
|
||||||
|
title: `${nickName}已参与 ${participant} 个话题,来夯拉评分一起围观`,
|
||||||
|
query: `shareToken=${shareToken}`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleUserClick = () => {
|
const handleUserClick = () => {
|
||||||
if (!isLoggedIn.value) {
|
if (!isLoggedIn.value) {
|
||||||
loginPopupRef.value.open();
|
openLoginPopup("请先登录");
|
||||||
} else {
|
} else {
|
||||||
// Navigate to profile details
|
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: "/pages/mine/profile",
|
url: "/pages/mine/profile",
|
||||||
});
|
});
|
||||||
@@ -160,40 +195,37 @@ const handleUserClick = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleLogind = async () => {
|
const handleLogind = async () => {
|
||||||
// Logic after successful login if needed
|
loadUserStats();
|
||||||
|
};
|
||||||
|
|
||||||
|
const openLoginPopup = (message = "请先登录") => {
|
||||||
|
loginPopupRef.value.open();
|
||||||
|
if (message) {
|
||||||
|
uni.showToast({ title: message, icon: "none" });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const navTo = (page) => {
|
const navTo = (page) => {
|
||||||
if (!isLoggedIn.value) {
|
if (!isLoggedIn.value) {
|
||||||
loginPopupRef.value.open();
|
openLoginPopup("请先登录");
|
||||||
uni.showToast({ title: "请先登录", icon: "none" });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page === "avatar") {
|
if (page === "profile") {
|
||||||
uni.navigateTo({
|
uni.navigateTo({ url: "/pages/mine/profile" });
|
||||||
url: "/pages/mine/avatar",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page === "feedback") {
|
if (page === "feedback") {
|
||||||
uni.navigateTo({
|
uni.navigateTo({ url: "/pages/feedback/index" });
|
||||||
url: "/pages/feedback/index",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (page === "help") {
|
|
||||||
uni.navigateTo({
|
if (page === "/pages/mine/topics") {
|
||||||
url: "/pages/mine/help",
|
uni.navigateTo({ url: page });
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (page === "vip") {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: "/pages/mine/vip",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uni.showToast({ title: "功能开发中", icon: "none" });
|
uni.showToast({ title: "功能开发中", icon: "none" });
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -201,7 +233,7 @@ const navTo = (page) => {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.mine-page {
|
.mine-page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f9f9f9;
|
background-color: #f8f9fc;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,297 +246,225 @@ const navTo = (page) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: #f9f9f9;
|
background: transparent;
|
||||||
}
|
|
||||||
.nav-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-scroll {
|
.content-scroll {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrap {
|
.content-wrap {
|
||||||
padding: 20rpx 30rpx 40rpx;
|
padding: 20rpx 32rpx 40rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* User Card */
|
/* User Card */
|
||||||
.user-card {
|
.user-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 30rpx;
|
border-radius: 40rpx;
|
||||||
padding: 40rpx;
|
padding: 60rpx 40rpx 40rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 40rpx;
|
margin-bottom: 32rpx;
|
||||||
position: relative;
|
box-shadow: 0 16rpx 40rpx rgba(77, 68, 241, 0.04);
|
||||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.03);
|
|
||||||
}
|
}
|
||||||
.avatar-box {
|
|
||||||
position: relative;
|
.avatar-container {
|
||||||
margin-right: 30rpx;
|
margin-bottom: 24rpx;
|
||||||
}
|
}
|
||||||
.avatar {
|
|
||||||
width: 120rpx;
|
.avatar-ring {
|
||||||
height: 120rpx;
|
width: 176rpx;
|
||||||
border-radius: 50%;
|
height: 176rpx;
|
||||||
border: 4rpx solid #fff;
|
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.red-badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: -10rpx;
|
|
||||||
width: 40rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
background: #ff3b30;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
padding: 6rpx;
|
||||||
|
background: linear-gradient(135deg, #7B46F1, #4d44f1, #00d2ff);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 2rpx solid #fff;
|
|
||||||
}
|
|
||||||
.fire {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-info {
|
.avatar {
|
||||||
flex: 1;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 4rpx solid #fff;
|
||||||
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
.row-1 {
|
|
||||||
display: flex;
|
.nickname {
|
||||||
align-items: center;
|
font-size: 40rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #111;
|
||||||
margin-bottom: 12rpx;
|
margin-bottom: 12rpx;
|
||||||
}
|
}
|
||||||
.nickname {
|
|
||||||
font-size: 36rpx;
|
.tags-container {
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-right: 16rpx;
|
|
||||||
}
|
|
||||||
.vip-tag {
|
|
||||||
background: #ffecec;
|
|
||||||
padding: 4rpx 16rpx;
|
|
||||||
border-radius: 999rpx;
|
|
||||||
}
|
|
||||||
.vip-text {
|
|
||||||
color: #ff3b30;
|
|
||||||
font-size: 20rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.row-2 {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-bottom: 48rpx;
|
||||||
}
|
}
|
||||||
.arrow-icon {
|
|
||||||
color: #ff3b30;
|
.sparkle-icon {
|
||||||
font-size: 20rpx;
|
font-size: 28rpx;
|
||||||
|
color: #7B46F1;
|
||||||
margin-right: 8rpx;
|
margin-right: 8rpx;
|
||||||
}
|
}
|
||||||
.stats-text {
|
|
||||||
|
.tags-text {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
.num {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin: 0 4rpx;
|
|
||||||
}
|
|
||||||
.card-arrow {
|
|
||||||
font-size: 40rpx;
|
|
||||||
color: #ccc;
|
|
||||||
margin-left: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VIP Banner */
|
.stats-container {
|
||||||
.vip-banner {
|
|
||||||
background: linear-gradient(135deg, #2e2424 0%, #1a1616 100%);
|
|
||||||
border-radius: 30rpx;
|
|
||||||
padding: 30rpx 40rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 40rpx;
|
|
||||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vip-left {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vip-icon-box {
|
|
||||||
width: 80rpx;
|
|
||||||
height: 80rpx;
|
|
||||||
background: #fceea9;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 24rpx;
|
width: 100%;
|
||||||
border: 4rpx solid #bfa46f;
|
margin-bottom: 48rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vip-content {
|
.stat-item {
|
||||||
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vip-title-row {
|
.stat-num {
|
||||||
display: flex;
|
font-size: 40rpx;
|
||||||
align-items: center;
|
font-weight: 800;
|
||||||
|
color: #111;
|
||||||
margin-bottom: 8rpx;
|
margin-bottom: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vip-title {
|
.stat-label {
|
||||||
font-size: 32rpx;
|
font-size: 24rpx;
|
||||||
font-weight: bold;
|
color: #888;
|
||||||
color: #fceea9;
|
|
||||||
margin-right: 12rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.diamond-tag {
|
.stat-divider {
|
||||||
background: rgba(252, 238, 169, 0.15);
|
width: 2rpx;
|
||||||
color: #fceea9;
|
height: 40rpx;
|
||||||
font-size: 18rpx;
|
background-color: #f0f0f0;
|
||||||
padding: 2rpx 8rpx;
|
|
||||||
border-radius: 6rpx;
|
|
||||||
border: 1px solid rgba(252, 238, 169, 0.4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vip-subtitle {
|
.action-buttons {
|
||||||
font-size: 22rpx;
|
|
||||||
color: rgba(252, 238, 169, 0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vip-right {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.open-text {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #fceea9;
|
|
||||||
margin-right: 4rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vip-banner .arrow {
|
|
||||||
color: #fceea9;
|
|
||||||
font-size: 32rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Section Title */
|
|
||||||
.section-title {
|
|
||||||
font-size: 26rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #999;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
padding-left: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Menu Group */
|
|
||||||
.menu-group {
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 40rpx;
|
|
||||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
|
||||||
}
|
|
||||||
.menu-group.mt-30 {
|
|
||||||
margin-top: 30rpx;
|
|
||||||
}
|
|
||||||
.menu-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 30rpx;
|
|
||||||
background: #fff;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.menu-item:active {
|
|
||||||
background: #f9f9f9;
|
|
||||||
}
|
|
||||||
/* For button reset */
|
|
||||||
.menu-item.share-btn {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
display: flex;
|
||||||
line-height: normal;
|
flex-direction: column;
|
||||||
border-radius: 0;
|
gap: 20rpx;
|
||||||
}
|
}
|
||||||
.menu-item.share-btn::after {
|
|
||||||
|
.btn-primary {
|
||||||
|
width: 100%;
|
||||||
|
height: 88rpx;
|
||||||
|
background: linear-gradient(135deg, #5b54df, #453bc7);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 100rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(77, 68, 241, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary::after {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-box {
|
.btn-secondary {
|
||||||
width: 72rpx;
|
width: 100%;
|
||||||
height: 72rpx;
|
height: 88rpx;
|
||||||
border-radius: 16rpx;
|
background-color: #f3f2ff;
|
||||||
|
color: #453bc7;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 100rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-icon-btn {
|
||||||
|
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='none' stroke='%23453bc7' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='18' cy='5' r='3'%3E%3C/circle%3E%3Ccircle cx='6' cy='12' r='3'%3E%3C/circle%3E%3Ccircle cx='18' cy='19' r='3'%3E%3C/circle%3E%3Cline x1='8.59' y1='13.51' x2='15.42' y2='17.49'%3E%3C/line%3E%3Cline x1='15.41' y1='6.51' x2='8.59' y2='10.49'%3E%3C/line%3E%3C/svg%3E");
|
||||||
|
background-size: cover;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Menu Card */
|
||||||
|
.menu-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 32rpx;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 32rpx 40rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item:active {
|
||||||
|
background: #fcfcfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-left {
|
||||||
|
width: 48rpx;
|
||||||
|
height: 48rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-right: 24rpx;
|
margin-right: 24rpx;
|
||||||
font-size: 36rpx;
|
|
||||||
}
|
|
||||||
.red-bg {
|
|
||||||
background: #fff0f0;
|
|
||||||
color: #ff3b30;
|
|
||||||
}
|
|
||||||
.orange-bg {
|
|
||||||
background: #fff8e6;
|
|
||||||
color: #ff9500;
|
|
||||||
}
|
|
||||||
.pink-bg {
|
|
||||||
background: #fff0f5;
|
|
||||||
color: #ff2d55;
|
|
||||||
}
|
|
||||||
.yellow-bg {
|
|
||||||
background: #fffae0;
|
|
||||||
color: #ffcc00;
|
|
||||||
}
|
|
||||||
.gray-bg {
|
|
||||||
background: #f5f5f5;
|
|
||||||
color: #666;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-left {
|
.icon-bookmark {
|
||||||
width: 40rpx;
|
width: 40rpx;
|
||||||
display: flex;
|
height: 40rpx;
|
||||||
justify-content: center;
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234d44f1' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'%3E%3C/path%3E%3C/svg%3E");
|
||||||
margin-right: 24rpx;
|
background-size: cover;
|
||||||
margin-left: 16rpx;
|
|
||||||
}
|
}
|
||||||
.share-icon,
|
|
||||||
.help-icon,
|
.icon-document {
|
||||||
.feedback-icon {
|
width: 40rpx;
|
||||||
font-size: 36rpx;
|
height: 40rpx;
|
||||||
color: #666;
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237B46F1' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'%3E%3C/path%3E%3Cpolyline points='14 2 14 8 20 8'%3E%3C/polyline%3E%3Cline x1='16' y1='13' x2='8' y2='13'%3E%3C/line%3E%3Cline x1='16' y1='17' x2='8' y2='17'%3E%3C/line%3E%3Cpolyline points='10 9 9 9 8 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-feedback {
|
||||||
|
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='%23453bc7' 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%3Cline x1='9' y1='10' x2='15' y2='10'%3E%3C/line%3E%3C/svg%3E");
|
||||||
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-text {
|
.menu-text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 28rpx;
|
font-size: 30rpx;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.new-badge {
|
|
||||||
background: #ff3b30;
|
.menu-value {
|
||||||
color: #fff;
|
|
||||||
font-size: 20rpx;
|
|
||||||
padding: 2rpx 10rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
margin-right: 16rpx;
|
|
||||||
}
|
|
||||||
.arrow {
|
|
||||||
color: #ccc;
|
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
|
color: #999;
|
||||||
|
font-family: 'DIN Alternate', -apple-system, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Version Info */
|
.arrow {
|
||||||
.version-info {
|
|
||||||
text-align: center;
|
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
font-size: 22rpx;
|
font-size: 36rpx;
|
||||||
margin-top: 60rpx;
|
margin-left: 12rpx;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
236
pages/mine/topics.vue
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
<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>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 占位防止内容被导航栏遮挡 -->
|
||||||
|
<view :style="{ height: statusBarHeight + 44 + 'px' }"></view>
|
||||||
|
|
||||||
|
<!-- 话题列表 -->
|
||||||
|
<view class="topic-list-wrap">
|
||||||
|
<view v-if="topics.length > 0">
|
||||||
|
<TopicCard
|
||||||
|
v-for="(item, index) in topics"
|
||||||
|
:key="item.id || index"
|
||||||
|
:topic="item"
|
||||||
|
@card-click="goToTopicDetail(item.id)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view class="empty-state" v-if="!loading && topics.length === 0">
|
||||||
|
<text class="empty-icon">📝</text>
|
||||||
|
<text class="empty-text">你还没有参与任何话题</text>
|
||||||
|
<view class="empty-btn" @tap="goToHome">去逛逛</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多指示器 -->
|
||||||
|
<view class="load-more" v-if="topics.length > 0">
|
||||||
|
<text>{{ loading ? '加载中...' : (hasMore ? '上拉加载更多' : '没有更多了') }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { getStatusBarHeight } from "@/utils/system";
|
||||||
|
import { onLoad, onPullDownRefresh, onReachBottom, onShow } from "@dcloudio/uni-app";
|
||||||
|
import { fetchUserTopics } from "@/api/topic";
|
||||||
|
import TopicCard from "@/components/TopicCard/TopicCard.vue";
|
||||||
|
|
||||||
|
const statusBarHeight = ref(getStatusBarHeight() || 44);
|
||||||
|
const topics = ref([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
const hasMore = ref(true);
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = 5; // 每次获取5个
|
||||||
|
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToHome = () => {
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/index/index'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToTopicDetail = (topicId) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/rating/index?topicId=${topicId}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadData = async (page = 1) => {
|
||||||
|
if (loading.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await fetchUserTopics(page);
|
||||||
|
const list = res?.data?.records || res?.records || res?.list || [];
|
||||||
|
|
||||||
|
// 如果是第一页,先清空原有数据
|
||||||
|
if (page === 1) {
|
||||||
|
topics.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造适合 TopicCard 渲染的数据格式
|
||||||
|
const formattedList = list.map(item => ({
|
||||||
|
id: item.topicId || item.id,
|
||||||
|
title: item.topicName || item.title || '未知话题',
|
||||||
|
image: item.image || item.coverUrl || 'https://api.dicebear.com/7.x/shapes/svg?seed=' + (item.topicId || item.id), // 给个默认图防止报错
|
||||||
|
views: item.participantCount || item.views || 0,
|
||||||
|
items: item.items || item.ratingItems || []
|
||||||
|
}));
|
||||||
|
|
||||||
|
topics.value = [...topics.value, ...formattedList];
|
||||||
|
|
||||||
|
// 判断是否有下一页
|
||||||
|
hasMore.value = list.length >= pageSize;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取参与话题列表失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '获取列表失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
if (page === 1) {
|
||||||
|
hasMore.value = false;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
// onLoad 只触发一次
|
||||||
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
// 每次进入页面都刷新,确保数据是最新的(如果刚才在详情页做了评分)
|
||||||
|
loadData(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
onPullDownRefresh(async () => {
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMore.value = true;
|
||||||
|
await loadData(1);
|
||||||
|
uni.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
onReachBottom(() => {
|
||||||
|
if (!hasMore.value || loading.value) return;
|
||||||
|
currentPage.value += 1;
|
||||||
|
loadData(currentPage.value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f6fa;
|
||||||
|
padding-bottom: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导航栏 */
|
||||||
|
.nav-bar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background-color: #f5f6fa;
|
||||||
|
backdrop-filter: blur(20rpx);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-content {
|
||||||
|
height: 44px;
|
||||||
|
display: flex;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表区 */
|
||||||
|
.topic-list-wrap {
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24rpx; /* 组件之间的间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 覆盖 TopicCard 的外边距,让外层控制间距 */
|
||||||
|
:deep(.topic-card) {
|
||||||
|
margin-bottom: 24rpx !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空状态 */
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 160rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 100rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 48rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-btn {
|
||||||
|
background: linear-gradient(135deg, #4d44f1, #2953ff);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 16rpx 64rpx;
|
||||||
|
border-radius: 100rpx;
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(41, 83, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载更多 */
|
||||||
|
.load-more {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20rpx 0 40rpx;
|
||||||
|
color: #999;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<view class="nav-content">
|
<view class="nav-content">
|
||||||
<view class="nav-left" @tap="goBack">
|
<view class="nav-left" @tap="goBack">
|
||||||
<text class="back-icon"></text>
|
<text class="back-icon"></text>
|
||||||
<text class="nav-title">全民评分</text>
|
<text class="nav-title">夯拉评分</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="nav-right">
|
<view class="nav-right">
|
||||||
<text class="search-icon"></text>
|
<text class="search-icon"></text>
|
||||||
@@ -27,7 +27,10 @@
|
|||||||
</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">🔥 {{ detailData.hotScore }}w 热度</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>
|
<text class="meta-count" style="margin-left: 20rpx; color: #999;">{{ detailData.ratingCount }} 人评分</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -36,7 +39,7 @@
|
|||||||
<!-- AI 观察员 -->
|
<!-- AI 观察员 -->
|
||||||
<view class="ai-card">
|
<view class="ai-card">
|
||||||
<view class="ai-header">
|
<view class="ai-header">
|
||||||
<text class="ai-icon"></text>
|
<image class="ai-icon ai-icon-img" src="/static/images/icon/robot.png" mode="aspectFit"></image>
|
||||||
<text class="ai-title">AI观察员</text>
|
<text class="ai-title">AI观察员</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="ai-content">{{ detailData.aiSummary }}</text>
|
<text class="ai-content">{{ detailData.aiSummary }}</text>
|
||||||
@@ -44,13 +47,15 @@
|
|||||||
|
|
||||||
<!-- 你的态度 -->
|
<!-- 你的态度 -->
|
||||||
<AttitudePanel
|
<AttitudePanel
|
||||||
|
ref="attitudePanelRef"
|
||||||
:initial-action="currentAction"
|
:initial-action="currentAction"
|
||||||
|
:sending="isSendingComment"
|
||||||
@action-change="handleActionChange"
|
@action-change="handleActionChange"
|
||||||
@send-comment="handleSendComment"
|
@send-comment="handleSendComment"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 大家都在 PK -->
|
<!-- 大家都在 PK -->
|
||||||
<view class="pk-panel card">
|
<!-- <view class="pk-panel card">
|
||||||
<view class="pk-header">
|
<view class="pk-header">
|
||||||
<text class="section-title">大家都在 PK</text>
|
<text class="section-title">大家都在 PK</text>
|
||||||
<view class="go-compare">去对比<text class="arrow-right"></text></view>
|
<view class="go-compare">去对比<text class="arrow-right"></text></view>
|
||||||
@@ -74,7 +79,7 @@
|
|||||||
<text class="blue-text">63% 支持</text>
|
<text class="blue-text">63% 支持</text>
|
||||||
<text class="yellow-text">37% 支持</text>
|
<text class="yellow-text">37% 支持</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view> -->
|
||||||
|
|
||||||
<!-- 评论区 -->
|
<!-- 评论区 -->
|
||||||
<view class="comments-section card">
|
<view class="comments-section card">
|
||||||
@@ -86,52 +91,97 @@
|
|||||||
<!-- <text class="filter-icon"></text> -->
|
<!-- <text class="filter-icon"></text> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="comment-list" style="min-height: 400rpx;">
|
<view class="comment-list">
|
||||||
|
<view v-if="commentLoading && !comments.length" class="comment-state">
|
||||||
|
<text class="comment-state-title">评论加载中...</text>
|
||||||
|
<text class="comment-state-desc">正在拉取大家的锐评</text>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="commentError && !comments.length" class="comment-state">
|
||||||
|
<text class="comment-state-title">评论加载失败</text>
|
||||||
|
<text class="comment-state-desc">{{ commentError }}</text>
|
||||||
|
<button class="comment-state-btn" @tap="retryLoadComments">重新加载</button>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="!comments.length" class="comment-state">
|
||||||
|
<view class="comment-empty-visual">
|
||||||
|
<view class="comment-empty-orbit orbit-1"></view>
|
||||||
|
<view class="comment-empty-orbit orbit-2"></view>
|
||||||
|
<view class="comment-empty-core">
|
||||||
|
<text class="comment-empty-emoji">💬</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="comment-state-title">还没有评论</text>
|
||||||
|
<text class="comment-state-desc">来发第一条锐评,带动一下讨论气氛</text>
|
||||||
|
<text class="comment-state-tip">你的观点,可能就是这条榜单最有意思的开始</text>
|
||||||
|
</view>
|
||||||
<CommentItem
|
<CommentItem
|
||||||
v-for="(comment, index) in comments"
|
v-for="(comment, index) in comments"
|
||||||
:key="comment.id"
|
:key="comment.id"
|
||||||
:comment="comment"
|
:comment="comment"
|
||||||
|
@need-login="openLoginPopup"
|
||||||
|
@like-change="handleCommentLikeChange"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 加载更多 -->
|
<!-- 加载更多 -->
|
||||||
<view class="load-more">
|
<view v-if="comments.length" class="load-more">
|
||||||
<text>{{ loading ? '加载中...' : (hasMore ? '上拉加载更多' : '没有更多了') }}</text>
|
<text>{{ commentLoadText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 评分成功特效弹窗 -->
|
<!-- 评分成功特效弹窗 -->
|
||||||
<SuccessPopup ref="successPopupRef" />
|
<SuccessPopup ref="successPopupRef" />
|
||||||
|
<LoginPopup ref="loginPopupRef" @logind="handleLoginSuccess" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { computed, 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, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
|
||||||
import AttitudePanel from "@/components/AttitudePanel/AttitudePanel.vue";
|
import AttitudePanel from "@/components/AttitudePanel/AttitudePanel.vue";
|
||||||
import SuccessPopup from "@/components/SuccessPopup/SuccessPopup.vue";
|
import SuccessPopup from "@/components/SuccessPopup/SuccessPopup.vue";
|
||||||
import ActionBadge from "@/components/ActionBadge/ActionBadge.vue";
|
|
||||||
import CommentItem from "@/components/CommentItem/CommentItem.vue";
|
import CommentItem from "@/components/CommentItem/CommentItem.vue";
|
||||||
|
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
||||||
import { FILE_BASE_URL } from "@/utils/constants";
|
import { FILE_BASE_URL } from "@/utils/constants";
|
||||||
import { fetchTopicRatingItems, topicItemScore, topicItemComment, topicItemCommentList } from "@/api/topicItem";
|
import { fetchTopicRatingItems, topicItemScore, topicItemComment, topicItemCommentList } from "@/api/topicItem";
|
||||||
import { formatDate } from "@/utils/date";
|
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 statusBarHeight = ref(getStatusBarHeight() || 44);
|
||||||
const loading = ref(false);
|
|
||||||
const hasMore = ref(true);
|
const hasMore = ref(true);
|
||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const currentAction = ref('');
|
const currentAction = ref('');
|
||||||
const successPopupRef = ref(null);
|
const successPopupRef = ref(null);
|
||||||
|
const loginPopupRef = ref(null);
|
||||||
|
const attitudePanelRef = ref(null);
|
||||||
const currentTab = ref('hot'); // 'hot' 或 'new'
|
const currentTab = ref('hot'); // 'hot' 或 'new'
|
||||||
|
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
|
||||||
|
const commentLoading = ref(false);
|
||||||
|
const isSendingComment = ref(false);
|
||||||
|
const commentError = ref('');
|
||||||
|
const commentLoadText = computed(() => {
|
||||||
|
if (commentLoading.value && comments.value.length) return '加载中...';
|
||||||
|
if (!hasMore.value) return '没有更多了';
|
||||||
|
return '上拉加载更多';
|
||||||
|
});
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
uni.navigateBack();
|
uni.navigateBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openLoginPopup = (message = '请先登录后再操作') => {
|
||||||
|
loginPopupRef.value?.open();
|
||||||
|
if (message) {
|
||||||
|
uni.showToast({
|
||||||
|
title: message,
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const detailData = ref({
|
const detailData = ref({
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
@@ -146,6 +196,11 @@ const comments = ref([]);
|
|||||||
const itemId = ref('');
|
const itemId = ref('');
|
||||||
|
|
||||||
const handleActionChange = async (action) => {
|
const handleActionChange = async (action) => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
openLoginPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const originAction = detailData.value.userAction;
|
const originAction = detailData.value.userAction;
|
||||||
const originScore = detailData.value.score;
|
const originScore = detailData.value.score;
|
||||||
const isCancel = detailData.value.userAction === action.label;
|
const isCancel = detailData.value.userAction === action.label;
|
||||||
@@ -165,6 +220,20 @@ const handleActionChange = async (action) => {
|
|||||||
if (!isCancel && successPopupRef.value) {
|
if (!isCancel && successPopupRef.value) {
|
||||||
successPopupRef.value.show(action);
|
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) {
|
} catch (error) {
|
||||||
detailData.value.userAction = originAction;
|
detailData.value.userAction = originAction;
|
||||||
detailData.value.score = originScore;
|
detailData.value.score = originScore;
|
||||||
@@ -173,9 +242,15 @@ const handleActionChange = async (action) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSendComment = async (payload) => {
|
const handleSendComment = async (payload) => {
|
||||||
if (!detailData.value.topicId || !itemId.value) return;
|
if (!isLoggedIn.value) {
|
||||||
|
openLoginPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!detailData.value.topicId || !itemId.value || isSendingComment.value) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
isSendingComment.value = true;
|
||||||
uni.showLoading({ title: '发送中...' });
|
uni.showLoading({ title: '发送中...' });
|
||||||
await topicItemComment({
|
await topicItemComment({
|
||||||
topicId: detailData.value.topicId,
|
topicId: detailData.value.topicId,
|
||||||
@@ -195,14 +270,26 @@ const handleSendComment = async (payload) => {
|
|||||||
// 刷新页面数据以获取最新评论
|
// 刷新页面数据以获取最新评论
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
hasMore.value = true;
|
hasMore.value = true;
|
||||||
comments.value = [];
|
uni.$trackRecord({
|
||||||
getCommentsData();
|
eventName: 'comment',
|
||||||
|
eventType: 'comment',
|
||||||
|
elementId: `item_${itemId.value}`,
|
||||||
|
elementContent: `评论项_${detailData.value.name}`,
|
||||||
|
customParams: {
|
||||||
|
comment: payload.content,
|
||||||
|
page: 'rating_detail'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
attitudePanelRef.value?.resetComment?.();
|
||||||
|
await getCommentsData({ replace: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '发送失败',
|
title: '发送失败',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
isSendingComment.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -211,23 +298,49 @@ const switchTab = (tab) => {
|
|||||||
currentTab.value = tab;
|
currentTab.value = tab;
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
hasMore.value = true;
|
hasMore.value = true;
|
||||||
// comments.value = []; // 移除这行,避免数据清空导致高度塌陷
|
commentError.value = '';
|
||||||
getCommentsData(true); // 传入标识表示是切换榜单
|
getCommentsData({ replace: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCommentsData = async (isSwitchTab = false) => {
|
const resolveCommentHasMore = (res, list) => {
|
||||||
if (!detailData.value.topicId || !itemId.value || loading.value) return;
|
const data = res?.data || res || {};
|
||||||
|
const hasNext = data?.hasNext || res?.hasNext;
|
||||||
|
if (typeof hasNext === 'boolean') return hasNext;
|
||||||
|
|
||||||
|
const pages = Number(data?.pages || res?.pages || 0);
|
||||||
|
if (pages > 0) return currentPage.value < pages;
|
||||||
|
|
||||||
|
const total = Number(data?.total || res?.total || 0);
|
||||||
|
if (total > 0) {
|
||||||
|
const size = Number(data?.size || data?.pageSize || res?.size || res?.pageSize || list.length || 1);
|
||||||
|
return currentPage.value * size < total;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.length > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCommentsData = async ({ replace = false } = {}) => {
|
||||||
|
if (!detailData.value.topicId || !itemId.value || commentLoading.value) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
commentLoading.value = true;
|
||||||
const orderBy = currentTab.value;
|
commentError.value = '';
|
||||||
|
const orderBy = currentTab.value === 'hot' ? 1 : 2;
|
||||||
const res = await topicItemCommentList(detailData.value.topicId, itemId.value, currentPage.value, orderBy);
|
const res = await topicItemCommentList(detailData.value.topicId, itemId.value, currentPage.value, orderBy);
|
||||||
const list = res?.list || [];
|
const rawList =
|
||||||
|
res?.data?.records ||
|
||||||
|
res?.data?.list ||
|
||||||
|
res?.records ||
|
||||||
|
res?.list ||
|
||||||
|
(Array.isArray(res?.data) ? res.data : []);
|
||||||
|
const list = Array.isArray(rawList) ? rawList : [];
|
||||||
|
|
||||||
const formattedList = list.map(item => ({
|
const formattedList = list.map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
name: item.user?.nickname || '匿名用户',
|
name: item.user?.nickname || '匿名用户',
|
||||||
avatar: item.user?.avatar ? item.user.avatar : 'https://api.dicebear.com/7.x/avataaars/svg?seed=fallback',
|
avatar: item.user?.avatar
|
||||||
|
? (String(item.user.avatar).startsWith('http') ? item.user.avatar : `${FILE_BASE_URL}${item.user.avatar}`)
|
||||||
|
: 'https://api.dicebear.com/7.x/avataaars/svg?seed=fallback',
|
||||||
time: formatDate(item.createdAt) || '刚刚', // 如果后端返回了时间字段可以替换
|
time: formatDate(item.createdAt) || '刚刚', // 如果后端返回了时间字段可以替换
|
||||||
content: item.content,
|
content: item.content,
|
||||||
likes: item.likeCount || 0,
|
likes: item.likeCount || 0,
|
||||||
@@ -237,21 +350,42 @@ const getCommentsData = async (isSwitchTab = false) => {
|
|||||||
userAction: item.userAction || "" // 夯/顶级/人上人/NPC/拉
|
userAction: item.userAction || "" // 夯/顶级/人上人/NPC/拉
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (currentPage.value === 1 || isSwitchTab) {
|
if (currentPage.value === 1 || replace) {
|
||||||
comments.value = formattedList;
|
comments.value = formattedList;
|
||||||
} else {
|
} else {
|
||||||
comments.value = [...comments.value, ...formattedList];
|
comments.value = [...comments.value, ...formattedList];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 假设每页10条,如果返回的少于10条则没有更多了
|
hasMore.value = resolveCommentHasMore(res, list);
|
||||||
hasMore.value = list.length >= 6;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取评论列表失败:', error);
|
console.error('获取评论列表失败:', error);
|
||||||
|
commentError.value = '请检查网络后重试';
|
||||||
|
if (currentPage.value > 1) {
|
||||||
|
currentPage.value -= 1;
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
commentLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const retryLoadComments = () => {
|
||||||
|
if (!comments.value.length) {
|
||||||
|
currentPage.value = 1;
|
||||||
|
}
|
||||||
|
getCommentsData({ replace: currentPage.value === 1 });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCommentLikeChange = ({ id, isLiked, likes }) => {
|
||||||
|
comments.value = comments.value.map((item) => {
|
||||||
|
if (item.id !== id) return item;
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
isLiked,
|
||||||
|
likes
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getDetailData = async () => {
|
const getDetailData = async () => {
|
||||||
if (!itemId.value) return;
|
if (!itemId.value) return;
|
||||||
try {
|
try {
|
||||||
@@ -284,7 +418,7 @@ const getDetailData = async () => {
|
|||||||
|
|
||||||
// 拉取真实的评论列表
|
// 拉取真实的评论列表
|
||||||
if (currentPage.value === 1) {
|
if (currentPage.value === 1) {
|
||||||
getCommentsData();
|
getCommentsData({ replace: true });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取详情失败:', error);
|
console.error('获取详情失败:', error);
|
||||||
@@ -313,15 +447,45 @@ onLoad((options) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleLoginSuccess = () => {
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMore.value = true;
|
||||||
|
comments.value = [];
|
||||||
|
commentError.value = '';
|
||||||
|
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(() => {
|
onPullDownRefresh(() => {
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
hasMore.value = true;
|
hasMore.value = true;
|
||||||
comments.value = [];
|
comments.value = [];
|
||||||
|
commentError.value = '';
|
||||||
getDetailData();
|
getDetailData();
|
||||||
});
|
});
|
||||||
|
|
||||||
onReachBottom(() => {
|
onReachBottom(() => {
|
||||||
if (!hasMore.value || loading.value) return;
|
if (!hasMore.value || commentLoading.value) return;
|
||||||
currentPage.value += 1;
|
currentPage.value += 1;
|
||||||
getCommentsData();
|
getCommentsData();
|
||||||
});
|
});
|
||||||
@@ -456,9 +620,18 @@ onReachBottom(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.meta-heat {
|
.meta-heat {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.meta-icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* AI 观察员 */
|
/* AI 观察员 */
|
||||||
.ai-card {
|
.ai-card {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
@@ -477,11 +650,13 @@ onReachBottom(() => {
|
|||||||
.ai-icon {
|
.ai-icon {
|
||||||
width: 36rpx;
|
width: 36rpx;
|
||||||
height: 36rpx;
|
height: 36rpx;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%237B46F1'%3E%3Cpath d='M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h5a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h5V5.73A2 2 0 0 1 10 4a2 2 0 0 1 2-2zm-3 8a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm6 0a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z'/%3E%3C/svg%3E");
|
|
||||||
background-size: cover;
|
|
||||||
margin-right: 12rpx;
|
margin-right: 12rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-icon-img {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.ai-title {
|
.ai-title {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
@@ -650,6 +825,100 @@ onReachBottom(() => {
|
|||||||
.comment-list {
|
.comment-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
min-height: 400rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-state {
|
||||||
|
min-height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
padding: 56rpx 40rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: linear-gradient(180deg, rgba(247, 248, 255, 0.92) 0%, rgba(255, 255, 255, 0.98) 100%);
|
||||||
|
border-radius: 28rpx;
|
||||||
|
border: 2rpx solid #f1f2fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-empty-visual {
|
||||||
|
position: relative;
|
||||||
|
width: 180rpx;
|
||||||
|
height: 180rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-empty-orbit {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2rpx solid rgba(123, 70, 241, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.orbit-1 {
|
||||||
|
width: 180rpx;
|
||||||
|
height: 180rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orbit-2 {
|
||||||
|
width: 132rpx;
|
||||||
|
height: 132rpx;
|
||||||
|
border-color: rgba(77, 68, 241, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-empty-core {
|
||||||
|
width: 96rpx;
|
||||||
|
height: 96rpx;
|
||||||
|
border-radius: 32rpx;
|
||||||
|
background: linear-gradient(135deg, #7668ff, #4d44f1);
|
||||||
|
box-shadow: 0 18rpx 36rpx rgba(94, 83, 255, 0.2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-empty-emoji {
|
||||||
|
font-size: 44rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-state-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #1f2430;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 14rpx;
|
||||||
|
letter-spacing: 0.5rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-state-desc {
|
||||||
|
font-size: 25rpx;
|
||||||
|
color: #70778a;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-state-tip {
|
||||||
|
margin-top: 14rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #a7adbc;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-state-btn {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
height: 72rpx;
|
||||||
|
line-height: 72rpx;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: #4d44f1;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-state-btn::after {
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.load-more {
|
.load-more {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
<text class="back-icon"></text>
|
<text class="back-icon"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="nav-logo-wrap">
|
<view class="nav-logo-wrap">
|
||||||
<image class="nav-logo" src="/static/images/default-avatar.png" mode="aspectFill" />
|
<image class="nav-logo" src="/static/images/logo.jpg" mode="aspectFill" />
|
||||||
</view>
|
</view>
|
||||||
<text class="nav-title">全民评分</text>
|
<text class="nav-title">夯拉评分</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -21,16 +21,17 @@
|
|||||||
<view class="topic-header">
|
<view class="topic-header">
|
||||||
<text class="topic-title">{{ topicData.title }}</text>
|
<text class="topic-title">{{ topicData.title }}</text>
|
||||||
<view class="topic-stats">
|
<view class="topic-stats">
|
||||||
<text class="stat-icon fire-icon"></text>
|
<image class="stat-icon stat-icon-img" src="/static/images/icon/fire.png" mode="aspectFit"></image>
|
||||||
<text class="stat-text">热度 {{ topicData.hotScore }} w</text>
|
<text class="stat-text">热度 {{ topicData.hotScore }} w</text>
|
||||||
<text class="stat-text margin-left">{{ topicData.participantCount }}人参与</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>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- AI 实时观察 -->
|
<!-- AI 实时观察 -->
|
||||||
<view class="ai-observe">
|
<view class="ai-observe">
|
||||||
<view class="ai-header">
|
<view class="ai-header">
|
||||||
<text class="ai-icon"></text>
|
<image class="ai-icon ai-icon-img" src="/static/images/icon/robot.png" mode="aspectFit"></image>
|
||||||
<text class="ai-title">AI 实时观察</text>
|
<text class="ai-title">AI 实时观察</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="ai-content">{{ topicData.aiSummary }}</text>
|
<text class="ai-content">{{ topicData.aiSummary }}</text>
|
||||||
@@ -118,7 +119,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 右下角悬浮添加按钮 -->
|
<!-- 右下角悬浮添加按钮 -->
|
||||||
<view class="fab-btn" @tap="showAddPopup = true">
|
<view class="fab-btn" @tap="handleAddClick">
|
||||||
<text class="plus-icon">+</text>
|
<text class="plus-icon">+</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -150,19 +151,26 @@
|
|||||||
|
|
||||||
<!-- 评分成功特效弹窗 -->
|
<!-- 评分成功特效弹窗 -->
|
||||||
<SuccessPopup ref="successPopupRef" />
|
<SuccessPopup ref="successPopupRef" />
|
||||||
|
<LoginPopup ref="loginPopupRef" @logind="handleLoginSuccess" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { getStatusBarHeight } from "@/utils/system";
|
import { getStatusBarHeight } from "@/utils/system";
|
||||||
import { onPullDownRefresh, onReachBottom, onLoad } from "@dcloudio/uni-app";
|
import { onPullDownRefresh, onReachBottom, onLoad, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
|
||||||
import { fetchTopicDetail, fetchTopicRatingItems } from "@/api/topic";
|
import { fetchTopicDetail, fetchTopicRatingItems } from "@/api/topic";
|
||||||
import { FILE_BASE_URL } from "@/utils/constants";
|
import { FILE_BASE_URL } from "@/utils/constants";
|
||||||
import RatingCard from "@/components/RatingCard/RatingCard.vue";
|
import RatingCard from "@/components/RatingCard/RatingCard.vue";
|
||||||
import SuccessPopup from "@/components/SuccessPopup/SuccessPopup.vue";
|
import SuccessPopup from "@/components/SuccessPopup/SuccessPopup.vue";
|
||||||
import { topicItemScore } from "@/api/topicItem";
|
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 statusBarHeight = ref(getStatusBarHeight() || 44);
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@@ -170,6 +178,8 @@ const hasMore = ref(true);
|
|||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const currentTopicId = ref(null);
|
const currentTopicId = ref(null);
|
||||||
const successPopupRef = ref(null);
|
const successPopupRef = ref(null);
|
||||||
|
const loginPopupRef = ref(null);
|
||||||
|
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
uni.navigateBack({
|
uni.navigateBack({
|
||||||
@@ -177,6 +187,13 @@ const goBack = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openLoginPopup = (message = '请先登录后再操作') => {
|
||||||
|
loginPopupRef.value?.open();
|
||||||
|
if (message) {
|
||||||
|
uni.showToast({ title: message, icon: 'none' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const topicData = ref({
|
const topicData = ref({
|
||||||
title: '',
|
title: '',
|
||||||
heat: '0',
|
heat: '0',
|
||||||
@@ -208,6 +225,11 @@ const newItem = ref({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const chooseAvatar = () => {
|
const chooseAvatar = () => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
openLoginPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uni.chooseImage({
|
uni.chooseImage({
|
||||||
count: 1,
|
count: 1,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
@@ -216,30 +238,51 @@ const chooseAvatar = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmAddItem = () => {
|
const confirmAddItem = async () => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
openLoginPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!newItem.value.name.trim()) {
|
if (!newItem.value.name.trim()) {
|
||||||
uni.showToast({ title: '请输入名称', icon: 'none' });
|
uni.showToast({ title: '请输入名称', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!newItem.value.avatar) {
|
||||||
|
uni.showToast({ title: '请上传头像', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 模拟添加
|
try {
|
||||||
const newRatingItem = {
|
uni.showLoading({ title: '添加中...' });
|
||||||
id: Date.now(),
|
|
||||||
rank: ratingItems.value.length + 1,
|
|
||||||
name: newItem.value.name,
|
|
||||||
avatar: newItem.value.avatar || `https://api.dicebear.com/7.x/avataaars/svg?seed=${Date.now()}`,
|
|
||||||
score: '0.0',
|
|
||||||
quote: '刚刚加入讨论,快来发表你的看法吧。',
|
|
||||||
userAction: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
ratingItems.value.push(newRatingItem);
|
// 1. 先通过 uni.uploadFile 上传头像图片
|
||||||
|
const uploadRes = await uploadImage(newItem.value.avatar);
|
||||||
|
|
||||||
// 重置并关闭
|
// 2. 将上传成功后的图片 URL 和其他数据一起提交给添加接口
|
||||||
newItem.value = { name: '', avatar: '' };
|
await topicItemAdd({
|
||||||
showAddPopup.value = false;
|
topicId: currentTopicId.value,
|
||||||
|
name: newItem.value.name,
|
||||||
|
avatarUrl: uploadRes
|
||||||
|
});
|
||||||
|
|
||||||
uni.showToast({ title: '添加成功', icon: 'success' });
|
uni.hideLoading();
|
||||||
|
uni.showToast({ title: '添加成功', icon: 'success' });
|
||||||
|
|
||||||
|
// 重置表单并关闭弹窗
|
||||||
|
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 ratingItems = ref([]);
|
||||||
@@ -256,7 +299,6 @@ const loadItems = async (topicId, page = 1) => {
|
|||||||
ratingItems.value = [...ratingItems.value, ...list];
|
ratingItems.value = [...ratingItems.value, ...list];
|
||||||
hasMore.value = list.length >= 10; // 假设 pageSize 是 10
|
hasMore.value = list.length >= 10; // 假设 pageSize 是 10
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取评分项列表失败:", error);
|
|
||||||
hasMore.value = false;
|
hasMore.value = false;
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -276,26 +318,12 @@ onLoad((options) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockComments = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: '史学家阿强',
|
|
||||||
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=c1',
|
|
||||||
time: '刚刚',
|
|
||||||
content: '李世民唯一缺点是儿子太抽象。他在位时那种包容力和大局观,放眼整个历史都是炸裂的存在。'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: '逻辑帝',
|
|
||||||
avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=c2',
|
|
||||||
time: '2小时前',
|
|
||||||
content: '秦始皇是开辟者,李世民是完善者。一个是0到1的惊天动地,一个是1到100的极致巅峰。'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const comments = ref([...mockComments]);
|
|
||||||
|
|
||||||
const handleAction = async (item, action) => {
|
const handleAction = async (item, action) => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
openLoginPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const originAction = item.userAction;
|
const originAction = item.userAction;
|
||||||
const originScore = item.scoreAvg;
|
const originScore = item.scoreAvg;
|
||||||
const isCancel = item.userAction === action.label;
|
const isCancel = item.userAction === action.label;
|
||||||
@@ -313,6 +341,20 @@ const handleAction = async (item, action) => {
|
|||||||
if (!isCancel && successPopupRef.value) {
|
if (!isCancel && successPopupRef.value) {
|
||||||
successPopupRef.value.show(action);
|
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) {
|
} catch (error) {
|
||||||
item.userAction = originAction;
|
item.userAction = originAction;
|
||||||
item.scoreAvg = originScore;
|
item.scoreAvg = originScore;
|
||||||
@@ -320,11 +362,59 @@ const handleAction = async (item, action) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const goToDetail = (itemId) => {
|
const goToDetail = (itemId) => {
|
||||||
|
uni.$trackRecord({
|
||||||
|
eventName: 'jumpto_detail',
|
||||||
|
eventType: 'jump',
|
||||||
|
elementId: `item_${itemId}`,
|
||||||
|
elementContent: `评分项${itemId}`,
|
||||||
|
});
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/rating/detail?itemId=${itemId}&topicId=${currentTopicId.value}`
|
url: `/pages/rating/detail?itemId=${itemId}&topicId=${currentTopicId.value}`
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 () => {
|
onPullDownRefresh(async () => {
|
||||||
if (currentTopicId.value) {
|
if (currentTopicId.value) {
|
||||||
@@ -335,11 +425,7 @@ onPullDownRefresh(async () => {
|
|||||||
loadItems(currentTopicId.value, currentPage.value)
|
loadItems(currentTopicId.value, currentPage.value)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
// 模拟评论刷新(此处未接接口,暂时保留原有模拟刷新逻辑,去掉 mockRatingItems 的重置)
|
uni.stopPullDownRefresh();
|
||||||
setTimeout(() => {
|
|
||||||
comments.value = [...mockComments];
|
|
||||||
uni.stopPullDownRefresh();
|
|
||||||
}, 1000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 上拉加载更多
|
// 上拉加载更多
|
||||||
@@ -349,12 +435,6 @@ onReachBottom(() => {
|
|||||||
currentPage.value += 1;
|
currentPage.value += 1;
|
||||||
loadItems(currentTopicId.value, currentPage.value);
|
loadItems(currentTopicId.value, currentPage.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 原有的评论模拟加载(根据需要决定是否保留)
|
|
||||||
setTimeout(() => {
|
|
||||||
const moreComments = mockComments.map(c => ({...c, id: c.id + comments.value.length}));
|
|
||||||
comments.value.push(...moreComments);
|
|
||||||
}, 1000);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -400,7 +480,6 @@ onReachBottom(() => {
|
|||||||
width: 56rpx;
|
width: 56rpx;
|
||||||
height: 56rpx;
|
height: 56rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: linear-gradient(135deg, #001f3f, #0088a9);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -443,12 +522,15 @@ onReachBottom(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon.fire-icon {
|
.stat-icon {
|
||||||
width: 26rpx;
|
width: 26rpx;
|
||||||
height: 26rpx;
|
height: 26rpx;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ff4d4f'%3E%3Cpath d='M19.48 13.03c-.02-.19-.13-.36-.29-.46-.17-.11-.38-.13-.56-.05-.98.42-2.12.3-2.92-.31-1.07-.81-1.55-2.22-1.28-3.75.12-.66.1-1.32-.06-1.97-.24-.95-1.1-1.6-2.09-1.58-1 .02-1.83.74-2 1.72-.25 1.55-.95 2.94-2 4.02-1.38 1.41-2.05 3.39-1.85 5.4.2 2.05 1.42 3.86 3.25 4.82 1.37.72 2.94 1.01 4.5.83 2.74-.32 5.06-2.28 5.86-4.95.23-.75.29-1.52.19-2.28-.06-.5-.21-.99-.41-1.45l-.34-.99zm-4.98 6.47c-1.37 0-2.5-1.13-2.5-2.5s1.13-2.5 2.5-2.5 2.5 1.13 2.5 2.5-1.13 2.5-2.5 2.5z'/%3E%3C/svg%3E");
|
|
||||||
background-size: cover;
|
|
||||||
margin-right: 12rpx;
|
margin-right: 12rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon-gap {
|
||||||
|
margin-left: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-text {
|
.stat-text {
|
||||||
@@ -457,8 +539,7 @@ onReachBottom(() => {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-text.margin-left {
|
.stat-text-participant {
|
||||||
margin-left: 32rpx;
|
|
||||||
color: #555;
|
color: #555;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
@@ -482,11 +563,13 @@ onReachBottom(() => {
|
|||||||
.ai-icon {
|
.ai-icon {
|
||||||
width: 32rpx;
|
width: 32rpx;
|
||||||
height: 32rpx;
|
height: 32rpx;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%234d44f1'%3E%3Cpath d='M12 2l2.4 5.6L20 10l-5.6 2.4L12 18l-2.4-5.6L4 10l5.6-2.4L12 2zm0 0'/%3E%3C/svg%3E");
|
|
||||||
background-size: cover;
|
|
||||||
margin-right: 12rpx;
|
margin-right: 12rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-icon-img {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.ai-title {
|
.ai-title {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
<view class="content-wrap">
|
<view class="content-wrap">
|
||||||
<view class="topic-editor card-block">
|
<view class="topic-editor card-block">
|
||||||
|
<view class="editor-badge">创建新话题</view>
|
||||||
<textarea
|
<textarea
|
||||||
v-model="formData.title"
|
v-model="formData.title"
|
||||||
class="topic-input"
|
class="topic-input"
|
||||||
@@ -24,14 +25,7 @@
|
|||||||
placeholder-class="topic-placeholder"
|
placeholder-class="topic-placeholder"
|
||||||
auto-height
|
auto-height
|
||||||
/>
|
/>
|
||||||
<!-- <textarea
|
<text class="topic-helper">标题越明确,越容易吸引大家参与评分和评论</text>
|
||||||
v-model="formData.description"
|
|
||||||
class="desc-input"
|
|
||||||
maxlength="120"
|
|
||||||
placeholder="简单介绍一下这个评分话题..."
|
|
||||||
placeholder-class="desc-placeholder"
|
|
||||||
auto-height
|
|
||||||
/> -->
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- <view class="cover-card" :class="{ 'has-cover': formData.coverUrl }" @tap="chooseCover">
|
<!-- <view class="cover-card" :class="{ 'has-cover': formData.coverUrl }" @tap="chooseCover">
|
||||||
@@ -47,20 +41,29 @@
|
|||||||
</view>
|
</view>
|
||||||
</view> -->
|
</view> -->
|
||||||
|
|
||||||
<view class="category-group">
|
<view class="category-group card-block">
|
||||||
<view
|
<view class="section-meta">
|
||||||
v-for="category in categories"
|
<text class="category-label">话题分类</text>
|
||||||
:key="category.id"
|
<text class="section-helper">选择一个更贴近内容的圈层</text>
|
||||||
class="category-chip"
|
</view>
|
||||||
:class="{ active: formData.categoryId === category.id }"
|
<view class="category-list">
|
||||||
@tap="formData.categoryId = category.id"
|
<view
|
||||||
>
|
v-for="category in categories"
|
||||||
{{ category.title }}
|
:key="category.id"
|
||||||
|
class="category-chip"
|
||||||
|
:class="{ active: formData.categoryId === category.id }"
|
||||||
|
@tap="formData.categoryId = category.id"
|
||||||
|
>
|
||||||
|
{{ category.title }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<text class="section-title">评分对象</text>
|
<view class="section-title-wrap">
|
||||||
|
<text class="section-title">评分对象</text>
|
||||||
|
<text class="section-subtitle">至少保留 1 个,最多添加 20 个</text>
|
||||||
|
</view>
|
||||||
<text class="section-count">{{ participants.length }}/20</text>
|
<text class="section-count">{{ participants.length }}/20</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -101,17 +104,19 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="participant-actions">
|
<view class="participant-actions">
|
||||||
<!-- <text class="drag-icon">≡</text> -->
|
<!-- <text class="drag-icon">≡</text> -->
|
||||||
<text class="delete-icon" @tap="removeParticipant(item.id)">🗑</text>
|
<image class="delete-icon-img" src="/static/images/icon/remove.png" @tap="removeParticipant(item.id)" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="add-participant" @tap="addParticipant">
|
<view class="add-participant" @tap="addParticipant">
|
||||||
<text class="add-plus">+</text>
|
<view class="add-plus-wrap">
|
||||||
|
<text class="add-plus">+</text>
|
||||||
|
</view>
|
||||||
<text class="add-text">添加评分对象</text>
|
<text class="add-text">添加评分对象</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="settings-card card-block">
|
<!-- <view class="settings-card card-block">
|
||||||
<view class="setting-row">
|
<view class="setting-row">
|
||||||
<view class="setting-info">
|
<view class="setting-info">
|
||||||
<text class="setting-title">允许用户锐评</text>
|
<text class="setting-title">允许用户锐评</text>
|
||||||
@@ -147,17 +152,21 @@
|
|||||||
@change="formData.anonymous = $event.detail.value"
|
@change="formData.anonymous = $event.detail.value"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view> -->
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="footer-bar">
|
<view class="footer-bar">
|
||||||
<button class="submit-btn" @tap="handleSubmit">发布全民评分</button>
|
<button class="submit-btn" @tap="handleSubmit">发布夯拉评分</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 成功特效弹窗 -->
|
||||||
|
<SuccessPopup ref="successPopupRef" />
|
||||||
|
<LoginPopup ref="loginPopupRef" @logind="handleLoginSuccess" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref } from "vue";
|
import { computed, reactive, ref } from "vue";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
import { getStatusBarHeight } from "@/utils/system";
|
import { getStatusBarHeight } from "@/utils/system";
|
||||||
import { releaseTopic, fetchTopicCategories } from "@/api/topic";
|
import { releaseTopic, fetchTopicCategories } from "@/api/topic";
|
||||||
@@ -166,9 +175,16 @@ import {
|
|||||||
uploadImage,
|
uploadImage,
|
||||||
chooseAndUploadImage,
|
chooseAndUploadImage,
|
||||||
} from "@/utils/common.js";
|
} from "@/utils/common.js";
|
||||||
|
import SuccessPopup from "@/components/SuccessPopup/SuccessPopup.vue";
|
||||||
|
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
||||||
import { FILE_BASE_URL } from "@/utils/constants";
|
import { FILE_BASE_URL } from "@/utils/constants";
|
||||||
|
import { useUserStore } from "@/stores/user";
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const successPopupRef = ref(null);
|
||||||
|
const loginPopupRef = ref(null);
|
||||||
const statusBarHeight = ref(getStatusBarHeight() || 0);
|
const statusBarHeight = ref(getStatusBarHeight() || 0);
|
||||||
|
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
|
||||||
|
|
||||||
const categories = ref([]);
|
const categories = ref([]);
|
||||||
|
|
||||||
@@ -219,7 +235,21 @@ const goBack = () => {
|
|||||||
smartNavigateBack();
|
smartNavigateBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openLoginPopup = (message = "请先登录后再操作") => {
|
||||||
|
loginPopupRef.value?.open();
|
||||||
|
if (message) {
|
||||||
|
uni.showToast({
|
||||||
|
title: message,
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handlePreview = () => {
|
const handlePreview = () => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
openLoginPopup("请先登录后再预览");
|
||||||
|
return;
|
||||||
|
}
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "预览功能待接入",
|
title: "预览功能待接入",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
@@ -238,6 +268,10 @@ const chooseCover = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const chooseParticipantAvatar = async (index) => {
|
const chooseParticipantAvatar = async (index) => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
openLoginPopup("请先登录后再上传头像");
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const urls = await chooseAndUploadImage({ count: 1 });
|
const urls = await chooseAndUploadImage({ count: 1 });
|
||||||
if (urls && urls.length > 0) {
|
if (urls && urls.length > 0) {
|
||||||
@@ -294,6 +328,10 @@ const removeParticipant = (id) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
openLoginPopup("请先登录后再发起评分");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!formData.title.trim()) {
|
if (!formData.title.trim()) {
|
||||||
uni.showToast({ title: '请输入话题', icon: 'none' });
|
uni.showToast({ title: '请输入话题', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
@@ -322,13 +360,43 @@ const handleSubmit = async () => {
|
|||||||
};
|
};
|
||||||
await releaseTopic(payload);
|
await releaseTopic(payload);
|
||||||
|
|
||||||
uni.hideLoading();
|
// 记录发布事件
|
||||||
uni.showToast({
|
uni.$trackRecord({
|
||||||
title: "发布成功",
|
eventName: 'release_topic',
|
||||||
icon: "success",
|
eventType: 'release',
|
||||||
|
elementContent: `话题_${payload.title}`,
|
||||||
|
customParams: {
|
||||||
|
title: payload.title,
|
||||||
|
categoryId: payload.categoryId,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
smartNavigateBack({ delay: 1500 });
|
uni.hideLoading();
|
||||||
|
|
||||||
|
if (successPopupRef.value) {
|
||||||
|
successPopupRef.value.show({
|
||||||
|
emoji: '🎉',
|
||||||
|
label: '请耐心等待管理员审核'
|
||||||
|
}, 'release');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化表单内容
|
||||||
|
Object.assign(formData, {
|
||||||
|
title: "",
|
||||||
|
description: "",
|
||||||
|
coverUrl: "",
|
||||||
|
categoryId: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化评分对象列表为默认的一个空项,并且赋予唯一的 ID
|
||||||
|
participants.value = [
|
||||||
|
{ id: Date.now(), name: "", desc: "", avatar: "" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// 延迟跳转,等待弹窗展示完成
|
||||||
|
setTimeout(() => {
|
||||||
|
smartNavigateBack();
|
||||||
|
}, 2000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@@ -338,14 +406,23 @@ const handleSubmit = async () => {
|
|||||||
console.error("发布失败:", error);
|
console.error("发布失败:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLoginSuccess = () => {
|
||||||
|
if (categories.value.length === 0) {
|
||||||
|
loadCategories();
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-container {
|
.page-container {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #f8f9fc; /* 整体背景调亮 */
|
background:
|
||||||
|
radial-gradient(circle at top, rgba(111, 90, 255, 0.1), transparent 34%),
|
||||||
|
linear-gradient(180deg, #f7f8ff 0%, #f6f7fb 28%, #ffffff 100%);
|
||||||
padding-bottom: 180rpx;
|
padding-bottom: 180rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Segoe UI, Arial, Roboto, "PingFang SC", "miui", "Hiragino Sans GB", "Microsoft Yahei", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-bar {
|
.nav-bar {
|
||||||
@@ -354,8 +431,9 @@ const handleSubmit = async () => {
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
background: rgba(244, 245, 251, 0.96);
|
background: rgba(247, 248, 255, 0.82);
|
||||||
backdrop-filter: blur(18rpx);
|
backdrop-filter: blur(24rpx);
|
||||||
|
border-bottom: 1rpx solid rgba(133, 142, 194, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-content {
|
.nav-content {
|
||||||
@@ -363,7 +441,7 @@ const handleSubmit = async () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 24rpx;
|
padding: 0 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-side {
|
.nav-side {
|
||||||
@@ -385,61 +463,68 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
.nav-title {
|
.nav-title {
|
||||||
font-size: 34rpx;
|
font-size: 34rpx;
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
color: #171717;
|
color: #16161d;
|
||||||
|
letter-spacing: 1rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-text {
|
.preview-text {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #5f63ff;
|
color: #5c43f5;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrap {
|
.content-wrap {
|
||||||
padding: 24rpx;
|
padding: 28rpx 24rpx 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 统一的卡片风格 */
|
|
||||||
.card-block {
|
.card-block {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-radius: 36rpx; /* 增大圆角,与设计图更统一 */
|
border-radius: 34rpx;
|
||||||
box-shadow: 0 4rpx 20rpx rgba(72, 82, 130, 0.03); /* 柔和的阴影 */
|
box-shadow: 0 18rpx 40rpx rgba(84, 88, 132, 0.08);
|
||||||
margin-bottom: 32rpx; /* 增加块级间距 */
|
margin-bottom: 28rpx;
|
||||||
border: 2rpx solid #eef0f7; /* 统一的极淡边框 */
|
border: 1rpx solid rgba(222, 227, 246, 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-editor {
|
.topic-editor {
|
||||||
padding: 32rpx; /* 增加内边距 */
|
padding: 28rpx 28rpx 30rpx;
|
||||||
margin-bottom: 24rpx;
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(245, 246, 255, 0.92));
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-input,
|
.editor-badge {
|
||||||
.desc-input {
|
display: inline-flex;
|
||||||
width: 100%;
|
align-items: center;
|
||||||
min-height: 52rpx;
|
height: 44rpx;
|
||||||
color: #232323;
|
padding: 0 18rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
margin-bottom: 22rpx;
|
||||||
|
background: rgba(92, 67, 245, 0.08);
|
||||||
|
color: #5c43f5;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-input {
|
.topic-input {
|
||||||
font-size: 44rpx; /* 稍微减小标题字号,显得更精致 */
|
width: 100%;
|
||||||
font-weight: 800; /* 加粗 */
|
min-height: 76rpx;
|
||||||
margin-bottom: 16rpx; /* 增加行距 */
|
color: #191a22;
|
||||||
}
|
font-size: 48rpx;
|
||||||
|
font-weight: 800;
|
||||||
.desc-input {
|
line-height: 1.3;
|
||||||
font-size: 28rpx;
|
|
||||||
color: #666;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-placeholder {
|
.topic-placeholder {
|
||||||
font-size: 44rpx;
|
font-size: 48rpx;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: #c1c2d3;
|
color: #c6cadc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desc-placeholder {
|
.topic-helper {
|
||||||
font-size: 28rpx;
|
margin-top: 18rpx;
|
||||||
color: #b8b9c9;
|
display: block;
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #8a8fa7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cover-card {
|
.cover-card {
|
||||||
@@ -483,29 +568,53 @@ const handleSubmit = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.category-group {
|
.category-group {
|
||||||
|
padding: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-meta {
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1c1d24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-helper {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9196ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 16rpx;
|
gap: 16rpx;
|
||||||
margin-bottom: 24rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-chip {
|
.category-chip {
|
||||||
min-width: 88rpx;
|
min-width: 88rpx;
|
||||||
padding: 14rpx 32rpx; /* 增加内边距 */
|
padding: 16rpx 30rpx;
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
background: #e9ecf5; /* 背景调浅一些,更接近设计图 */
|
background: #f3f5fc;
|
||||||
color: #5c6176; /* 字体颜色调浅 */
|
color: #5f647d;
|
||||||
font-size: 28rpx; /* 字体调大 */
|
font-size: 28rpx;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: all 0.2s ease;
|
border: 1rpx solid transparent;
|
||||||
|
transition: all 0.22s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-chip.active {
|
.category-chip.active {
|
||||||
background: linear-gradient(135deg, #6c4af2, #883cf3); /* 更接近设计图的紫色渐变 */
|
background: linear-gradient(135deg, #5e43f4, #7d4dff);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
box-shadow: 0 8rpx 20rpx rgba(108, 74, 242, 0.3); /* 调整阴影颜色 */
|
border-color: rgba(116, 85, 255, 0.24);
|
||||||
|
box-shadow: 0 12rpx 26rpx rgba(101, 74, 245, 0.25);
|
||||||
|
transform: translateY(-2rpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-box {
|
.tag-box {
|
||||||
@@ -555,16 +664,27 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
.section-header {
|
.section-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 24rpx; /* 增加底部间距 */
|
margin-bottom: 20rpx;
|
||||||
padding: 0 8rpx; /* 标题与内容对齐 */
|
padding: 0 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 34rpx; /* 调整大小 */
|
font-size: 34rpx;
|
||||||
font-weight: 800; /* 加粗 */
|
font-weight: 800;
|
||||||
color: #171717;
|
color: #1b1c24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-subtitle {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9297ad;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-count {
|
.section-count {
|
||||||
@@ -572,10 +692,11 @@ const handleSubmit = async () => {
|
|||||||
height: 46rpx;
|
height: 46rpx;
|
||||||
line-height: 46rpx;
|
line-height: 46rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 14rpx;
|
border-radius: 999rpx;
|
||||||
background: #e6e9f7;
|
background: rgba(92, 67, 245, 0.1);
|
||||||
color: #5c6176;
|
color: #5c43f5;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.participant-list {
|
.participant-list {
|
||||||
@@ -587,11 +708,15 @@ const handleSubmit = async () => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 36rpx; /* 更圆润的边角 */
|
border-radius: 32rpx;
|
||||||
padding: 32rpx 28rpx; /* 增加内边距 */
|
padding: 28rpx 24rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
border: 2rpx solid #eef0f7; /* 增加细微的描边,更接近设计图质感 */
|
border: 1rpx solid #edf0fb;
|
||||||
box-shadow: 0 4rpx 20rpx rgba(72, 82, 130, 0.03); /* 阴影调弱 */
|
box-shadow: 0 12rpx 28rpx rgba(72, 82, 130, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.participant-card:active {
|
||||||
|
transform: scale(0.995);
|
||||||
}
|
}
|
||||||
|
|
||||||
.participant-main {
|
.participant-main {
|
||||||
@@ -602,21 +727,23 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
.participant-avatar-wrap {
|
.participant-avatar-wrap {
|
||||||
margin-right: 20rpx;
|
margin-right: 20rpx;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.participant-avatar {
|
.participant-avatar {
|
||||||
width: 92rpx;
|
width: 96rpx;
|
||||||
height: 92rpx;
|
height: 96rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #e8ebf7;
|
background: #eceffc;
|
||||||
|
box-shadow: 0 8rpx 18rpx rgba(95, 99, 255, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.participant-avatar.empty {
|
.participant-avatar.empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: 2rpx dashed #b5bad0; /* 加深虚线颜色 */
|
border: 2rpx dashed #c8cde1;
|
||||||
background: #f8f9fc; /* 空状态背景色 */
|
background: linear-gradient(180deg, #fafbff, #f3f5fc);
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-small {
|
.camera-small {
|
||||||
@@ -636,65 +763,64 @@ const handleSubmit = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.participant-name {
|
.participant-name {
|
||||||
height: 42rpx;
|
height: 44rpx;
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #202020;
|
color: #1c1d24;
|
||||||
margin-bottom: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.participant-desc {
|
|
||||||
height: 38rpx;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #676c7f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.participant-placeholder {
|
.participant-placeholder {
|
||||||
color: #b6b9c7;
|
color: #b7bbcd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.participant-actions {
|
.participant-actions {
|
||||||
width: 56rpx;
|
width: 72rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 16rpx;
|
|
||||||
margin-left: 16rpx;
|
margin-left: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drag-icon {
|
.delete-icon-img {
|
||||||
font-size: 34rpx;
|
width: 40rpx;
|
||||||
color: #b2b4c5;
|
height: 40rpx;
|
||||||
line-height: 1;
|
padding: 12rpx;
|
||||||
}
|
border-radius: 50%;
|
||||||
|
background: #fff5f5;
|
||||||
.delete-icon {
|
|
||||||
font-size: 32rpx; /* 图标放大 */
|
|
||||||
color: #ff6b6b; /* 明确的删除红色 */
|
|
||||||
line-height: 1;
|
|
||||||
padding: 8rpx; /* 增加点击区域 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-participant {
|
.add-participant {
|
||||||
height: 120rpx; /* 增加高度 */
|
height: 120rpx;
|
||||||
border: 2rpx dashed #c0c4d6; /* 边框颜色更明显 */
|
border: 2rpx dashed #cfd5e8;
|
||||||
border-radius: 36rpx; /* 圆角一致 */
|
border-radius: 32rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #888d9e; /* 字体颜色微调 */
|
color: #737993;
|
||||||
gap: 16rpx; /* 间距变大 */
|
gap: 16rpx;
|
||||||
background: #f8f9fc; /* 添加底色 */
|
background: rgba(255, 255, 255, 0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-plus-wrap {
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(92, 67, 245, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-plus {
|
.add-plus {
|
||||||
font-size: 38rpx;
|
font-size: 34rpx;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
color: #5c43f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-text {
|
.add-text {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-card {
|
.settings-card {
|
||||||
@@ -738,19 +864,20 @@ const handleSubmit = async () => {
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
padding: 24rpx 24rpx calc(24rpx + env(safe-area-inset-bottom));
|
padding: 24rpx 24rpx calc(28rpx + env(safe-area-inset-bottom));
|
||||||
background: linear-gradient(180deg, rgba(244, 245, 251, 0) 0%, rgba(244, 245, 251, 0.98) 34%);
|
background: linear-gradient(180deg, rgba(247, 248, 255, 0) 0%, rgba(247, 248, 255, 0.94) 26%, rgba(247, 248, 255, 1) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
height: 100rpx;
|
height: 104rpx;
|
||||||
line-height: 100rpx;
|
line-height: 104rpx;
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
background: linear-gradient(135deg, #5c43f5, #7a2cf3); /* 更接近设计图的深紫色 */
|
background: linear-gradient(135deg, #5c43f5, #6f55ff 58%, #8b66ff);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 34rpx;
|
font-size: 34rpx;
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
box-shadow: 0 16rpx 32rpx rgba(100, 60, 240, 0.3); /* 加强底部按钮阴影 */
|
letter-spacing: 1rpx;
|
||||||
|
box-shadow: 0 18rpx 36rpx rgba(98, 69, 246, 0.34);
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-btn::after {
|
.submit-btn::after {
|
||||||
|
|||||||
BIN
static/images/icon/1.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
static/images/icon/2.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
static/images/icon/3.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
static/images/icon/fire.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
static/images/icon/group.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
static/images/icon/remove.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
static/images/icon/robot.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
static/images/logo.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
@@ -154,6 +154,15 @@ export const getShareToken = async (scene, targetId = "") => {
|
|||||||
return shareTokenRes?.shareToken || "";
|
return shareTokenRes?.shareToken || "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录用户行为事件
|
||||||
|
* @param {Object} event 事件对象,包含事件名称和参数
|
||||||
|
* eventName 事件名称,例如 'switchCategory'
|
||||||
|
* eventType 事件类型,例如 'click',可选
|
||||||
|
* elementId 事件元素 ID,例如 'category_123',可选
|
||||||
|
* elementContent 事件元素内容,例如 '分类1',可选
|
||||||
|
* customParams 自定义参数,例如 { categoryId: 123 },可选
|
||||||
|
*/
|
||||||
export const trackRecord = (event) => {
|
export const trackRecord = (event) => {
|
||||||
const pages = getCurrentPages();
|
const pages = getCurrentPages();
|
||||||
// 核心修复:在小程序刚启动或页面尚未压栈时,pages 可能为空数组
|
// 核心修复:在小程序刚启动或页面尚未压栈时,pages 可能为空数组
|
||||||
|
|||||||