fix: rating

This commit is contained in:
zzc
2026-06-08 19:08:13 +08:00
parent 03cb2edf5e
commit 36dbf05c11
2 changed files with 244 additions and 314 deletions

View File

@@ -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",

View File

@@ -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,84 +14,72 @@
: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">
<view class="avatar-ring">
<image :src="userInfo.avatarUrl" class="avatar" mode="aspectFill" /> <image :src="userInfo.avatarUrl" class="avatar" mode="aspectFill" />
<!-- <view class="red-badge"><text class="fire">🔥</text></view> -->
</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 class="card-arrow"></view>
</view>
<!-- VIP Banner -->
<view v-if="!isIos" class="vip-banner" @tap="navTo('vip')">
<view class="vip-left">
<view class="vip-icon-box">
<uni-icons type="vip-filled" size="32" color="#5d4037" />
</view>
<view class="vip-content">
<view class="vip-title-row">
<text class="vip-title">祥瑞会员中心</text>
<view class="diamond-tag">DIAMOND</view>
</view>
<text class="vip-subtitle">开通会员解锁全部特权</text>
</view>
</view>
<view class="vip-right">
<text class="open-text">立即开通</text>
<text class="arrow"></text>
</view> </view>
</view> </view>
<!-- My Creations --> <text class="nickname" @tap="handleUserClick">{{ userInfo.nickName }}</text>
<view class="section-title">我的xx</view>
<view class="menu-group"> <view class="tags-container" v-if="isLoggedIn && userStats.label && userStats.label.length">
<view class="menu-item" @tap="navTo('/pages/mine/topics')"> <text class="sparkle-icon"></text>
<view class="icon-box pink-bg"><text></text></view> <text class="tags-text">{{ userStats.label.join(' | ') }}</text>
<text class="menu-text">我参与的话题</text> </view>
<text class="arrow"></text> <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 class="stat-divider"></view>
<view class="stat-item">
<text class="stat-num">{{ isLoggedIn ? userStats.comment : '-' }}</text>
<text class="stat-label">评论数</text>
</view>
<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>
<!-- Other Actions --> <view class="action-buttons">
<view class="menu-group mt-30"> <button class="btn-primary" @tap="navTo('profile')">编辑资料</button>
<button class="menu-item share-btn" open-type="share"> <button class="btn-secondary" open-type="share">
<view class="icon-left"><text class="share-icon">🔗</text></view> <text class="share-icon-btn"></text> 分享主页
<text class="menu-text">分享给好友</text>
<text class="arrow"></text>
</button> </button>
</view>
</view>
<!-- Menu Group 1 -->
<view class="menu-card">
<view class="menu-item" @tap="navTo('/pages/mine/topics')">
<view class="icon-left"><text class="icon-bookmark"></text></view>
<text class="menu-text">我的参与</text>
<text class="menu-value" v-if="isLoggedIn">{{ userStats.participant }}</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>
<!-- Menu Group 2 -->
<view class="menu-card">
<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>
@@ -105,9 +93,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, 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 +107,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 || "点击登录",
@@ -129,22 +117,47 @@ const userInfo = computed(() => ({
const isLoggedIn = computed(() => !!userStore.userInfo.nickName); const isLoggedIn = computed(() => !!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) 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" });
return { return {
title: "分享", title: "分享",
path: "/pages/index/index?shareToken=" + shareToken, path: "/pages/index/index?shareToken=" + shareToken,
imageUrl: imageUrl: "https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png",
"https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png",
}; };
}); });
@@ -152,7 +165,6 @@ const handleUserClick = () => {
if (!isLoggedIn.value) { if (!isLoggedIn.value) {
loginPopupRef.value.open(); loginPopupRef.value.open();
} else { } else {
// Navigate to profile details
uni.navigateTo({ uni.navigateTo({
url: "/pages/mine/profile", url: "/pages/mine/profile",
}); });
@@ -160,7 +172,7 @@ const handleUserClick = () => {
}; };
const handleLogind = async () => { const handleLogind = async () => {
// Logic after successful login if needed loadUserStats();
}; };
const navTo = (page) => { const navTo = (page) => {
@@ -171,36 +183,15 @@ const navTo = (page) => {
} }
if (page === "profile") { if (page === "profile") {
uni.navigateTo({ uni.navigateTo({ url: "/pages/mine/profile" });
url: "/pages/mine/profile",
});
return; return;
} }
if (page === "avatar") {
uni.navigateTo({
url: "/pages/mine/avatar",
});
return;
}
if (page === "feedback") { if (page === "feedback") {
uni.navigateTo({ uni.navigateTo({ url: "/pages/feedback/index" });
url: "/pages/feedback/index",
});
return;
}
if (page === "help") {
uni.navigateTo({
url: "/pages/mine/help",
});
return;
}
if (page === "vip") {
uni.navigateTo({
url: "/pages/mine/vip",
});
return; return;
} }
if (page === "/pages/mine/topics") { if (page === "/pages/mine/topics") {
uni.navigateTo({ url: page }); uni.navigateTo({ url: page });
return; return;
@@ -213,7 +204,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;
} }
@@ -226,297 +217,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>