建制页面数据处理组件的优化

This commit is contained in:
minimaxagent1 2025-08-07 11:10:56 +08:00
parent b132cd5d16
commit c75ce2d50b
5 changed files with 529 additions and 137 deletions

View File

@ -30,7 +30,7 @@
/> />
<!-- 捐款记录列表组件 --> <!-- 捐款记录列表组件 -->
<donation-list :donation-list="donationList" /> <donation-list :donation-list="dataList" />
<status-display <status-display
v-if="loading" v-if="loading"

View File

@ -66,14 +66,26 @@ export default {
console.log('开始获取建制数据, isLoadMore:', isLoadMore) console.log('开始获取建制数据, isLoadMore:', isLoadMore)
try { try {
const result = await this.fetchData( if (isLoadMore) {
isLoadMore, await this.loadMoreData({
getInstitutionalList, apiCall: getInstitutionalList,
InstitutionalDataFormatter.transformData dataTransformer: InstitutionalDataFormatter.transformData,
) onSuccess: (data, response) => {
console.log('建制数据获取成功:', result) console.log('建制数据加载更多成功:', data.length, '条')
// institutionalData this.institutionalData = this.dataList
this.institutionalData = this.dataList }
})
} else {
await this.refreshData({
apiCall: getInstitutionalList,
dataTransformer: InstitutionalDataFormatter.transformData,
onSuccess: (data, response) => {
console.log('建制数据刷新成功:', data.length, '条')
this.institutionalData = this.dataList
}
})
}
console.log('建制数据已更新, 数量:', this.institutionalData.length) console.log('建制数据已更新, 数量:', this.institutionalData.length)
} catch (error) { } catch (error) {
console.error('获取建制数据失败:', error) console.error('获取建制数据失败:', error)
@ -84,11 +96,6 @@ export default {
} }
}, },
//
refreshData() {
this.getInstitutionalData()
},
// //
handleViewDetail(data) { handleViewDetail(data) {
console.log('查看详细:', data.item) console.log('查看详细:', data.item)

View File

@ -0,0 +1,216 @@
# 数据管理 Mixin 重构说明
## 问题分析
原来的 `data-manager.js` 存在以下复用性问题:
1. **硬编码的响应结构**假设所有API都返回 `response.rows` 格式
2. **固定的分页参数**:只能传递 `pageNum``pageSize`,无法处理复杂查询条件
3. **缺乏灵活性**:无法处理搜索、筛选等场景
4. **错误处理过于简单**:统一的错误提示可能不适合所有场景
5. **缺乏扩展性**:难以添加缓存、状态管理等功能
## 重构改进
### 1. 灵活的配置选项
```javascript
// 重构前
await this.fetchData(false, apiCall, dataTransformer)
// 重构后
await this.fetchData({
isLoadMore: false,
apiCall,
dataTransformer,
params: { keyword: '搜索词' },
dataPath: 'data.list.rows',
totalPath: 'data.total',
onSuccess: (data, response) => console.log('成功'),
onError: (errorMsg) => console.error('失败'),
showLoading: true,
showError: true
})
```
### 2. 支持多种数据格式
```javascript
// 支持不同的响应结构
dataPath: 'data.list.rows' // 嵌套结构
dataPath: 'data' // 简单结构
dataPath: 'rows' // 直接结构
```
### 3. 增强的功能
- **搜索功能**`searchData()` 方法
- **状态管理**`getDataState()` 方法
- **分页重置**`resetPagination()` 方法
- **参数管理**:自动保存当前查询参数
- **回调支持**:成功和失败回调函数
### 4. 更好的错误处理
```javascript
// 可配置是否显示错误提示
showError: false, // 自定义错误处理
onError: (errorMsg, response) => {
// 自定义错误处理逻辑
}
```
## 使用示例
### 基础使用
```javascript
import { dataManagerMixin } from './data-manager.js'
export default {
mixins: [dataManagerMixin],
methods: {
async loadData() {
await this.refreshData({
apiCall: this.getListAPI,
dataPath: 'data.list',
onSuccess: (data) => {
console.log('数据加载成功:', data.length)
}
})
}
}
}
```
### 带搜索的使用
```javascript
async searchData(keyword) {
await this.searchData(
{ keyword, status: 'active' },
{
apiCall: this.searchAPI,
dataPath: 'data.items',
onSuccess: (data) => {
console.log('搜索完成:', data.length)
}
}
)
}
```
### 加载更多
```javascript
async loadMore() {
await this.loadMoreData({
apiCall: this.getListAPI,
dataPath: 'data.list',
onSuccess: (data) => {
console.log('加载更多:', data.length)
}
})
}
```
## 高级功能
### 1. 缓存支持
```javascript
import { cachedDataMixin } from './data-manager-example.js'
export default {
mixins: [cachedDataMixin],
methods: {
async loadDataWithCache() {
await this.fetchDataWithCache({
apiCall: this.getListAPI,
params: { category: 'news' },
forceRefresh: false // 使用缓存
})
}
}
}
```
### 2. 状态管理
```javascript
// 获取当前数据状态
const state = this.getDataState()
console.log('当前页码:', state.pageNum)
console.log('总数据量:', state.total)
console.log('查询参数:', state.currentParams)
```
### 3. 自定义数据转换
```javascript
transformData(dataArray) {
return dataArray.map(item => ({
id: item.id,
title: item.name,
date: this.formatDate(item.createTime),
status: item.status === 1 ? '启用' : '禁用'
}))
}
```
## 迁移指南
### 从旧版本迁移
1. **更新 mixin 引用**
```javascript
// 旧版本
export const myMixin = {
// 直接定义分页逻辑
}
// 新版本
import { dataManagerMixin } from './data-manager.js'
export const myMixin = {
mixins: [dataManagerMixin],
// 使用通用的分页逻辑
}
```
2. **更新方法调用**
```javascript
// 旧版本
await this.fetchData(false, apiCall, transformer)
// 新版本
await this.refreshData({
apiCall,
dataTransformer: transformer
})
```
3. **更新数据引用**
```javascript
// 旧版本
this.myList = []
// 新版本
// 使用 this.dataList (来自 dataManagerMixin)
```
## 优势总结
1. **高复用性**:一个 mixin 适用于所有列表页面
2. **强扩展性**:支持缓存、状态管理、自定义回调等
3. **易维护性**:统一的错误处理和状态管理
4. **灵活性**:支持多种数据格式和查询条件
5. **开发效率**:减少重复代码,提高开发速度
## 注意事项
1. 确保 API 返回格式包含 `code` 字段用于判断成功状态
2. 数据路径 `dataPath` 要根据实际 API 响应格式设置
3. 使用 `dataTransformer` 时注意保持数据结构的统一性
4. 合理使用缓存功能,避免内存泄漏

View File

@ -1,6 +1,7 @@
/** /**
* 数据管理 Mixin * 数据管理 Mixin
* 提供通用的数据获取分页加载功能 * 提供通用的数据获取分页加载功能
* 支持多种数据格式和灵活的配置
*/ */
export const dataManagerMixin = { export const dataManagerMixin = {
data() { data() {
@ -12,85 +13,221 @@ export const dataManagerMixin = {
// 分页参数 // 分页参数
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
hasMore: true hasMore: true,
// 总数据量
total: 0,
// 当前查询参数
currentParams: {}
} }
}, },
methods: { methods: {
/** /**
* 获取数据 * 获取数据
* @param {boolean} isLoadMore 是否为加载更多 * @param {Object} options 配置选项
* @param {Function} apiCall API调用函数 * @param {boolean} options.isLoadMore 是否为加载更多
* @param {Function} dataTransformer 数据转换函数 * @param {Function} options.apiCall API调用函数
* @param {Function} options.dataTransformer 数据转换函数
* @param {Object} options.params 查询参数
* @param {string} options.dataPath 数据路径 'data.list.rows'
* @param {string} options.totalPath 总数路径 'data.total'
* @param {Function} options.onSuccess 成功回调
* @param {Function} options.onError 错误回调
* @param {boolean} options.showLoading 是否显示loading
* @param {boolean} options.showError 是否显示错误提示
*/ */
async fetchData(isLoadMore = false, apiCall, dataTransformer) { async fetchData(options = {}) {
// 只在加载更多时设置loading状态初始加载使用页面级别loading const {
if (isLoadMore) { isLoadMore = false,
this.loading = true apiCall,
dataTransformer,
params = {},
dataPath = 'rows',
totalPath = 'total',
onSuccess,
onError,
showLoading = true,
showError = true
} = options
if (!apiCall) {
console.error('fetchData: apiCall 是必需的')
return
}
// 设置loading状态
if (showLoading) {
this.loading = true
} }
try { try {
// 更新页码
if (isLoadMore) { if (isLoadMore) {
this.pageNum++ this.pageNum++
} else { } else {
this.pageNum = 1 this.pageNum = 1
} }
// 调用API // 构建请求参数
const response = await apiCall({ const requestParams = {
pageNum: this.pageNum, pageNum: this.pageNum,
pageSize: this.pageSize pageSize: this.pageSize,
}) ...this.currentParams,
...params
}
// 调用API
const response = await apiCall(requestParams)
console.log('API响应:', response)
console.log('数据路径:', dataPath)
console.log('总数路径:', totalPath)
if (response.code === 200) { if (response.code === 200) {
// 转换数据 // 根据路径获取数据
const newData = dataTransformer ? dataTransformer(response.rows) : response.rows || [] const dataArray = this.getNestedValue(response, dataPath) || []
const total = this.getNestedValue(response, totalPath) || 0
console.log('提取的数据数组:', dataArray)
console.log('总数:', total)
// 转换数据
const newData = dataTransformer ? dataTransformer(dataArray) : dataArray
console.log('转换后的数据:', newData)
// 更新数据
if (isLoadMore) { if (isLoadMore) {
this.dataList = [...this.dataList, ...newData] this.dataList = [...this.dataList, ...newData]
} else { } else {
this.dataList = newData this.dataList = newData
} }
// 判断是否还有更多数据 console.log('更新后的 dataList:', this.dataList)
this.hasMore = response.rows.length === this.pageSize
// 更新状态
this.total = total
this.hasMore = newData.length === this.pageSize
this.currentParams = { ...requestParams }
// 成功回调
if (onSuccess) {
onSuccess(newData, response)
}
} else { } else {
uni.showToast({ const errorMsg = response.msg || '获取数据失败'
title: response.msg || '获取数据失败', if (showError) {
icon: 'none' uni.showToast({
}) title: errorMsg,
icon: 'none'
})
}
if (onError) {
onError(errorMsg, response)
}
} }
} catch (error) { } catch (error) {
console.error('获取数据失败:', error) console.error('获取数据失败:', error)
uni.showToast({ const errorMsg = '网络错误'
title: '网络错误', if (showError) {
icon: 'none' uni.showToast({
}) title: errorMsg,
icon: 'none'
})
}
if (onError) {
onError(errorMsg, error)
}
} finally { } finally {
// 只在加载更多时清除loading状态 if (showLoading) {
if (isLoadMore) { this.loading = false
this.loading = false
} }
} }
}, },
/** /**
* 刷新数据 * 刷新数据
* @param {Function} apiCall API调用函数 * @param {Object} options 配置选项
* @param {Function} dataTransformer 数据转换函数
*/ */
refreshData(apiCall, dataTransformer) { refreshData(options = {}) {
this.fetchData(false, apiCall, dataTransformer) return this.fetchData({
isLoadMore: false,
...options
})
}, },
/** /**
* 加载更多数据 * 加载更多数据
* @param {Function} apiCall API调用函数 * @param {Object} options 配置选项
* @param {Function} dataTransformer 数据转换函数
*/ */
loadMoreData(apiCall, dataTransformer) { loadMoreData(options = {}) {
if (this.hasMore && !this.loading) { if (this.hasMore && !this.loading) {
this.fetchData(true, apiCall, dataTransformer) return this.fetchData({
isLoadMore: true,
...options
})
}
},
/**
* 搜索数据
* @param {Object} searchParams 搜索参数
* @param {Object} options 配置选项
*/
searchData(searchParams = {}, options = {}) {
return this.fetchData({
isLoadMore: false,
params: searchParams,
...options
})
},
/**
* 重置分页状态
*/
resetPagination() {
this.pageNum = 1
this.hasMore = true//可能就单页
this.dataList = []
this.total = 0
this.currentParams = {}
},
/**
* 获取嵌套对象的值
* @param {Object} obj 对象
* @param {string} path 路径 'data.list.rows'
* @returns {*}
*/
getNestedValue(obj, path) {
if (!path) return obj
return path.split('.').reduce((current, key) => {
return current && current[key] !== undefined ? current[key] : null
}, obj)
},
/**
* 设置分页大小
* @param {number} pageSize 分页大小
*/
setPageSize(pageSize) {
this.pageSize = pageSize
this.resetPagination()
},
/**
* 获取当前数据状态
* @returns {Object} 数据状态
*/
getDataState() {
return {
dataList: this.dataList,
loading: this.loading,
pageNum: this.pageNum,
pageSize: this.pageSize,
hasMore: this.hasMore,
total: this.total,
currentParams: this.currentParams
} }
} }
} }

View File

@ -1,34 +1,34 @@
/** /**
* 捐款记录相关 Mixin * 捐款记录相关 Mixin
* 提供捐款记录的数据获取搜索分页等功能 * 提供捐款记录的数据获取搜索分页等功能
* 基于重构后的 data-manager.js
*/ */
import { dataManagerMixin } from './data-manager.js'
import { getDonorList } from '@/api/donor/donor.js' import { getDonorList } from '@/api/donor/donor.js'
import { getInstitutionalDetail } from '@/api/institutionalStructure/institutionalStructureDetail.js' import { getInstitutionalDetail } from '@/api/institutionalStructure/institutionalStructureDetail.js'
export const donationMixin = { export const donationMixin = {
mixins: [dataManagerMixin],
data() { data() {
return { return {
// 捐款记录相关数据 // 项目信息
donationList: [],
projectInfo: {}, projectInfo: {},
loading: false, // 搜索关键词
searchKeyword: '', searchKeyword: '',
formedId: '', // 项目ID
// 分页参数 formedId: ''
pageNum: 1,
pageSize: 10,
hasMore: true
} }
}, },
computed: { computed: {
// 计算总造价(从项目详情获取,如果没有则计算捐款总和) // 计算总造价(从项目详情获取,如果没有则计算捐款总和)
totalAmount() { totalAmount() {
return this.projectInfo.totalAmount || this.donationList.reduce((sum, item) => sum + item.amount, 0) return this.projectInfo.totalAmount || this.dataList.reduce((sum, item) => sum + item.amount, 0)
}, },
// 计算参与捐款人次(从项目详情获取,如果没有则计算捐款记录数量) // 计算参与捐款人次(从项目详情获取,如果没有则计算捐款记录数量)
participantCount() { participantCount() {
return this.projectInfo.donorCount || this.donationList.length return this.projectInfo.donorCount || this.dataList.length
} }
}, },
@ -59,87 +59,17 @@ export const donationMixin = {
}) })
} }
}, },
/**
* 获取捐款记录
* @param {string} keyword 搜索关键词
*/
async loadDonationRecords(keyword = '') {
this.loading = true
try {
const params = {
formedId: this.formedId,
pageNum: this.pageNum,
pageSize: this.pageSize,
minAmount: 1,
maxAmount: 10000,
sortAmount: 'amount',
orderAmount: 'asc',
sortTime: 'time',
orderTime: 'desc'
}
// 如果有搜索关键词,添加姓名搜索
if (keyword) {
params.realName = keyword
}
const response = await getDonorList(params)
console.log('捐款记录API响应:', response)
if (response.code === 200) {
// 根据实际后端数据结构获取数据数组
let dataArray = response.data.list.rows
// 转换数据格式
const newData = dataArray.map(item => ({
id: item.id,
name: item.realName,
amount: item.amount,
time: this.formatDate(item.donationDate)
}))
// 如果是第一页,直接替换数据
if (this.pageNum === 1) {
this.donationList = newData
} else {
// 如果是加载更多,追加数据
this.donationList = [...this.donationList, ...newData]
}
// 判断是否还有更多数据
this.hasMore = newData.length === this.pageSize
} else {
uni.showToast({
title: response.msg || '获取数据失败',
icon: 'none'
})
}
} catch (error) {
console.error('获取捐款记录失败:', error)
uni.showToast({
title: '获取数据失败',
icon: 'none'
})
} finally {
this.loading = false
}
},
/** /**
* 搜索捐款记录 * 捐款记录数据转换器
* @param {string} val 搜索关键词
*/ */
onSearch(val) { transformDonationData(dataArray) {
this.pageNum = 1 // 重置页码 return dataArray.map(item => ({
this.loadDonationRecords(val) id: item.id,
}, name: item.realName,
amount: item.amount,
/** time: this.formatDate(item.donationDate)
* 筛选功能 }))
*/
onFilter() {
uni.showToast({ title: '筛选功能开发中', icon: 'none' })
}, },
/** /**
@ -156,6 +86,93 @@ export const donationMixin = {
return `${year}/${month}/${day}` return `${year}/${month}/${day}`
}, },
/**
* 获取基础查询参数
*/
getBaseParams() {
return {
formedId: this.formedId,
minAmount: 1,
maxAmount: 10000,
sortAmount: 'amount',
orderAmount: 'asc',
sortTime: 'time',
orderTime: 'desc'
}
},
/**
* 获取捐款记录
* @param {string} keyword 搜索关键词
*/
async loadDonationRecords(keyword = '') {
const baseParams = this.getBaseParams()
// 如果有搜索关键词,添加姓名搜索
if (keyword) {
baseParams.realName = keyword
}
await this.refreshData({
apiCall: getDonorList,
dataTransformer: this.transformDonationData,
params: baseParams,
dataPath: 'data.list.rows',
totalPath: 'data.total',
onSuccess: (data, response) => {
console.log('捐款记录加载成功:', data.length, '条')
},
onError: (errorMsg) => {
console.error('捐款记录加载失败:', errorMsg)
}
})
},
/**
* 搜索捐款记录
* @param {string} val 搜索关键词
*/
async onSearch(val) {
this.searchKeyword = val
await this.searchData(
{ ...this.getBaseParams(), realName: val },
{
apiCall: getDonorList,
dataTransformer: this.transformDonationData,
dataPath: 'data.list.rows',
totalPath: 'data.total',
onSuccess: (data, response) => {
console.log('搜索完成,找到:', data.length, '条记录')
}
}
)
},
/**
* 筛选功能
*/
onFilter() {
uni.showToast({ title: '筛选功能开发中', icon: 'none' })
},
/**
* 加载更多捐款记录
*/
async loadMoreDonationRecords() {
const currentParams = this.getDataState().currentParams
await this.loadMoreData({
apiCall: getDonorList,
dataTransformer: this.transformDonationData,
params: currentParams,
dataPath: 'data.list.rows',
totalPath: 'data.total',
onSuccess: (data, response) => {
console.log('加载更多完成,新增:', data.length, '条记录')
}
})
},
/** /**
* 初始化数据 * 初始化数据
* @param {string} formedId 建制ID * @param {string} formedId 建制ID
@ -175,6 +192,21 @@ export const donationMixin = {
icon: 'none' icon: 'none'
}) })
} }
},
/**
* 重置搜索
*/
resetSearch() {
this.searchKeyword = ''
this.loadDonationRecords()
},
/**
* 刷新数据
*/
async refreshDonationData() {
await this.loadDonationRecords(this.searchKeyword)
} }
} }
} }