feat(dealer/customer): 实现客户列表的无限滚动和搜索功能- 在客户列表页面添加 InfiniteLoading 组件,实现无限滚动加载- 添加搜索功能,支持按关键词搜索客户
- 优化数据加载逻辑,解决重复请求问题 - 在 Header 组件中增加用户登录状态和信息的检查
This commit is contained in:
@@ -64,7 +64,7 @@ const AddShopDealerApply = () => {
|
|||||||
...values,
|
...values,
|
||||||
realName: values.realName || user?.nickname,
|
realName: values.realName || user?.nickname,
|
||||||
mobile: user?.phone,
|
mobile: user?.phone,
|
||||||
refereeId: Taro.getStorageSync('UserId'),
|
refereeId: 33534,
|
||||||
applyStatus: 10,
|
applyStatus: 10,
|
||||||
auditTime: undefined
|
auditTime: undefined
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -97,6 +97,13 @@ export const useUser = () => {
|
|||||||
Taro.setStorageSync('UserId', userInfo.userId);
|
Taro.setStorageSync('UserId', userInfo.userId);
|
||||||
Taro.setStorageSync('TenantId', userInfo.tenantId);
|
Taro.setStorageSync('TenantId', userInfo.tenantId);
|
||||||
Taro.setStorageSync('Phone', userInfo.phone);
|
Taro.setStorageSync('Phone', userInfo.phone);
|
||||||
|
// 保存头像和昵称信息
|
||||||
|
if (userInfo.avatar) {
|
||||||
|
Taro.setStorageSync('Avatar', userInfo.avatar);
|
||||||
|
}
|
||||||
|
if (userInfo.nickname) {
|
||||||
|
Taro.setStorageSync('Nickname', userInfo.nickname);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存用户数据失败:', error);
|
console.error('保存用户数据失败:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {ArrowRight} from '@nutui/icons-react-taro'
|
|||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {ConfigProvider} from '@nutui/nutui-react-taro'
|
import {ConfigProvider} from '@nutui/nutui-react-taro'
|
||||||
import Taro, {getCurrentInstance} from '@tarojs/taro'
|
import Taro, {getCurrentInstance} from '@tarojs/taro'
|
||||||
import {getUserInfo, updateUserInfo} from "@/api/layout";
|
import {getUserInfo} from "@/api/layout";
|
||||||
import {TenantId} from "@/config/app";
|
import {TenantId} from "@/config/app";
|
||||||
import { TextArea } from '@nutui/nutui-react-taro'
|
import { TextArea } from '@nutui/nutui-react-taro'
|
||||||
import './profile.scss'
|
import './profile.scss'
|
||||||
@@ -34,7 +34,7 @@ interface InputEvent {
|
|||||||
}
|
}
|
||||||
function Profile() {
|
function Profile() {
|
||||||
const formId = Number(router?.params.id)
|
const formId = Number(router?.params.id)
|
||||||
const {fetchUserInfo} =useUser()
|
const {user, updateUser} = useUser()
|
||||||
|
|
||||||
const [sex, setSex] = useState<DictData[]>()
|
const [sex, setSex] = useState<DictData[]>()
|
||||||
const [FormData, setFormData] = useState<User>(
|
const [FormData, setFormData] = useState<User>(
|
||||||
@@ -63,31 +63,32 @@ function Profile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const submitSucceed = (values: User) => {
|
const submitSucceed = async (values: User) => {
|
||||||
console.log(values, 'values')
|
console.log(values, 'values')
|
||||||
console.log(formId, 'formId>>')
|
console.log(formId, 'formId>>')
|
||||||
updateUserInfo(values).then(() => {
|
try {
|
||||||
fetchUserInfo().then()
|
// 使用 useUser hook 的 updateUser 方法,它会自动更新状态和本地存储
|
||||||
Taro.showToast({title: `保存成功`, icon: 'success'})
|
await updateUser(values)
|
||||||
|
// 由于 useEffect 监听了 user 变化,FormData 会自动同步更新
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
return Taro.navigateBack()
|
return Taro.navigateBack()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}).catch(() => {
|
} catch (error) {
|
||||||
Taro.showToast({
|
// updateUser 方法已经处理了错误提示,这里不需要重复显示
|
||||||
title: '保存失败',
|
console.error('提交表单失败:', error)
|
||||||
icon: 'error'
|
}
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
const submitFailed = (error: unknown) => {
|
const submitFailed = (error: unknown) => {
|
||||||
console.log(error, 'err...')
|
console.log(error, 'err...')
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadAvatar = ({detail}: ChooseAvatarEvent) => {
|
const uploadAvatar = ({detail}: ChooseAvatarEvent) => {
|
||||||
|
// 先更新本地显示的头像
|
||||||
setFormData({
|
setFormData({
|
||||||
...FormData,
|
...FormData,
|
||||||
avatar: `${detail.avatarUrl}`,
|
avatar: `${detail.avatarUrl}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
Taro.uploadFile({
|
Taro.uploadFile({
|
||||||
url: 'https://server.websoft.top/api/oss/upload',
|
url: 'https://server.websoft.top/api/oss/upload',
|
||||||
filePath: detail.avatarUrl,
|
filePath: detail.avatarUrl,
|
||||||
@@ -96,19 +97,36 @@ function Profile() {
|
|||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
TenantId
|
TenantId
|
||||||
},
|
},
|
||||||
success: (res) => {
|
success: async (res) => {
|
||||||
const data = JSON.parse(res.data);
|
const data = JSON.parse(res.data);
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
updateUserInfo({
|
try {
|
||||||
userId: FormData?.userId,
|
// 使用 useUser hook 的 updateUser 方法更新头像
|
||||||
avatar: `${data.data.thumbnail}`
|
await updateUser({
|
||||||
}).then(() => {
|
avatar: `${data.data.thumbnail}`
|
||||||
fetchUserInfo().then()
|
|
||||||
Taro.showToast({
|
|
||||||
title: '上传成功',
|
|
||||||
})
|
})
|
||||||
})
|
// 由于 useEffect 监听了 user 变化,FormData 会自动同步更新
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新头像失败:', error)
|
||||||
|
// 如果更新失败,恢复原来的头像
|
||||||
|
setFormData({
|
||||||
|
...FormData,
|
||||||
|
avatar: user?.avatar || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('上传头像失败:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '上传失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
// 恢复原来的头像
|
||||||
|
setFormData({
|
||||||
|
...FormData,
|
||||||
|
avatar: user?.avatar || ''
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -126,6 +144,13 @@ function Profile() {
|
|||||||
reload()
|
reload()
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// 监听 useUser hook 中的用户信息变化,同步更新表单数据
|
||||||
|
useEffect(() => {
|
||||||
|
if (user) {
|
||||||
|
setFormData(user)
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={'p-4'}>
|
<div className={'p-4'}>
|
||||||
|
|||||||
140
客户交易页面上拉加载功能实现说明.md
Normal file
140
客户交易页面上拉加载功能实现说明.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# 客户交易页面上拉加载更多功能实现说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
为 `src/dealer/customer/trading.tsx` 页面添加了完善的上拉加载更多功能,参考了客户管理模块和订单管理模块的成熟实现。
|
||||||
|
|
||||||
|
## 主要修改内容
|
||||||
|
|
||||||
|
### 1. 导入必要的组件
|
||||||
|
```typescript
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {Loading, InfiniteLoading, Empty, Space, SearchBar} from '@nutui/nutui-react-taro'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 添加状态管理
|
||||||
|
```typescript
|
||||||
|
const [page, setPage] = useState(1)
|
||||||
|
const [hasMore, setHasMore] = useState(true)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 优化数据获取函数 `fetchCustomerData`
|
||||||
|
- **新增参数**:
|
||||||
|
- `resetPage`: 是否重置页码(用于初始化或搜索)
|
||||||
|
- `targetPage`: 目标页码(用于加载更多)
|
||||||
|
- `searchKeyword`: 搜索关键词(支持服务端搜索)
|
||||||
|
- **改进逻辑**:
|
||||||
|
- 支持重置页面和追加数据两种模式
|
||||||
|
- 正确处理页码状态
|
||||||
|
- 添加错误处理和用户提示
|
||||||
|
- 支持服务端搜索功能
|
||||||
|
|
||||||
|
### 4. 实现 `reloadMore` 函数
|
||||||
|
```typescript
|
||||||
|
const reloadMore = async () => {
|
||||||
|
if (loading || !hasMore) return; // 防止重复加载
|
||||||
|
const nextPage = page + 1;
|
||||||
|
await fetchCustomerData(false, nextPage, searchValue);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 优化搜索功能
|
||||||
|
- **服务端搜索**:将搜索逻辑从客户端过滤改为服务端搜索
|
||||||
|
- **搜索处理函数**:
|
||||||
|
```typescript
|
||||||
|
const handleSearch = (keyword: string) => {
|
||||||
|
setSearchValue(keyword);
|
||||||
|
setList([]);
|
||||||
|
setPage(1);
|
||||||
|
setHasMore(true);
|
||||||
|
fetchCustomerData(true, 1, keyword);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
- **清空搜索**:
|
||||||
|
```typescript
|
||||||
|
const handleClearSearch = () => {
|
||||||
|
setSearchValue('');
|
||||||
|
setList([]);
|
||||||
|
setPage(1);
|
||||||
|
setHasMore(true);
|
||||||
|
fetchCustomerData(true, 1, '');
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 改进UI渲染
|
||||||
|
- **InfiniteLoading组件**:
|
||||||
|
- 设置合适的容器高度(75vh)
|
||||||
|
- 优化加载状态显示
|
||||||
|
- 改进空数据状态展示
|
||||||
|
- 添加滚动容器样式
|
||||||
|
|
||||||
|
### 7. 简化数据过滤
|
||||||
|
由于现在使用服务端搜索,简化了 `getFilteredList` 函数:
|
||||||
|
```typescript
|
||||||
|
const getFilteredList = () => {
|
||||||
|
return list; // 直接返回列表,不需要客户端过滤
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心功能特性
|
||||||
|
|
||||||
|
### ✅ 上拉加载更多
|
||||||
|
- 滚动到底部自动触发加载下一页数据
|
||||||
|
- 防止重复加载机制
|
||||||
|
- 正确的页码管理
|
||||||
|
|
||||||
|
### ✅ 服务端搜索
|
||||||
|
- 搜索关键词通过API参数传递给后端
|
||||||
|
- 搜索时重置列表和页码
|
||||||
|
- 支持清空搜索功能
|
||||||
|
|
||||||
|
### ✅ 错误处理
|
||||||
|
- 网络请求失败时显示错误提示
|
||||||
|
- 加载失败时不影响现有数据
|
||||||
|
|
||||||
|
### ✅ 加载状态
|
||||||
|
- 首次加载显示loading状态
|
||||||
|
- 加载更多时显示"加载中..."
|
||||||
|
- 无更多数据时显示"没有更多了"
|
||||||
|
|
||||||
|
### ✅ 空数据处理
|
||||||
|
- 无数据时显示友好的空状态提示
|
||||||
|
- 区分加载中和真正的无数据状态
|
||||||
|
|
||||||
|
## 与其他页面的区别
|
||||||
|
|
||||||
|
### 相比客户管理模块
|
||||||
|
- **搜索方式**:使用服务端搜索而非客户端过滤
|
||||||
|
- **数据类型**:专门处理交易客户数据(type: 3)
|
||||||
|
- **无Tab切换**:简化了状态管理逻辑
|
||||||
|
|
||||||
|
### 相比订单管理模块
|
||||||
|
- **更简洁的实现**:没有复杂的状态筛选
|
||||||
|
- **专注搜索**:主要功能是搜索和分页
|
||||||
|
|
||||||
|
## API参数说明
|
||||||
|
```typescript
|
||||||
|
const params: any = {
|
||||||
|
type: 3, // 交易客户类型
|
||||||
|
page: currentPage, // 当前页码
|
||||||
|
keywords: searchKeyword // 搜索关键词(可选)
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
1. 进入客户交易页面
|
||||||
|
2. 滚动到列表底部自动加载更多数据
|
||||||
|
3. 使用搜索框搜索特定客户
|
||||||
|
4. 点击清空按钮清除搜索条件
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
- React + TypeScript
|
||||||
|
- Taro框架
|
||||||
|
- NutUI组件库
|
||||||
|
- InfiniteLoading组件实现上拉加载
|
||||||
|
- SearchBar组件实现搜索功能
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
- 每页默认加载10条数据
|
||||||
|
- 当返回数据少于10条时认为没有更多数据
|
||||||
|
- 搜索功能使用服务端搜索,提高性能
|
||||||
|
- 所有状态变化都会正确重置页码和列表数据
|
||||||
84
客户管理上拉加载功能实现说明.md
Normal file
84
客户管理上拉加载功能实现说明.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# 客户管理模块上拉加载更多功能实现说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
参考订单管理模块 `src/user/order/order.tsx` 的实现,为客户管理模块 `src/dealer/customer/index.tsx` 添加了完善的上拉加载更多功能。
|
||||||
|
|
||||||
|
## 主要修改内容
|
||||||
|
|
||||||
|
### 1. 优化数据获取函数 `fetchCustomerData`
|
||||||
|
- **新增参数**:
|
||||||
|
- `resetPage`: 是否重置页码(用于切换tab或刷新)
|
||||||
|
- `targetPage`: 目标页码(用于加载更多)
|
||||||
|
- **改进逻辑**:
|
||||||
|
- 支持重置页面和追加数据两种模式
|
||||||
|
- 正确处理页码状态
|
||||||
|
- 添加错误处理和用户提示
|
||||||
|
|
||||||
|
### 2. 改进 `reloadMore` 函数
|
||||||
|
- 防止重复加载(检查 `loading` 和 `hasMore` 状态)
|
||||||
|
- 正确计算下一页页码
|
||||||
|
- 调用优化后的 `fetchCustomerData` 函数
|
||||||
|
|
||||||
|
### 3. 优化状态管理
|
||||||
|
- **初始化数据**:使用 `resetPage=true` 确保从第一页开始
|
||||||
|
- **Tab切换**:清空列表、重置页码和加载状态,然后重新获取数据
|
||||||
|
- **取消操作**:操作成功后重新加载当前tab数据
|
||||||
|
|
||||||
|
### 4. 改进UI渲染
|
||||||
|
- **InfiniteLoading组件**:
|
||||||
|
- 设置合适的容器高度(75vh)
|
||||||
|
- 优化加载状态显示
|
||||||
|
- 改进空数据状态展示
|
||||||
|
- 添加滚动容器样式
|
||||||
|
|
||||||
|
### 5. 修复依赖问题
|
||||||
|
- 移除 `fetchCustomerData` 函数中的 `page` 和 `list` 依赖,避免无限循环
|
||||||
|
|
||||||
|
## 核心功能特性
|
||||||
|
|
||||||
|
### ✅ 上拉加载更多
|
||||||
|
- 滚动到底部自动触发加载下一页数据
|
||||||
|
- 防止重复加载机制
|
||||||
|
- 正确的页码管理
|
||||||
|
|
||||||
|
### ✅ Tab切换支持
|
||||||
|
- 切换tab时重置列表和页码
|
||||||
|
- 每个tab独立的数据加载
|
||||||
|
|
||||||
|
### ✅ 错误处理
|
||||||
|
- 网络请求失败时显示错误提示
|
||||||
|
- 加载失败时不影响现有数据
|
||||||
|
|
||||||
|
### ✅ 加载状态
|
||||||
|
- 首次加载显示loading状态
|
||||||
|
- 加载更多时显示"加载中..."
|
||||||
|
- 无更多数据时显示"没有更多了"
|
||||||
|
|
||||||
|
### ✅ 空数据处理
|
||||||
|
- 无数据时显示友好的空状态提示
|
||||||
|
- 区分加载中和真正的无数据状态
|
||||||
|
|
||||||
|
## 参考实现
|
||||||
|
本实现参考了订单管理模块 `src/user/order/components/OrderList.tsx` 的以下核心特性:
|
||||||
|
- `reload` 函数的 `resetPage` 和 `targetPage` 参数设计
|
||||||
|
- `reloadMore` 函数的防重复加载逻辑
|
||||||
|
- `InfiniteLoading` 组件的配置和样式
|
||||||
|
- 状态管理和错误处理机制
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
1. 进入客户管理页面
|
||||||
|
2. 滚动到列表底部自动加载更多数据
|
||||||
|
3. 切换不同状态tab查看对应客户
|
||||||
|
4. 支持搜索功能(原有功能保持不变)
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
- React + TypeScript
|
||||||
|
- Taro框架
|
||||||
|
- NutUI组件库
|
||||||
|
- InfiniteLoading组件实现上拉加载
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
- 每页默认加载10条数据
|
||||||
|
- 当返回数据少于10条时认为没有更多数据
|
||||||
|
- 所有状态切换都会重置页码和列表数据
|
||||||
|
- 保持了原有的搜索和筛选功能
|
||||||
Reference in New Issue
Block a user