2025-06-16 14:42:26 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view>
|
2025-06-17 11:18:39 +08:00
|
|
|
|
<topHeader ref="topHeaderRef" @search="search"></topHeader>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<div class="h-360px w-100% flex mb-24px">
|
|
|
|
|
|
<!-- 1. 性别分布 -->
|
|
|
|
|
|
<div class="bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid w-100% py-0 px-20px w-600px mr-24px">
|
|
|
|
|
|
<div class="title-row">
|
|
|
|
|
|
<span class="title mr-4px">性别分布</span>
|
|
|
|
|
|
<a-tooltip>
|
|
|
|
|
|
<template #content>基于社交内容平台中用户资料、互动行为及语义特征进行智能识别与估算。</template>
|
|
|
|
|
|
<icon-question-circle size="16" class="!color-#737478" />
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</div>
|
2025-07-01 16:39:47 +08:00
|
|
|
|
<a-space v-if="genderData.length > 0">
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<div id="container" class="w-300px h-300px"></div>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
|
|
|
|
|
|
<a-space direction="vertical" style="font-size: 14px">
|
|
|
|
|
|
<a-space>
|
|
|
|
|
|
<span style="width: 8px; height: 8px; background-color: #f64b31; border-radius: 50%"></span>
|
|
|
|
|
|
<span>女性</span>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<span v-if="genderData.length > 0" style="width: 40px">{{ genderData[0].rate * 100 }}%</span>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<span>TGI</span>
|
|
|
|
|
|
<span v-if="genderData.length > 0">{{ genderData[0].tgi }}</span>
|
|
|
|
|
|
</a-space>
|
|
|
|
|
|
<a-space>
|
|
|
|
|
|
<span style="width: 8px; height: 8px; background-color: #2a59f3; border-radius: 50%"></span>
|
|
|
|
|
|
<span>男性</span>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<span v-if="genderData.length > 1" style="width: 40px">{{ genderData[1].rate * 100 }}%</span>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<span>TGI</span>
|
|
|
|
|
|
<span v-if="genderData.length > 1">{{ genderData[1].tgi }}</span>
|
|
|
|
|
|
</a-space>
|
|
|
|
|
|
</a-space>
|
|
|
|
|
|
</a-space>
|
2025-07-01 16:39:47 +08:00
|
|
|
|
<div v-else>
|
|
|
|
|
|
<NoData class="w-100% h-100%" />
|
|
|
|
|
|
</div>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 2. 年龄分布 -->
|
|
|
|
|
|
<div class="bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid w-100% py-0 px-20px flex-1 flex flex-col">
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<a-space style="display: flex; justify-content: space-between; width: 100%; font-size: 12px">
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<div class="title-row">
|
|
|
|
|
|
<span class="title mr-4px">年龄分布</span>
|
|
|
|
|
|
<a-tooltip>
|
|
|
|
|
|
<template #content>基于社交平台的公开信息、内容偏好与行为模式,通过算法进行年龄段归类和统计。</template>
|
|
|
|
|
|
<icon-question-circle size="16" class="!color-#737478" />
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</div>
|
2025-07-01 16:39:47 +08:00
|
|
|
|
<a-space v-if="ageValueData.length > 0" align="center">
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<span style="width: 16px; height: 8px; background-color: #6d4cfe; border-radius: 2px"></span>
|
|
|
|
|
|
<span style="color: #6d4cfe">占比</span>
|
|
|
|
|
|
<span style="width: 16px; height: 8px; background-color: #f64b31; border-radius: 2px"></span>
|
|
|
|
|
|
<span style="color: #f64b31">TGI比</span>
|
|
|
|
|
|
</a-space>
|
|
|
|
|
|
</a-space>
|
2025-07-01 16:39:47 +08:00
|
|
|
|
<div v-if="ageValueData.length === 0" class="w-100% flex-1">
|
|
|
|
|
|
<NoData />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-else id="age-container" class="w-100% flex-1"></div>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="bg-#fff rounded-8px border-1px border-#D7D7D9 border-solid w-100% py-0 px-20px flex-1 pb-20px">
|
|
|
|
|
|
<div class="title-row">
|
|
|
|
|
|
<span class="title mr-4px">地域分布</span>
|
|
|
|
|
|
<a-tooltip>
|
|
|
|
|
|
<template #content>基于社交平台的IP归属地、位置标签、内容发布地等数据推测用户活跃区域。</template>
|
|
|
|
|
|
<icon-question-circle size="16" class="!color-#737478" />
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex">
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<a-space direction="vertical">
|
|
|
|
|
|
<div id="chinaMap" style="height: 416px; width: 640px"></div>
|
|
|
|
|
|
<a-space direction="vertical" style="font-size: 14px">
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<span class="cts">搜索指数</span>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<a-space>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<span class="cts">高</span>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<span
|
|
|
|
|
|
v-for="item in 5"
|
|
|
|
|
|
:key="item"
|
|
|
|
|
|
:style="{
|
|
|
|
|
|
width: `${18 - item * 2}px`,
|
|
|
|
|
|
height: `${18 - item * 2}px`,
|
|
|
|
|
|
backgroundColor: '#6D4CFE',
|
|
|
|
|
|
borderRadius: '50%',
|
|
|
|
|
|
display: 'inline-block',
|
|
|
|
|
|
margin: '0 2px',
|
|
|
|
|
|
}"
|
|
|
|
|
|
></span>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<span class="cts">低</span>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
</a-space>
|
|
|
|
|
|
</a-space>
|
|
|
|
|
|
</a-space>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<div class="flex flex-col h-486px">
|
|
|
|
|
|
<a-tabs default-active-key="1" class="h-100%" @change="tabChange">
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<a-tab-pane key="1" title="省份">
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<a-table :data="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }">
|
2025-07-01 16:39:47 +08:00
|
|
|
|
<template #empty>
|
|
|
|
|
|
<NoData />
|
|
|
|
|
|
</template>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<template #columns>
|
|
|
|
|
|
<a-table-column title="排名" data-index="rank" />
|
|
|
|
|
|
<a-table-column title="省份" data-index="geo" />
|
|
|
|
|
|
<a-table-column title="分布占比" data-index="rate" />
|
|
|
|
|
|
|
|
|
|
|
|
<a-table-column title="TGI指数" data-index="tgi" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
<a-tab-pane key="2" title="城市">
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<a-table :data="geoList" :pagination="false" class="h-100%" :scroll="{ y: '100%' }">
|
2025-07-01 16:39:47 +08:00
|
|
|
|
<template #empty>
|
|
|
|
|
|
<NoData />
|
|
|
|
|
|
</template>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
<template #columns>
|
|
|
|
|
|
<a-table-column title="排名" data-index="rank" />
|
|
|
|
|
|
<a-table-column title="城市" data-index="geo" />
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<a-table-column title="分布占比" data-index="rate">
|
|
|
|
|
|
<template #cell="{ record }">
|
|
|
|
|
|
<span class="cts">{{ (record.rate * 100).toFixed(2) }}%</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table-column>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
|
|
|
|
|
|
<a-table-column title="TGI指数" data-index="tgi" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
</a-tabs>
|
2025-07-01 14:34:16 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-06-16 14:42:26 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import topHeader from './topHeader.vue';
|
|
|
|
|
|
import { fetchAgeDistributionsList, fetchGeoDistributionsList, fetchGenderDistributionsList } from '@/api/all/index';
|
|
|
|
|
|
import { ref, onMounted, computed } from 'vue';
|
|
|
|
|
|
import * as echarts from 'echarts';
|
|
|
|
|
|
import chinaJson from '@/assets/maps/china.json';
|
|
|
|
|
|
|
|
|
|
|
|
echarts.registerMap('china', chinaJson);
|
2025-07-01 14:34:16 +08:00
|
|
|
|
const scope = ref(1); // 地域范围,1-省,2-市
|
2025-06-16 14:42:26 +08:00
|
|
|
|
const chartInstance = (ref < echarts.ECharts) | (null > null);
|
|
|
|
|
|
const topHeaderRef = ref();
|
|
|
|
|
|
// 从topHeader获取统一的状态
|
|
|
|
|
|
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
|
|
|
|
|
|
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
|
|
|
|
|
|
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
|
|
|
|
|
|
const genderData = ref([]);
|
|
|
|
|
|
const genderValueData = ref([]);
|
|
|
|
|
|
const ageValueData = ref([]);
|
|
|
|
|
|
const geoList = ref([]);
|
|
|
|
|
|
// 监听筛选条件变化
|
2025-06-17 11:18:39 +08:00
|
|
|
|
watch([selectedIndustry, selectedTimePeriod, selectedSubCategory], () => {
|
2025-06-16 14:42:26 +08:00
|
|
|
|
getAgeDistributionsList();
|
|
|
|
|
|
getGeoDistributionsList();
|
|
|
|
|
|
getGenderDistributionsList();
|
|
|
|
|
|
drawChinaMap();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-06-17 11:18:39 +08:00
|
|
|
|
const search = () => {
|
|
|
|
|
|
getAgeDistributionsList();
|
|
|
|
|
|
getGeoDistributionsList();
|
|
|
|
|
|
getGenderDistributionsList();
|
|
|
|
|
|
drawChinaMap();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-06-16 14:42:26 +08:00
|
|
|
|
// 获取年龄分布列表
|
|
|
|
|
|
const getAgeDistributionsList = async () => {
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
industry_id: selectedIndustry.value,
|
|
|
|
|
|
time_dimension: selectedTimePeriod.value,
|
|
|
|
|
|
};
|
2025-06-21 16:57:01 +08:00
|
|
|
|
if (selectedIndustry.value == undefined) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (selectedSubCategory.value == undefined) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-06-17 11:18:39 +08:00
|
|
|
|
if (selectedSubCategory.value != 0) {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
params['industry_id'] = selectedSubCategory.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
const { code, data } = await fetchAgeDistributionsList(params);
|
|
|
|
|
|
if (code === 200) {
|
|
|
|
|
|
ageValueData.value = data;
|
2025-07-01 16:39:47 +08:00
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
drawAgeChart();
|
|
|
|
|
|
});
|
2025-06-17 11:18:39 +08:00
|
|
|
|
}
|
2025-06-16 14:42:26 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获得地理分布列表
|
|
|
|
|
|
const getGeoDistributionsList = async () => {
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
scope: scope.value,
|
|
|
|
|
|
industry_id: selectedIndustry.value,
|
|
|
|
|
|
time_dimension: selectedTimePeriod.value,
|
|
|
|
|
|
};
|
2025-06-21 16:57:01 +08:00
|
|
|
|
if (selectedIndustry.value == undefined) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (selectedSubCategory.value == undefined) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-06-17 11:18:39 +08:00
|
|
|
|
if (selectedSubCategory.value != 0) {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
params['industry_id'] = selectedSubCategory.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
const { code, data } = await fetchGeoDistributionsList(params);
|
|
|
|
|
|
if (code === 200) {
|
|
|
|
|
|
geoList.value = data;
|
2025-06-17 11:18:39 +08:00
|
|
|
|
}
|
2025-06-16 14:42:26 +08:00
|
|
|
|
};
|
|
|
|
|
|
// 获取性别分布列表
|
|
|
|
|
|
const getGenderDistributionsList = async () => {
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
industry_id: selectedIndustry.value,
|
|
|
|
|
|
time_dimension: selectedTimePeriod.value,
|
|
|
|
|
|
};
|
2025-06-21 16:57:01 +08:00
|
|
|
|
if (selectedIndustry.value == undefined) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (selectedSubCategory.value == undefined) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-06-17 11:18:39 +08:00
|
|
|
|
if (selectedSubCategory.value != 0) {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
params['industry_id'] = selectedSubCategory.value;
|
2025-06-16 14:42:26 +08:00
|
|
|
|
}
|
2025-07-01 14:34:16 +08:00
|
|
|
|
const { code, data } = await fetchGenderDistributionsList(params);
|
|
|
|
|
|
if (code === 200) {
|
|
|
|
|
|
genderData.value = [];
|
|
|
|
|
|
genderValueData.value = [];
|
|
|
|
|
|
|
|
|
|
|
|
genderData.value = [...data];
|
|
|
|
|
|
await nextTick();
|
2025-06-16 14:42:26 +08:00
|
|
|
|
|
2025-07-01 14:34:16 +08:00
|
|
|
|
genderValueData.value = data.map((item) => ({
|
|
|
|
|
|
value: item.rate * 100,
|
|
|
|
|
|
tgi: item.tgi,
|
|
|
|
|
|
name: item.gender === 1 ? '女性' : '男性',
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
drawChart();
|
|
|
|
|
|
}
|
2025-06-16 14:42:26 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const drawChart = () => {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
let dom = document.getElementById('container');
|
|
|
|
|
|
let myChart = echarts.init(dom, null, {
|
2025-06-16 14:42:26 +08:00
|
|
|
|
renderer: 'canvas',
|
|
|
|
|
|
useDirtyRect: false,
|
|
|
|
|
|
});
|
2025-07-01 14:34:16 +08:00
|
|
|
|
let option = {
|
2025-06-16 14:42:26 +08:00
|
|
|
|
color: ['#F64B31', '#2A59F3'],
|
2025-07-01 14:34:16 +08:00
|
|
|
|
tooltip: {
|
|
|
|
|
|
trigger: 'item',
|
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
|
borderColor: 'rgba(0,0,0,0.05)',
|
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
textStyle: {
|
|
|
|
|
|
color: '#222',
|
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
|
},
|
|
|
|
|
|
extraCssText: 'box-shadow:0 2px 8px 0 rgba(0,0,0,0.08);border-radius:8px;',
|
|
|
|
|
|
formatter: function (params) {
|
|
|
|
|
|
return `
|
|
|
|
|
|
<div class="w-140px">
|
|
|
|
|
|
<div style="font-weight:500;margin-bottom:8px;">${params.name}</div>
|
|
|
|
|
|
<div style="display:flex;align-items:center;margin-bottom:4px;">
|
|
|
|
|
|
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#2a59f3;margin-right:8px;"></span>
|
|
|
|
|
|
<span>占比</span>
|
|
|
|
|
|
<span style="margin-left:auto;">${params.value}%</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style="display:flex;align-items:center;">
|
|
|
|
|
|
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#F64B31;margin-right:8px;"></span>
|
|
|
|
|
|
<span>TGI</span>
|
|
|
|
|
|
<span style="margin-left:auto;">${params.data.tgi || '-'}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2025-06-16 14:42:26 +08:00
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'pie',
|
|
|
|
|
|
avoidLabelOverlap: false,
|
|
|
|
|
|
data: genderValueData.value,
|
2025-07-01 14:34:16 +08:00
|
|
|
|
label: {
|
|
|
|
|
|
show: false, // 关闭扇区外的文字
|
|
|
|
|
|
},
|
2025-06-16 14:42:26 +08:00
|
|
|
|
labelLine: {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
show: false,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
radius: ['40%', '55%'],
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
2025-07-01 14:34:16 +08:00
|
|
|
|
myChart.setOption(option);
|
2025-06-16 14:42:26 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const drawAgeChart = () => {
|
|
|
|
|
|
const dom = document.getElementById('age-container');
|
|
|
|
|
|
// 数据准备
|
|
|
|
|
|
const xAxis = ageValueData.value.map((item) => item.age);
|
|
|
|
|
|
const yAxis = ageValueData.value.map((item) => item.rate * 100);
|
|
|
|
|
|
const yAxis2 = ageValueData.value.map((item) => item.tgi);
|
2025-07-01 14:34:16 +08:00
|
|
|
|
// const average = yAxis2.reduce((sum, val) => sum + val, 0) / yAxis2.length;
|
2025-06-16 14:42:26 +08:00
|
|
|
|
|
|
|
|
|
|
// 图表初始化(强制使用2D渲染)
|
|
|
|
|
|
const myChart = echarts.init(dom, null, {
|
|
|
|
|
|
renderer: 'canvas',
|
|
|
|
|
|
useDirtyRect: false,
|
|
|
|
|
|
devicePixelRatio: window.devicePixelRatio * 1.5, // 提高渲染精度
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 动态计算刻度间隔
|
|
|
|
|
|
const calcInterval = (data, base = 10) => {
|
|
|
|
|
|
const range = Math.max(...data) - Math.min(...data);
|
|
|
|
|
|
return Math.max(base, Math.ceil(range / 4 / base) * base);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 核心配置
|
|
|
|
|
|
const option = {
|
|
|
|
|
|
animation: false, // 小尺寸下关闭动画提升性能
|
|
|
|
|
|
tooltip: {
|
|
|
|
|
|
trigger: 'axis',
|
2025-07-01 14:34:16 +08:00
|
|
|
|
confine: true,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
axisPointer: {
|
|
|
|
|
|
type: 'shadow',
|
|
|
|
|
|
label: {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
fontSize: 12,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
backgroundColor: 'rgba(0,0,0,0.7)',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2025-07-01 14:34:16 +08:00
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
|
borderColor: 'rgba(0,0,0,0.05)',
|
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
textStyle: {
|
|
|
|
|
|
color: '#333',
|
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
|
},
|
|
|
|
|
|
extraCssText: 'box-shadow:0 2px 8px 0 rgba(0,0,0,0.08);border-radius:8px;',
|
|
|
|
|
|
formatter: function (params) {
|
|
|
|
|
|
const name = params[0].name;
|
|
|
|
|
|
const percent = params[0].value;
|
|
|
|
|
|
const tgi = params[1].value;
|
|
|
|
|
|
return `
|
|
|
|
|
|
<div class="w-140px">
|
|
|
|
|
|
<div style="margin-bottom: 4px;" class="color-#000">${name}岁</div>
|
|
|
|
|
|
<div style="display: flex;align-items: center;margin-bottom:2px;">
|
|
|
|
|
|
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#6d4cfe;margin-right:6px;"></span>
|
|
|
|
|
|
<span>占比</span>
|
|
|
|
|
|
<span style="color:#333;margin-left:auto;">${percent.toFixed(2)}%</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style="display: flex;align-items: center;">
|
|
|
|
|
|
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#F64B31;margin-right:6px;"></span>
|
|
|
|
|
|
<span>TGI</span>
|
|
|
|
|
|
<span style="color:#333;margin-left:auto;">${tgi}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
},
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
grid: {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
top: 25,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
right: 30,
|
2025-07-01 14:34:16 +08:00
|
|
|
|
bottom: 40,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
left: 40,
|
|
|
|
|
|
containLabel: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
xAxis: {
|
|
|
|
|
|
type: 'category',
|
|
|
|
|
|
data: xAxis,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
interval: 0,
|
|
|
|
|
|
rotate: 0,
|
|
|
|
|
|
fontSize: 12,
|
2025-07-01 14:34:16 +08:00
|
|
|
|
margin: 10,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
hideOverlap: true, // 自动隐藏重叠标签
|
2025-07-01 14:34:16 +08:00
|
|
|
|
color: '#939499',
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
axisTick: {
|
|
|
|
|
|
alignWithLabel: true,
|
|
|
|
|
|
length: 3, // 缩短刻度线
|
|
|
|
|
|
},
|
|
|
|
|
|
axisLine: {
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
width: 0.5, // 减细轴线
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: [
|
|
|
|
|
|
{
|
|
|
|
|
|
// 左侧百分比轴
|
|
|
|
|
|
type: 'value',
|
|
|
|
|
|
name: '占比',
|
2025-07-01 14:34:16 +08:00
|
|
|
|
nameLocation: 'end',
|
|
|
|
|
|
nameGap: 10,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
nameTextStyle: {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
color: '#939499',
|
2025-06-16 14:42:26 +08:00
|
|
|
|
fontSize: 12,
|
2025-07-01 14:34:16 +08:00
|
|
|
|
padding: [0, 50, 0, 0], // 微调名称位置
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
min: 0,
|
|
|
|
|
|
max: Math.ceil(Math.max(...yAxis) / 20) * 20,
|
|
|
|
|
|
interval: calcInterval(yAxis, 20),
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
formatter: '{value}%',
|
2025-07-01 14:34:16 +08:00
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
margin: 10,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
showMinLabel: true,
|
|
|
|
|
|
showMaxLabel: true,
|
2025-07-01 14:34:16 +08:00
|
|
|
|
color: '#939499',
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
splitLine: {
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
type: 'dashed',
|
2025-07-01 14:34:16 +08:00
|
|
|
|
// color: 'rgba(255,255,255,0.3)',
|
|
|
|
|
|
// width: 0.8,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 右侧TGI轴
|
|
|
|
|
|
type: 'value',
|
2025-07-01 14:34:16 +08:00
|
|
|
|
name: 'TGI',
|
|
|
|
|
|
nameLocation: 'end',
|
|
|
|
|
|
nameGap: 10,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
nameTextStyle: {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
color: '#939499',
|
|
|
|
|
|
padding: [0, 0, 0, 20],
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
min: Math.floor(Math.min(...yAxis2) / 50) * 50,
|
|
|
|
|
|
max: Math.ceil(Math.max(...yAxis2) / 50) * 50,
|
|
|
|
|
|
interval: calcInterval(yAxis2, 50),
|
|
|
|
|
|
axisLabel: {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
fontSize: 12,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
margin: 4,
|
2025-07-01 14:34:16 +08:00
|
|
|
|
color: '#939499',
|
|
|
|
|
|
},
|
|
|
|
|
|
splitLine: {
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
type: 'dashed',
|
|
|
|
|
|
// color: 'rgba(255,255,255,0.3)',
|
|
|
|
|
|
// width: 0.8,
|
|
|
|
|
|
},
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
axisLine: {
|
|
|
|
|
|
lineStyle: {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
type: 'dashed',
|
2025-06-16 14:42:26 +08:00
|
|
|
|
color: '#F64B31',
|
|
|
|
|
|
width: 0.8,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
// 柱状图
|
|
|
|
|
|
name: '占比',
|
|
|
|
|
|
type: 'bar',
|
2025-07-01 14:34:16 +08:00
|
|
|
|
barWidth: 16,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: '#6d4cfe',
|
|
|
|
|
|
borderRadius: [1, 1, 0, 0], // 微圆角
|
|
|
|
|
|
},
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
position: 'top',
|
|
|
|
|
|
formatter: '{c}%',
|
2025-07-01 14:34:16 +08:00
|
|
|
|
fontSize: 12,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
color: '#fff',
|
|
|
|
|
|
distance: 1, // 减小标签与柱子的距离
|
|
|
|
|
|
},
|
|
|
|
|
|
data: yAxis,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
// 折线图
|
|
|
|
|
|
name: 'TGI',
|
|
|
|
|
|
type: 'line',
|
|
|
|
|
|
yAxisIndex: 1,
|
|
|
|
|
|
symbol: 'none',
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: '#F64B31',
|
|
|
|
|
|
width: 1.8,
|
|
|
|
|
|
},
|
|
|
|
|
|
markLine: {
|
|
|
|
|
|
silent: true,
|
|
|
|
|
|
symbol: 'none',
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
color: '#FFAE00',
|
|
|
|
|
|
width: 1.2,
|
|
|
|
|
|
type: 'dashed',
|
|
|
|
|
|
},
|
|
|
|
|
|
label: {
|
2025-07-01 14:34:16 +08:00
|
|
|
|
fontSize: 12,
|
2025-06-16 14:42:26 +08:00
|
|
|
|
color: '#FFAE00',
|
|
|
|
|
|
formatter: (params) => {
|
|
|
|
|
|
// 改用回调函数
|
|
|
|
|
|
const avg = params.data.coord[1]; // 获取平均值坐标
|
|
|
|
|
|
return 'TGI:' + avg.toFixed(0); // 格式化显示
|
|
|
|
|
|
},
|
2025-07-01 14:34:16 +08:00
|
|
|
|
position: 'insideEnd',
|
|
|
|
|
|
offset: [0, -10],
|
2025-06-16 14:42:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
data: [{ type: 'average' }],
|
|
|
|
|
|
},
|
|
|
|
|
|
data: yAxis2,
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 应用配置
|
|
|
|
|
|
myChart.setOption(option);
|
|
|
|
|
|
|
|
|
|
|
|
// 超小尺寸适配
|
|
|
|
|
|
const handleResize = () => {
|
|
|
|
|
|
const currentWidth = dom.clientWidth;
|
|
|
|
|
|
if (currentWidth < 400) {
|
|
|
|
|
|
myChart.setOption({
|
|
|
|
|
|
xAxis: { axisLabel: { rotate: 90, fontSize: 7 } },
|
|
|
|
|
|
grid: { bottom: 35 },
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
myChart.resize();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
|
|
|
return () => window.removeEventListener('resize', handleResize);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const tabChange = (val) => {
|
|
|
|
|
|
scope.value = val;
|
|
|
|
|
|
getGeoDistributionsList();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const drawChinaMap = () => {
|
|
|
|
|
|
const dom = document.getElementById('chinaMap');
|
|
|
|
|
|
// 图表初始化(强制使用2D渲染)
|
|
|
|
|
|
const myChart = echarts.init(dom, null, {
|
|
|
|
|
|
renderer: 'canvas',
|
|
|
|
|
|
useDirtyRect: false,
|
|
|
|
|
|
devicePixelRatio: window.devicePixelRatio * 1.5, // 提高渲染精度
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const option = {
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '中国',
|
|
|
|
|
|
type: 'map',
|
|
|
|
|
|
map: 'china',
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
data: [
|
|
|
|
|
|
{ name: '北京', value: 1000 },
|
|
|
|
|
|
{ name: '上海', value: 800 },
|
|
|
|
|
|
{ name: '广东', value: 900 },
|
|
|
|
|
|
// 其他省份数据...
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
myChart.setOption(option);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
getAgeDistributionsList();
|
|
|
|
|
|
getGeoDistributionsList();
|
|
|
|
|
|
getGenderDistributionsList();
|
|
|
|
|
|
drawChinaMap();
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
2025-07-01 14:34:16 +08:00
|
|
|
|
<style scoped lang="scss">
|
2025-06-16 14:42:26 +08:00
|
|
|
|
/* 自定义样式 */
|
|
|
|
|
|
:deep(.arco-table-th) {
|
|
|
|
|
|
background-color: var(--color-fill-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.arco-table-tr):hover {
|
|
|
|
|
|
background-color: var(--color-fill-1);
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.arco-statistic-content .arco-statistic-value-integer) {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pop-btn {
|
|
|
|
|
|
background: #fff !important;
|
|
|
|
|
|
border-color: #fff !important;
|
|
|
|
|
|
color: #737478 !important;
|
|
|
|
|
|
margin-left: -5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.arco-btn-outline) {
|
|
|
|
|
|
color: #6d4cfe !important;
|
|
|
|
|
|
border-color: #6d4cfe !important;
|
|
|
|
|
|
}
|
2025-07-01 14:34:16 +08:00
|
|
|
|
.title-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
height: 64px;
|
|
|
|
|
|
padding: 10px 0 2px 0;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
.title {
|
|
|
|
|
|
color: var(--Text-1, #211f24);
|
2025-07-11 16:50:48 +08:00
|
|
|
|
font-family: $font-family-medium;
|
2025-07-01 14:34:16 +08:00
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
line-height: 24px; /* 150% */
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.cts {
|
|
|
|
|
|
color: var(--Text-2, #3c4043);
|
2025-07-11 16:50:48 +08:00
|
|
|
|
font-family: $font-family-regular;
|
2025-07-01 14:34:16 +08:00
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
line-height: 22px; /* 157.143% */
|
|
|
|
|
|
&.num {
|
2025-07-14 09:46:16 +08:00
|
|
|
|
font-family: $font-family-manrope-regular;
|
2025-07-01 14:34:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.arco-tabs) {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
.arco-tabs-content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
.arco-tabs-content-list,
|
|
|
|
|
|
.arco-tabs-pane,
|
|
|
|
|
|
.arco-table-container {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-06-16 14:42:26 +08:00
|
|
|
|
</style>
|