-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/area-card.vue b/apps/web-ele/src/views/mall/statistics/member/modules/area-card.vue
new file mode 100644
index 000000000..43d41aac7
--- /dev/null
+++ b/apps/web-ele/src/views/mall/statistics/member/modules/area-card.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+ 会员地域分布
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/area-chart-options.ts b/apps/web-ele/src/views/mall/statistics/member/modules/area-chart-options.ts
new file mode 100644
index 000000000..b2d0c6a66
--- /dev/null
+++ b/apps/web-ele/src/views/mall/statistics/member/modules/area-chart-options.ts
@@ -0,0 +1,130 @@
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MallMemberStatisticsApi } from '#/api/mall/statistics/member';
+
+import { fenToYuan } from '@vben/utils';
+
+/** 会员地域分布图表配置 */
+export function getAreaChartOptions(
+ data: MallMemberStatisticsApi.AreaStatistics[],
+): any {
+ if (!data || data.length === 0) {
+ return {
+ title: {
+ text: '暂无数据',
+ left: 'center',
+ top: 'center',
+ textStyle: {
+ color: '#999',
+ fontSize: 14,
+ },
+ },
+ };
+ }
+
+ // 计算 min 和 max 值
+ let min = Number.POSITIVE_INFINITY;
+ let max = Number.NEGATIVE_INFINITY;
+ const mapData = data.map((item) => {
+ const payUserCount = item.orderPayUserCount || 0;
+ min = Math.min(min, payUserCount);
+ max = Math.max(max, payUserCount);
+ return {
+ ...item,
+ name: item.areaName,
+ value: payUserCount,
+ };
+ });
+ // 如果所有值都为 0,设置合理的 min 和 max 值
+ if (min === max && min === 0) {
+ min = 0;
+ max = 10;
+ }
+
+ // 返回图表配置
+ return {
+ tooltip: {
+ trigger: 'item',
+ formatter: (params: any) => {
+ const itemData = params?.data;
+ if (!itemData) {
+ return `${params?.name || ''}
暂无数据`;
+ }
+ return `${itemData.areaName || params.name}
+会员数量:${itemData.userCount || 0}
+订单创建数量:${itemData.orderCreateUserCount || 0}
+订单支付数量:${itemData.orderPayUserCount || 0}
+订单支付金额:¥${Number(fenToYuan(itemData.orderPayPrice || 0)).toFixed(2)}`;
+ },
+ },
+ visualMap: {
+ text: ['高', '低'],
+ realtime: false,
+ calculable: true,
+ top: 'middle',
+ left: 10,
+ min,
+ max,
+ inRange: {
+ color: ['#e6f3ff', '#1890ff', '#0050b3'],
+ },
+ },
+ series: [
+ {
+ name: '会员地域分布',
+ type: 'map',
+ map: 'china',
+ roam: false,
+ selectedMode: false,
+ itemStyle: {
+ borderColor: '#389e0d',
+ borderWidth: 0.5,
+ },
+ emphasis: {
+ itemStyle: {
+ areaColor: '#ffec3d',
+ borderWidth: 1,
+ },
+ },
+ data: mapData,
+ },
+ ],
+ };
+}
+
+/** VXE Grid 表格列配置 */
+export function getAreaTableColumns(): VxeTableGridOptions['columns'] {
+ return [
+ {
+ field: 'areaName',
+ title: '省份',
+ minWidth: 80,
+ sortable: true,
+ showOverflow: 'tooltip',
+ },
+ {
+ field: 'userCount',
+ title: '会员数量',
+ minWidth: 100,
+ sortable: true,
+ },
+ {
+ field: 'orderCreateUserCount',
+ title: '订单创建数量',
+ minWidth: 120,
+ sortable: true,
+ },
+ {
+ field: 'orderPayUserCount',
+ title: '订单支付数量',
+ minWidth: 120,
+ sortable: true,
+ },
+ {
+ field: 'orderPayPrice',
+ title: '订单支付金额',
+ minWidth: 120,
+ sortable: true,
+ formatter: 'formatFenToYuanAmount',
+ },
+ ];
+}
diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/funnel-card.vue b/apps/web-ele/src/views/mall/statistics/member/modules/funnel-card.vue
index 1ac8b2cc3..e2d3252e2 100644
--- a/apps/web-ele/src/views/mall/statistics/member/modules/funnel-card.vue
+++ b/apps/web-ele/src/views/mall/statistics/member/modules/funnel-card.vue
@@ -46,14 +46,14 @@ const calculateRelativeRate = (value?: number, reference?: number) => {
-
+
会员概览
-
+
diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/sex-card.vue b/apps/web-ele/src/views/mall/statistics/member/modules/sex-card.vue
new file mode 100644
index 000000000..930555812
--- /dev/null
+++ b/apps/web-ele/src/views/mall/statistics/member/modules/sex-card.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+ 会员性别比例
+
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/sex-chart-options.ts b/apps/web-ele/src/views/mall/statistics/member/modules/sex-chart-options.ts
new file mode 100644
index 000000000..81a7faf87
--- /dev/null
+++ b/apps/web-ele/src/views/mall/statistics/member/modules/sex-chart-options.ts
@@ -0,0 +1,28 @@
+/** 会员性别比例图表配置 */
+export function getSexChartOptions(data: any[]): any {
+ return {
+ tooltip: {
+ trigger: 'item',
+ confine: true,
+ formatter: '{a}
{b} : {c} ({d}%)',
+ },
+ legend: {
+ orient: 'vertical',
+ left: 'right',
+ },
+ series: [
+ {
+ name: '会员性别',
+ type: 'pie',
+ roseType: 'area',
+ label: {
+ show: false,
+ },
+ labelLine: {
+ show: false,
+ },
+ data,
+ },
+ ],
+ };
+}
diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/terminal-card.vue b/apps/web-ele/src/views/mall/statistics/member/modules/terminal-card.vue
index 4ff172296..624dcf25c 100644
--- a/apps/web-ele/src/views/mall/statistics/member/modules/terminal-card.vue
+++ b/apps/web-ele/src/views/mall/statistics/member/modules/terminal-card.vue
@@ -34,6 +34,7 @@ const getMemberTerminalStatisticsList = async () => {
value: userCount || 0,
};
});
+ // 更新 Echarts 数据
await renderEcharts(getTerminalChartOptions(chartData));
} finally {
loading.value = false;
@@ -47,9 +48,9 @@ onMounted(() => {
-
+
- 会员终端
+ 会员终端