瀏覽代碼

Implement caching mechanism for data fetching in various analysis components to improve performance and reduce redundant API calls. Added cache management logic to handle chart and table data, ensuring efficient data retrieval and updates. Enhanced error handling and updated comments for clarity.

Zenas 4 天之前
父節點
當前提交
1e47f69fdb

+ 142 - 90
src/views/adweb/data/components/DestPortAnalysis.vue

@@ -69,6 +69,17 @@ const viewMode = ref('chart');
 const sortMode = ref('count');
 const allBuckets = ref({});
 
+// 添加缓存对象
+const cache = ref({
+    chart: new Map(), // 用于存储图表数据
+    table: new Map(), // 用于存储表格数据
+});
+
+// 生成缓存key
+const getCacheKey = (params) => {
+    return `${params.sort}_${params.page}_${params.page_size}_${JSON.stringify(props.queryParam)}`;
+};
+
 // 定义表格列
 const columns = [
     {
@@ -155,7 +166,6 @@ const initChart = () => {
 };
 
 const updateChart = async () => {
-    let chartData = [];
     const params = {
         sort: sortMode.value,
         page: 1,
@@ -163,116 +173,154 @@ const updateChart = async () => {
         ...props.queryParam,
     };
 
+    const cacheKey = getCacheKey(params);
+    // 检查缓存中是否存在数据
+    if (cache.value.chart.has(cacheKey)) {
+        const cachedData = cache.value.chart.get(cacheKey);
+        setChartOptions(cachedData.chartData, cachedData.total);
+        return;
+    }
+
+    loading.value = true;
     try {
         const res = await getDestPortReport(params);
         if (res.result.data.result && res.result.data.result.buckets) {
-            chartData = res.result.data.result.buckets;
+            const chartData = res.result.data.result.buckets;
             allBuckets.value = res.result.data.result.allBuckets;
+            
+            // 将数据存入缓存
+            cache.value.chart.set(cacheKey, {
+                chartData,
+                total: allBuckets.value,
+            });
+            
+            setChartOptions(chartData, allBuckets.value);
         }
+    } catch (error) {
+        console.error('Failed to update chart:', error);
+    } finally {
+        loading.value = false;
+    }
+};
 
-        if (chartData.length === 0) {
-            console.warn('No data available for chart');
-            return;
-        }
+// 新增设置图表选项的方法
+const setChartOptions = (chartData, totalBuckets) => {
+    if (chartData.length === 0) {
+        console.warn('No data available for chart');
+        return;
+    }
 
-        const valueKeyMap = {
-            count: 'count',
-            weight: 'sum_weight',
-            amount: 'sum_amount'
-        };
+    const valueKeyMap = {
+        count: 'count',
+        weight: 'sum_weight',
+        amount: 'sum_amount'
+    };
 
-        const totalKeyMap = {
-            count: 'count',
-            weight: 'weight_sum',
-            amount: 'amount_sum'
-        };
-
-        const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0;
-        const data = chartData.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]]));
-
-        const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
-
-        const option = {
-            tooltip: {
-                trigger: 'item',
-                formatter: (params) => {
-                    const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
-                    return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
-                }
-            },
-            legend: {
-                orient: 'vertical',
-                right: 10,
-                top: 'center',
-                type: 'scroll'
-            },
-            series: [
-                {
-                    name: '交易次数',
-                    type: 'pie',
-                    radius: ['40%', '70%'],
-                    avoidLabelOverlap: true,
-                    itemStyle: {
-                        borderRadius: 10,
-                        borderColor: '#fff',
-                        borderWidth: 2
-                    },
+    const totalKeyMap = {
+        count: 'count',
+        weight: 'weight_sum',
+        amount: 'amount_sum'
+    };
+
+    const total = totalBuckets[totalKeyMap[sortMode.value]] || 0;
+    const data = chartData.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]]));
+
+    const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
+
+    const option = {
+        tooltip: {
+            trigger: 'item',
+            formatter: (params) => {
+                const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
+                return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
+            }
+        },
+        legend: {
+            orient: 'vertical',
+            right: 10,
+            top: 'center',
+            type: 'scroll'
+        },
+        series: [
+            {
+                name: '交易次数',
+                type: 'pie',
+                radius: ['40%', '70%'],
+                avoidLabelOverlap: true,
+                itemStyle: {
+                    borderRadius: 10,
+                    borderColor: '#fff',
+                    borderWidth: 2
+                },
+                label: {
+                    show: true,
+                    formatter: (params) => {
+                        const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
+                        return `${params.name}\n${percent}%`;
+                    }
+                },
+                emphasis: {
                     label: {
                         show: true,
-                        formatter: (params) => {
-                            const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
-                            return `${params.name}\n${percent}%`;
-                        }
-                    },
-                    emphasis: {
-                        label: {
-                            show: true,
-                            fontSize: 14,
-                            fontWeight: 'bold'
-                        }
-                    },
-                    data: [
-                        ...data.map(item => ({
-                            name: item.val_cn || item.val,
-                            value: Number(item[valueKeyMap[sortMode.value]])
-                        })),
-                        {
-                            name: '其他',
-                            value: otherCount
-                        }
-                    ]
-                }
-            ]
-        };
-
-        nextTick(() => {
-            setOptions(option, true, true); // Ensure options are set after DOM updates
-        });
-    } catch (error) {
-        console.error('Failed to update chart:', error);
-    }
+                        fontSize: 14,
+                        fontWeight: 'bold'
+                    }
+                },
+                data: [
+                    ...data.map(item => ({
+                        name: item.val_cn || item.val,
+                        value: Number(item[valueKeyMap[sortMode.value]])
+                    })),
+                    {
+                        name: '其他',
+                        value: otherCount
+                    }
+                ]
+            }
+        ]
+    };
+
+    nextTick(() => {
+        setOptions(option, true, true);
+    });
 };
 
-// Replace registerTransactionTable with this new method
+// 修改handleTableChange方法
 const handleTableChange = async (pag, filters, sorter) => {
+    const params = {
+        sort: sortMode.value,
+        page: pag.current,
+        page_size: pag.pageSize,
+        ...props.queryParam,
+    };
+
+    const cacheKey = getCacheKey(params);
+    // 检查缓存中是否存在数据
+    if (cache.value.table.has(cacheKey)) {
+        const cachedData = cache.value.table.get(cacheKey);
+        tableData.value = cachedData.buckets;
+        pagination.value = cachedData.pagination;
+        return;
+    }
+
     loading.value = true;
     try {
-        const params = {
-            sort: sortMode.value,
-            page: pag.current,
-            page_size: pag.pageSize,
-            ...props.queryParam,
-        };
-
         const res = await getDestPortReport(params);
         if (res.result.data.result && res.result.data.result.buckets) {
-            pagination.value = {
+            const newPagination = {
                 current: pag.current,
                 pageSize: pag.pageSize,
                 total: res.result.data.result.numBuckets || 0,
             };
+            
+            // 将数据存入缓存
+            cache.value.table.set(cacheKey, {
+                buckets: res.result.data.result.buckets,
+                pagination: newPagination,
+            });
+
+            pagination.value = newPagination;
             tableData.value = res.result.data.result.buckets;
-
         }
     } catch (error) {
         console.error('Failed to fetch data:', error);
@@ -281,10 +329,14 @@ const handleTableChange = async (pag, filters, sorter) => {
     }
 };
 
-// 监听数据变化
+// 修改watch监听,当查询参数改变时清除缓存
 watch(
     () => props.queryParam,
     () => {
+        // 清除缓存
+        cache.value.chart.clear();
+        cache.value.table.clear();
+        
         if (viewMode.value === 'chart') {
             updateChart();
         } else {

+ 129 - 82
src/views/adweb/data/components/DestinationCountryAnalysis.vue

@@ -69,6 +69,22 @@
     const sortMode = ref('count');
     const allBuckets = ref({});
     
+    // 添加缓存相关的响应式变量
+    const cache = ref({
+      chart: new Map(), // 图表数据缓存
+      table: new Map(), // 表格数据缓存
+    });
+  
+    // 获取缓存key
+    const getCacheKey = (params) => {
+      return JSON.stringify({
+        sort: params.sort,
+        page: params.page,
+        page_size: params.page_size,
+        ...params,
+      });
+    };
+  
     // 定义表格列
     const columns = [
       {
@@ -158,98 +174,108 @@
         ...props.queryParam,
       };
   
-      try {
-        const res = await getDestCountryReport(params);
-  
-        if (res && res.result && res.result.data && res.result.data.result && res.result.data.result.buckets) {
-          chartData.value = res.result.data.result.buckets;
-          allBuckets.value = res.result.data.result.allBuckets;
-        } else {
-          console.error('Unexpected API response structure:', res);
-          return; // Exit if the response structure is not as expected
+      const cacheKey = getCacheKey(params);
+      // 检查缓存
+      if (cache.value.chart.has(cacheKey)) {
+        const cachedData = cache.value.chart.get(cacheKey);
+        chartData.value = cachedData.buckets;
+        allBuckets.value = cachedData.allBuckets;
+      } else {
+        try {
+          const res = await getDestCountryReport(params);
+          if (res?.result?.data?.result?.buckets) {
+            chartData.value = res.result.data.result.buckets;
+            allBuckets.value = res.result.data.result.allBuckets;
+            // 存入缓存
+            cache.value.chart.set(cacheKey, {
+              buckets: res.result.data.result.buckets,
+              allBuckets: res.result.data.result.allBuckets,
+            });
+          }
+        } catch (error) {
+          console.error('Failed to fetch data:', error);
+          return;
         }
+      }
   
-        const valueKeyMap = {
-          count: 'count',
-          weight: 'sum_weight',
-          amount: 'sum_amount',
-        };
+      const valueKeyMap = {
+        count: 'count',
+        weight: 'sum_weight',
+        amount: 'sum_amount',
+      };
   
-        // 获取前10个数据并计算总数
-        const data = chartData.value.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]])).slice(0, 10);
+      // 获取前10个数据并计算总数
+      const data = chartData.value.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]])).slice(0, 10);
   
-        // 根据 sortMode 映射到 allBuckets 的对应关系
-        const totalKeyMap = {
-          count: 'count',
-          weight: 'weight_sum',
-          amount: 'amount_sum',
-        };
+      // 根据 sortMode 映射到 allBuckets 的对应关系
+      const totalKeyMap = {
+        count: 'count',
+        weight: 'weight_sum',
+        amount: 'amount_sum',
+      };
   
-        const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0; // 使用映射获取总数
-        const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
+      const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0; // 使用映射获取总数
+      const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
   
-        const option = {
-          tooltip: {
-            trigger: 'item',
-            formatter: (params) => {
-              const percent = ((params.value / total) * 100).toFixed(2);
-              return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
-            },
-          },
-          legend: {
-            orient: 'vertical',
-            right: 10,
-            top: 'center',
-            type: 'scroll',
+      const option = {
+        tooltip: {
+          trigger: 'item',
+          formatter: (params) => {
+            const percent = ((params.value / total) * 100).toFixed(2);
+            return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
           },
-          series: [
-            {
-              name: '交易次数',
-              type: 'pie',
-              radius: ['40%', '70%'],
-              avoidLabelOverlap: true,
-              itemStyle: {
-                borderRadius: 10,
-                borderColor: '#fff',
-                borderWidth: 2,
+        },
+        legend: {
+          orient: 'vertical',
+          right: 10,
+          top: 'center',
+          type: 'scroll',
+        },
+        series: [
+          {
+            name: '交易次数',
+            type: 'pie',
+            radius: ['40%', '70%'],
+            avoidLabelOverlap: true,
+            itemStyle: {
+              borderRadius: 10,
+              borderColor: '#fff',
+              borderWidth: 2,
+            },
+            label: {
+              show: true,
+              formatter: (params) => {
+                const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.0;
+                return `${params.name}\n${percent}%`;
               },
+            },
+            emphasis: {
               label: {
                 show: true,
-                formatter: (params) => {
-                  const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.0;
-                  return `${params.name}\n${percent}%`;
-                },
+                fontSize: 14,
+                fontWeight: 'bold',
               },
-              emphasis: {
-                label: {
-                  show: true,
-                  fontSize: 14,
-                  fontWeight: 'bold',
-                },
-              },
-              data: [
-                ...data.map((item) => ({
-                  name: item.val_cn || item.val,
-                  value: Number(item[valueKeyMap[sortMode.value]]),
-                })),
-                {
-                  name: '其他',
-                  value: otherCount,
-                },
-              ],
             },
-          ],
-        };
+            data: [
+              ...data.map((item) => ({
+                name: item.val_cn || item.val,
+                value: Number(item[valueKeyMap[sortMode.value]]),
+              })),
+              {
+                name: '其他',
+                value: otherCount,
+              },
+            ],
+          },
+        ],
+      };
   
-        nextTick(() => {
-          setOptions(option, true, true);
-        });
-      } catch (error) {
-        console.error('Failed to fetch data:', error);
-      }
+      nextTick(() => {
+        setOptions(option, true, true);
+      });
     };
   
-    // Replace registerTransactionTable with this new method
+    // 修改handleTableChange方法
     async function handleTableChange(pag, filters, sorter) {
       loading.value = true;
       try {
@@ -260,14 +286,31 @@
           ...props.queryParam,
         };
   
-        const res = await getDestCountryReport(params);
-        if (res.result.data.result && res.result.data.result.buckets) {
+        const cacheKey = getCacheKey(params);
+        // 检查缓存
+        if (cache.value.table.has(cacheKey)) {
+          const cachedData = cache.value.table.get(cacheKey);
           pagination.value = {
             current: pag.current,
             pageSize: pag.pageSize,
-            total: res.result.data.result.numBuckets || 0,
+            total: cachedData.total,
           };
-          tableData.value = res.result.data.result.buckets;
+          tableData.value = cachedData.buckets;
+        } else {
+          const res = await getDestCountryReport(params);
+          if (res.result.data.result?.buckets) {
+            pagination.value = {
+              current: pag.current,
+              pageSize: pag.pageSize,
+              total: res.result.data.result.numBuckets || 0,
+            };
+            tableData.value = res.result.data.result.buckets;
+            // 存入缓存
+            cache.value.table.set(cacheKey, {
+              buckets: res.result.data.result.buckets,
+              total: res.result.data.result.numBuckets || 0,
+            });
+          }
         }
       } catch (error) {
         console.error('Failed to fetch data:', error);
@@ -276,10 +319,14 @@
       }
     }
   
-    // 监听数据变化
+    // 修改watch queryParam,当查询参数变化时清除缓存
     watch(
       () => props.queryParam,
       () => {
+        // 清除缓存
+        cache.value.chart.clear();
+        cache.value.table.clear();
+        
         if (viewMode.value === 'chart') {
           updateChart();
         } else {

+ 129 - 74
src/views/adweb/data/components/IncotermsAnalysis.vue

@@ -62,6 +62,17 @@
   const sortMode = ref('count');
   const allBuckets = ref({});
   
+  // 添加缓存相关的响应式变量
+  const cache = ref({
+    chart: new Map(), // 图表数据缓存
+    table: new Map(), // 表格数据缓存
+  });
+  
+  // 获取缓存key
+  const getCacheKey = (params) => {
+    return `${params.sort}_${params.page}_${params.page_size}_${JSON.stringify(props.queryParam)}`;
+  };
+  
   // 定义表格列
   const columns = [
     {
@@ -146,98 +157,118 @@
       ...props.queryParam,
     };
   
+    const cacheKey = getCacheKey(params);
+    
+    // 检查缓存中是否存在数据
+    if (cache.value.chart.has(cacheKey)) {
+      const cachedData = cache.value.chart.get(cacheKey);
+      chartData.value = cachedData.buckets;
+      allBuckets.value = cachedData.allBuckets;
+      // 使用缓存数据更新图表
+      updateChartOptions();
+      return;
+    }
+  
     try {
       const res = await getIncotermsReport(params);
   
-      if (res && res.result && res.result.data && res.result.data.result && res.result.data.result.buckets) {
+      if (res?.result?.data?.result?.buckets) {
+        // 存入缓存
+        cache.value.chart.set(cacheKey, {
+          buckets: res.result.data.result.buckets,
+          allBuckets: res.result.data.result.allBuckets
+        });
+        
         chartData.value = res.result.data.result.buckets;
         allBuckets.value = res.result.data.result.allBuckets;
-      } else {
-        console.error('Unexpected API response structure:', res);
-        return; // Exit if the response structure is not as expected
+        // 更新图表
+        updateChartOptions();
       }
+    } catch (error) {
+      console.error('Failed to fetch data:', error);
+    }
+  };
   
-      const valueKeyMap = {
-        count: 'count',
-        weight: 'sum_weight',
-        amount: 'sum_amount',
-      };
+  // 将图表配置更新逻辑抽离出来
+  const updateChartOptions = () => {
+    const valueKeyMap = {
+      count: 'count',
+      weight: 'sum_weight',
+      amount: 'sum_amount',
+    };
   
-      // 获取前10个数据并计算总数
-      const data = chartData.value.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]]));
+    // 获取前10个数据并计算总数
+    const data = chartData.value.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]]));
   
-      // 根据 sortMode 映射到 allBuckets 的对应关系
-      const totalKeyMap = {
-        count: 'count',
-        weight: 'weight_sum',
-        amount: 'amount_sum',
-      };
+    // 根据 sortMode 映射到 allBuckets 的对应关系
+    const totalKeyMap = {
+      count: 'count',
+      weight: 'weight_sum',
+      amount: 'amount_sum',
+    };
   
-      const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0; // 使用映射获取总数
-      const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
+    const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0; // 使用映射获取总数
+    const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
   
-      const option = {
-        tooltip: {
-          trigger: 'item',
-          formatter: (params) => {
-            const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
-            return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
-          },
-        },
-        legend: {
-          orient: 'vertical',
-          right: 10,
-          top: 'center',
-          type: 'scroll',
+    const option = {
+      tooltip: {
+        trigger: 'item',
+        formatter: (params) => {
+          const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
+          return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
         },
-        series: [
-          {
-            name: '交易次数',
-            type: 'pie',
-            radius: ['40%', '70%'],
-            avoidLabelOverlap: true,
-            itemStyle: {
-              borderRadius: 10,
-              borderColor: '#fff',
-              borderWidth: 2,
+      },
+      legend: {
+        orient: 'vertical',
+        right: 10,
+        top: 'center',
+        type: 'scroll',
+      },
+      series: [
+        {
+          name: '交易次数',
+          type: 'pie',
+          radius: ['40%', '70%'],
+          avoidLabelOverlap: true,
+          itemStyle: {
+            borderRadius: 10,
+            borderColor: '#fff',
+            borderWidth: 2,
+          },
+          label: {
+            show: true,
+            formatter: (params) => {
+              const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
+              return `${params.name}\n${percent}%`;
             },
+          },
+          emphasis: {
             label: {
               show: true,
-              formatter: (params) => {
-                const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
-                return `${params.name}\n${percent}%`;
-              },
-            },
-            emphasis: {
-              label: {
-                show: true,
-                fontSize: 14,
-                fontWeight: 'bold',
-              },
+              fontSize: 14,
+              fontWeight: 'bold',
             },
-            data: [
-              ...data.map((item) => ({
-                name: item.val_cn || item.val,
-                value: Number(item[valueKeyMap[sortMode.value]]),
-              })),
-              {
-                name: '其他',
-                value: otherCount,
-              },
-            ],
           },
-        ],
-      };
+          data: [
+            ...data.map((item) => ({
+              name: item.val_cn || item.val,
+              value: Number(item[valueKeyMap[sortMode.value]]),
+            })),
+            {
+              name: '其他',
+              value: otherCount,
+            },
+          ],
+        },
+      ],
+    };
   
-      nextTick(() => {
-        setOptions(option, true, true);
-      });
-    } catch (error) {
-      console.error('Failed to fetch data:', error);
-    }
+    nextTick(() => {
+      setOptions(option, true, true);
+    });
   };
   
-  // Replace registerTransactionTable with this new method
+  // 表格数据获取方法改造
   async function handleTableChange(pag, filters, sorter) {
     loading.value = true;
     try {
@@ -248,8 +279,25 @@
         ...props.queryParam,
       };
   
+      const cacheKey = getCacheKey(params);
+  
+      // 检查缓存中是否存在数据
+      if (cache.value.table.has(cacheKey)) {
+        const cachedData = cache.value.table.get(cacheKey);
+        pagination.value = {
+          current: pag.current,
+          pageSize: pag.pageSize,
+          total: cachedData.numBuckets || 0,
+        };
+        tableData.value = cachedData.buckets;
+        return;
+      }
+  
       const res = await getIncotermsReport(params);
-      if (res.result.data.result && res.result.data.result.buckets) {
+      if (res.result.data.result?.buckets) {
+        // 存入缓存
+        cache.value.table.set(cacheKey, res.result.data.result);
+        
         pagination.value = {
           current: pag.current,
           pageSize: pag.pageSize,
@@ -264,10 +312,17 @@
     }
   }
   
-  // 监听数据变化
+  // 添加清除缓存的方法
+  const clearCache = () => {
+    cache.value.chart.clear();
+    cache.value.table.clear();
+  };
+  
+  // 监听查询参数变化时,清除缓存并重新请求数据
   watch(
     () => props.queryParam,
     () => {
+      clearCache(); // 清除缓存
       if (viewMode.value === 'chart') {
         updateChart();
       } else {

+ 69 - 16
src/views/adweb/data/components/OrigPortAnalysis.vue

@@ -70,6 +70,22 @@ const viewMode = ref('chart');
 const sortMode = ref('count');
 const allBuckets = ref({});
 
+// 添加缓存相关的变量
+const dataCache = ref({
+    chart: new Map(), // 图表数据缓存
+    table: new Map(), // 表格数据缓存
+});
+
+// 生成缓存key的函数
+const getCacheKey = (params) => {
+    return JSON.stringify({
+        sort: params.sort,
+        page: params.page,
+        page_size: params.page_size,
+        ...params
+    });
+};
+
 // 定义表格列
 const columns = [
     {
@@ -164,11 +180,24 @@ const updateChart = async () => {
         ...props.queryParam,
     };
 
+    const cacheKey = getCacheKey(params);
     try {
-        const res = await getOrigPortReport(params);
-        if (res.result.data.result && res.result.data.result.buckets) {
-            chartData = res.result.data.result.buckets;
-            allBuckets.value = res.result.data.result.allBuckets;
+        // 检查缓存中是否有数据
+        if (dataCache.value.chart.has(cacheKey)) {
+            const cachedData = dataCache.value.chart.get(cacheKey);
+            chartData = cachedData.chartData;
+            allBuckets.value = cachedData.allBuckets;
+        } else {
+            const res = await getOrigPortReport(params);
+            if (res.result.data.result && res.result.data.result.buckets) {
+                chartData = res.result.data.result.buckets;
+                allBuckets.value = res.result.data.result.allBuckets;
+                // 存入缓存
+                dataCache.value.chart.set(cacheKey, {
+                    chartData,
+                    allBuckets: allBuckets.value
+                });
+            }
         }
 
         if (chartData.length === 0) {
@@ -247,14 +276,14 @@ const updateChart = async () => {
         };
 
         nextTick(() => {
-            setOptions(option, true, true); // Ensure options are set after DOM updates
+            setOptions(option, true, true);
         });
     } catch (error) {
         console.error('Failed to update chart:', error);
     }
 };
 
-// Replace registerTransactionTable with this new method
+// 修改handleTableChange方法
 const handleTableChange = async (pag, filters, sorter) => {
     loading.value = true;
     try {
@@ -265,15 +294,32 @@ const handleTableChange = async (pag, filters, sorter) => {
             ...props.queryParam,
         };
 
-        const res = await getOrigPortReport(params);
-        if (res.result.data.result && res.result.data.result.buckets) {
-            pagination.value = {
-                current: pag.current,
-                pageSize: pag.pageSize,
-                total: res.result.data.result.numBuckets || 0,
-            };
-            tableData.value = res.result.data.result.buckets;
-
+        const cacheKey = getCacheKey(params);
+        // 检查缓存中是否有数据
+        if (dataCache.value.table.has(cacheKey)) {
+            const cachedData = dataCache.value.table.get(cacheKey);
+            pagination.value = cachedData.pagination;
+            tableData.value = cachedData.tableData;
+            allBuckets.value = cachedData.allBuckets;
+        } else {
+            const res = await getOrigPortReport(params);
+            if (res.result.data.result && res.result.data.result.buckets) {
+                const newPagination = {
+                    current: pag.current,
+                    pageSize: pag.pageSize,
+                    total: res.result.data.result.numBuckets || 0,
+                };
+                pagination.value = newPagination;
+                tableData.value = res.result.data.result.buckets;
+                allBuckets.value = res.result.data.result.allBuckets;
+                
+                // 存入缓存
+                dataCache.value.table.set(cacheKey, {
+                    pagination: newPagination,
+                    tableData: res.result.data.result.buckets,
+                    allBuckets: res.result.data.result.allBuckets
+                });
+            }
         }
     } catch (error) {
         console.error('Failed to fetch data:', error);
@@ -282,10 +328,17 @@ const handleTableChange = async (pag, filters, sorter) => {
     }
 };
 
-// 监听数据变化
+// 添加清除缓存的方法(当查询参数改变时调用)
+const clearCache = () => {
+    dataCache.value.chart.clear();
+    dataCache.value.table.clear();
+};
+
+// 修改watch监听
 watch(
     () => props.queryParam,
     () => {
+        clearCache(); // 查询参数改变时清除缓存
         if (viewMode.value === 'chart') {
             updateChart();
         } else {

+ 143 - 99
src/views/adweb/data/components/OriginCountryAnalysis.vue

@@ -62,6 +62,19 @@ const viewMode = ref('chart');
 const sortMode = ref('count');
 const allBuckets = ref({});
 
+// 添加缓存变量
+const cachedData = ref({
+  chart: null,
+  table: {
+    data: [],
+    pagination: {
+      current: 1,
+      pageSize: 10,
+      total: 0,
+    }
+  }
+});
+
 // 定义表格列
 const columns = [
   {
@@ -144,106 +157,122 @@ const initChart = () => {
 };
 
 const updateChart = async () => {
-  const params = {
-    sort: sortMode.value,
-    page: 1,
-    page_size: 10,
-    ...props.queryParam,
-  };
-
-  try {
-    const res = await getOrigCountryReport(params);
+  // 如果有缓存且排序模式没变,直接使用缓存
+  if (cachedData.value.chart) {
+    chartData.value = cachedData.value.chart;
+    allBuckets.value = cachedData.value.allBuckets;
+  } else {
+    const params = {
+      sort: sortMode.value,
+      page: 1,
+      page_size: 10,
+      ...props.queryParam,
+    };
 
-    if (res && res.result && res.result.data && res.result.data.result && res.result.data.result.buckets) {
-      chartData.value = res.result.data.result.buckets;
-      allBuckets.value = res.result.data.result.allBuckets;
-    } else {
-      console.error('Unexpected API response structure:', res);
-      return; // Exit if the response structure is not as expected
+    try {
+      const res = await getOrigCountryReport(params);
+      if (res?.result?.data?.result?.buckets) {
+        chartData.value = res.result.data.result.buckets;
+        // 保存到缓存
+        cachedData.value.chart = res.result.data.result.buckets;
+        cachedData.value.allBuckets = res.result.data.result.allBuckets;
+        allBuckets.value = res.result.data.result.allBuckets;
+      }
+    } catch (error) {
+      console.error('Failed to fetch data:', error);
+      return;
     }
+  }
 
-    const valueKeyMap = {
-      count: 'count',
-      weight: 'sum_weight',
-      amount: 'sum_amount',
-    };
+  // 无论是否使用缓存,都执行以下图表渲染逻辑
+  const valueKeyMap = {
+    count: 'count',
+    weight: 'sum_weight',
+    amount: 'sum_amount',
+  };
 
-    // 获取前10个数据并计算总数
-    const data = chartData.value.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]]));
+  // 获取前10个数据并计算总数
+  const data = chartData.value.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]]));
 
-    // 根据 sortMode 映射到 allBuckets 的对应关系
-    const totalKeyMap = {
-      count: 'count',
-      weight: 'weight_sum',
-      amount: 'amount_sum',
-    };
+  // 根据 sortMode 映射到 allBuckets 的对应关系
+  const totalKeyMap = {
+    count: 'count',
+    weight: 'weight_sum',
+    amount: 'amount_sum',
+  };
 
-    const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0; // 使用映射获取总数
-    const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
+  const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0;
+  const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
 
-    const option = {
-      tooltip: {
-        trigger: 'item',
-        formatter: (params) => {
-          const percent = ((params.value / total) * 100).toFixed(2);
-          return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
-        },
+  const option = {
+    tooltip: {
+      trigger: 'item',
+      formatter: (params) => {
+        const percent = ((params.value / total) * 100).toFixed(2);
+        return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
       },
-      legend: {
-        orient: 'vertical',
-        right: 10,
-        top: 'center',
-        type: 'scroll',
-      },
-      series: [
-        {
-          name: '交易次数',
-          type: 'pie',
-          radius: ['40%', '70%'],
-          avoidLabelOverlap: true,
-          itemStyle: {
-            borderRadius: 10,
-            borderColor: '#fff',
-            borderWidth: 2,
+    },
+    legend: {
+      orient: 'vertical',
+      right: 10,
+      top: 'center',
+      type: 'scroll',
+    },
+    series: [
+      {
+        name: '交易次数',
+        type: 'pie',
+        radius: ['40%', '70%'],
+        avoidLabelOverlap: true,
+        itemStyle: {
+          borderRadius: 10,
+          borderColor: '#fff',
+          borderWidth: 2,
+        },
+        label: {
+          show: true,
+          formatter: (params) => {
+            const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
+            return `${params.name}\n${percent}%`;
           },
+        },
+        emphasis: {
           label: {
             show: true,
-            formatter: (params) => {
-              const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
-              return `${params.name}\n${percent}%`;
-            },
-          },
-          emphasis: {
-            label: {
-              show: true,
-              fontSize: 14,
-              fontWeight: 'bold',
-            },
+            fontSize: 14,
+            fontWeight: 'bold',
           },
-          data: [
-            ...data.map((item) => ({
-              name: item.val_cn || item.val,
-              value: Number(item[valueKeyMap[sortMode.value]]),
-            })),
-            {
-              name: '其他',
-              value: otherCount,
-            },
-          ],
         },
-      ],
-    };
+        data: [
+          ...data.map((item) => ({
+            name: item.val_cn || item.val,
+            value: Number(item[valueKeyMap[sortMode.value]]),
+          })),
+          {
+            name: '其他',
+            value: otherCount,
+          },
+        ],
+      },
+    ],
+  };
 
-    nextTick(() => {
-      setOptions(option, true, true);
-    });
-  } catch (error) {
-    console.error('Failed to fetch data:', error);
-  }
+  nextTick(() => {
+    setOptions(option, true, true);
+  });
 };
 
-// Replace registerTransactionTable with this new method
+// 修改 handleTableChange 函数
 async function handleTableChange(pag, filters, sorter) {
+  // 检查缓存中是否有当前页的数据
+  if (cachedData.value.table.data.length > 0 &&
+      cachedData.value.table.pagination.current === pag.current &&
+      cachedData.value.table.pagination.pageSize === pag.pageSize) {
+    pagination.value = cachedData.value.table.pagination;
+    tableData.value = cachedData.value.table.data;
+    return;
+  }
+
   loading.value = true;
   try {
     const params = {
@@ -254,12 +283,18 @@ async function handleTableChange(pag, filters, sorter) {
     };
 
     const res = await getOrigCountryReport(params);
-    if (res.result.data.result && res.result.data.result.buckets) {
-      pagination.value = {
+    if (res.result.data.result?.buckets) {
+      const newPagination = {
         current: pag.current,
         pageSize: pag.pageSize,
         total: res.result.data.result.numBuckets || 0,
       };
+      // 更新缓存
+      cachedData.value.table = {
+        data: res.result.data.result.buckets,
+        pagination: newPagination
+      };
+      pagination.value = newPagination;
       tableData.value = res.result.data.result.buckets;
     }
   } catch (error) {
@@ -269,20 +304,7 @@ async function handleTableChange(pag, filters, sorter) {
   }
 }
 
-// 监听数据变化
-watch(
-  () => props.queryParam,
-  () => {
-    if (viewMode.value === 'chart') {
-      updateChart();
-    } else {
-      handleTableChange(pagination.value, {}, {});
-    }
-  },
-  { deep: true }
-);
-
-// 监听视图模式变化
+// 修改视图模式监听
 watch(viewMode, (newValue) => {
   if (newValue === 'chart') {
     updateChart();
@@ -291,7 +313,12 @@ watch(viewMode, (newValue) => {
   }
 });
 
+// 修改排序模式监听,清除缓存并重新获取数据
 watch(sortMode, () => {
+  // 清除缓存
+  cachedData.value.chart = null;
+  cachedData.value.table.data = [];
+  
   if (viewMode.value === 'chart') {
     updateChart();
   } else {
@@ -299,6 +326,23 @@ watch(sortMode, () => {
   }
 });
 
+// 监听查询参数变化,清除缓存并重新获取数据
+watch(
+  () => props.queryParam,
+  () => {
+    // 清除缓存
+    cachedData.value.chart = null;
+    cachedData.value.table.data = [];
+
+    if (viewMode.value === 'chart') {
+      updateChart();
+    } else {
+      handleTableChange(pagination.value, {}, {});
+    }
+  },
+  { deep: true }
+);
+
 onMounted(() => {
   if (viewMode.value === 'chart') {
     initChart();

+ 5 - 1
src/views/adweb/data/components/SupplierList.vue

@@ -12,6 +12,7 @@
             <a-table :columns="columns" :data-source="tableData" :loading="loading" row-key="fk" :pagination="{
                 current: pagination.current,
                 pageSize: pagination.pageSize,
+                defaultPageSize: 20,
                 total: pagination.total,
                 showSizeChanger: true,
                 showQuickJumper: true,
@@ -118,7 +119,10 @@ const formatNumber = (value: number | string) => {
 };
 
 onMounted(async () => {
-    await handleTableChange(pagination.value.current, pagination.value.pageSize, sortMode.value);
+    await handleTableChange({
+        current: pagination.value.current,
+        pageSize: 20
+    }, {}, {});
 });
 // 监听数据变化
 watch(

+ 139 - 100
src/views/adweb/data/components/TransTypeAnalysis.vue

@@ -62,6 +62,12 @@
   const sortMode = ref('count');
   const allBuckets = ref({});
   
+  // 添加数据缓存对象
+  const dataCache = ref({
+    chart: null,
+    table: new Map(), // 使用 Map 存储不同分页的表格数据
+  });
+  
   // 定义表格列
   const columns = [
     {
@@ -139,123 +145,147 @@
   };
   
   const updateChart = async () => {
-    const params = {
-      sort: sortMode.value,
-      page: 1,
-      page_size: 10,
-      ...props.queryParam,
-    };
-  
-    try {
-      const res = await getTransTypeReport(params);
+    // 如果有缓存且查询参数没变,直接使用缓存
+    if (dataCache.value.chart) {
+      chartData.value = dataCache.value.chart.buckets;
+      allBuckets.value = dataCache.value.chart.allBuckets;
+    } else {
+      const params = {
+        sort: sortMode.value,
+        page: 1,
+        page_size: 10,
+        ...props.queryParam,
+      };
   
-      if (res && res.result && res.result.data && res.result.data.result && res.result.data.result.buckets) {
-        chartData.value = res.result.data.result.buckets;
-        allBuckets.value = res.result.data.result.allBuckets;
-      } else {
-        console.error('Unexpected API response structure:', res);
-        return; // Exit if the response structure is not as expected
+      try {
+        const res = await getTransTypeReport(params);
+        if (res?.result?.data?.result?.buckets) {
+          // 更新缓存
+          dataCache.value.chart = res.result.data.result;
+          chartData.value = res.result.data.result.buckets;
+          allBuckets.value = res.result.data.result.allBuckets;
+        } else {
+          console.error('Unexpected API response structure:', res);
+          return;
+        }
+      } catch (error) {
+        console.error('Failed to fetch data:', error);
+        return;
       }
+    }
   
-      const valueKeyMap = {
-        count: 'count',
-        weight: 'sum_weight',
-        amount: 'sum_amount',
-      };
+    const valueKeyMap = {
+      count: 'count',
+      weight: 'sum_weight',
+      amount: 'sum_amount',
+    };
   
-      // 获取前10个数据并计算总数
-      const data = chartData.value.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]]));
+    // 获取前10个数据并计算总数
+    const data = chartData.value.sort((a, b) => Number(b[valueKeyMap[sortMode.value]]) - Number(a[valueKeyMap[sortMode.value]]));
   
-      // 根据 sortMode 映射到 allBuckets 的对应关系
-      const totalKeyMap = {
-        count: 'count',
-        weight: 'weight_sum',
-        amount: 'amount_sum',
-      };
+    // 根据 sortMode 映射到 allBuckets 的对应关系
+    const totalKeyMap = {
+      count: 'count',
+      weight: 'weight_sum',
+      amount: 'amount_sum',
+    };
   
-      const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0; // 使用映射获取总数
-      const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
+    const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0; // 使用映射获取总数
+    const otherCount = total - data.reduce((sum, item) => sum + Number(item[valueKeyMap[sortMode.value]]), 0);
   
-      const option = {
-        tooltip: {
-          trigger: 'item',
-          formatter: (params) => {
-            const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
-            return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
-          },
-        },
-        legend: {
-          orient: 'vertical',
-          right: 10,
-          top: 'center',
-          type: 'scroll',
+    const option = {
+      tooltip: {
+        trigger: 'item',
+        formatter: (params) => {
+          const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
+          return `${params.name}<br/>${sortMode.value === 'count' ? '交易次数' : sortMode.value === 'weight' ? '交易重量' : '交易金额'}: ${formatNumber(params.value)}<br/>占比: ${percent}%`;
         },
-        series: [
-          {
-            name: '交易次数',
-            type: 'pie',
-            radius: ['40%', '70%'],
-            avoidLabelOverlap: true,
-            itemStyle: {
-              borderRadius: 10,
-              borderColor: '#fff',
-              borderWidth: 2,
+      },
+      legend: {
+        orient: 'vertical',
+        right: 10,
+        top: 'center',
+        type: 'scroll',
+      },
+      series: [
+        {
+          name: '交易次数',
+          type: 'pie',
+          radius: ['40%', '70%'],
+          avoidLabelOverlap: true,
+          itemStyle: {
+            borderRadius: 10,
+            borderColor: '#fff',
+            borderWidth: 2,
+          },
+          label: {
+            show: true,
+            formatter: (params) => {
+              const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
+              return `${params.name}\n${percent}%`;
             },
+          },
+          emphasis: {
             label: {
               show: true,
-              formatter: (params) => {
-                const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
-                return `${params.name}\n${percent}%`;
-              },
+              fontSize: 14,
+              fontWeight: 'bold',
             },
-            emphasis: {
-              label: {
-                show: true,
-                fontSize: 14,
-                fontWeight: 'bold',
-              },
-            },
-            data: [
-              ...data.map((item) => ({
-                name: item.val_cn || item.val,
-                value: Number(item[valueKeyMap[sortMode.value]]),
-              })),
-              {
-                name: '其他',
-                value: otherCount,
-              },
-            ],
           },
-        ],
-      };
+          data: [
+            ...data.map((item) => ({
+              name: item.val_cn || item.val,
+              value: Number(item[valueKeyMap[sortMode.value]]),
+            })),
+            {
+              name: '其他',
+              value: otherCount,
+            },
+          ],
+        },
+      ],
+    };
   
-      nextTick(() => {
-        setOptions(option, true, true);
-      });
-    } catch (error) {
-      console.error('Failed to fetch data:', error);
-    }
+    nextTick(() => {
+      setOptions(option, true, true);
+    });
   };
   
-  // Replace registerTransactionTable with this new method
+  // 修改 handleTableChange 函数
   async function handleTableChange(pag, filters, sorter) {
     loading.value = true;
     try {
-      const params = {
-        sort: sortMode.value,
-        page: pag.current,
-        page_size: pag.pageSize,
-        ...props.queryParam,
-      };
-  
-      const res = await getTransTypeReport(params);
-      if (res.result.data.result && res.result.data.result.buckets) {
+      const cacheKey = `${pag.current}-${pag.pageSize}-${sortMode.value}`;
+      
+      // 检查是否有缓存
+      if (dataCache.value.table.has(cacheKey)) {
+        const cachedData = dataCache.value.table.get(cacheKey);
+        tableData.value = cachedData.buckets;
         pagination.value = {
           current: pag.current,
           pageSize: pag.pageSize,
-          total: res.result.data.result.numBuckets || 0,
+          total: cachedData.numBuckets || 0,
+        };
+      } else {
+        const params = {
+          sort: sortMode.value,
+          page: pag.current,
+          page_size: pag.pageSize,
+          ...props.queryParam,
         };
-        tableData.value = res.result.data.result.buckets;
+  
+        const res = await getTransTypeReport(params);
+        if (res.result.data.result?.buckets) {
+          // 更新缓存
+          dataCache.value.table.set(cacheKey, res.result.data.result);
+          
+          pagination.value = {
+            current: pag.current,
+            pageSize: pag.pageSize,
+            total: res.result.data.result.numBuckets || 0,
+          };
+          tableData.value = res.result.data.result.buckets;
+        }
       }
     } catch (error) {
       console.error('Failed to fetch data:', error);
@@ -264,10 +294,14 @@
     }
   }
   
-  // 监听数据变化
+  // 修改 queryParam 的监听函数,当查询参数变化时清除缓存
   watch(
     () => props.queryParam,
     () => {
+      // 清除缓存
+      dataCache.value.chart = null;
+      dataCache.value.table.clear();
+      
       if (viewMode.value === 'chart') {
         updateChart();
       } else {
@@ -277,17 +311,22 @@
     { deep: true }
   );
   
-  // 监听视图模式变化
-  watch(viewMode, (newValue) => {
-    if (newValue === 'chart') {
+  // 修改 sortMode 的监听函数
+  watch(sortMode, () => {
+    // 清除缓存
+    dataCache.value.chart = null;
+    dataCache.value.table.clear();
+    
+    if (viewMode.value === 'chart') {
       updateChart();
     } else {
       handleTableChange(pagination.value, {}, {});
     }
   });
   
-  watch(sortMode, () => {
-    if (viewMode.value === 'chart') {
+  // 监听视图模式变化
+  watch(viewMode, (newValue) => {
+    if (newValue === 'chart') {
       updateChart();
     } else {
       handleTableChange(pagination.value, {}, {});