添加程序选择
This commit is contained in:
204
DEBUG_GUIDEBAR.md
Normal file
204
DEBUG_GUIDEBAR.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# GuideBar 隐藏问题调试指南
|
||||
|
||||
## 当前实现
|
||||
|
||||
### 1. Store 配置 (`src/store/global.ts`)
|
||||
```typescript
|
||||
// GuideBar 状态
|
||||
showGuideBar: true,
|
||||
guideBarZIndex: 'high',
|
||||
|
||||
// 控制方法
|
||||
setShowGuideBar: (show: boolean) => {
|
||||
console.log('[Store] setShowGuideBar called with:', show);
|
||||
set({ showGuideBar: show });
|
||||
console.log('[Store] showGuideBar updated to:', show);
|
||||
},
|
||||
```
|
||||
|
||||
### 2. HomeNavbar 使用 (`src/components/HomeNavbar/index.tsx`)
|
||||
```typescript
|
||||
const { getLocationLoading, statusNavbarHeightInfo, setShowGuideBar } = useGlobalState();
|
||||
|
||||
const showLocationConfirmDialog = (detectedProvince: string, cachedCity: [string, string]) => {
|
||||
console.log('[LocationDialog] 准备显示定位确认弹窗,隐藏 GuideBar');
|
||||
setLocationDialogData({ detectedProvince, cachedCity });
|
||||
setLocationDialogVisible(true);
|
||||
setShowGuideBar(false); // 隐藏 GuideBar
|
||||
console.log('[LocationDialog] setShowGuideBar(false) 已调用');
|
||||
};
|
||||
```
|
||||
|
||||
### 3. MainPage 渲染 (`src/main_pages/index.tsx`)
|
||||
```typescript
|
||||
const { showGuideBar, guideBarZIndex, setShowGuideBar, setGuideBarZIndex } = useGlobalState();
|
||||
|
||||
// 渲染逻辑
|
||||
{
|
||||
showGuideBar ?
|
||||
<GuideBar
|
||||
currentPage={currentTab}
|
||||
guideBarClassName={guideBarZIndex === 'low' ? 'guide-bar-low-z-index' : 'guide-bar-high-z-index'}
|
||||
onTabChange={handleTabChange}
|
||||
onPublishMenuVisibleChange={handlePublishMenuVisibleChange}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
```
|
||||
|
||||
## 调试步骤
|
||||
|
||||
### 步骤 1: 检查控制台日志
|
||||
当 `LocationConfirmDialog` 弹窗显示时,应该能看到以下日志:
|
||||
```
|
||||
[LocationDialog] 准备显示定位确认弹窗,隐藏 GuideBar
|
||||
[Store] setShowGuideBar called with: false
|
||||
[Store] showGuideBar updated to: false
|
||||
[LocationDialog] setShowGuideBar(false) 已调用
|
||||
```
|
||||
|
||||
### 步骤 2: 检查是否有其他组件覆盖状态
|
||||
搜索以下可能的干扰:
|
||||
- `UserInfo` 组件通过 `FamilyContext` 调用 `handleGrandchildTrigger`
|
||||
- 其他组件可能直接调用 `setShowGuideBar(true)`
|
||||
|
||||
### 步骤 3: 使用 React DevTools 检查状态
|
||||
1. 打开 React DevTools
|
||||
2. 找到 `MainPage` 组件
|
||||
3. 查看 `showGuideBar` 的实时值
|
||||
4. 当弹窗显示时,检查值是否变为 `false`
|
||||
|
||||
### 步骤 4: 检查 CSS 样式
|
||||
如果状态正确但 GuideBar 仍然可见,可能是 CSS 问题:
|
||||
```scss
|
||||
// 检查 GuideBar 的样式是否有 !important 覆盖
|
||||
.guide-bar-container {
|
||||
display: block; // 不应该有 !important
|
||||
}
|
||||
```
|
||||
|
||||
## 可能的问题原因
|
||||
|
||||
### 原因 1: UserInfo 组件干扰
|
||||
`src/components/UserInfo/index.tsx` 使用 `FamilyContext` 控制 GuideBar:
|
||||
```typescript
|
||||
const { handleGrandchildTrigger } = useContext(FamilyContext);
|
||||
|
||||
// 可能在某些情况下调用
|
||||
handleGrandchildTrigger(false); // 这会设置 setShowGuideBar(true)
|
||||
```
|
||||
|
||||
**解决方案**:检查 `UserInfo` 组件是否在 `LocationConfirmDialog` 显示时被触发。
|
||||
|
||||
### 原因 2: useEffect 依赖问题
|
||||
`HomeNavbar` 中的 `useEffect` 可能在状态更新后重新运行:
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
// 检查这里的逻辑
|
||||
}, [locationProvince]);
|
||||
```
|
||||
|
||||
**解决方案**:确保 `useEffect` 不会在弹窗显示后重置状态。
|
||||
|
||||
### 原因 3: 异步状态更新
|
||||
Zustand 的状态更新应该是同步的,但如果有异步操作可能延迟:
|
||||
```typescript
|
||||
setShowGuideBar(false);
|
||||
// 如果这里有异步操作,可能导致状态被覆盖
|
||||
await someAsyncOperation();
|
||||
```
|
||||
|
||||
**解决方案**:确保 `setShowGuideBar(false)` 之后没有异步操作重置状态。
|
||||
|
||||
## 测试方法
|
||||
|
||||
### 方法 1: 手动测试
|
||||
1. 清除缓存:`Taro.clearStorageSync()`
|
||||
2. 重新打开小程序
|
||||
3. 等待定位完成
|
||||
4. 观察是否弹出 `LocationConfirmDialog`
|
||||
5. 检查 GuideBar 是否隐藏
|
||||
|
||||
### 方法 2: 代码测试
|
||||
在 `HomeNavbar` 中添加测试按钮:
|
||||
```tsx
|
||||
<Button onClick={() => {
|
||||
console.log('[Test] 手动触发弹窗');
|
||||
showLocationConfirmDialog('北京', ['中国', '上海']);
|
||||
}}>
|
||||
测试弹窗
|
||||
</Button>
|
||||
```
|
||||
|
||||
### 方法 3: 添加更多日志
|
||||
在 `MainPage` 中监听 `showGuideBar` 变化:
|
||||
```tsx
|
||||
useEffect(() => {
|
||||
console.log('[MainPage] showGuideBar 状态变化:', showGuideBar);
|
||||
}, [showGuideBar]);
|
||||
```
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [ ] 控制台显示正确的日志
|
||||
- [ ] React DevTools 显示 `showGuideBar` 为 `false`
|
||||
- [ ] GuideBar 组件不在 DOM 中渲染(使用元素审查工具检查)
|
||||
- [ ] 没有其他组件调用 `setShowGuideBar(true)`
|
||||
- [ ] CSS 样式没有强制显示 GuideBar
|
||||
|
||||
## 紧急修复方案
|
||||
|
||||
如果以上都无法解决,可以尝试以下紧急方案:
|
||||
|
||||
### 方案 1: 直接在 LocationConfirmDialog 中控制
|
||||
```tsx
|
||||
// src/components/LocationConfirmDialog/index.tsx
|
||||
import { useGlobalState } from "@/store/global";
|
||||
|
||||
const LocationConfirmDialog = (props) => {
|
||||
const { setShowGuideBar } = useGlobalState();
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setShowGuideBar(false);
|
||||
}
|
||||
return () => {
|
||||
setShowGuideBar(true);
|
||||
};
|
||||
}, [visible, setShowGuideBar]);
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
### 方案 2: 使用 CSS 强制隐藏
|
||||
```scss
|
||||
// 当弹窗显示时,添加 class 隐藏 GuideBar
|
||||
.location-dialog-visible {
|
||||
.guide-bar-container {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方案 3: 添加延迟
|
||||
```typescript
|
||||
const showLocationConfirmDialog = (detectedProvince: string, cachedCity: [string, string]) => {
|
||||
setLocationDialogData({ detectedProvince, cachedCity });
|
||||
setLocationDialogVisible(true);
|
||||
|
||||
// 添加微小延迟确保状态更新
|
||||
setTimeout(() => {
|
||||
setShowGuideBar(false);
|
||||
}, 0);
|
||||
};
|
||||
```
|
||||
|
||||
## 联系支持
|
||||
|
||||
如果问题仍然存在,请提供:
|
||||
1. 完整的控制台日志
|
||||
2. React DevTools 截图
|
||||
3. 复现步骤
|
||||
4. 小程序版本和环境信息
|
||||
|
||||
379
GUIDEBAR_UNIFIED_CONTROL.md
Normal file
379
GUIDEBAR_UNIFIED_CONTROL.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# GuideBar 统一控制实现总结
|
||||
|
||||
## ✅ 已完成的统一改造
|
||||
|
||||
所有页面和组件现在都使用 **`global store`** 统一管理 `GuideBar` 的显示与隐藏。
|
||||
|
||||
---
|
||||
|
||||
## 📦 核心实现
|
||||
|
||||
### 1. **Global Store** (`src/store/global.ts`)
|
||||
|
||||
```typescript
|
||||
interface GlobalState {
|
||||
showGuideBar: boolean;
|
||||
guideBarZIndex: 'low' | 'high';
|
||||
}
|
||||
|
||||
interface GlobalActions {
|
||||
setShowGuideBar: (show: boolean) => void;
|
||||
setGuideBarZIndex: (zIndex: 'low' | 'high') => void;
|
||||
toggleGuideBar: () => void;
|
||||
}
|
||||
|
||||
// 使用方法
|
||||
import { useGlobalState } from "@/store/global";
|
||||
|
||||
const { showGuideBar, setShowGuideBar } = useGlobalState();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 已改造的组件
|
||||
|
||||
### 1️⃣ **LocationConfirmDialog** - 定位确认弹窗
|
||||
**文件:** `src/components/LocationConfirmDialog/index.tsx`
|
||||
|
||||
**改造内容:**
|
||||
- 使用 `useEffect` 监听 `visible` 状态
|
||||
- `visible = true` 时自动隐藏 GuideBar
|
||||
- `visible = false` 时自动显示 GuideBar
|
||||
|
||||
```typescript
|
||||
const { setShowGuideBar } = useGlobalState();
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setShowGuideBar(false); // 弹窗显示时隐藏
|
||||
} else {
|
||||
setShowGuideBar(true); // 弹窗关闭时显示
|
||||
}
|
||||
}, [visible, setShowGuideBar]);
|
||||
```
|
||||
|
||||
**使用场景:**
|
||||
- 用户首次打开应用时的定位确认
|
||||
- 检测到位置变化时的切换提示
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ **HomeNavbar** - 首页导航栏
|
||||
**文件:** `src/components/HomeNavbar/index.tsx`
|
||||
|
||||
**改造内容:**
|
||||
- 引入 `setShowGuideBar` 方法
|
||||
- 在显示定位弹窗时调用隐藏
|
||||
- 在确认/取消时调用显示
|
||||
|
||||
```typescript
|
||||
const { setShowGuideBar } = useGlobalState();
|
||||
|
||||
const showLocationConfirmDialog = () => {
|
||||
setShowGuideBar(false);
|
||||
// ...
|
||||
};
|
||||
|
||||
const handleLocationDialogConfirm = () => {
|
||||
// ...
|
||||
setShowGuideBar(true);
|
||||
};
|
||||
|
||||
const handleLocationDialogCancel = () => {
|
||||
// ...
|
||||
setShowGuideBar(true);
|
||||
};
|
||||
```
|
||||
|
||||
**使用场景:**
|
||||
- 城市切换提示
|
||||
- 定位权限请求
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ **UserInfo** - 用户信息组件(✨ 重要改造)
|
||||
**文件:** `src/components/UserInfo/index.tsx`
|
||||
|
||||
**改造内容:**
|
||||
- ❌ 移除 `FamilyContext` 依赖
|
||||
- ✅ 改用 `useGlobalState` 统一管理
|
||||
- 优化了所有选择器(性别、地区、NTRP、职业)的 GuideBar 控制逻辑
|
||||
|
||||
**之前(使用 Context):**
|
||||
```typescript
|
||||
const { handleGrandchildTrigger } = useContext(FamilyContext);
|
||||
handleGrandchildTrigger(true); // 隐藏(逻辑反转)
|
||||
handleGrandchildTrigger(false); // 显示
|
||||
```
|
||||
|
||||
**现在(使用 Store):**
|
||||
```typescript
|
||||
const { setShowGuideBar } = useGlobalState();
|
||||
setShowGuideBar(false); // 隐藏(直观明了)
|
||||
setShowGuideBar(true); // 显示
|
||||
```
|
||||
|
||||
**具体改动:**
|
||||
|
||||
1. **打开编辑弹窗时隐藏 GuideBar**
|
||||
```typescript
|
||||
const handle_open_edit_modal = (field: string) => {
|
||||
setShowGuideBar(false); // 之前: handleGrandchildTrigger(true)
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
2. **关闭编辑弹窗时显示 GuideBar**
|
||||
```typescript
|
||||
const handle_edit_modal_cancel = () => {
|
||||
setShowGuideBar(true); // 之前: handleGrandchildTrigger(false)
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
3. **选择器状态联动**
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
const visibles = [
|
||||
gender_picker_visible,
|
||||
location_picker_visible,
|
||||
ntrp_picker_visible,
|
||||
occupation_picker_visible,
|
||||
];
|
||||
const allPickersClosed = visibles.every((item) => !item);
|
||||
// 所有选择器都关闭时显示 GuideBar,否则隐藏
|
||||
setShowGuideBar(allPickersClosed);
|
||||
}, [
|
||||
gender_picker_visible,
|
||||
location_picker_visible,
|
||||
ntrp_picker_visible,
|
||||
occupation_picker_visible,
|
||||
]);
|
||||
```
|
||||
|
||||
**使用场景:**
|
||||
- "我的"页面编辑个人信息
|
||||
- 性别选择器
|
||||
- 地区选择器
|
||||
- NTRP 等级选择器
|
||||
- 职业选择器
|
||||
- 昵称/简介编辑
|
||||
|
||||
---
|
||||
|
||||
### 4️⃣ **MainPage** - 主页面容器
|
||||
**文件:** `src/main_pages/index.tsx`
|
||||
|
||||
**改造内容:**
|
||||
- 从 store 获取 `showGuideBar` 和 `setShowGuideBar`
|
||||
- 保留 `handleGrandchildTrigger` 以保持向后兼容(但已无实际使用)
|
||||
|
||||
```typescript
|
||||
const { showGuideBar, guideBarZIndex, setShowGuideBar, setGuideBarZIndex } = useGlobalState();
|
||||
|
||||
// 根据状态渲染 GuideBar
|
||||
{
|
||||
showGuideBar ?
|
||||
<GuideBar
|
||||
currentPage={currentTab}
|
||||
guideBarClassName={guideBarZIndex === 'low' ? 'guide-bar-low-z-index' : 'guide-bar-high-z-index'}
|
||||
onTabChange={handleTabChange}
|
||||
onPublishMenuVisibleChange={handlePublishMenuVisibleChange}
|
||||
/> :
|
||||
null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 工作流程
|
||||
|
||||
### 示例:用户编辑个人信息
|
||||
|
||||
1. **用户点击"编辑"按钮**
|
||||
```
|
||||
handle_open_edit_modal() 调用
|
||||
→ setShowGuideBar(false)
|
||||
→ GuideBar 隐藏
|
||||
```
|
||||
|
||||
2. **用户打开性别选择器**
|
||||
```
|
||||
setGenderPickerVisible(true)
|
||||
→ useEffect 检测到 visibles 变化
|
||||
→ setShowGuideBar(false)
|
||||
→ GuideBar 保持隐藏
|
||||
```
|
||||
|
||||
3. **用户关闭选择器**
|
||||
```
|
||||
setGenderPickerVisible(false)
|
||||
→ useEffect 检测到所有选择器都关闭
|
||||
→ setShowGuideBar(true)
|
||||
→ GuideBar 显示
|
||||
```
|
||||
|
||||
4. **用户点击"取消"**
|
||||
```
|
||||
handle_edit_modal_cancel() 调用
|
||||
→ setShowGuideBar(true)
|
||||
→ GuideBar 显示
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 对比表
|
||||
|
||||
| 项目 | 之前(FamilyContext) | 现在(Global Store) |
|
||||
|------|---------------------|---------------------|
|
||||
| **状态管理** | Context API(跨层传递) | Zustand Store(全局统一) |
|
||||
| **调用方式** | `handleGrandchildTrigger(value)` | `setShowGuideBar(show)` |
|
||||
| **逻辑清晰度** | ❌ 反转逻辑(true=隐藏) | ✅ 直观逻辑(false=隐藏) |
|
||||
| **依赖关系** | ❌ 需要 Context Provider | ✅ 直接引入 hook |
|
||||
| **类型安全** | ⚠️ `any` 类型 | ✅ 完整 TypeScript 类型 |
|
||||
| **调试能力** | ❌ 难以追踪状态变化 | ✅ 集中日志,易于调试 |
|
||||
| **可维护性** | ⚠️ 分散在多处 | ✅ 统一管理 |
|
||||
| **性能** | ⚠️ Context 更新触发重渲染 | ✅ Zustand 精确订阅 |
|
||||
|
||||
---
|
||||
|
||||
## 🎨 优势
|
||||
|
||||
### 1. **代码更简洁**
|
||||
```typescript
|
||||
// 之前
|
||||
const { handleGrandchildTrigger } = useContext(FamilyContext);
|
||||
handleGrandchildTrigger(true); // 反直觉
|
||||
|
||||
// 现在
|
||||
const { setShowGuideBar } = useGlobalState();
|
||||
setShowGuideBar(false); // 直观明了
|
||||
```
|
||||
|
||||
### 2. **逻辑更清晰**
|
||||
- `setShowGuideBar(true)` → 显示 GuideBar
|
||||
- `setShowGuideBar(false)` → 隐藏 GuideBar
|
||||
- 不需要记忆反转逻辑
|
||||
|
||||
### 3. **调试更方便**
|
||||
所有状态变化都有日志:
|
||||
```
|
||||
[UserInfo] 打开编辑弹窗,隐藏 GuideBar
|
||||
[Store] setShowGuideBar called with: false
|
||||
[Store] showGuideBar updated to: false
|
||||
```
|
||||
|
||||
### 4. **类型安全**
|
||||
```typescript
|
||||
// 完整的 TypeScript 类型定义
|
||||
interface GlobalActions {
|
||||
setShowGuideBar: (show: boolean) => void; // ✅ 明确的参数类型
|
||||
toggleGuideBar: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. **易于扩展**
|
||||
需要新功能时,只需在 store 中添加:
|
||||
```typescript
|
||||
// 未来可以轻松添加更多控制方法
|
||||
interface GlobalActions {
|
||||
setShowGuideBar: (show: boolean) => void;
|
||||
setGuideBarZIndex: (zIndex: 'low' | 'high') => void;
|
||||
toggleGuideBar: () => void;
|
||||
hideGuideBarTemporarily: (duration: number) => void; // 新功能
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 使用指南
|
||||
|
||||
### 在任何组件中使用
|
||||
|
||||
```typescript
|
||||
import { useGlobalState } from "@/store/global";
|
||||
|
||||
function YourComponent() {
|
||||
const { showGuideBar, setShowGuideBar, toggleGuideBar } = useGlobalState();
|
||||
|
||||
// 隐藏 GuideBar
|
||||
const hideGuideBar = () => setShowGuideBar(false);
|
||||
|
||||
// 显示 GuideBar
|
||||
const showGuideBar = () => setShowGuideBar(true);
|
||||
|
||||
// 切换显示/隐藏
|
||||
const toggle = () => toggleGuideBar();
|
||||
|
||||
return <View>...</View>;
|
||||
}
|
||||
```
|
||||
|
||||
### 自动控制(推荐)
|
||||
|
||||
使用 `useEffect` 监听状态变化:
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
if (modalVisible) {
|
||||
setShowGuideBar(false);
|
||||
} else {
|
||||
setShowGuideBar(true);
|
||||
}
|
||||
}, [modalVisible, setShowGuideBar]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 测试清单
|
||||
|
||||
### 定位弹窗测试
|
||||
- [ ] 首次打开应用时,定位弹窗显示,GuideBar 隐藏
|
||||
- [ ] 点击"切换到XX",弹窗关闭,GuideBar 显示
|
||||
- [ ] 点击"继续浏览XX",弹窗关闭,GuideBar 显示
|
||||
|
||||
### 用户信息编辑测试
|
||||
- [ ] 点击"编辑"按钮,编辑弹窗显示,GuideBar 隐藏
|
||||
- [ ] 打开性别选择器,GuideBar 保持隐藏
|
||||
- [ ] 关闭性别选择器,GuideBar 显示
|
||||
- [ ] 打开地区选择器,GuideBar 隐藏
|
||||
- [ ] 关闭地区选择器,GuideBar 显示
|
||||
- [ ] 打开 NTRP 选择器,GuideBar 隐藏
|
||||
- [ ] 关闭 NTRP 选择器,GuideBar 显示
|
||||
- [ ] 点击"取消",编辑弹窗关闭,GuideBar 显示
|
||||
|
||||
### 多选择器联动测试
|
||||
- [ ] 同时打开多个选择器时,GuideBar 保持隐藏
|
||||
- [ ] 只有所有选择器都关闭时,GuideBar 才显示
|
||||
|
||||
---
|
||||
|
||||
## 🔍 调试方法
|
||||
|
||||
### 查看控制台日志
|
||||
```
|
||||
[UserInfo] 打开编辑弹窗,隐藏 GuideBar
|
||||
[Store] setShowGuideBar called with: false
|
||||
[Store] showGuideBar updated to: false
|
||||
|
||||
[UserInfo] 关闭编辑弹窗,显示 GuideBar
|
||||
[Store] setShowGuideBar called with: true
|
||||
[Store] showGuideBar updated to: true
|
||||
```
|
||||
|
||||
### React DevTools
|
||||
1. 打开 React DevTools
|
||||
2. 找到 `MainPage` 组件
|
||||
3. 查看 `showGuideBar` 的实时值
|
||||
4. 观察状态变化是否符合预期
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
✅ **LocationConfirmDialog** - 使用 store 统一控制
|
||||
✅ **HomeNavbar** - 使用 store 统一控制
|
||||
✅ **UserInfo** - 已从 FamilyContext 迁移到 store
|
||||
✅ **MainPage** - 使用 store 统一渲染
|
||||
|
||||
所有组件现在都通过 **`useGlobalState`** 统一管理 GuideBar,代码更简洁、逻辑更清晰、维护更容易!🚀
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
169
src/components/GuideBar/README.md
Normal file
169
src/components/GuideBar/README.md
Normal 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. **性能优化**:频繁切换显示/隐藏可能影响性能,建议使用防抖或节流
|
||||
|
||||
203
src/components/GuideBar/usage-example.tsx
Normal file
203
src/components/GuideBar/usage-example.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
console.log('[LocationDialog] 准备显示定位确认弹窗,隐藏 GuideBar');
|
||||
setLocationDialogData({ detectedProvince, cachedCity });
|
||||
setLocationDialogVisible(true);
|
||||
// 显示弹窗时隐藏 GuideBar
|
||||
setShowGuideBar(false);
|
||||
console.log('[LocationDialog] setShowGuideBar(false) 已调用');
|
||||
};
|
||||
|
||||
// 刷新数据
|
||||
handleCityChangeWithoutCache();
|
||||
} else {
|
||||
// 用户选择"否",保持缓存的定位城市
|
||||
console.log("保持缓存的定位城市:", cachedCity[1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
// 处理定位弹窗确认
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
122
src/components/LocationConfirmDialog/index.scss
Normal file
122
src/components/LocationConfirmDialog/index.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
57
src/components/LocationConfirmDialog/index.tsx
Normal file
57
src/components/LocationConfirmDialog/index.tsx
Normal 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;
|
||||
|
||||
@@ -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("");
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
// 隐藏所有子页面中的GuideBar(使用全局样式)
|
||||
.tab-content .guide-bar-container {
|
||||
display: none !important;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -121,3 +121,8 @@ $color-background: #FAFAFA !default;
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user