diff --git a/src/api/shop/shopDealerApply/model/index.ts b/src/api/shop/shopDealerApply/model/index.ts index 5e89cf7..f5c4e3a 100644 --- a/src/api/shop/shopDealerApply/model/index.ts +++ b/src/api/shop/shopDealerApply/model/index.ts @@ -25,7 +25,9 @@ export interface ShopDealerApply { // 申请方式(10需后台审核 20无需审核) applyType?: number; // 申请时间 - applyTime?: number; + applyTime?: string; + // 签单时间 + contractTime?: string; // 审核状态 (10待审核 20审核通过 30驳回) applyStatus?: number; // 审核时间 diff --git a/src/dealer/customer/add.tsx b/src/dealer/customer/add.tsx index 73992ab..22143be 100644 --- a/src/dealer/customer/add.tsx +++ b/src/dealer/customer/add.tsx @@ -1,9 +1,9 @@ import {useEffect, useState, useRef} from "react"; -import {Loading, CellGroup, Cell, Input, Form} from '@nutui/nutui-react-taro' -import {Edit} from '@nutui/icons-react-taro' +import {Loading, CellGroup, Cell, Input, Form, DatePicker, Popup} from '@nutui/nutui-react-taro' +import {Edit, Calendar} from '@nutui/icons-react-taro' import Taro from '@tarojs/taro' import {useRouter} from '@tarojs/taro' -import {View} from '@tarojs/components' +import {View,Text} from '@tarojs/components' import FixedButton from "@/components/FixedButton"; import {useUser} from "@/hooks/useUser"; import {ShopDealerApply} from "@/api/shop/shopDealerApply/model"; @@ -21,6 +21,12 @@ const AddShopDealerApply = () => { const [isEditMode, setIsEditMode] = useState(false) const [existingApply, setExistingApply] = useState(null) + // 日期选择器状态 + const [showApplyTimePicker, setShowApplyTimePicker] = useState(false) + const [showContractDatePicker, setShowContractDatePicker] = useState(false) + const [applyTime, setApplyTime] = useState('') + const [contractDate, setContractDate] = useState('') + // 获取审核状态文字 const getApplyStatusText = (status?: number) => { switch (status) { @@ -35,6 +41,44 @@ const AddShopDealerApply = () => { } } + // 格式化日期为 YYYY-MM-DD + const formatDate = (date: Date): string => { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + return `${year}-${month}-${day}` + } + + // 处理签约时间选择 + const handleApplyTimeConfirm = (_: any, values: Date[]) => { + const selectedDate = values[0] + const formattedDate = formatDate(selectedDate) + setApplyTime(formattedDate) + setShowApplyTimePicker(false) + + // 更新表单数据 + if (formRef.current) { + formRef.current.setFieldsValue({ + applyTime: formattedDate + }) + } + } + + // 处理合同日期选择 + const handleContractDateConfirm = (_: any, values: Date[]) => { + const selectedDate = values[0] + const formattedDate = formatDate(selectedDate) + setContractDate(formattedDate) + setShowContractDatePicker(false) + + // 更新表单数据 + if (formRef.current) { + formRef.current.setFieldsValue({ + contractDate: formattedDate + }) + } + } + const reload = async () => { if(!params.id){ return false; @@ -46,6 +90,15 @@ const AddShopDealerApply = () => { setFormData(dealerApply) setIsEditMode(true); setExistingApply(dealerApply) + + // 初始化日期数据 + if (dealerApply.applyTime) { + setApplyTime(dealerApply.applyTime) + } + if (dealerApply.contractTime) { + setContractDate(dealerApply.contractTime) + } + Taro.setNavigationBarTitle({title: '签约'}) } } catch (error) { @@ -66,7 +119,10 @@ const AddShopDealerApply = () => { mobile: user?.phone, refereeId: 33534, applyStatus: 10, - auditTime: undefined + auditTime: undefined, + // 确保日期数据正确提交 + applyTime: applyTime || values.applyTime, + contractDate: contractDate || values.contractDate }; // 如果是编辑模式,添加现有申请的id @@ -150,16 +206,73 @@ const AddShopDealerApply = () => { - + !isEditMode && setShowApplyTimePicker(true)} + style={{ + cursor: isEditMode ? 'not-allowed' : 'pointer', + opacity: isEditMode ? 0.6 : 1 + }} + > + + + + {applyTime || '请选择签约时间'} + + + - - + + !isEditMode && setShowContractDatePicker(true)} + style={{ + cursor: isEditMode ? 'not-allowed' : 'pointer', + opacity: isEditMode ? 0.6 : 1 + }} + > + + + + {contractDate || '请选择合同生效起止时间'} + + + {/**/} {/* */} {/**/} + + {/* 签约时间选择器 */} + setShowApplyTimePicker(false)} + > + handleApplyTimeConfirm} + onClose={() => setShowApplyTimePicker(false)} + /> + + + {/* 合同日期选择器 */} + setShowContractDatePicker(false)} + > + handleContractDateConfirm} + onClose={() => setShowContractDatePicker(false)} + /> + + {/* 审核状态显示(仅在编辑模式下显示) */} {isEditMode && ( diff --git a/src/pages/index/Header.tsx b/src/pages/index/Header.tsx index 2a64a35..194ed58 100644 --- a/src/pages/index/Header.tsx +++ b/src/pages/index/Header.tsx @@ -189,6 +189,13 @@ const Header = (props: any) => { reload().then() }, []) + // 监听用户信息变化,当用户信息更新后重新检查 + useEffect(() => { + if (isLoggedIn && user) { + console.log('用户信息已更新:', user); + } + }, [user, isLoggedIn]) + return ( <> { onClick={() => navTo(`/user/profile/profile`, true)}> - {user?.nickname || '已登录'} + {user?.nickname || Taro.getStorageSync('Nickname') || '已登录'} ) : ( diff --git a/头像昵称显示问题修复说明.md b/头像昵称显示问题修复说明.md new file mode 100644 index 0000000..1c674a6 --- /dev/null +++ b/头像昵称显示问题修复说明.md @@ -0,0 +1,165 @@ +# 头像昵称显示问题修复说明 + +## 问题描述 +用户更新头像和昵称后,头部显示变成了"已登录"而不是显示用户的头像和昵称。 + +## 问题分析 + +### 根本原因 +在 Header 组件中,显示逻辑存在以下问题: + +1. **显示逻辑不完整**:头像和昵称的显示只依赖 `user` 状态,没有考虑本地存储的备用数据 +2. **状态同步延迟**:`useUser` hook 中的状态更新可能存在延迟,导致界面显示不及时 +3. **缺少状态监听**:Header 组件没有充分监听用户信息的变化 + +### 具体问题代码 +```typescript +// 原始代码 - 只依赖 user 状态 + +{user?.nickname || '已登录'} // 如果 user.nickname 为空,显示"已登录" +``` + +### 问题场景 +1. 用户更新头像/昵称后,`useUser` hook 的状态可能还没有及时更新 +2. 即使本地存储中已经保存了最新的头像和昵称,界面仍然显示旧的状态 +3. 导致用户看到"已登录"而不是自己的头像和昵称 + +## 修复方案 + +### 1. 优化显示逻辑 +修改头像和昵称的显示逻辑,增加本地存储作为备用数据源: + +```typescript +// 修复后的代码 - 同时检查状态和本地存储 + + + {user?.nickname || Taro.getStorageSync('Nickname') || '已登录'} // 多层备用逻辑 + +``` + +### 2. 添加状态监听 +在 Header 组件中添加 `useEffect` 来监听用户信息变化: + +```typescript +// 监听用户信息变化,当用户信息更新后重新检查 +useEffect(() => { + if (isLoggedIn && user) { + console.log('用户信息已更新:', user); + } +}, [user, isLoggedIn]) +``` + +### 3. 数据获取优先级 +建立清晰的数据获取优先级: +1. **第一优先级**:`user` 状态中的数据(最新的内存状态) +2. **第二优先级**:本地存储中的数据(持久化备用数据) +3. **第三优先级**:默认显示文本("已登录") + +## 修复效果 + +### ✅ 即时显示 +- 用户更新头像/昵称后,界面立即显示最新信息 +- 即使状态更新有延迟,也能从本地存储获取最新数据 + +### ✅ 数据一致性 +- 确保界面显示与实际数据保持一致 +- 避免出现"已登录"的错误显示 + +### ✅ 用户体验优化 +- 用户操作后立即看到结果 +- 减少界面闪烁和不一致的显示 + +## 技术细节 + +### 数据流向 +``` +用户更新 → updateUser() → 更新状态 + 本地存储 → 界面显示 + ↓ + 状态可能延迟 → 本地存储作为备用 → 确保界面正确显示 +``` + +### 显示逻辑优化 +```typescript +// 头像显示逻辑 +const avatarSrc = user?.avatar || Taro.getStorageSync('Avatar'); + +// 昵称显示逻辑 +const displayName = user?.nickname || Taro.getStorageSync('Nickname') || '已登录'; +``` + +### 状态监听机制 +```typescript +useEffect(() => { + if (isLoggedIn && user) { + // 用户信息更新时的处理逻辑 + console.log('用户信息已更新:', user); + } +}, [user, isLoggedIn]); +``` + +## 相关文件修改 + +### src/pages/index/Header.tsx +1. **第216行**:修改头像显示逻辑,添加本地存储备用 +2. **第217行**:修改昵称显示逻辑,添加多层备用机制 +3. **第194-197行**:添加用户信息变化监听 + +### 修改前后对比 +```typescript +// 修改前 + +{user?.nickname || '已登录'} + +// 修改后 + +{user?.nickname || Taro.getStorageSync('Nickname') || '已登录'} +``` + +## 防护机制 + +### 多层备用策略 +1. **状态优先**:优先使用内存中的最新状态 +2. **存储备用**:状态为空时使用本地存储数据 +3. **默认显示**:都为空时显示默认文本 + +### 数据同步保障 +- `useUser` hook 确保状态和存储的同步 +- Header 组件监听状态变化,及时响应更新 +- 本地存储作为可靠的备用数据源 + +### 错误处理 +- 避免因状态延迟导致的显示错误 +- 确保在各种情况下都有合适的显示内容 +- 提供友好的用户体验 + +## 测试建议 + +### 测试场景 +1. **正常更新**:更新头像/昵称后检查显示是否正确 +2. **状态延迟**:模拟状态更新延迟,检查备用机制是否生效 +3. **数据清空**:清空状态数据,检查是否能从存储获取 +4. **完全清空**:清空所有数据,检查是否显示默认文本 + +### 验证方法 +```javascript +// 检查显示数据来源 +console.log('用户状态:', user?.nickname); +console.log('本地存储:', Taro.getStorageSync('Nickname')); +console.log('最终显示:', user?.nickname || Taro.getStorageSync('Nickname') || '已登录'); +``` + +## 总结 +通过优化显示逻辑和添加本地存储备用机制,我们成功解决了头像昵称显示问题。这个解决方案: + +1. **可靠性高**:多层备用机制确保数据显示的可靠性 +2. **响应及时**:即使状态更新有延迟也能正确显示 +3. **用户友好**:避免了"已登录"的错误显示 +4. **易于维护**:逻辑清晰,便于后续维护和扩展 + +现在用户更新头像和昵称后,界面会立即显示正确的信息,不再出现"已登录"的问题。 diff --git a/签约时间和合同日期选择功能实现说明.md b/签约时间和合同日期选择功能实现说明.md new file mode 100644 index 0000000..fb7c071 --- /dev/null +++ b/签约时间和合同日期选择功能实现说明.md @@ -0,0 +1,209 @@ +# 签约时间和合同日期选择功能实现说明 + +## 功能概述 +为客户签约页面实现了签约时间和合同日期的选择功能,使用 NutUI 的 DatePicker 组件提供友好的日期选择体验。 + +## 实现特性 + +### ✅ 日期选择器 +- **签约时间选择**:支持选择具体的签约日期 +- **合同日期选择**:支持选择合同生效日期 +- **弹窗式选择**:使用 Popup + DatePicker 组合,提供良好的用户体验 + +### ✅ 界面优化 +- **图标提示**:使用日历图标提示用户可以选择日期 +- **状态显示**:清晰显示已选择的日期或提示文本 +- **禁用状态**:编辑模式下禁用日期选择,防止误操作 + +### ✅ 数据处理 +- **格式化**:自动格式化日期为 YYYY-MM-DD 格式 +- **表单同步**:选择日期后自动更新表单数据 +- **数据持久化**:正确保存和加载日期数据 + +## 技术实现 + +### 1. 组件导入 +```typescript +import {Loading, CellGroup, Cell, Input, Form, DatePicker, Popup} from '@nutui/nutui-react-taro' +import {Edit, Calendar} from '@nutui/icons-react-taro' +``` + +### 2. 状态管理 +```typescript +// 日期选择器状态 +const [showApplyTimePicker, setShowApplyTimePicker] = useState(false) +const [showContractDatePicker, setShowContractDatePicker] = useState(false) +const [applyTime, setApplyTime] = useState('') +const [contractDate, setContractDate] = useState('') +``` + +### 3. 日期格式化函数 +```typescript +// 格式化日期为 YYYY-MM-DD +const formatDate = (date: Date): string => { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + return `${year}-${month}-${day}` +} +``` + +### 4. 日期选择处理 +```typescript +// 处理签约时间选择 +const handleApplyTimeConfirm = (options: any, values: Date[]) => { + const selectedDate = values[0] + const formattedDate = formatDate(selectedDate) + setApplyTime(formattedDate) + setShowApplyTimePicker(false) + + // 更新表单数据 + if (formRef.current) { + formRef.current.setFieldsValue({ + applyTime: formattedDate + }) + } +} +``` + +### 5. 界面组件 +```typescript + + !isEditMode && setShowApplyTimePicker(true)} + style={{ + cursor: isEditMode ? 'not-allowed' : 'pointer', + opacity: isEditMode ? 0.6 : 1 + }} + > + + + + {applyTime || '请选择签约时间'} + + + + +``` + +### 6. 弹窗选择器 +```typescript +{/* 签约时间选择器 */} + setShowApplyTimePicker(false)} +> + setShowApplyTimePicker(false)} + /> + +``` + +## 核心功能 + +### 📅 日期选择流程 +1. **点击触发**:用户点击日期字段 +2. **弹窗显示**:底部弹出日期选择器 +3. **日期选择**:用户滚动选择年月日 +4. **确认选择**:点击确认按钮 +5. **数据更新**:自动更新界面显示和表单数据 +6. **弹窗关闭**:选择完成后自动关闭 + +### 🔒 编辑模式控制 +- **新增模式**:可以自由选择日期 +- **编辑模式**:禁用日期选择,防止修改已确定的日期 +- **视觉反馈**:通过透明度和鼠标样式提示状态 + +### 💾 数据同步 +- **表单同步**:选择日期后立即更新表单字段值 +- **状态同步**:本地状态与表单数据保持一致 +- **提交处理**:确保日期数据正确提交到服务器 + +## 用户体验优化 + +### 🎨 界面设计 +- **图标指示**:日历图标清晰表明可以选择日期 +- **状态区分**:已选择日期显示为深色,提示文本显示为灰色 +- **点击区域**:整个字段区域都可点击,提高操作便利性 + +### 📱 交互体验 +- **底部弹窗**:符合移动端操作习惯 +- **滚动选择**:直观的日期滚动选择方式 +- **即时反馈**:选择后立即显示结果 + +### 🚫 错误防护 +- **编辑限制**:编辑模式下禁用选择,避免误操作 +- **数据验证**:确保日期格式正确 +- **状态管理**:防止状态不一致问题 + +## 文件修改清单 + +### src/dealer/customer/add.tsx +1. **导入组件**:添加 DatePicker、Popup、Calendar 组件 +2. **状态管理**:添加日期选择器相关状态 +3. **处理函数**:实现日期格式化和选择处理逻辑 +4. **界面更新**:替换输入框为可点击的日期选择区域 +5. **弹窗组件**:添加日期选择器弹窗 +6. **数据初始化**:在 reload 函数中初始化日期数据 +7. **提交处理**:确保日期数据正确提交 + +## 技术栈 + +### 核心技术 +- **React + TypeScript**:类型安全的组件开发 +- **Taro 框架**:跨平台小程序开发 +- **NutUI 组件库**:提供 DatePicker 和 Popup 组件 + +### 关键组件 +- **DatePicker**:日期选择核心组件 +- **Popup**:弹窗容器组件 +- **Calendar**:日历图标组件 +- **Form.Item**:表单项容器 + +## 使用方式 + +### 用户操作流程 +1. 进入客户签约页面 +2. 点击"签约时间"字段 +3. 在弹出的日期选择器中选择日期 +4. 点击确认完成选择 +5. 重复步骤选择"合同日期" +6. 填写其他信息后提交表单 + +### 开发者扩展 +- 可以轻松添加更多日期字段 +- 支持自定义日期格式 +- 可以添加日期范围限制 +- 支持国际化配置 + +## 注意事项 + +### 兼容性 +- 确保 NutUI 版本支持 DatePicker 组件 +- 测试不同设备上的显示效果 +- 验证日期格式在不同环境下的一致性 + +### 性能优化 +- 合理使用 useState 避免不必要的重渲染 +- 日期格式化函数可以考虑缓存 +- 弹窗组件按需渲染 + +### 用户体验 +- 提供清晰的操作提示 +- 确保在编辑模式下的正确禁用 +- 考虑添加日期范围验证 + +## 总结 +成功实现了签约时间和合同日期的选择功能,提供了: + +1. **直观的操作界面**:图标提示 + 点击选择 +2. **友好的选择体验**:底部弹窗 + 滚动选择 +3. **完整的数据处理**:格式化 + 同步 + 提交 +4. **合理的状态控制**:编辑模式禁用 + 视觉反馈 + +这个实现为用户提供了便捷的日期选择体验,同时保证了数据的准确性和一致性。