|
@@ -2,14 +2,14 @@
|
|
|
<a-card title="原产国" :loading="loading">
|
|
|
<template #extra>
|
|
|
<span>排序:</span>
|
|
|
- <a-radio-group v-model:value="sortMode" button-style="solid" style="margin-left: 16px;" @change="handleSortChange">
|
|
|
+ <a-radio-group v-model:value="sortMode" button-style="solid" style="margin-left: 16px;">
|
|
|
<a-radio-button value="count">交易次数</a-radio-button>
|
|
|
<a-radio-button value="weight">交易重量</a-radio-button>
|
|
|
<a-radio-button value="amount">交易金额</a-radio-button>
|
|
|
</a-radio-group>
|
|
|
</template>
|
|
|
<!-- 添加切换按钮组 -->
|
|
|
- <div>
|
|
|
+ <div class="switch-button">
|
|
|
<a-radio-group v-model:value="viewMode" button-style="solid">
|
|
|
<a-radio-button value="chart">图表</a-radio-button>
|
|
|
<a-radio-button value="table">列表</a-radio-button>
|
|
@@ -20,8 +20,15 @@
|
|
|
<div v-show="viewMode === 'chart'" ref="chartRef" style="height: 400px"></div>
|
|
|
|
|
|
<!-- 表格视图 -->
|
|
|
- <a-table v-show="viewMode === 'table'" :columns="columns" :data-source="tableData" :pagination="false"
|
|
|
- size="middle">
|
|
|
+ <a-table v-show="viewMode === 'table'" :columns="columns" :data-source="tableData" :pagination="{
|
|
|
+ current: pagination.current,
|
|
|
+ pageSize: pagination.pageSize,
|
|
|
+ total: pagination.total,
|
|
|
+ showSizeChanger: true,
|
|
|
+ showQuickJumper: true,
|
|
|
+ showTotal: (total) => `共 ${total} 条`,
|
|
|
+ onShowSizeChange: handleTableChange,
|
|
|
+ }" @change="handleTableChange" size="middle">
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
<template v-if="column.dataIndex === 'count' || column.dataIndex === 'num_supplier'">
|
|
|
{{ formatNumber(record[column.dataIndex]) }}
|
|
@@ -35,31 +42,33 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, onMounted, watch, computed, nextTick, onUnmounted, defineEmits } from 'vue';
|
|
|
-import * as echarts from 'echarts';
|
|
|
+import { ref, onMounted, watch, nextTick, onUnmounted, Ref } from 'vue';
|
|
|
+import { useECharts } from '/@/hooks/web/useECharts';
|
|
|
+import { getOrigCountryReport } from '../customsData.api';
|
|
|
|
|
|
-// Define emits
|
|
|
-const emit = defineEmits();
|
|
|
+const chartRef = ref<HTMLDivElement | null>(null);
|
|
|
+const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
|
|
|
|
|
|
const props = defineProps({
|
|
|
- originCountryData: {
|
|
|
+ queryParam: {
|
|
|
type: Object,
|
|
|
required: true
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-const chartRef = ref(null);
|
|
|
const loading = ref(false);
|
|
|
const viewMode = ref('chart');
|
|
|
const sortMode = ref('count');
|
|
|
+const allBuckets = ref({});
|
|
|
+
|
|
|
let chart = null;
|
|
|
|
|
|
// 定义表格列
|
|
|
const columns = [
|
|
|
{
|
|
|
title: '原产国',
|
|
|
- dataIndex: 'country',
|
|
|
- key: 'country',
|
|
|
+ dataIndex: 'val_cn',
|
|
|
+ key: 'val_cn',
|
|
|
},
|
|
|
{
|
|
|
title: '交易次数',
|
|
@@ -86,10 +95,26 @@ const columns = [
|
|
|
dataIndex: 'percentage',
|
|
|
key: 'percentage',
|
|
|
customRender: ({ record }) => {
|
|
|
- const total = props.originCountryData.allBuckets.count; // 计算总数
|
|
|
- const percent = total > 0 && (record.count > 0 || record.sum_weight > 0 || record.sum_amount > 0)
|
|
|
- ? ((record.count / total) * 100).toFixed(2)
|
|
|
- : 0.00; // 计算百分比
|
|
|
+ const percent = (() => {
|
|
|
+ const valueKeyMap = {
|
|
|
+ count: 'count',
|
|
|
+ weight: 'sum_weight',
|
|
|
+ amount: 'sum_amount'
|
|
|
+ };
|
|
|
+ const totalKeyMap = {
|
|
|
+ count: 'count',
|
|
|
+ weight: 'weight_sum',
|
|
|
+ amount: 'amount_sum'
|
|
|
+ };
|
|
|
+
|
|
|
+ const selectedValue = record[valueKeyMap[sortMode.value]];
|
|
|
+ const total = allBuckets.value[totalKeyMap[sortMode.value]] || 0;
|
|
|
+
|
|
|
+ return total > 0 && selectedValue > 0
|
|
|
+ ? ((selectedValue / total) * 100).toFixed(2)
|
|
|
+ : 0.00;
|
|
|
+ })();
|
|
|
+
|
|
|
return `${percent}%`;
|
|
|
}
|
|
|
}
|
|
@@ -109,116 +134,158 @@ const formatDecimal = (num: string | number) => {
|
|
|
};
|
|
|
|
|
|
// 计算表格数据
|
|
|
-const tableData = computed(() => {
|
|
|
- if (!props.originCountryData?.buckets) return [];
|
|
|
-
|
|
|
- return props.originCountryData.buckets.map((item, index) => ({
|
|
|
- key: index,
|
|
|
- country: item.val_cn || item.val,
|
|
|
- count: item.count,
|
|
|
- num_supplier: item.num_supplier,
|
|
|
- sum_weight: item.sum_weight,
|
|
|
- sum_amount: item.sum_amount,
|
|
|
- percentage: 0
|
|
|
- }));
|
|
|
+const tableData = ref([]);
|
|
|
+const pagination = ref({
|
|
|
+ current: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ total: 0
|
|
|
});
|
|
|
+const chartData = ref([]);
|
|
|
|
|
|
const initChart = () => {
|
|
|
- if (!chartRef.value) return;
|
|
|
-
|
|
|
- chart = echarts.init(chartRef.value);
|
|
|
updateChart();
|
|
|
};
|
|
|
|
|
|
-const updateChart = () => {
|
|
|
- if (!chart || !props.originCountryData?.buckets) return;
|
|
|
-
|
|
|
- const valueKeyMap = {
|
|
|
- count: 'count',
|
|
|
- weight: 'sum_weight',
|
|
|
- amount: 'sum_amount'
|
|
|
- }
|
|
|
- // 获取前10个数据并计算总数
|
|
|
- const data = props.originCountryData.buckets
|
|
|
- .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'
|
|
|
+const updateChart = async () => {
|
|
|
+ console.log('updateChart');
|
|
|
+ const params = {
|
|
|
+ sort: sortMode.value,
|
|
|
+ page: 1,
|
|
|
+ page_size: 10,
|
|
|
+ ...props.queryParam,
|
|
|
};
|
|
|
|
|
|
- const total = props.originCountryData.allBuckets[totalKeyMap[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 = ((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
|
|
|
- },
|
|
|
- label: {
|
|
|
- show: true,
|
|
|
- formatter: (params) => {
|
|
|
- const percent = total > 0 ? ((params.value / total) * 100).toFixed(2) : 0.00;
|
|
|
- return `${params.name}\n${percent}%`;
|
|
|
- }
|
|
|
- },
|
|
|
- emphasis: {
|
|
|
+ try {
|
|
|
+ const res = await getOrigCountryReport(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 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);
|
|
|
+
|
|
|
+ // 根据 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 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
|
|
|
+ },
|
|
|
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
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
- ]
|
|
|
- };
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
|
|
|
- chart.setOption(option);
|
|
|
+ nextTick(() => {
|
|
|
+ console.log('setOptions');
|
|
|
+ setOptions(option);
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to fetch data:', error);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-const handleSortChange = (event) => {
|
|
|
- sortMode.value = event.target.value; // Update sort mode correctly
|
|
|
- emit('update:sortMode', sortMode.value); // Emit the updated sort mode
|
|
|
+// Replace registerTransactionTable with this new method
|
|
|
+const handleTableChange = async (pag, filters, sorter) => {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ sort: sortMode.value,
|
|
|
+ page: pag.current,
|
|
|
+ page_size: pag.pageSize,
|
|
|
+ ...props.queryParam,
|
|
|
+ };
|
|
|
+
|
|
|
+ const res = await getOrigCountryReport(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;
|
|
|
+
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to fetch data:', error);
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
// 监听数据变化
|
|
|
watch(
|
|
|
- () => props.originCountryData,
|
|
|
+ () => props.queryParam,
|
|
|
() => {
|
|
|
- updateChart();
|
|
|
+ if (viewMode.value === 'chart') {
|
|
|
+ updateChart();
|
|
|
+ } else {
|
|
|
+ handleTableChange(pagination.value, {}, {});
|
|
|
+ }
|
|
|
},
|
|
|
{ deep: true }
|
|
|
);
|
|
@@ -226,10 +293,17 @@ watch(
|
|
|
// 监听视图模式变化
|
|
|
watch(viewMode, (newValue) => {
|
|
|
if (newValue === 'chart') {
|
|
|
- // 在下一个 tick 后初始化图表,确保 DOM 已更新
|
|
|
- nextTick(() => {
|
|
|
- initChart();
|
|
|
- });
|
|
|
+ updateChart();
|
|
|
+ } else {
|
|
|
+ handleTableChange(pagination.value, {}, {});
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+watch(sortMode, () => {
|
|
|
+ if (viewMode.value === 'chart') {
|
|
|
+ updateChart();
|
|
|
+ } else {
|
|
|
+ handleTableChange(pagination.value, {}, {});
|
|
|
}
|
|
|
});
|
|
|
|
|
@@ -263,4 +337,8 @@ onUnmounted(() => {
|
|
|
:deep(.ant-table-pagination) {
|
|
|
margin: 16px 0;
|
|
|
}
|
|
|
-</style>
|
|
|
+
|
|
|
+.switch-button {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+</style>
|