diff --git a/pages/institutionalStructure/donationRecord.vue b/pages/institutionalStructure/donationRecord.vue
index 0bf5ea3..3ef2ca7 100644
--- a/pages/institutionalStructure/donationRecord.vue
+++ b/pages/institutionalStructure/donationRecord.vue
@@ -30,7 +30,7 @@
/>
-
+
{
+ console.log('建制数据加载更多成功:', data.length, '条')
+ 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)
} catch (error) {
console.error('获取建制数据失败:', error)
@@ -84,11 +96,6 @@ export default {
}
},
- // 刷新数据
- refreshData() {
- this.getInstitutionalData()
- },
-
// 处理查看详细
handleViewDetail(data) {
console.log('查看详细:', data.item)
diff --git a/pages/institutionalStructure/mixins/README.md b/pages/institutionalStructure/mixins/README.md
new file mode 100644
index 0000000..1fedd12
--- /dev/null
+++ b/pages/institutionalStructure/mixins/README.md
@@ -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. 合理使用缓存功能,避免内存泄漏
\ No newline at end of file
diff --git a/pages/institutionalStructure/mixins/data-manager.js b/pages/institutionalStructure/mixins/data-manager.js
index ce60296..4951be0 100644
--- a/pages/institutionalStructure/mixins/data-manager.js
+++ b/pages/institutionalStructure/mixins/data-manager.js
@@ -1,6 +1,7 @@
/**
* 数据管理 Mixin
* 提供通用的数据获取、分页加载功能
+ * 支持多种数据格式和灵活的配置
*/
export const dataManagerMixin = {
data() {
@@ -12,85 +13,221 @@ export const dataManagerMixin = {
// 分页参数
pageNum: 1,
pageSize: 10,
- hasMore: true
+ hasMore: true,
+ // 总数据量
+ total: 0,
+ // 当前查询参数
+ currentParams: {}
}
},
methods: {
/**
* 获取数据
- * @param {boolean} isLoadMore 是否为加载更多
- * @param {Function} apiCall API调用函数
- * @param {Function} dataTransformer 数据转换函数
+ * @param {Object} options 配置选项
+ * @param {boolean} options.isLoadMore 是否为加载更多
+ * @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) {
- // 只在加载更多时设置loading状态,初始加载使用页面级别loading
- if (isLoadMore) {
- this.loading = true
+ async fetchData(options = {}) {
+ const {
+ isLoadMore = false,
+ 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 {
+ // 更新页码
if (isLoadMore) {
this.pageNum++
} else {
this.pageNum = 1
}
- // 调用API
- const response = await apiCall({
+ // 构建请求参数
+ const requestParams = {
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) {
- // 转换数据
- 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) {
this.dataList = [...this.dataList, ...newData]
} else {
this.dataList = newData
}
- // 判断是否还有更多数据
- this.hasMore = response.rows.length === this.pageSize
+ console.log('更新后的 dataList:', this.dataList)
+
+ // 更新状态
+ this.total = total
+ this.hasMore = newData.length === this.pageSize
+ this.currentParams = { ...requestParams }
+
+ // 成功回调
+ if (onSuccess) {
+ onSuccess(newData, response)
+ }
} else {
- uni.showToast({
- title: response.msg || '获取数据失败',
- icon: 'none'
- })
+ const errorMsg = response.msg || '获取数据失败'
+ if (showError) {
+ uni.showToast({
+ title: errorMsg,
+ icon: 'none'
+ })
+ }
+ if (onError) {
+ onError(errorMsg, response)
+ }
}
} catch (error) {
console.error('获取数据失败:', error)
- uni.showToast({
- title: '网络错误',
- icon: 'none'
- })
+ const errorMsg = '网络错误'
+ if (showError) {
+ uni.showToast({
+ title: errorMsg,
+ icon: 'none'
+ })
+ }
+ if (onError) {
+ onError(errorMsg, error)
+ }
} finally {
- // 只在加载更多时清除loading状态
- if (isLoadMore) {
- this.loading = false
+ if (showLoading) {
+ this.loading = false
}
}
},
/**
* 刷新数据
- * @param {Function} apiCall API调用函数
- * @param {Function} dataTransformer 数据转换函数
+ * @param {Object} options 配置选项
*/
- refreshData(apiCall, dataTransformer) {
- this.fetchData(false, apiCall, dataTransformer)
+ refreshData(options = {}) {
+ return this.fetchData({
+ isLoadMore: false,
+ ...options
+ })
},
/**
* 加载更多数据
- * @param {Function} apiCall API调用函数
- * @param {Function} dataTransformer 数据转换函数
+ * @param {Object} options 配置选项
*/
- loadMoreData(apiCall, dataTransformer) {
+ loadMoreData(options = {}) {
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
}
}
}
diff --git a/pages/institutionalStructure/mixins/donation-mixin.js b/pages/institutionalStructure/mixins/donation-mixin.js
index 44aedc6..026741f 100644
--- a/pages/institutionalStructure/mixins/donation-mixin.js
+++ b/pages/institutionalStructure/mixins/donation-mixin.js
@@ -1,34 +1,34 @@
/**
* 捐款记录相关 Mixin
* 提供捐款记录的数据获取、搜索、分页等功能
+ * 基于重构后的 data-manager.js
*/
+import { dataManagerMixin } from './data-manager.js'
import { getDonorList } from '@/api/donor/donor.js'
import { getInstitutionalDetail } from '@/api/institutionalStructure/institutionalStructureDetail.js'
export const donationMixin = {
+ mixins: [dataManagerMixin],
+
data() {
return {
- // 捐款记录相关数据
- donationList: [],
+ // 项目信息
projectInfo: {},
- loading: false,
+ // 搜索关键词
searchKeyword: '',
- formedId: '',
- // 分页参数
- pageNum: 1,
- pageSize: 10,
- hasMore: true
+ // 项目ID
+ formedId: ''
}
},
computed: {
// 计算总造价(从项目详情获取,如果没有则计算捐款总和)
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() {
- 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) {
- this.pageNum = 1 // 重置页码
- this.loadDonationRecords(val)
- },
-
- /**
- * 筛选功能
- */
- onFilter() {
- uni.showToast({ title: '筛选功能开发中', icon: 'none' })
+ transformDonationData(dataArray) {
+ return dataArray.map(item => ({
+ id: item.id,
+ name: item.realName,
+ amount: item.amount,
+ time: this.formatDate(item.donationDate)
+ }))
},
/**
@@ -156,6 +86,93 @@ export const donationMixin = {
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
@@ -175,6 +192,21 @@ export const donationMixin = {
icon: 'none'
})
}
+ },
+
+ /**
+ * 重置搜索
+ */
+ resetSearch() {
+ this.searchKeyword = ''
+ this.loadDonationRecords()
+ },
+
+ /**
+ * 刷新数据
+ */
+ async refreshDonationData() {
+ await this.loadDonationRecords(this.searchKeyword)
}
}
}
\ No newline at end of file