添加红点修复

This commit is contained in:
张成
2025-11-21 14:50:42 +08:00
parent 27264011a1
commit 1c8b6163bb
17 changed files with 485 additions and 1202 deletions

View File

@@ -0,0 +1,299 @@
@use "~@/scss/images.scss" as img;
.messageContainer {
width: 100%;
height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
background: #FAFAFA;
// 分类标签区
.categoryTabs {
display: flex;
align-items: stretch;
justify-content: space-between;
gap: 20px;
padding: 6px 24px;
box-sizing: border-box;
height: 124px;
.tabItem {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 161px;
.tabIconWrapper {
position: relative;
width: 56px;
height: 56px;
.tabIcon {
width: 56px;
height: 56px;
border-radius: 56px;
transition: all 0.3s;
}
.badge {
position: absolute;
top: -4px;
right: -8px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 4.5px;
min-width: 20px;
height: 20px;
background: #FF2541;
border-radius: 10px;
font-family: "PingFang SC";
font-weight: 600;
font-size: 10px;
line-height: 20px;
color: #ffffff;
text-align: center;
box-sizing: border-box;
}
}
.tabIcon {
width: 56px;
height: 56px;
border-radius: 56px;
transition: all 0.3s;
}
.tabText {
font-family: "PingFang SC";
font-weight: 600;
font-size: 16px;
line-height: 1.5;
color: #000000;
}
&.active {
.tabIcon {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
transform: scale(1.05);
}
}
&:active {
.tabIcon {
transform: scale(0.95);
}
}
}
}
// 消息列表
// .messageList {
// flex: 1;
// overflow: hidden;
// box-sizing: border-box;
// // margin-bottom:100px;
// background-color: none !important;
// .messageListContent {
// display: flex;
// flex-direction: column;
// padding: 12px 12px 112px;
// gap: 8px;
// padding: 12px 15px;
// flex: 1;
// cursor: pointer;
// transition: all 0.2s;
// }
// }
// 消息滚动区域
.messageScroll {
flex: 1;
overflow: hidden;
.messageCards {
padding: 12px;
display: flex;
flex-direction: column;
gap: 8px;
padding-bottom: 120px;
}
// 系统消息卡片
.messageCard {
background: #ffffff;
border: 0.5px solid rgba(0, 0, 0, 0.08);
border-radius: 20px;
// box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
padding: 0 0 12px;
transition: all 0.2s;
cursor: pointer;
&:active {
transform: scale(0.98);
opacity: 0.95;
}
.cardTitleRow {
padding: 12px 15px 0;
.cardTitle {
font-family: "PingFang SC";
font-weight: 600;
font-size: 16px;
line-height: 1.5;
color: #000000;
}
}
.cardTimeRow {
padding: 4px 15px 0;
display: flex;
align-items: center;
gap: 2px;
.cardTime {
font-family: "PingFang SC";
font-weight: 400;
font-size: 12px;
line-height: 1.5;
color: rgba(60, 60, 67, 0.6);
}
}
.cardContentRow {
padding: 8px 15px 0;
display: flex;
align-items: stretch;
gap: 2px;
.cardContent {
font-family: "PingFang SC";
font-weight: 400;
font-size: 14px;
line-height: 1.43;
color: rgba(0, 0, 0, 0.7);
flex: 1;
}
}
.cardFooter {
padding: 12px 15px 0;
.footerDivider {
height: 0.5px;
background: rgba(0, 0, 0, 0.08);
margin-bottom: 12px;
width: 100%;
}
.footerAction {
display: flex;
justify-content: space-between;
align-items: center;
.actionText {
font-family: "PingFang SC";
font-weight: 600;
font-size: 14px;
line-height: 1.43;
color: rgba(0, 0, 0, 0.85);
}
.actionArrow {
position: relative;
.img {
position: absolute;
left: -16px;
top: -9px;
width: 14px;
height: 14px;
}
}
}
}
}
// 到底了提示
.bottomTip {
display: flex;
justify-content: center;
align-items: center;
padding: 24px 0 12px;
.tipText {
font-family: "PingFang SC";
font-weight: 400;
font-size: 14px;
line-height: 1.71;
color: rgba(0, 0, 0, 0.35);
}
}
}
// 悬浮新建消息按钮
.floatingButton {
position: fixed;
right: 12px;
bottom: 132px;
width: 60px;
height: 60px;
border-radius: 50%;
background: radial-gradient(circle at 27% 8%,
rgba(189, 255, 74, 1) 17%,
rgba(149, 242, 62, 1) 54%,
rgba(50, 216, 56, 1) 100%);
border: 2px solid rgba(0, 0, 0, 0.06);
box-shadow: 0px 4px 48px 0px rgba(0, 0, 0, 0.08);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 99;
transition: all 0.2s;
&:active {
transform: scale(0.92);
}
.buttonIcon {
width: 36px;
height: 36px;
position: relative;
// 加号图标 - 竖线
&::before {
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 4px;
height: 25px;
background: #ffffff;
border-radius: 2px;
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
}
// 加号图标 - 横线
&::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 25px;
height: 4px;
background: #ffffff;
border-radius: 2px;
box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.2);
}
}
}
}

View File

@@ -7,7 +7,7 @@ import Taro from "@tarojs/taro";
import { useGlobalState } from "@/store/global";
import { navigateTo } from "@/utils/navigation";
import { useReddotInfo, useFetchReddotInfo } from "@/store/messageStore";
import "@/other_pages/message/index.scss";
import styles from "./MessagePageContent.module.scss";
interface MessageItem {
id: string;
@@ -127,49 +127,49 @@ const MessagePageContent = () => {
};
return (
<View className="message-container" style={{ paddingTop: `${totalHeight}px` }}>
<View className="category-tabs">
<View className={styles.messageContainer} style={{ paddingTop: `${totalHeight}px` }}>
<View className={styles.categoryTabs}>
<View
className={`tab-item ${activeTab === "comment" ? "active" : ""}`}
className={`${styles.tabItem} ${activeTab === "comment" ? styles.active : ""}`}
onClick={() => handleTabClick("comment")}
>
<View className="tab-icon-wrapper">
<View className={styles.tabIconWrapper}>
<Image
className="tab-icon"
className={styles.tabIcon}
src={require('@/static/message/comment-icon.svg')}
mode="aspectFit"
/>
{(reddotInfo?.comment_unread_count || 0) > 0 && (
<View className="badge">
{(reddotInfo?.comment_unread_count || 0) > 99 ? '99+' : reddotInfo?.comment_unread_count}
<View className={styles.badge}>
{`+${(reddotInfo?.comment_unread_count || 0) > 99 ? 99 : reddotInfo?.comment_unread_count}`}
</View>
)}
</View>
<Text className="tab-text"></Text>
<Text className={styles.tabText}></Text>
</View>
<View
className={`tab-item ${activeTab === "follow" ? "active" : ""}`}
className={`${styles.tabItem} ${activeTab === "follow" ? styles.active : ""}`}
onClick={() => handleTabClick("follow")}
>
<View className="tab-icon-wrapper">
<View className={styles.tabIconWrapper}>
<Image
className="tab-icon"
className={styles.tabIcon}
src={require('@/static/message/follow-icon.svg')}
mode="aspectFit"
/>
{(reddotInfo?.follow_unread_count || 0) > 0 && (
<View className="badge">
{(reddotInfo?.follow_unread_count || 0) > 99 ? '99+' : reddotInfo?.follow_unread_count}
<View className={styles.badge}>
{`+${(reddotInfo?.follow_unread_count || 0) > 99 ? 99 : reddotInfo?.follow_unread_count}`}
</View>
)}
</View>
<Text className="tab-text"></Text>
<Text className={styles.tabText}></Text>
</View>
</View>
<ScrollView
scrollY
className="message-scroll"
className={styles.messageScroll}
scrollWithAnimation
enhanced
showScrollbar={false}
@@ -180,32 +180,32 @@ const MessagePageContent = () => {
onRefresherRefresh={handleRefresh}
>
{filteredMessages.length > 0 ? (
<View className="message-cards">
<View className={styles.messageCards}>
{filteredMessages.map((message) => (
<View className="message-card" key={message.id} onClick={() => handleViewDetail(message)}>
<View className="card-title-row">
<Text className="card-title">{message.title}</Text>
<View className={styles.messageCard} key={message.id} onClick={() => handleViewDetail(message)}>
<View className={styles.cardTitleRow}>
<Text className={styles.cardTitle}>{message.title}</Text>
</View>
<View className="card-time-row">
<Text className="card-time">{formatRelativeTime(message.create_time)}</Text>
<View className={styles.cardTimeRow}>
<Text className={styles.cardTime}>{formatRelativeTime(message.create_time)}</Text>
</View>
<View className="card-content-row">
<Text className="card-content">{message.content}</Text>
<View className={styles.cardContentRow}>
<Text className={styles.cardContent}>{message.content}</Text>
</View>
<View className="card-footer">
<View className="footer-divider"></View>
<View className="footer-action">
<Text className="action-text"></Text>
<View className="action-arrow">
<Image className="img" src={require('@/static/message/ar-right.svg')} ></Image>
<View className={styles.cardFooter}>
<View className={styles.footerDivider}></View>
<View className={styles.footerAction}>
<Text className={styles.actionText}></Text>
<View className={styles.actionArrow}>
<Image className={styles.img} src={require('@/static/message/ar-right.svg')} ></Image>
</View>
</View>
</View>
</View>
))}
{filteredMessages.length > 0 && (
<View className="bottom-tip">
<Text className="tip-text"></Text>
<View className={styles.bottomTip}>
<Text className={styles.tipText}></Text>
</View>
)}
</View>

View File

@@ -0,0 +1,393 @@
@use "../../scss/common.scss" as *;
// 背景渐变过渡动画
@keyframes backgroundGradient {
0% {
background: #ffffff;
}
100% {
background: radial-gradient(
circle at 50% 0,
/* 光晕圆心在顶部中间 */ rgba(191, 255, 239, 0.9) 0px,
/* 中间更深的浅蓝 */ rgba(191, 255, 239, 0.5) 200px,
/* 100px 处开始淡化 */ #fafafa 300px,
/* 到 200px 变成白色 */ #fafafa 100% /* 200px 以下全白 */
);
}
}
// 个人页面样式
.myselfPage {
height: 100vh;
position: relative;
overflow-y: auto;
box-sizing: border-box;
background-color: #fafafa;
&::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
color: transparent;
}
}
// MyselfPageContent 组件使用的类名
.myselfPageContentMain {
animation: backgroundGradient 0s ease-in-out forwards;
z-index: 5;
flex: 1;
margin-top: 0;
box-sizing: border-box;
padding-bottom: 15px;
// padding-top 由内联样式控制
// 用户信息区域
.userInfoSection {
display: flex;
flex-direction: column;
gap: 16px;
padding: 0 15px;
// 加载状态
.loadingContainer {
display: flex;
justify-content: center;
align-items: center;
padding: 40px 0;
.loadingText {
@include text-style(16px, 400, 1.4em);
color: $color-primary-lightest;
}
}
// 统计数据
.statsSection {
display: flex;
justify-content: flex-start;
align-items: center;
.statsContainer {
display: flex;
align-items: center;
gap: 20px;
.statItem {
display: flex;
flex-direction: column;
align-items: center;
.statNumber {
@include text-medium;
}
.statLabel {
@include text-small;
}
}
}
}
// 标签和简介
.tagsBioSection {
display: flex;
flex-direction: column;
gap: 10px;
.tagsContainer {
display: flex;
gap: 8px;
flex-wrap: wrap;
.tagItem {
@include tag-base;
.tagIcon {
width: 12px;
height: 12px;
}
.tagText {
@include text-tag;
}
}
}
.bioText {
@include text-body;
white-space: pre-line;
}
}
// 球局订单和收藏功能
.quickActionsSection {
margin-bottom: 16px;
.actionCard {
@include card-base;
border-radius: 12px;
display: flex;
align-items: center;
overflow: hidden;
height: 48px;
.actionContent {
height: 100%;
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 8px;
cursor: pointer;
transition: background-color 0.3s ease;
.actionIcon {
width: 20px;
height: 20px;
}
.actionText {
@include text-style(15px, 600, 1.4em);
color: $color-primary;
}
}
.actionDivider {
width: 1px;
height: 16px;
background: $color-primary-lightest-5;
}
}
}
}
.testEntryCardBox {
padding: 0 15px;
}
// 球局类型标签页
.gameTabsSection {
margin-bottom: 0;
.tabContainer {
display: flex;
gap: 16px;
padding: 12px 15px;
.tabItem {
padding: 12px 0;
cursor: pointer;
transition: all 0.3s ease;
.tabText {
@include text-primary;
transition: color 0.3s ease;
}
&.active {
.tabText {
color: $color-primary;
}
}
&:not(.active) {
.tabText {
color: $color-primary-lightest-2;
}
}
}
}
}
// 球局列表区域
.gameListSection {
.dateHeader {
display: flex;
align-items: center;
gap: 4px;
padding: 10px 15px;
margin-bottom: 16px;
.dateText {
@include text-style(14px, 600, 1.4em, 2.71%);
color: $color-primary-light;
}
.separator {
@include text-style(18px, 400, 1.4em, 2.11%);
color: $color-primary-lightest;
}
.weekdayText {
@include text-style(14px, 600, 1.4em, 2.71%);
color: $color-primary-light;
}
}
// 球局卡片
.gameCards {
display: flex;
flex-direction: column;
gap: 5px;
padding: 0 5px 15px;
.gameCard {
@include card-base;
padding: 0 0 12px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
&:active {
transform: scale(0.98);
}
// 球局标题和类型
.gameHeader {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 15px 0;
.gameTitle {
@include text-style(16px, 600, 1.5em);
color: $color-primary;
}
.gameTypeIcon {
width: 16px;
height: 16px;
.typeIcon {
width: 100%;
height: 100%;
}
}
}
// 球局时间
.gameTime {
padding: 6px 15px 0;
.timeText {
@include text-caption;
}
}
// 球局地点和类型
.gameLocation {
display: flex;
align-items: center;
gap: 2px;
padding: 4px 15px 0;
.locationText,
.typeText,
.distanceText {
@include text-caption;
}
.separator {
@include text-style(14px, 400, 1.3em);
color: $color-text-tertiary;
}
}
// 球局图片
.gameImages {
position: absolute;
top: 11px;
right: 5px;
width: 100px;
height: 100px;
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.2);
.gameImage {
position: absolute;
width: 56.44px;
height: 56.44px;
border-radius: 9px;
border: 1.5px solid $color-white;
&:nth-child(1) {
top: 4.18px;
left: 19.18px;
}
&:nth-child(2) {
top: 26.5px;
left: 38px;
width: 61.86px;
height: 61.86px;
}
&:nth-child(3) {
top: 32.5px;
left: 0;
width: 62.04px;
height: 62.04px;
}
}
}
// 球局信息标签
.gameTags {
display: flex;
flex-direction: row;
gap: 6px;
padding: 8px 15px 0;
.participantsInfo {
display: flex;
gap: 4px;
.avatars {
display: flex;
align-items: center;
gap: -8px;
.participantAvatar {
@include avatar-base(20px);
border: 1px solid $color-white;
}
}
.participantsCount {
@include tag-base;
padding: 6px;
.countText {
@include text-tag;
}
}
}
.gameInfoTags {
display: flex;
gap: 4px;
.infoTag {
@include tag-base;
.tagText {
@include text-tag;
}
}
}
}
}
}
}
.endedGameText {
font-family: "PingFang SC";
font-weight: 600;
font-size: 20px;
line-height: 1.4em;
letter-spacing: 1.9%;
color: rgba(0, 0, 0, 0.85);
transition: color 0.3s ease;
padding: 24px 15px;
}
}

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useCallback } from "react";
import { View, Text, Image, ScrollView } from "@tarojs/components";
import Taro from "@tarojs/taro";
import "@/user_pages/myself/index.scss";
import styles from "./MyselfPageContent.module.scss";
import { UserInfoCard } from "@/components/UserInfo/index";
import { UserService } from "@/services/userService";
import ListContainer from "@/container/listContainer";
@@ -139,12 +139,12 @@ const MyselfPageContent: React.FC = () => {
};
return (
<View className="myself_page">
<View className={styles.myselfPage}>
<View
className="myself_page_content_main"
className={styles.myselfPageContentMain}
style={{ paddingTop: `${totalHeight}px` }}
>
<View className="user_info_section">
<View className={styles.userInfoSection}>
<UserInfoCard
editable={is_current_user}
user_info={user_info}
@@ -153,51 +153,51 @@ const MyselfPageContent: React.FC = () => {
on_follow={handle_follow}
onTab={handleOnTab}
/>
<View className="quick_actions_section">
<View className="action_card">
<View className="action_content" onClick={handle_game_orders}>
<View className={styles.quickActionsSection}>
<View className={styles.actionCard}>
<View className={styles.actionContent} onClick={handle_game_orders}>
<Image
className="action_icon"
className={styles.actionIcon}
src={require("@/static/userInfo/order_btn.svg")}
/>
<Text className="action_text"></Text>
<Text className={styles.actionText}></Text>
</View>
<View className="action_divider"></View>
<View className="action_content" onClick={handle_wallet}>
<View className={styles.actionDivider}></View>
<View className={styles.actionContent} onClick={handle_wallet}>
<Image
className="action_icon"
className={styles.actionIcon}
src={require("@/static/userInfo/wallet.svg")}
/>
<Text className="action_text"></Text>
<Text className={styles.actionText}></Text>
</View>
</View>
</View>
</View>
<View className="test-entry-card-box">
<View className={styles.testEntryCardBox}>
<NTRPTestEntryCard type={EvaluateScene.user} />
</View>
<View className="game_tabs_section">
<View className="tab_container">
<View className={styles.gameTabsSection}>
<View className={styles.tabContainer}>
<View
className={`tab_item ${active_tab === "hosted" ? "active" : ""}`}
className={`${styles.tabItem} ${active_tab === "hosted" ? styles.active : ""}`}
onClick={() => setActiveTab("hosted")}
>
<Text className="tab_text"></Text>
<Text className={styles.tabText}></Text>
</View>
<View
className={`tab_item ${
active_tab === "participated" ? "active" : ""
className={`${styles.tabItem} ${
active_tab === "participated" ? styles.active : ""
}`}
onClick={() => setActiveTab("participated")}
>
<Text className="tab_text"></Text>
<Text className={styles.tabText}></Text>
</View>
</View>
</View>
<View className="game_list_section">
<View className={styles.gameListSection}>
<ScrollView scrollY>
<ListContainer
data={game_records}
@@ -218,8 +218,8 @@ const MyselfPageContent: React.FC = () => {
</ScrollView>
</View>
<View className="ended_game_text"></View>
<View className="game_list_section">
<View className={styles.endedGameText}></View>
<View className={styles.gameListSection}>
<ScrollView scrollY>
<ListContainer
data={ended_game_records}