Files
art-design/src/views/dashboard/console/index.vue
T
浅念 a514bf4009 Squashed commit of the following:
commit 9e260ddcd8
Author: 浅念 <email@sample.com>
Date:   Thu Sep 18 09:49:33 2025 +0800

    修改 kpi

commit 6feb3ace75
Author: 浅念 <email@sample.com>
Date:   Thu Sep 18 09:42:44 2025 +0800

    kpi 统计

commit ec257a246d
Author: 浅念 <email@sample.com>
Date:   Wed Sep 17 17:04:35 2025 +0800

    修改 弹框表格

commit fa764852ab
Author: 浅念 <email@sample.com>
Date:   Wed Sep 17 15:54:35 2025 +0800

    添加 账户简称

commit 65f7f4a123
Author: 浅念 <email@sample.com>
Date:   Wed Sep 17 15:10:28 2025 +0800

    品牌加热门  修改 仪表盘接口

commit 922cfec81d
Author: 浅念 <email@sample.com>
Date:   Tue Sep 16 15:57:17 2025 +0800

    调整备货单  crm 端页面

commit 5c8df36f12
Author: 浅念 <email@sample.com>
Date:   Mon Sep 15 17:14:38 2025 +0800

    调整备货单 及各页面名称

commit 5503a14408
Author: 浅念 <email@sample.com>
Date:   Fri Sep 12 18:10:03 2025 +0800

    账户添加公司

commit 3439b45d9a
Author: 浅念 <email@sample.com>
Date:   Fri Sep 12 17:53:26 2025 +0800

    修改终版备货单

commit 045d13eb7b
Author: 浅念 <email@sample.com>
Date:   Fri Sep 12 17:49:07 2025 +0800

    修改备货单 等页面

commit da690dfa06
Author: 浅念 <email@sample.com>
Date:   Thu Sep 11 17:44:37 2025 +0800

    修改 备货单 详情

commit 853e9591b3
Author: 浅念 <email@sample.com>
Date:   Wed Sep 10 18:11:39 2025 +0800

    修改 备货单页面

commit 9052bd1027
Author: 浅念 <email@sample.com>
Date:   Tue Sep 9 20:53:30 2025 +0800

    备货单  审核功能

commit 079d9babb8
Merge: ebd7479 91d810b
Author: 浅念 <email@sample.com>
Date:   Tue Sep 9 14:51:43 2025 +0800

    Merge branch 'feature/crm_pc' of http://219.157.255.213:25311/xinyouhe/art-design into feature/crm_vehicle

commit ebd7479573
Merge: 64e53b6 459d306
Author: 浅念 <email@sample.com>
Date:   Tue Sep 9 13:57:28 2025 +0800

    Merge branch 'feature/crm_pc' of http://219.157.255.213:25311/xinyouhe/art-design into feature/crm_vehicle

commit 64e53b6ee9
Author: 浅念 <email@sample.com>
Date:   Mon Sep 8 19:09:55 2025 +0800

    备货单 加 审批流程

commit 9bd7c89cb4
Author: 浅念 <email@sample.com>
Date:   Thu Sep 4 15:09:17 2025 +0800

    车辆信息,备货单  等页面
2025-09-18 09:55:10 +08:00

1459 lines
42 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="data-statistics">
<!-- 页面标题 -->
<!-- <div class="page-header">
<h1 class="page-title">数据统计</h1>
<div class="current-time">
<div class="time">{{ time }}</div>
<div class="date">{{ date }}</div>
</div>
</div> -->
<!-- 世界地图背景 -->
<div class="world-map-container">
<div ref="worldMapRef" class="world-map"></div>
</div>
<!-- 左侧面板 -->
<div class="left-panel">
<!-- 总体情况 -->
<div class="flex-d">
<!-- <transition name="slide-left"> -->
<div class="summary-section" v-show="showallChart">
<h3 class="section-title" style="margin-bottom: 10px">总体情况</h3>
<div class="summary-grid">
<div class="summary-item">
<div class="summary-number">{{ statisticswholedata.totals }}</div>
<div class="summary-label">总单数</div>
</div>
<div class="summary-item">
<div class="summary-number">{{ statisticswholedata.channelCount }}</div>
<div class="summary-label">渠道总数</div>
</div>
<div class="summary-item">
<div class="summary-number">{{ statisticswholedata.todayCount }}</div>
<div class="summary-label">今日录单</div>
</div>
<div class="summary-item">
<div class="summary-number">{{ statisticswholedata.releaseCount }}</div>
<div class="summary-label">发布人总数</div>
</div>
<div class="summary-item">
<div class="summary-number">{{ statisticswholedata.countryCount }}</div>
<div class="summary-label">国家总数</div>
</div>
<div class="summary-item">
<div class="summary-number">{{ statisticswholedata.trackCount }}</div>
<div class="summary-label">跟单人总数</div>
</div>
</div>
</div>
<!-- </transition> -->
<div class="icons1" v-if="!showallChart" @click="showallChart = !showallChart" >
<img src="@/assets/img/admin/Vector1.png" alt="" />
</div>
<div class="icons" v-if="showallChart" @click="showallChart = !showallChart" >
<img src="@/assets/img/admin/Vector.png" alt=""/>
</div>
</div>
<!-- 发布人计数 -->
<div class="flex-d">
<!-- <transition name="slide-left"> -->
<div class="chart-section" v-show="showPublisherChart">
<div class="chart-header">
<h3 class="section-title">询单计数</h3>
<div class="chart-controls">
<ElSelect
v-model="publisherFilter"
class="filter-select"
@change="updatePublisherChart"
>
<ElOption label="全部" value="0" />
<ElOption label="合塑" value="1" />
<ElOption label="派卡" value="3" />
</ElSelect>
</div>
</div>
<div ref="publisherChartRef" class="chart-container"></div>
</div>
<!-- </transition> -->
<div class="icons1" v-if="!showPublisherChart" @click="togglePublisherChart" >
<img src="@/assets/img/admin/Vector1.png" alt="" />
</div>
<div class="icons" v-if="showPublisherChart" @click="togglePublisherChart" >
<img src="@/assets/img/admin/Vector.png" alt=""/>
</div>
</div>
<!-- 跟单人计数显示/隐藏按钮,单独放置 -->
<div class="show-chart-btn">
<button class="toggle-btn" @click="toggleFollowUpChart">
跟单人<i class="iconfont-sys">{{ showFollowUpChart ? '&#xe864;' : ' &#xe867;' }}</i>
</button>
</div>
</div>
<!-- 右侧面板 -->
<div class="right-panel">
<!-- 各渠道计数 -->
<div class="flex-d-r">
<!-- <transition name="slide-right"> -->
<div class="icons" v-if="showchannelChart" @click="toggleshowchannelChart">
<img src="@/assets/img/admin/Vector1.png" alt=""/>
</div>
<div class="chart-section" v-show="showchannelChart">
<h3 class="section-title" style="margin-bottom: 10px">各渠道计数</h3>
<div ref="channelChartRef" class="chart-container"></div>
</div>
<div class="icons1" v-if="!showchannelChart" @click="toggleshowchannelChart">
<img src="@/assets/img/admin/Vector.png" alt="" />
</div>
<!-- </transition> -->
</div>
<!-- 每日录单 -->
<div class="flex-d-r">
<div class="icons" v-if="showOrdersChart" @click="toggleOrdersChart">
<img src="@/assets/img/admin/Vector1.png" alt=""/>
</div>
<!-- <transition name="slide-right"> -->
<div class="chart-section" v-show="showOrdersChart">
<h3 class="section-title" style="line-height: 36px">每日录单</h3>
<div ref="dailyOrdersChartRef" class="chart-container"></div>
</div>
<!-- </transition> -->
<div class="icons1" v-if="!showOrdersChart" @click="toggleOrdersChart">
<img src="@/assets/img/admin/Vector.png" alt="" />
</div>
</div>
</div>
<!-- 跟单人计数图表区域 -->
<transition name="slide-up">
<div class="full-width-chart-section" v-show="showFollowUpChart">
<div class="chart-header">
<h3 class="section-title">跟单人计数</h3>
</div>
<div ref="followUpChartRef" class="full-width-chart-container"></div>
</div>
</transition>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import * as echarts from 'echarts'
import { useCommon } from '@/composables/useCommon'
import worldGeoJson from '@/assets/geo/world.json'
import { manageService } from '@/api/adminApi'
defineOptions({ name: 'DataStatistics' })
// 图表显示状态
const showallChart = ref(true)
const showchannelChart = ref(true)
const showPublisherChart = ref(true)
const showOrdersChart = ref(true)
// Define interfaces for the data structures
interface CountryData {
name: string
value: number
}
interface ChartData {
optionType: string[]
counts: number[]
}
interface ChannelData {
name: string
value: number
}
useCommon().scrollToTop()
const time = ref('')
const date = ref('')
const worldMapRef = ref<HTMLElement>()
const publisherChartRef = ref<HTMLElement>()
const dailyOrdersChartRef = ref<HTMLElement>()
const channelChartRef = ref<HTMLElement>()
const followUpChartRef = ref<HTMLElement>()
const statisticswholedata = ref<any>({})
const channelcountdata = ref<any>({})
const countrycountdata = ref<any>({})
const trackcountdata = ref<any>({})
const daycountdata = ref<any>({})
const inquirycountdata = ref<any>({})
//总体 情况
const statisticswhole = async () => {
const res = await manageService.statisticswhole()
statisticswholedata.value = res
}
//各渠道 情况
const channelcount = async () => {
const res = await manageService.channelcount()
channelcountdata.value = res
initChannelChart(channelcountdata)
}
//各国家
const countrycount = async () => {
const res = await manageService.countrycount()
countrycountdata.value = res
initWorldMap(countrycountdata)
}
//跟单人
const trackcount = async () => {
const res = await manageService.trackcount()
trackcountdata.value = res
initFollowUpChart(trackcountdata)
}
const inquirycount = async () => {
const res = await manageService.inquirycount({ company_id: '0' })
inquirycountdata.value = res
initPublisherChart(inquirycountdata)
}
//每日录单
const daycount = async () => {
const res = await manageService.daycount()
daycountdata.value = res
initDailyOrdersChart(daycountdata)
}
// 跟单人图表显示状态
const showFollowUpChart = ref(false)
// 发布人计数筛选器
const publisherFilter = ref('0')
let timer: number
let worldMapChart: echarts.ECharts
let publisherChart: echarts.ECharts
let dailyOrdersChart: echarts.ECharts
let channelChart: echarts.ECharts
let followUpChart: echarts.ECharts
function updateTime() {
const now = new Date()
time.value = now.toLocaleTimeString('zh-CN', { hour12: false })
date.value = now.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long'
})
}
// 切换发布人图表显示状态
function togglePublisherChart() {
showPublisherChart.value = !showPublisherChart.value
if (showPublisherChart.value) {
nextTick(() => {
setTimeout(() => {
initPublisherChart(inquirycountdata)
// 关键:强制resize一次,确保图表自适应容器
setTimeout(() => {
publisherChart?.resize()
}, 50)
}, 100)
})
}
}
function toggleshowchannelChart() {
showchannelChart.value = !showchannelChart.value
if (showchannelChart.value) {
nextTick(() => {
setTimeout(() => {
initChannelChart(channelcountdata)
// 关键:强制resize一次,确保图表自适应容器
setTimeout(() => {
channelChart?.resize()
}, 50)
}, 100)
})
}
}
function toggleOrdersChart() {
showOrdersChart.value = !showOrdersChart.value
if (showOrdersChart.value) {
nextTick(() => {
setTimeout(() => {
initDailyOrdersChart(daycountdata)
// 关键:强制resize一次,确保图表自适应容器
setTimeout(() => {
dailyOrdersChart?.resize()
}, 50)
}, 100)
})
}
}
// 切换跟单人图表显示状态
function toggleFollowUpChart() {
showFollowUpChart.value = !showFollowUpChart.value
if (showFollowUpChart.value) {
nextTick(() => {
setTimeout(() => {
initFollowUpChart(trackcountdata)
// 关键:强制resize一次,确保图表自适应容器
setTimeout(() => {
followUpChart?.resize()
}, 50)
}, 100)
})
}
}
// 世界地图坐标
const chinaGeoCoordMap = ref<Record<string, [number, number]>>({
"阿富汗": [67.709953, 33.93911],
"安哥拉": [17.873887, -11.202692],
"阿尔巴尼亚": [20.168331, 41.153332],
"阿拉伯联合酋长国": [53.847818, 23.424076],
"阿根廷": [-63.61667199999999, -38.416097],
"亚美尼亚": [45.038189, 40.069099],
"法属南半球和南极领地": [69.348557, -49.280366],
"澳大利亚": [133.775136, -25.274398],
"奥地利": [14.550072, 47.516231],
"阿塞拜疆": [47.576927, 40.143105],
"布隆迪": [29.918886, -3.373056],
"比利时": [4.469936, 50.503887],
"贝宁": [2.315834, 9.30769],
"布基纳法索": [-1.561593, 12.238333],
"孟加拉人民共和国": [90.356331, 23.684994],
"保加利亚": [25.48583, 42.733883],
"巴哈马": [-77.39627999999999, 25.03428],
"波斯尼亚和黑塞哥维那": [17.679076, 43.915886],
"白俄罗斯": [27.953389, 53.709807],
"伯利兹": [-88.49765, 17.189877],
"百慕大": [-64.7505, 32.3078],
"玻利维亚": [-63.58865299999999, -16.290154],
"巴西": [-51.92528, -14.235004],
"文莱": [114.727669, 4.535277],
"不丹": [90.433601, 27.514162],
"博茨瓦纳": [24.684866, -22.328474],
"中非共和国": [20.939444, 6.611110999999999],
"加拿大": [-106.346771, 56.130366],
"瑞士": [8.227511999999999, 46.818188],
"智利": [-71.542969, -35.675147],
"中华人民共和国": [104.195397, 35.86166],
"象牙海岸": [-5.547079999999999, 7.539988999999999],
"喀麦隆": [12.354722, 7.369721999999999],
"刚果民主共和国": [21.758664, -4.038333],
"刚果共和国": [15.827659, -0.228021],
"哥伦比亚": [-74.297333, 4.570868],
"哥斯达黎加共和国": [-83.753428, 9.748916999999999],
"古巴": [-77.781167, 21.521757],
"北塞浦路斯": [33.429859, 35.126413],
"塞浦路斯": [33.429859, 35.126413],
"捷克共和国": [15.472962, 49.81749199999999],
"德国": [10.451526, 51.165691],
"吉布提": [42.590275, 11.825138],
"丹麦": [9.501785, 56.26392],
"多明尼加共和国": [-70.162651, 18.735693],
"阿尔及利亚": [1.659626, 28.033886],
"厄瓜多尔": [-78.18340599999999, -1.831239],
"埃及": [30.802498, 26.820553],
"厄立特里亚": [39.782334, 15.179384],
"西班牙": [-3.74922, 40.46366700000001],
"爱沙尼亚": [25.013607, 58.595272],
"埃塞俄比亚": [40.489673, 9.145000000000001],
"芬兰": [25.748151, 61.92410999999999],
"斐": [178.065032, -17.713371],
"福克兰群岛": [-59.523613, -51.796253],
"法国": [2.213749, 46.227638],
"加蓬共和国": [11.609444, -0.803689],
"英国": [-3.435973, 55.378051],
"格鲁吉亚": [-82.9000751, 32.1656221],
"加纳": [-1.023194, 7.946527],
"几内亚": [-9.696645, 9.945587],
"冈比亚": [-15.310139, 13.443182],
"几内亚比绍": [-15.180413, 11.803749],
"赤道几内亚": [10.267895, 1.650801],
"希腊": [21.824312, 39.074208],
"格陵兰": [-42.604303, 71.706936],
"危地马拉": [-90.23075899999999, 15.783471],
"法属圭亚那": [-53.125782, 3.933889],
"圭亚那": [-58.93018, 4.860416],
"洪都拉斯": [-86.241905, 15.199999],
"克罗地亚": [15.2, 45.1],
"海地": [-72.285215, 18.971187],
"匈牙利共和国": [19.503304, 47.162494],
"印尼": [113.921327, -0.789275],
"爱尔兰": [-8.24389, 53.41291],
"伊朗": [53.688046, 32.427908],
"伊拉克": [43.679291, 33.223191],
"冰岛": [-19.020835, 64.963051],
"以色列": [34.851612, 31.046051],
"意大利": [12.56738, 41.87194],
"牙买加": [-77.297508, 18.109581],
"约旦": [36.238414, 30.585164],
"日本": [138.252924, 36.204824],
"哈萨克斯坦": [66.923684, 48.019573],
"肯尼亚": [37.906193, -0.023559],
"吉尔吉斯斯坦": [74.766098, 41.20438],
"柬埔寨": [104.990963, 12.565679],
"韩国": [127.766922, 35.907757],
"科索沃": [20.902977, 42.6026359],
"科威特": [47.481766, 29.31166],
"老挝": [102.495496, 19.85627],
"黎巴嫩": [35.862285, 33.854721],
"利比里亚共和国": [-9.429499000000002, 6.428055],
"利比亚": [17.228331, 26.3351],
"斯里兰卡": [80.77179699999999, 7.873053999999999],
"莱索托": [28.233608, -29.609988],
"立陶宛": [23.881275, 55.169438],
"巴林": [50.30, 26.10],
"卢森堡": [6.129582999999999, 49.815273],
"拉脱维亚共和国": [24.603189, 56.879635],
"摩洛哥": [-7.092619999999999, 31.791702],
"摩尔多瓦": [28.369885, 47.411631],
"马达加斯加": [46.869107, -18.766947],
"墨西哥": [-102.552784, 23.634501],
"马其顿": [21.745275, 41.608635],
"马里": [-3.996166, 17.570692],
"缅甸": [95.956223, 21.913965],
"黑山共和国": [19.37439, 42.708678],
"蒙古": [103.846656, 46.862496],
"莫桑比克": [35.529562, -18.665695],
"毛里塔尼亚": [-10.940835, 21.00789],
"马拉维": [34.301525, -13.254308],
"马来西亚": [101.975766, 4.210484],
"纳米比亚": [18.49041, -22.95764],
"新喀里多尼亚": [165.618042, -20.904305],
"尼日尔": [8.081666, 17.607789],
"尼日利亚": [8.675277, 9.081999],
"尼加拉瓜": [-85.207229, 12.865416],
"荷兰": [5.291265999999999, 52.132633],
"挪威": [8.468945999999999, 60.47202399999999],
"尼泊尔": [84.12400799999999, 28.394857],
"新西兰": [174.885971, -40.900557],
"阿曼": [55.923255, 21.512583],
"巴基斯坦": [69.34511599999999, 30.375321],
"巴拿马共和国": [-80.782127, 8.537981],
"秘鲁": [-75.015152, -9.189967],
"菲律宾": [121.774017, 12.879721],
"巴布亚新几内亚": [143.95555, -6.314992999999999],
"波兰": [19.145136, 51.919438],
"波多黎各": [-66.590149, 18.220833],
"北朝鲜": [127.510093, 40.339852],
"葡萄牙": [-8.224454, 39.39987199999999],
"巴拉圭": [-58.443832, -23.442503],
"卡塔尔": [51.183884, 25.354826],
"罗马尼亚": [24.96676, 45.943161],
"俄罗斯": [105.318756, 61.52401],
"俄罗斯联邦": [105.318756, 61.52401],
"卢旺达": [29.873888, -1.940278],
"西撒哈拉": [-12.885834, 24.215527],
"沙特阿拉伯": [45.079162, 23.885942],
"苏丹": [30.217636, 12.862807],
"南苏丹": [31.3069788, 6.876991899999999],
"塞内加尔": [-14.452362, 14.497401],
"所罗门群岛": [160.156194, -9.64571],
"塞拉利昂": [-11.779889, 8.460555],
"萨尔瓦多": [-88.89653, 13.794185],
"索马里兰": [46.8252838, 9.411743399999999],
"索马里": [46.199616, 5.152149],
"塞尔维亚": [21.005859, 44.016521],
"苏里南": [-56.027783, 3.919305],
"斯洛伐克共和国": [19.699024, 48.669026],
"斯洛文尼亚": [14.995463, 46.151241],
"瑞典": [18.643501, 60.12816100000001],
"斯威士兰": [31.465866, -26.522503],
"叙利亚": [38.996815, 34.80207499999999],
"乍得": [18.732207, 15.454166],
"多哥": [0.824782, 8.619543],
"泰国": [100.992541, 15.870032],
"塔吉克斯坦共和国": [71.276093, 38.861034],
"土库曼斯坦": [59.556278, 38.969719],
"东帝汶": [125.727539, -8.874217],
"特里尼达和多巴哥": [-61.222503, 10.691803],
"突尼斯": [9.537499, 33.886917],
"土耳其": [35.243322, 38.963745],
"坦桑尼亚": [34.888822, -6.369028],
"乌干达": [32.290275, 1.373333],
"乌克兰": [31.16558, 48.379433],
"乌拉圭": [-55.765835, -32.522779],
"美国": [-95.712891, 37.09024],
"乌兹别克斯坦": [64.585262, 41.377491],
"委内瑞拉": [-66.58973, 6.42375],
"越南": [108.277199, 14.058324],
"瓦努阿图": [166.959158, -15.376706],
"西岸": [35.3027226, 31.9465703],
"也门": [48.516388, 15.552727],
"南非": [22.937506, -30.559482],
"赞比亚": [27.849332, -13.133897],
"津巴布韦": [29.154857, -19.015438],
"科摩罗": [43.872219, -11.875001],
"印度尼西亚": [106.515414, -6.103040],
"印度": [78.96288, 20.593684],
"刚果": [15.12, -4.09],
"萨摩亚独立国": [-171.46, 13.5],
"朝鲜": [125.30, 39.09],
"新加坡": [103.87546, 1.30243],
"中非": [18.33, 4.21],
"巴勒斯坦": [35.25, 31.79],
"圣卢西亚": [-60.58, 14.02],
"安提瓜和巴布达": [-61.48, 17.20],
"巴巴多斯": [-59.30, 13.05],
"开曼群岛": [-81.24, 19.20],
"法罗群岛": [-6.56, 62.05],
"圣文森特和格林纳丁斯": [-61.10, 13.10],
"特立尼达和多巴哥": [-60.32, 11.18],
"台澎金马单独关税区": [121.21, 25.02],
"中国香港": [114.12, 22.26],
"中国澳门": [113.5, 22.2],
"格林纳达": [-61.4, 12.7],
"塞舌尔共和国": [55.28, -4.4],
"多米尼克": [61.23, 15.18],
"斐济": [178.30, -18.06],
"科特迪瓦": [-5.17, 6.49],
"欧盟": [4.47, 50.50]
})
// 配置地名中英文转换
const nameMap = ref({
"Afghanistan": "阿富汗",
"Singapore": "新加坡",
"Angola": "安哥拉",
"Albania": "阿尔巴尼亚",
"United Arab Emirates": "阿拉伯联合酋长国",
"Argentina": "阿根廷",
"Armenia": "亚美尼亚",
"French Southern and Antarctic Lands": "法属南半球和南极领地",
"Australia": "澳大利亚",
"Austria": "奥地利",
"Azerbaijan": "阿塞拜疆",
"Seychelles": "塞舌尔共和国",
"Burundi": "布隆迪",
"Belgium": "比利时",
"Benin": "贝宁",
"Burkina Faso": "布基纳法索",
"Côte d Ivoire": "科特迪瓦",
"Bangladesh": "孟加拉人民共和国",
"Bulgaria": "保加利亚",
"The Bahamas": "巴哈马",
"Bosnia and Herzegovina": "波斯尼亚和黑塞哥维那",
"Belarus": "白俄罗斯",
"Belize": "伯利兹",
"Bermuda": "百慕大",
"Bolivia": "玻利维亚",
"Brazil": "巴西",
"Brunei": "文莱",
"Bhutan": "不丹",
"Botswana": "博茨瓦纳",
"Central African Republic": "中非共和国",
"Canada": "加拿大",
"Switzerland": "瑞士",
"Chile": "智利",
"China": "中华人民共和国",
"Ivory Coast": "象牙海岸",
"Cameroon": "喀麦隆",
"Dem. Rep. Congo": "刚果民主共和国",
"Republic of the Congo": "刚果共和国",
"Colombia": "哥伦比亚",
"Costa Rica": "哥斯达黎加共和国",
"Cuba": "古巴",
"Northern Cyprus": "北塞浦路斯",
"Cyprus": "塞浦路斯",
"Czech Republic": "捷克共和国",
"Germany": "德国",
"Djibouti": "吉布提",
"Denmark": "丹麦",
"Dominican Republic": "多明尼加共和国",
"Algeria": "阿尔及利亚",
"Ecuador": "厄瓜多尔",
"Egypt": "埃及",
"Eritrea": "厄立特里亚",
"Spain": "西班牙",
"Estonia": "爱沙尼亚",
"Ethiopia": "埃塞俄比亚",
"Bahrain": "巴林",
"Finland": "芬兰",
"Fiji": "斐济",
"Falkland Islands": "福克兰群岛",
"France": "法国",
"Gabon": "加蓬共和国",
"United Kingdom": "英国",
"Georgia": "格鲁吉亚",
"Ghana": "加纳",
"Guinea": "几内亚",
"Gambia": "冈比亚",
"Guinea Bissau": "几内亚比绍",
"Greece": "希腊",
"Greenland": "格陵兰",
"Guatemala": "危地马拉",
"French Guiana": "法属圭亚那",
"Guyana": "圭亚那",
"Honduras": "洪都拉斯",
"Croatia": "克罗地亚",
"Haiti": "海地",
"Hungary": "匈牙利共和国",
"Indonesia": "印度尼西亚",
"India": "印度",
"Ireland": "爱尔兰",
"Iran": "伊朗",
"Iraq": "伊拉克",
"Iceland": "冰岛",
"Israel": "以色列",
"Italy": "意大利",
"Jamaica": "牙买加",
"Jordan": "约旦",
"Japan": "日本",
"Kazakhstan": "哈萨克斯坦",
"Kenya": "肯尼亚",
"Kyrgyzstan": "吉尔吉斯斯坦",
"Cambodia": "柬埔寨",
"Kosovo": "科索沃",
"Kuwait": "科威特",
"Laos": "老挝",
"Lebanon": "黎巴嫩",
"Liberia": "利比里亚共和国",
"Libya": "利比亚",
"Sri Lanka": "斯里兰卡",
"Lesotho": "莱索托",
"Lithuania": "立陶宛",
"Luxembourg": "卢森堡",
"Latvia": "拉脱维亚共和国",
"Morocco": "摩洛哥",
"Moldova": "摩尔多瓦",
"Madagascar": "马达加斯加",
"Mexico": "墨西哥",
"Macedonia": "马其顿",
"Mali": "马里",
"Myanmar": "缅甸",
"Montenegro": "黑山共和国",
"Mongolia": "蒙古",
"Mozambique": "莫桑比克",
"Mauritania": "毛里塔尼亚",
"Malawi": "马拉维",
"Malaysia": "马来西亚",
"Namibia": "纳米比亚",
"New Caledonia": "新喀里多尼亚",
"Niger": "尼日尔",
"Nigeria": "尼日利亚",
"Nicaragua": "尼加拉瓜",
"Netherlands": "荷兰",
"Norway": "挪威",
"Nepal": "尼泊尔",
"New Zealand": "新西兰",
"Oman": "阿曼",
"Pakistan": "巴基斯坦",
"Panama": "巴拿马共和国",
"Peru": "秘鲁",
"Philippines": "菲律宾",
"Papua New Guinea": "巴布亚新几内亚",
"Poland": "波兰",
"Puerto Rico": "波多黎各",
"North Korea": "北朝鲜",
"Portugal": "葡萄牙",
"Paraguay": "巴拉圭",
"Qatar": "卡塔尔",
"Romania": "罗马尼亚",
"Russia": "俄罗斯",
"Rwanda": "卢旺达",
"Western Sahara": "西撒哈拉",
"Saudi Arabia": "沙特阿拉伯",
"Sudan": "苏丹",
"S. Sudan": "南苏丹",
"Senegal": "塞内加尔",
"Solomon Islands": "所罗门群岛",
"Sierra Leone": "塞拉利昂",
"El Salvador": "萨尔瓦多",
"Somaliland": "索马里兰",
"Somalia": "索马里",
"Republic of Serbia": "塞尔维亚",
"Suriname": "苏里南",
"Slovakia": "斯洛伐克共和国",
"Slovenia": "斯洛文尼亚",
"Sweden": "瑞典",
"Swaziland": "斯威士兰",
"Syria": "叙利亚",
"Chad": "乍得",
"Togo": "多哥",
"Thailand": "泰国",
"Tajikistan": "塔吉克斯坦共和国",
"Turkmenistan": "土库曼斯坦",
"East Timor": "东帝汶",
"Trinidad and Tobago": "特立尼达和多巴哥",
"Tunisia": "突尼斯",
"Turkey": "土耳其",
"Uganda": "乌干达",
"Ukraine": "乌克兰",
"Uruguay": "乌拉圭",
"United States": "美国",
"Uzbekistan": "乌兹别克斯坦",
"Venezuela": "委内瑞拉",
"Vietnam": "越南",
"Vanuatu": "瓦努阿图",
"West Bank": "西岸",
"Yemen": "也门",
"South Africa": "南非",
"Zambia": "赞比亚",
"Korea": "韩国",
"Tanzania": "坦桑尼亚",
"Zimbabwe": "津巴布韦",
"Congo": "刚果",
"Central African Rep.": "中非",
"Serbia": "塞尔维亚",
"Bosnia and Herz.": "波黑",
"Czech Rep.": "捷克",
"W. Sahara": "西撒哈拉",
"Lao PDR": "老挝",
"Dem. Rep. Korea": "朝鲜",
"Falkland Is.": "福克兰群岛",
"Timor-Leste": "东帝汶",
"Solomon Is.": "所罗门群岛",
"Palestine": "巴勒斯坦",
"N. Cyprus": "北塞浦路斯",
"Aland": "奥兰群岛",
"Fr. S. Antarctic Lands": "法属南半球和南极陆地",
"Mauritius": "毛里求斯",
"Comoros": "科摩罗",
"Eq. Guinea": "赤道几内亚",
"Guinea-Bissau": "几内亚比绍",
"Dominican Rep.": "多米尼加共和国",
"Saint Lucia": "圣卢西亚",
"Dominica": "多米尼克",
"Antigua and Barb.": "安提瓜和巴布达",
"U.S. Virgin Is.": "美国原始岛屿",
"Montserrat": "蒙塞拉特",
"Grenada": "格林纳达",
"Barbados": "巴巴多斯",
"Samoa": "萨摩亚独立国",
"Bahamas": "巴哈马",
"Cayman Is.": "开曼群岛",
"Faeroe Is.": "法罗群岛",
"IsIe of Man": "马恩岛",
"Malta": "马耳他共和国",
"Jersey": "泽西",
"Cape Verde": "佛得角共和国",
"Turks and Caicos Is.": "特克斯和凯科斯群岛",
"St. Vin. and Gren.": "圣文森特和格林纳丁斯"
});
// eslint-disable-next-line no-redeclare
interface CountryData {
name: string
value: number
}
// eslint-disable-next-line no-redeclare
interface ChartData {
optionType: string[]
counts: number[]
}
// eslint-disable-next-line no-redeclare
interface ChannelData {
name: string
value: number
}
const convertData1 = (lineDatas: CountryData[]): any[] => {
const map: any[] = []
for (let n = 0; n < lineDatas.length; n++) {
const obj: any = {}
const dataItem = lineDatas[n]
obj.name = dataItem.name
obj.value = chinaGeoCoordMap.value[dataItem.name]
obj.num = dataItem.value
map.push(obj)
}
return map
}
// 初始化世界地图
function initWorldMap(countrycountdata?: Ref<any>) {
if (!worldMapRef.value) {
return
}
// 注册世界地图
echarts.registerMap('world', worldGeoJson as any)
worldMapChart = echarts.init(worldMapRef.value)
// Use countrycountdata if provided, otherwise use lineDatas
const dataToUse = countrycountdata?.value
const option = {
backgroundColor: 'transparent',
geo: {
map: 'world',
roam: true,
zoom: 0.7,
center: [0, 0],
itemStyle: {
areaColor: '#90adff',
borderColor: '#ccc',
borderWidth: 0.5
},
emphasis: {
itemStyle: {
areaColor: '#e6f7ff'
}
},
nameMap: nameMap.value
},
tooltip: {
trigger: 'item',
borderColor: '#666', //区域边框颜色
formatter: function (params: { name: string; data: { num: any } }) {
if (params.name) {
return (
params.name +
' : ' +
(params.data.num
? params.data.num
: isNaN(params.data.num)
? 0
: parseInt(params.data.num))
)
}
}
},
series: [
{
type: 'scatter',
coordinateSystem: 'geo',
data: convertData1(dataToUse),
symbolSize: 12,
itemStyle: {
color: '#5D87FF',
shadowBlur: 15,
shadowColor: '#5D87FF',
borderColor: '#5D87FF',
borderWidth: 2
},
label: {
show: true, // 显示标签
formatter: (params: any) => params.data.num, // 显示数据值
position: 'inside', // 标签位置(可选:'top', 'left', 'right', 'bottom', 'inside' 等)
color: '#fff', // 标签文字颜色
fontSize: 10, // 标签字体大小
fontWeight: 'bold', // 标签字体粗细
backgroundColor: '#5D87FF', // 背景色
borderColor: '#fff',
borderRadius: 9, // 圆角
padding: [4, 4] // 内边距
},
tooltip: {
show: true,
formatter: function (params: any) {
return `${params.data.name}<br/>数据值: ${params.data.num}`
}
},
emphasis: {
itemStyle: {
color: '#4A6FE3',
shadowBlur: 20,
shadowColor: '#4A6FE3'
}
}
}
]
}
worldMapChart.setOption(option)
console.log('World map initialized successfully')
}
// 更新发布人计数图表
async function updatePublisherChart() {
const res = await manageService.inquirycount({ company_id: publisherFilter.value })
const option = {
xAxis: {
data: res.optionType
},
series: [
{
data: res.counts
}
]
}
publisherChart.setOption(option)
}
// 初始化发布人计数图表
function initPublisherChart(inquirycountdata: Ref<ChartData>) {
if (!publisherChartRef.value) return
publisherChart = echarts.init(publisherChartRef.value)
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: inquirycountdata.value.optionType,
axisLabel: {
color: '#666'
}
},
yAxis: {
type: 'value',
axisLabel: {
color: '#666'
}
},
series: [
{
data: inquirycountdata.value.counts,
type: 'bar',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#5D87FF' },
{ offset: 1, color: '#4A6FE3' }
])
}
}
]
}
publisherChart.setOption(option)
}
// 初始化每日录单图表
function initDailyOrdersChart(daycountdata: Ref<ChartData>) {
if (!dailyOrdersChartRef.value) return
dailyOrdersChart = echarts.init(dailyOrdersChartRef.value)
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: daycountdata.value.optionType,
axisLabel: {
color: '#666',
rotate: 45
}
},
yAxis: {
type: 'value',
axisLabel: {
color: '#666'
}
},
series: [
{
name: '录单数',
type: 'line',
smooth: true,
data: daycountdata.value.counts,
itemStyle: {
color: '#5D87FF'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(93, 135, 255, 0.3)' },
{ offset: 1, color: 'rgba(93, 135, 255, 0.1)' }
])
}
}
]
}
dailyOrdersChart.setOption(option)
}
// 初始化渠道计数图表
function initChannelChart(channelcountdata: Ref<ChannelData[]>) {
console.log(channelcountdata)
if (!channelChartRef.value) return
channelChart = echarts.init(channelChartRef.value)
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left',
textStyle: {
color: '#666'
}
},
series: [
{
center: ['75%', '50%'],
name: '渠道统计',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '20',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: channelcountdata.value
}
]
}
channelChart.setOption(option)
}
// 初始化跟单人计数图表
function initFollowUpChart(trackcountdata: Ref<ChartData>) {
if (!followUpChartRef.value) return
followUpChart = echarts.init(followUpChartRef.value)
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '8%',
right: '5%',
bottom: 40, // 为 dataZoom 预留空间
top: '5%',
containLabel: true
},
xAxis: {
type: 'category',
data: trackcountdata.value.optionType,
axisLabel: {
color: '#666',
fontSize: 12
}
},
yAxis: {
type: 'value',
axisLabel: {
color: '#666'
}
},
series: [
{
name: '跟单数',
type: 'bar',
data: trackcountdata.value.counts
}
],
dataZoom: [
{
type: 'slider',
show: true,
xAxisIndex: 0,
height: 24,
bottom: 0,
start: 0,
end: 30,
handleSize: '100%',
handleStyle: {
color: '#5D87FF'
}
}
]
}
followUpChart.setOption(option)
}
// 窗口大小改变时重新调整图表
function handleResize() {
worldMapChart?.resize()
publisherChart?.resize()
dailyOrdersChart?.resize()
channelChart?.resize()
followUpChart?.resize()
}
onMounted(() => {
statisticswhole()
channelcount()
inquirycount()
countrycount()
trackcount()
daycount()
updateTime()
timer = window.setInterval(updateTime, 1000)
// 初始化所有图表
// nextTick(() => {
// setTimeout(() => {
// initWorldMap(countrycountdata)
// initPublisherChart()
// initDailyOrdersChart(daycountdata)
// initChannelChart(channelcountdata)
// initFollowUpChart(trackcountdata)
// }, 100)
// })
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
clearInterval(timer)
window.removeEventListener('resize', handleResize)
// 销毁图表实例
worldMapChart?.dispose()
publisherChart?.dispose()
dailyOrdersChart?.dispose()
channelChart?.dispose()
followUpChart?.dispose()
})
</script>
<style lang="scss" scoped>
.flex-d {
display: flex;
align-items: center;
height: 274px;
.icons {
padding: 10px 5px;
background: rgba(93, 135, 255, 0.1);
border-radius:0px 5px 5px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
cursor: pointer;
}
.icons1 {
padding: 10px 5px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
cursor: pointer;
}
// .icons i {
// font-size: 26px;
// font-weight: bold;
// color: #5d87ff;
// cursor: pointer;
// }
.chart-section {
flex: 1;
}
}
.flex-d-r {
display: flex;
align-items: center;
height: 274px;
justify-content: end;
.icons {
text-align: right;
padding: 10px 5px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
background: #fff;
border-radius:5px 0px 0px 5px;
cursor: pointer;
}
.icons1 {
text-align: right;
padding: 10px 5px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
cursor: pointer;
}
.icons i {
font-size: 26px;
font-weight: bold;
color: #5d87ff;
cursor: pointer;
}
.chart-section {
flex: 1;
}
}
.data-statistics {
position: relative;
min-height: 100vh;
background: var(--art-main-bg-color);
overflow: hidden;
}
.page-header {
position: relative;
z-index: 10;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 30px;
background: linear-gradient(90deg, rgba(255, 255, 255, 0.8) 0%, rgba(93, 135, 255, 0.15) 100%);
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
.page-title {
font-size: 24px;
font-weight: bold;
color: #333;
margin: 0;
}
.current-time {
text-align: right;
.time {
font-size: 18px;
font-weight: bold;
color: #333;
}
.date {
font-size: 14px;
color: #666;
margin-top: 4px;
}
}
}
.world-map-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
// background:#c9d5f8;
// background: linear-gradient(135deg, #f5f7fa 0%, #89a7fa 100%);
opacity: 0.8;
.world-map {
width: 100%;
height: 100%;
min-height: 600px;
}
}
.left-panel {
position: relative;
z-index: 10;
width: 400px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 20px;
}
.right-panel {
position: absolute;
top: 20px;
right: 20px;
z-index: 10;
width: 450px;
display: flex;
flex-direction: column;
gap: 20px;
}
.full-width-chart-section {
position: relative;
z-index: 10;
// width: calc(100vw - 0px); // 或 100vw
left: 0;
margin: 0px 20px;
background: linear-gradient(90deg, rgba(255, 255, 255, 0.7) 0%, rgba(93, 135, 255, 0.1) 100%);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
// border: 1px solid rgba(255, 255, 255, 0.3);
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin: 0;
}
.full-width-chart-container {
height: 260px;
width: 100%;
}
}
.toggle-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;
background: linear-gradient(90deg, rgba(93, 135, 255, 0.1) 0%, rgba(93, 135, 255, 0.2) 100%);
border: 1px solid rgba(93, 135, 255, 0.3);
border-radius: 8px;
color: #5D87FF;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
&:hover {
background: linear-gradient(90deg, rgba(93, 135, 255, 0.2) 0%, rgba(93, 135, 255, 0.3) 100%);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(93, 135, 255, 0.2);
}
&:active {
transform: translateY(0);
}
i {
font-size: 16px;
}
}
.summary-section {
background: linear-gradient(90deg, rgba(255, 255, 255, 0.7) 0%, rgba(93, 135, 255, 0.1) 100%);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
// border: 1px solid rgba(255, 255, 255, 0.3);
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
}
.summary-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
height: 200px;
.summary-item {
text-align: center;
padding: 15px;
background: linear-gradient(90deg, rgba(248, 249, 250, 0.8) 0%, rgba(93, 135, 255, 0.05) 100%);
border-radius: 8px;
border: 1px solid rgba(233, 236, 239, 0.6);
.summary-number {
font-size: 24px;
font-weight: bold;
color: #5D87FF;
margin-bottom: 5px;
}
.summary-label {
font-size: 12px;
color: #666;
}
}
}
}
.chart-section {
background: linear-gradient(90deg, rgba(255, 255, 255, 0.7) 0%, rgba(93, 135, 255, 0.1) 100%);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
// border: 1px solid rgba(255, 255, 255, 0.3);
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin: 0;
}
.chart-controls {
display: flex;
gap: 10px;
}
.filter-select {
width: 100px;
}
.chart-container {
height: 200px;
width: 100%;
}
.chart-container2 {
height: 475px;
width: 100%;
}
}
// 响应式设计
@media (max-width: 1200px) {
.left-panel {
width: 350px;
}
.right-panel {
width: 400px;
}
}
@media (max-width: 768px) {
.page-header {
flex-direction: column;
gap: 10px;
text-align: center;
}
.left-panel,
.right-panel {
position: relative;
width: 100%;
top: auto;
right: auto;
.summary-grid {
grid-template-columns: repeat(3, 1fr);
}
}
}
// 丝滑动画定义
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active,
.slide-up-enter-active,
.slide-up-leave-active {
transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.slide-left-enter-from {
transform: translateX(-100%);
opacity: 0;
}
.slide-left-leave-to {
transform: translateX(-100%);
opacity: 0;
}
.slide-right-enter-from {
transform: translateX(100%);
opacity: 0;
}
.slide-right-leave-to {
transform: translateX(100%);
opacity: 0;
}
.slide-up-enter-from {
transform: translateY(100%);
opacity: 0;
}
.slide-up-leave-to {
transform: translateY(100%);
opacity: 0;
}
</style>