添加程序选择

This commit is contained in:
张成
2025-11-23 00:24:31 +08:00
parent 17015c0cca
commit 8b3f6c5a3a
18 changed files with 1296 additions and 47 deletions

View File

@@ -4,7 +4,8 @@ import { useState, useRef } from "react";
import Bubble from "../Bubble";
import { Image } from "@tarojs/components";
import img from "../../config/images";
import {DistanceFilterProps} from '../../../types/list/types'
import {DistanceFilterProps} from '../../../types/list/types';
import { useListState } from "@/store/listStore";
@@ -15,6 +16,10 @@ const MenuComponent = (props: DistanceFilterProps) => {
const [iOpen, setIsOpen] = useState(false);
const itemRef = useRef(null);
// 从 store 获取当前城市信息
const { area } = useListState();
const currentCity = area?.at(-1) || ""; // 获取省份/城市名称
const handleChange = (name: string, value: string) => {
setIsChange(true);
onChange && onChange(name, value);
@@ -59,7 +64,7 @@ const MenuComponent = (props: DistanceFilterProps) => {
>
<div className={styles.positionWrap}>
<p className={styles.title}></p>
<p className={styles.cityName}></p>
<p className={styles.cityName}>{currentCity}</p>
</div>
<div className={styles.distanceWrap}>
<Bubble

View File

@@ -10,6 +10,7 @@ import Taro from "@tarojs/taro";
import dayjs from "dayjs";
import classnames from "classnames";
import CommentServices from "@/services/commentServices";
import messageService from "@/services/messageService";
import { delay } from "@/utils";
import type {
BaseComment,
@@ -308,6 +309,18 @@ export default forwardRef(function Comments(
await getComments(1);
if (message_id) {
scrollToComment();
// 标记评论已读
markCommentAsRead();
}
}
// 标记评论已读
async function markCommentAsRead() {
if (!message_id) return;
try {
await messageService.markAsRead('comment', [message_id]);
} catch (e) {
console.error("标记评论已读失败:", e);
}
}

View File

@@ -3,6 +3,7 @@ import { Menu } from "@nutui/nutui-react-taro";
import { Image, View } from "@tarojs/components";
import img from "@/config/images";
import Bubble from "../Bubble";
import { useListState } from "@/store/listStore";
import "./index.scss";
const DistanceQuickFilter = (props) => {
@@ -21,6 +22,10 @@ const DistanceQuickFilter = (props) => {
const [changePosition, setChangePosition] = useState<number[]>([]);
const [isMenuOpen, setIsMenuOpen] = useState(false);
// 从 store 获取当前城市信息
const { area } = useListState();
const currentCity = area?.at(-1) || ""; // 获取省份/城市名称
// 全城筛选显示的标题
const cityTitle = cityOptions.find((item) => item.value === cityValue)?.label;
@@ -69,13 +74,16 @@ const DistanceQuickFilter = (props) => {
>
<div className="positionWrap">
<p className="title"></p>
<p className="cityName"></p>
<p className="cityName">{currentCity}</p>
</div>
<div className="distanceWrap">
<Bubble
options={cityOptions}
value={cityValue}
onChange={(name, value) => handleChange(name, value, 0)}
onChange={(name, value) => {
const singleValue = Array.isArray(value) ? value[0] : value;
handleChange(name, singleValue, 0);
}}
layout="grid"
size="small"
columns={4}

View File

@@ -19,9 +19,7 @@
background-color: #fafafa !important;
z-index: 1100 !important;
box-sizing: border-box !important;
max-height: auto !important;
height: 380px !important;
max-height: 100vh !important;
}
.nut-menu-container-content {
@@ -30,7 +28,7 @@
padding-right: 0px !important;
padding-bottom: 0px !important;
background-color: transparent !important;
max-height: auto !important;
max-height: 100vh !important;
}
@@ -121,6 +119,7 @@
padding-top: 0;
display: flex;
flex-direction: column;
height: 280px;
.districtContent {
display: flex;

View File

@@ -3,6 +3,7 @@ import { Menu } from "@nutui/nutui-react-taro";
import { Image, View, ScrollView } from "@tarojs/components";
import img from "@/config/images";
import Bubble from "../Bubble";
import { useListState } from "@/store/listStore";
import "./index.scss";
const DistanceQuickFilterV2 = (props) => {
@@ -24,6 +25,10 @@ const DistanceQuickFilterV2 = (props) => {
const [changePosition, setChangePosition] = useState<number[]>([]);
const [isMenuOpen, setIsMenuOpen] = useState(false);
// 从 store 获取当前城市信息
const { area } = useListState();
const currentCity = area?.at(-1) || ""; // 获取省份/城市名称
// 全城筛选显示的标题 - 如果选择了行政区,显示行政区名称
const getCityTitle = () => {
if (districtValue) {
@@ -56,6 +61,17 @@ const DistanceQuickFilterV2 = (props) => {
const newData = new Set([...preState, index]);
return Array.from(newData);
});
// 处理互斥关系:距离筛选和行政区完全互斥
if (name === cityName) {
// 选择了距离筛选(包括"全城"),清空行政区选择
onChange && onChange(districtName, "");
} else if (name === districtName) {
// 选择了行政区,将距离重置为"全城"(空字符串)
onChange && onChange(cityName, "");
}
// 触发当前选择的变化
onChange && onChange(name, value);
// 控制隐藏
@@ -82,7 +98,7 @@ const DistanceQuickFilterV2 = (props) => {
>
<div className="positionWrap">
<p className="title"></p>
<p className="cityName"></p>
<p className="cityName">{currentCity}</p>
</div>
<div className="distanceWrap">
<Bubble

View File

@@ -0,0 +1,169 @@
# GuideBar 组件使用说明
## 概述
`GuideBar` 组件的显示/隐藏状态已经集中管理在 `global store` 中,方便各个组件调用。
## 在组件中使用
### 1. 基础用法:获取状态
```tsx
import { useGlobalState } from "@/store/global";
function YourComponent() {
const { showGuideBar, guideBarZIndex } = useGlobalState();
return (
<View>
{showGuideBar ? <Text>GuideBar </Text> : <Text>GuideBar </Text>}
</View>
);
}
```
### 2. 控制 GuideBar 显示/隐藏
```tsx
import { useGlobalState } from "@/store/global";
function YourComponent() {
const { setShowGuideBar, toggleGuideBar } = useGlobalState();
const handleHideGuideBar = () => {
setShowGuideBar(false); // 隐藏 GuideBar
};
const handleShowGuideBar = () => {
setShowGuideBar(true); // 显示 GuideBar
};
const handleToggle = () => {
toggleGuideBar(); // 切换显示/隐藏状态
};
return (
<View>
<Button onClick={handleHideGuideBar}></Button>
<Button onClick={handleShowGuideBar}></Button>
<Button onClick={handleToggle}></Button>
</View>
);
}
```
### 3. 控制 GuideBar z-index
```tsx
import { useGlobalState } from "@/store/global";
function YourComponent() {
const { setGuideBarZIndex } = useGlobalState();
const handleOpenModal = () => {
// 打开弹窗时,降低 GuideBar 层级
setGuideBarZIndex('low');
};
const handleCloseModal = () => {
// 关闭弹窗时,恢复 GuideBar 层级
setGuideBarZIndex('high');
};
return (
<View>
<Button onClick={handleOpenModal}></Button>
</View>
);
}
```
### 4. 完整示例:视频播放器
```tsx
import { useGlobalState } from "@/store/global";
import { useEffect } from "react";
function VideoPlayer() {
const { setShowGuideBar } = useGlobalState();
// 进入全屏时隐藏 GuideBar
const handleFullscreen = () => {
setShowGuideBar(false);
};
// 退出全屏时显示 GuideBar
const handleExitFullscreen = () => {
setShowGuideBar(true);
};
// 组件卸载时恢复 GuideBar
useEffect(() => {
return () => {
setShowGuideBar(true);
};
}, [setShowGuideBar]);
return (
<View>
<Video onFullscreenChange={handleFullscreen} />
</View>
);
}
```
## API 说明
### 状态
| 属性 | 类型 | 说明 |
|------|------|------|
| `showGuideBar` | `boolean` | GuideBar 是否显示 |
| `guideBarZIndex` | `'low' \| 'high'` | GuideBar 的层级,用于处理与弹窗的层级关系 |
### 方法
| 方法 | 参数 | 说明 |
|------|------|------|
| `setShowGuideBar` | `(show: boolean) => void` | 设置 GuideBar 显示/隐藏 |
| `setGuideBarZIndex` | `(zIndex: 'low' \| 'high') => void` | 设置 GuideBar z-index 层级 |
| `toggleGuideBar` | `() => void` | 切换 GuideBar 显示/隐藏状态 |
## 使用场景
### 1. 全屏播放/浏览
当用户进入全屏模式(如视频播放、图片预览)时,应该隐藏 GuideBar
```tsx
const handleEnterFullscreen = () => {
setShowGuideBar(false);
};
```
### 2. 弹窗/遮罩层
当页面显示弹窗或遮罩层时,需要调整 GuideBar 的层级:
```tsx
const handleShowModal = () => {
setGuideBarZIndex('low'); // 让弹窗在 GuideBar 之上
};
```
### 3. 沉浸式阅读
在某些需要沉浸式体验的页面(如长文阅读、详情页滚动):
```tsx
const handleScroll = (e) => {
if (e.detail.scrollTop > 200) {
setShowGuideBar(false); // 滚动到一定距离后隐藏
} else {
setShowGuideBar(true);
}
};
```
## 注意事项
1. **恢复状态**:在组件卸载时,记得恢复 GuideBar 的状态,避免影响其他页面
2. **层级管理**`guideBarZIndex` 通常由主页面自动管理,除非有特殊需求,否则不建议手动设置
3. **性能优化**:频繁切换显示/隐藏可能影响性能,建议使用防抖或节流

View File

@@ -0,0 +1,203 @@
/**
* GuideBar Store 使用示例
*
* 这个文件展示了如何在不同场景下使用 GuideBar 的 store
*/
import { useGlobalState } from "@/store/global";
import { useEffect } from "react";
import { View, Button } from "@tarojs/components";
// ============ 示例 1: 视频播放器组件 ============
export function VideoPlayerExample() {
const { setShowGuideBar } = useGlobalState();
// 进入全屏
const handleFullscreen = () => {
setShowGuideBar(false);
};
// 退出全屏
const handleExitFullscreen = () => {
setShowGuideBar(true);
};
// 组件卸载时恢复
useEffect(() => {
return () => {
setShowGuideBar(true);
};
}, [setShowGuideBar]);
return (
<View>
<Button onClick={handleFullscreen}></Button>
<Button onClick={handleExitFullscreen}>退</Button>
</View>
);
}
// ============ 示例 2: 弹窗组件 ============
export function ModalExample() {
const { setGuideBarZIndex } = useGlobalState();
const handleShowModal = () => {
// 显示弹窗时,降低 GuideBar 层级
setGuideBarZIndex('low');
};
const handleCloseModal = () => {
// 关闭弹窗时,恢复 GuideBar 层级
setGuideBarZIndex('high');
};
return (
<View>
<Button onClick={handleShowModal}></Button>
</View>
);
}
// ============ 示例 3: 沉浸式滚动页面 ============
export function ImmersiveScrollExample() {
const { showGuideBar, setShowGuideBar } = useGlobalState();
const handleScroll = (e: any) => {
const scrollTop = e.detail.scrollTop;
// 向下滚动超过 200px 时隐藏 GuideBar
if (scrollTop > 200 && showGuideBar) {
setShowGuideBar(false);
}
// 向上滚动回到顶部时显示 GuideBar
else if (scrollTop <= 200 && !showGuideBar) {
setShowGuideBar(true);
}
};
return (
<View onScroll={handleScroll}>
{/* 页面内容 */}
</View>
);
}
// ============ 示例 4: 图片预览器 ============
export function ImagePreviewExample() {
const { setShowGuideBar } = useGlobalState();
const handlePreviewImage = () => {
// 预览图片时隐藏 GuideBar
setShowGuideBar(false);
};
const handleClosePreview = () => {
// 关闭预览时显示 GuideBar
setShowGuideBar(true);
};
useEffect(() => {
return () => {
// 组件卸载时恢复 GuideBar
setShowGuideBar(true);
};
}, [setShowGuideBar]);
return (
<View>
<Button onClick={handlePreviewImage}></Button>
</View>
);
}
// ============ 示例 5: 切换开关 ============
export function ToggleExample() {
const { showGuideBar, toggleGuideBar } = useGlobalState();
return (
<View>
<Button onClick={toggleGuideBar}>
{showGuideBar ? '隐藏底部导航' : '显示底部导航'}
</Button>
</View>
);
}
// ============ 示例 6: 游戏/互动页面 ============
export function GamePageExample() {
const { setShowGuideBar } = useGlobalState();
// 游戏开始时隐藏 GuideBar
const handleStartGame = () => {
setShowGuideBar(false);
};
// 游戏结束时显示 GuideBar
const handleGameOver = () => {
setShowGuideBar(true);
};
// 页面显示时隐藏,离开时恢复
useEffect(() => {
setShowGuideBar(false);
return () => {
setShowGuideBar(true);
};
}, [setShowGuideBar]);
return (
<View>
<Button onClick={handleStartGame}></Button>
<Button onClick={handleGameOver}></Button>
</View>
);
}
// ============ 示例 7: 详情页(根据内容动态显示) ============
export function DetailPageExample() {
const { setShowGuideBar } = useGlobalState();
useEffect(() => {
// 进入详情页时根据内容长度决定是否显示 GuideBar
const contentHeight = 1500; // 假设内容高度
const screenHeight = 800; // 假设屏幕高度
if (contentHeight > screenHeight * 2) {
// 内容很长时,初始隐藏 GuideBar提供更好的阅读体验
setShowGuideBar(false);
}
return () => {
setShowGuideBar(true);
};
}, [setShowGuideBar]);
return (
<View>
{/* 详情内容 */}
</View>
);
}
// ============ 示例 8: 表单页面(避免键盘遮挡) ============
export function FormPageExample() {
const { setShowGuideBar } = useGlobalState();
// 输入框聚焦时隐藏 GuideBar避免键盘遮挡
const handleFocus = () => {
setShowGuideBar(false);
};
// 输入框失焦时显示 GuideBar
const handleBlur = () => {
setShowGuideBar(true);
};
return (
<View>
<input onFocus={handleFocus} onBlur={handleBlur} />
</View>
);
}

View File

@@ -9,6 +9,7 @@ import Taro from "@tarojs/taro";
import "./index.scss";
import { getCurrentFullPath } from "@/utils";
import { CityPickerV2 as PopupPicker } from "@/components/Picker";
import LocationConfirmDialog from "@/components/LocationConfirmDialog";
// 城市缓存 key
const CITY_CACHE_KEY = "USER_SELECTED_CITY";
@@ -70,7 +71,7 @@ const HomeNavbar = (props: IProps) => {
title,
showTitle = false,
} = config || {};
const { getLocationLoading, statusNavbarHeightInfo } = useGlobalState();
const { getLocationLoading, statusNavbarHeightInfo, setShowGuideBar } = useGlobalState();
const {
gamesNum,
searchValue,
@@ -84,6 +85,11 @@ const HomeNavbar = (props: IProps) => {
statusNavbarHeightInfo || {};
const [cityPopupVisible, setCityPopupVisible] = useState(false);
const [locationDialogVisible, setLocationDialogVisible] = useState(false);
const [locationDialogData, setLocationDialogData] = useState<{
detectedProvince: string;
cachedCity: [string, string];
} | null>(null);
const hasShownLocationDialog = useRef(false); // 防止重复弹窗
// 监听城市选择器状态变化,通知父组件
@@ -122,28 +128,49 @@ const HomeNavbar = (props: IProps) => {
// 显示定位确认弹窗
const showLocationConfirmDialog = (detectedProvince: string, cachedCity: [string, string]) => {
(Taro as any).showModal({
title: "提示",
content: `检测到您当前位置在${detectedProvince},是否切换到${detectedProvince}`,
confirmText: "是",
cancelText: "否",
success: (res) => {
if (res.confirm) {
// 用户选择"是",切换到当前定位城市
const newArea: [string, string] = ["中国", detectedProvince];
updateArea(newArea);
// 更新缓存为新的定位信息(只有定位的才缓存)
(Taro as any).setStorageSync(CITY_CACHE_KEY, newArea);
console.log("切换到当前定位城市并更新缓存:", detectedProvince);
// 刷新数据
handleCityChangeWithoutCache();
} else {
// 用户选择"否",保持缓存的定位城市
console.log("保持缓存的定位城市:", cachedCity[1]);
}
}
});
console.log('[LocationDialog] 准备显示定位确认弹窗,隐藏 GuideBar');
setLocationDialogData({ detectedProvince, cachedCity });
setLocationDialogVisible(true);
// 显示弹窗时隐藏 GuideBar
setShowGuideBar(false);
console.log('[LocationDialog] setShowGuideBar(false) 已调用');
};
// 处理定位弹窗确认
const handleLocationDialogConfirm = () => {
if (!locationDialogData) return;
const { detectedProvince } = locationDialogData;
// 用户选择"是",切换到当前定位城市
const newArea: [string, string] = ["中国", detectedProvince];
updateArea(newArea);
// 更新缓存为新的定位信息(只有定位的才缓存)
(Taro as any).setStorageSync(CITY_CACHE_KEY, newArea);
console.log("切换到当前定位城市并更新缓存:", detectedProvince);
// 关闭弹窗
setLocationDialogVisible(false);
setLocationDialogData(null);
// 关闭弹窗时显示 GuideBar
setShowGuideBar(true);
// 刷新数据
handleCityChangeWithoutCache();
};
// 处理定位弹窗取消
const handleLocationDialogCancel = () => {
if (!locationDialogData) return;
const { cachedCity } = locationDialogData;
// 用户选择"否",保持缓存的定位城市
console.log("保持缓存的定位城市:", cachedCity[1]);
// 关闭弹窗
setLocationDialogVisible(false);
setLocationDialogData(null);
// 关闭弹窗时显示 GuideBar
setShowGuideBar(true);
};
// const currentAddress = city + district;
@@ -318,6 +345,16 @@ const HomeNavbar = (props: IProps) => {
onCityChange={handleCityChange}
/>
)}
{/* 定位确认弹窗 */}
{locationDialogData && (
<LocationConfirmDialog
visible={locationDialogVisible}
currentCity={locationDialogData.cachedCity[1]}
detectedCity={locationDialogData.detectedProvince}
onConfirm={handleLocationDialogConfirm}
onCancel={handleLocationDialogCancel}
/>
)}
</View>
);
};

View File

@@ -0,0 +1,122 @@
.locationDialogMask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
display: flex;
align-items: flex-end;
justify-content: center;
z-index: 10000;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
.locationDialogContainer {
width: 100%;
background: transparent;
animation: slideUp 0.3s ease-out;
padding-bottom: 20px;
background: #ffffff;
border-radius: 20px 20px 0px 0px;
height: 246px;
}
.locationDialogContent {
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
margin-left: 24px;
margin-right: 24px;
}
.locationDialogTitle {
padding-top: 48px;
padding-bottom: 32px;
margin-left: 32px;
margin-right: 32px;
font-family: "PingFang SC";
font-weight: 600;
font-size: 20px;
line-height: 28px;
text-align: center;
color: #000000;
}
.locationDialogButtons {
display: flex;
flex-direction: column;
padding-bottom: 20px;
width: calc(100% - 40px);
}
.locationDialogButton {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16px;
cursor: pointer;
transition: opacity 0.2s;
margin-top: 20px;
&:first-child {
margin-top: 0;
}
&.primary {
background: #000000;
height: 52px;
&:active {
opacity: 0.8;
}
}
&.secondary {
width: 100%;
background: #ffffff;
height: 52px;
border: 1px solid rgba(0, 0, 0, 0.1);
&:active {
background: rgba(0, 0, 0, 0.05);
}
}
}
.locationDialogButtonText {
font-family: "PingFang SC";
font-weight: 600;
font-size: 16px;
line-height: 22px;
text-align: center;
&.primary {
color: #ffffff;
}
&.secondary {
color: #000000;
}
}

View File

@@ -0,0 +1,57 @@
import React, { useEffect } from "react";
import { View, Text } from "@tarojs/components";
import { useGlobalState } from "@/store/global";
import "./index.scss";
interface LocationConfirmDialogProps {
visible: boolean;
currentCity: string; // 缓存的城市
detectedCity: string; // 定位的城市
onConfirm: () => void;
onCancel: () => void;
}
const LocationConfirmDialog: React.FC<LocationConfirmDialogProps> = ({
visible,
currentCity,
detectedCity,
onConfirm,
onCancel,
}) => {
const { setShowGuideBar } = useGlobalState();
// 当弹窗显示/隐藏时,控制 GuideBar
useEffect(() => {
console.log('[LocationConfirmDialog] visible 变化:', visible);
if (visible) {
console.log('[LocationConfirmDialog] 弹窗显示,隐藏 GuideBar');
setShowGuideBar(false);
} else {
console.log('[LocationConfirmDialog] 弹窗隐藏,显示 GuideBar');
setShowGuideBar(true);
}
}, [visible, setShowGuideBar]);
if (!visible) return null;
return (
<View className="locationDialogMask" onClick={onCancel}>
<View className="locationDialogContainer" onClick={(e) => e.stopPropagation()}>
<View className="locationDialogContent">
<Text className="locationDialogTitle">{detectedCity}</Text>
<View className="locationDialogButtons">
<View className="locationDialogButton primary" onClick={onConfirm}>
<Text className="locationDialogButtonText primary">{detectedCity}</Text>
</View>
<View className="locationDialogButton secondary" onClick={onCancel}>
<Text className="locationDialogButtonText secondary">{currentCity}</Text>
</View>
</View>
</View>
</View>
</View>
);
};
export default LocationConfirmDialog;

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef, useContext } from "react";
import React, { useState, useEffect, useRef } from "react";
import Taro, { useDidShow } from "@tarojs/taro";
import { View, Text, Image, Button } from "@tarojs/components";
import "./index.scss";
@@ -10,7 +10,7 @@ import { useUserActions } from "@/store/userStore";
import { UserInfoType } from "@/services/userService";
import { useCities, useProfessions } from "@/store/pickerOptionsStore";
import { formatNtrpDisplay } from "@/utils/helper";
import FamilyContext from "@/context";
import { useGlobalState } from "@/store/global";
// 用户信息接口
// export interface UserInfo {
@@ -72,7 +72,7 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
set_user_info,
onTab,
}) => {
const { handleGrandchildTrigger } = useContext(FamilyContext);
const { setShowGuideBar } = useGlobalState();
const { updateUserInfo } = useUserActions();
// 使用 useRef 记录上一次的 user_info只在真正变化时打印
@@ -111,10 +111,9 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
ntrp_picker_visible,
occupation_picker_visible,
];
const showGuideBar = visibles.every((item) => !item);
if (showGuideBar) {
handleGrandchildTrigger(false);
}
const allPickersClosed = visibles.every((item) => !item);
// 所有选择器都关闭时,显示 GuideBar否则隐藏
setShowGuideBar(allPickersClosed);
}, [
gender_picker_visible,
location_picker_visible,
@@ -152,7 +151,8 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
// };
// 处理编辑弹窗
const handle_open_edit_modal = (field: string) => {
handleGrandchildTrigger(true);
// 打开编辑弹窗时隐藏 GuideBar
setShowGuideBar(false);
if (field === "gender") {
setGenderPickerVisible(true);
return;
@@ -171,11 +171,11 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
}
if (field === "nickname") {
// 手动输入
handleGrandchildTrigger(true);
setShowGuideBar(false);
setEditingField(field);
setEditModalVisible(true);
} else {
handleGrandchildTrigger(true);
setShowGuideBar(false);
setEditingField(field);
setEditModalVisible(true);
}
@@ -283,7 +283,8 @@ const UserInfoCardComponent: React.FC<UserInfoCardProps> = ({
handle_field_edit("occupation", `${country} ${province} ${city}`);
};
const handle_edit_modal_cancel = () => {
handleGrandchildTrigger(false);
// 关闭编辑弹窗时显示 GuideBar
setShowGuideBar(true);
setEditModalVisible(false);
setEditingField("");
};

View File

@@ -42,6 +42,7 @@
// 隐藏所有子页面中的GuideBar使用全局样式
.tab-content .guide-bar-container {
display: none !important;
}
}

View File

@@ -3,6 +3,7 @@ import { View } from "@tarojs/components";
import Taro from "@tarojs/taro";
import { wechat_auth_login, save_login_state } from "@/services/loginService";
import { useUserActions } from "@/store/userStore";
import { useGlobalState } from "@/store/global";
import tokenManager from "@/utils/tokenManager";
import GuideBar from "@/components/GuideBar";
import { GeneralNavbar } from "@/components";
@@ -18,17 +19,17 @@ type TabType = "list" | "message" | "personal";
const MainPage: React.FC = () => {
const [currentTab, setCurrentTab] = useState<TabType>("list");
const [isPublishMenuVisible, setIsPublishMenuVisible] = useState(false);
const [guideBarZIndex, setGuideBarZIndex] = useState<'low' | 'high'>('high');
const [isDistanceFilterVisible, setIsDistanceFilterVisible] = useState(false);
const [isCityPickerVisible, setIsCityPickerVisible] = useState(false);
const [isFilterPopupVisible, setIsFilterPopupVisible] = useState(false);
const [isShowInputCustomerNavBar, setIsShowInputCustomerNavBar] = useState(false);
const [listPageScrollToTopTrigger, setListPageScrollToTopTrigger] = useState(0);
const [showGuideBar, setShowGuideBar] = useState(true);
const [showAuthError, setShowAuthError] = useState(false);
const [authErrorMessage, setAuthErrorMessage] = useState('');
const { fetchUserInfo } = useUserActions();
// 从 store 获取 GuideBar 相关状态和方法
const { showGuideBar, guideBarZIndex, setShowGuideBar, setGuideBarZIndex } = useGlobalState();
// 初始化:自动微信授权并获取用户信息
useEffect(() => {
@@ -201,7 +202,8 @@ const MainPage: React.FC = () => {
};
const handleGrandchildTrigger = (value) => {
setShowGuideBar(!value)
console.log('[MainPage] handleGrandchildTrigger called with:', value);
setShowGuideBar(!value);
}
return (

View File

@@ -73,7 +73,7 @@ const CommentReply = () => {
// 获取未读评论ID并标记已读
const unreadIds = res.data.rows
.filter((item: any) => item.is_read === 0 && item.activity_type === 'received_reply')
.filter((item: any) => item.is_read === 0)
.map((item: any) => item.id);
if (unreadIds.length > 0) {

View File

@@ -120,4 +120,9 @@ $color-background: #FAFAFA !default;
border-radius: 50%;
overflow: hidden;
box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.12), 0px 0px 1px 0px rgba(0, 0, 0, 0.2);
}
.avatar
{
border: none;
}

View File

@@ -11,12 +11,20 @@ interface GlobalState {
navBarHeight: number;
totalHeight: number;
};
// GuideBar 显示/隐藏状态
showGuideBar: boolean;
// GuideBar z-index 层级
guideBarZIndex: 'low' | 'high';
}
interface GlobalActions {
updateState: (payload: Record<string, any>) => void;
getNavbarHeightInfo: () => void;
getCurrentLocationInfo: () => any;
// GuideBar 控制方法
setShowGuideBar: (show: boolean) => void;
setGuideBarZIndex: (zIndex: 'low' | 'high') => void;
toggleGuideBar: () => void;
}
// 完整的 Store 类型
type GlobalStore = GlobalState & GlobalActions;
@@ -37,6 +45,10 @@ export const useGlobalStore = create<GlobalStore>()((set, get) => ({
totalHeight: 0,
},
// GuideBar 状态
showGuideBar: true,
guideBarZIndex: 'high',
// 获取导航栏高度信息
getNavbarHeightInfo: () => {
const { statusBarHeight, navBarHeight } = getNavbarHeight();
@@ -67,6 +79,22 @@ export const useGlobalStore = create<GlobalStore>()((set, get) => ({
...(payload || {}),
});
},
// GuideBar 控制方法
setShowGuideBar: (show: boolean) => {
console.log('[Store] setShowGuideBar called with:', show);
set({ showGuideBar: show });
console.log('[Store] showGuideBar updated to:', show);
},
setGuideBarZIndex: (zIndex: 'low' | 'high') => {
set({ guideBarZIndex: zIndex });
},
toggleGuideBar: () => {
const { showGuideBar } = get();
set({ showGuideBar: !showGuideBar });
},
}));
// 导出便捷的 hooks