first commit
This commit is contained in:
184
src/views/components/dataEngine/hotCloud.vue
Normal file
184
src/views/components/dataEngine/hotCloud.vue
Normal file
@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef"></topHeader>
|
||||
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
|
||||
<a-space align="center">
|
||||
<span>行业词云</span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<div class="multi-row-tag-cloud">
|
||||
<!-- 动态生成多行标签 -->
|
||||
<div
|
||||
v-for="(row, rowIndex) in tagRows"
|
||||
:key="rowIndex"
|
||||
class="tag-row"
|
||||
:style="{ justifyContent: row.align || 'center' }"
|
||||
>
|
||||
<a-tag
|
||||
v-for="(tag, tagIndex) in row.tags"
|
||||
:key="tagIndex"
|
||||
:style="{
|
||||
fontSize: `${getFontSize(rowIndex, tagIndex)}px`,
|
||||
lineHeight: `${getLineHeight(rowIndex, tagIndex) + 10}px`,
|
||||
color: '#6d4cfe',
|
||||
backgroundColor: '#F0EDFF',
|
||||
margin: '12px',
|
||||
transition: 'all 0.3s',
|
||||
paddingLeft: `${getPaddingLeft(rowIndex, tagIndex)}px`,
|
||||
paddingRight: `${getPaddingLeft(rowIndex, tagIndex)}px`,
|
||||
paddingTop: `${getPadding(rowIndex, tagIndex)}px`,
|
||||
paddingBottom: `${getPadding(rowIndex, tagIndex)}px`,
|
||||
borderRadius: '100px',
|
||||
borderRadius: '100px',
|
||||
}"
|
||||
@mouseenter="hoverTag = tag"
|
||||
@mouseleave="hoverTag = null"
|
||||
>
|
||||
<a-space>
|
||||
<a-tooltip :content="`性价比:${Number(tag.rate * 100)}%`" position="tl">
|
||||
<a-space>{{ tag.term }}</a-space>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</a-space>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import { fetchindustryTerms } from '@/api/all/index';
|
||||
|
||||
const topHeaderRef = ref();
|
||||
// 从topHeader获取统一的状态
|
||||
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
|
||||
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
|
||||
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
|
||||
|
||||
const getIndustryTerms = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchindustryTerms(params);
|
||||
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
||||
tagRows.value = processTagData(res);
|
||||
};
|
||||
|
||||
// 标签数据(按行分组)
|
||||
const tagRows = ref();
|
||||
const hoverTag = ref(null);
|
||||
|
||||
// 根据行列位置计算字体大小(中间最大)
|
||||
const getFontSize = (rowIndex, tagIndex) => {
|
||||
const centerRow = Math.floor(tagRows.value.length / 2);
|
||||
const distance = Math.abs(rowIndex - centerRow);
|
||||
return 38 - distance * 8 + (tagIndex % 2) * 5; // 基础24px,行距影响4px,微调差异2px
|
||||
};
|
||||
|
||||
const getLineHeight = (rowIndex, tagIndex) => {
|
||||
const centerRow = Math.floor(tagRows.value.length / 2);
|
||||
const distance = Math.abs(rowIndex - centerRow);
|
||||
return 48 - distance * 8 + (tagIndex % 2) * 5; // 基础24px,行距影响4px,微调差异2px
|
||||
};
|
||||
|
||||
const getPaddingLeft = (rowIndex, tagIndex) => {
|
||||
const centerRow = Math.floor(tagRows.value.length / 2);
|
||||
const distance = Math.abs(rowIndex - centerRow);
|
||||
return 30 - distance * 8 + (tagIndex % 2) * 5; // 基础24px,行距影响4px,微调差异2px
|
||||
};
|
||||
|
||||
const getPadding = (rowIndex, tagIndex) => {
|
||||
const centerRow = Math.floor(tagRows.value.length / 2);
|
||||
const distance = Math.abs(rowIndex - centerRow);
|
||||
return 28 - distance * 8 + (tagIndex % 2) * 5; // 基础24px,行距影响4px,微调差异2px
|
||||
};
|
||||
|
||||
// 处理API返回数据为tagRows格式
|
||||
const processTagData = (apiData) => {
|
||||
const totalGroups = 7; // 总组数
|
||||
const middleIndex = Math.floor(totalGroups / 2); // 中间位置(索引3)
|
||||
const chunkSize = Math.ceil(apiData.length / totalGroups); // 每组大小
|
||||
const arr = [];
|
||||
|
||||
// 1. 先按顺序分组
|
||||
const tempGroups = [];
|
||||
for (let i = 0; i < totalGroups; i++) {
|
||||
const start = i * chunkSize;
|
||||
const end = start + chunkSize;
|
||||
tempGroups.push(apiData.slice(start, end));
|
||||
}
|
||||
|
||||
// 2. 从中间开始,左右交替插入
|
||||
for (let i = 0; i < totalGroups; i++) {
|
||||
// 计算当前组应该插入的位置
|
||||
const insertPos = i % 2 === 0 ? middleIndex + i / 2 : middleIndex - Math.ceil(i / 2);
|
||||
|
||||
// 确保不越界
|
||||
if (insertPos >= 0 && insertPos < totalGroups) {
|
||||
arr[insertPos] = { tags: tempGroups[i], align: 'center' };
|
||||
}
|
||||
}
|
||||
|
||||
return arr.filter(Boolean); // 移除可能的空项
|
||||
};
|
||||
|
||||
// 监听筛选条件变化
|
||||
watch([selectedIndustry, selectedTimePeriod], () => {
|
||||
getIndustryTerms();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getIndustryTerms();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
:deep(.arco-table-th) {
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
|
||||
:deep(.arco-table-tr):hover {
|
||||
background-color: var(--color-fill-1);
|
||||
}
|
||||
:deep(.arco-btn-outline) {
|
||||
color: #6d4cfe !important;
|
||||
border-color: #6d4cfe !important;
|
||||
}
|
||||
:deep(.arco-modal-body) {
|
||||
padding: 0px;
|
||||
}
|
||||
.multi-row-tag-cloud {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.tag-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* 悬停放大效果 */
|
||||
a-tag:hover {
|
||||
transform: scale(1.1);
|
||||
z-index: 1;
|
||||
}
|
||||
.pop-btn {
|
||||
background: #fff !important;
|
||||
border-color: #fff !important;
|
||||
color: #737478 !important;
|
||||
margin-left: -5px;
|
||||
}
|
||||
</style>
|
||||
224
src/views/components/dataEngine/hotTranslation.vue
Normal file
224
src/views/components/dataEngine/hotTranslation.vue
Normal file
@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef"></topHeader>
|
||||
<!-- tabel -->
|
||||
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin-bottom: 24px">
|
||||
<a-space align="center">
|
||||
<span>行业热门话题洞察</span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-table :data="dataList">
|
||||
<template #columns>
|
||||
<a-table-column title="排名" data-index="rank">
|
||||
<template #cell="{ record }">
|
||||
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
||||
<span v-else>{{ record.rank }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="话题名称" data-index="name" />
|
||||
<a-table-column title="关键词" data-index="keywords">
|
||||
<template #cell="{ record }">
|
||||
<a-tag v-for="item in record.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="热度" data-index="heatLevel">
|
||||
<template #cell="{ record }">
|
||||
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="情感" data-index="sentiment">
|
||||
<template #cell="{ record }">
|
||||
<img
|
||||
v-if="record.felling == '2'"
|
||||
src="@/assets/img/hottranslation/good.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
<img
|
||||
v-else-if="record.felling == '1'"
|
||||
src="@/assets/img/hottranslation/normal.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
<img
|
||||
v-else-if="record.felling == '0'"
|
||||
src="@/assets/img/hottranslation/poor.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="操作" data-index="optional">
|
||||
<template #cell="{ record }">
|
||||
<a-button type="outline" @click="gotoDetail(record)">详情</a-button>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
<template #rank="{ record }">
|
||||
<a-tag color="blue" v-if="record.rank == 1">1</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
<!-- modal -->
|
||||
<a-modal :visible="visible" @ok="handleOk" @cancel="handleCancel" unmountOnClose>
|
||||
<template #title>
|
||||
<span style="text-align: left; width: 100%">行业热门话题洞察</span>
|
||||
</template>
|
||||
<div>
|
||||
<a-space direction="vertical">
|
||||
<a-space>
|
||||
<span style="margin-right: 16px">话题名称</span>
|
||||
<span>{{ topicInfo.name }}</span>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span style="margin-right: 16px">话题简介</span>
|
||||
<span>{{ topicInfo.intro }}</span>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span style="margin-right: 16px">关键词</span>
|
||||
<a-tag v-for="item in topicInfo.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span style="margin-right: 16px">热度指数</span>
|
||||
<img v-for="i in topicInfo.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span style="margin-right: 16px">情感指数</span>
|
||||
<img
|
||||
v-if="topicInfo.felling == '2'"
|
||||
src="@/assets/img/hottranslation/good.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
<img
|
||||
v-else-if="topicInfo.felling == '1'"
|
||||
src="@/assets/img/hottranslation/normal.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
<img
|
||||
v-else-if="topicInfo.felling == '0'"
|
||||
src="@/assets/img/hottranslation/poor.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
</a-space>
|
||||
<a-space direction="top">
|
||||
<span style="margin-right: 16px; width: 60px; font-size: 12px">原始来源 </span>
|
||||
<a-space direction="vertical" style="margin-left: 15px">
|
||||
<a-space v-for="item in topicInfo.industry_topic_sources" :key="item">
|
||||
<a-link style="background-color: initial" :href="item.link">{{ item.title }}</a-link>
|
||||
<img src="@/assets/img/hottranslation/xhs.png" style="width: 16px; height: 16px" />
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import { fetchIndustriesTree, fetchIndustryTopics, fetchIndustryTopicDetail } from '@/api/all/index';
|
||||
import star1 from '@/assets/img/hottranslation/star-fill1.png';
|
||||
import star2 from '@/assets/img/hottranslation/star-fill2.png';
|
||||
import star3 from '@/assets/img/hottranslation/star-fill3.png';
|
||||
import star4 from '@/assets/img/hottranslation/star-fill4.png';
|
||||
import star5 from '@/assets/img/hottranslation/star-fill5.png';
|
||||
import top1 from '@/assets/img/captcha/top1.svg';
|
||||
import top2 from '@/assets/img/captcha/top2.svg';
|
||||
import top3 from '@/assets/img/captcha/top3.svg';
|
||||
|
||||
const starImages = [star1, star2, star3, star4, star5];
|
||||
const topImages = [top1, top2, top3];
|
||||
// 行业大类
|
||||
const industriesTree = ref([]);
|
||||
|
||||
// 行业热门话题洞察
|
||||
const dataList = ref([]);
|
||||
// 显示详情
|
||||
const visible = ref(false);
|
||||
const topicInfo = ref({});
|
||||
const topHeaderRef = ref();
|
||||
// 从topHeader获取统一的状态
|
||||
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
|
||||
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
|
||||
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
|
||||
|
||||
// 监听筛选条件变化
|
||||
watch([selectedIndustry, selectedTimePeriod], () => {
|
||||
getIndustryTopics();
|
||||
});
|
||||
onMounted(() => {
|
||||
getIndustriesTree();
|
||||
});
|
||||
// 获取行业大类数据
|
||||
const getIndustriesTree = async () => {
|
||||
const res = await fetchIndustriesTree();
|
||||
industriesTree.value = res;
|
||||
selectedIndustry.value = res[0].id;
|
||||
getIndustryTopics();
|
||||
};
|
||||
|
||||
// 行业热门话题
|
||||
const getIndustryTopics = async () => {
|
||||
let parms = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchIndustryTopics(parms);
|
||||
console.log(res);
|
||||
dataList.value = res;
|
||||
};
|
||||
|
||||
// 详情
|
||||
const gotoDetail = async (record) => {
|
||||
console.log(record);
|
||||
const res = await fetchIndustryTopicDetail(record.id);
|
||||
console.log(res);
|
||||
visible.value = true;
|
||||
topicInfo.value = res;
|
||||
};
|
||||
|
||||
// 弹窗的取消
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
// 弹窗的确定
|
||||
const handleOk = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
:deep(.arco-table-th) {
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
|
||||
:deep(.arco-table-tr):hover {
|
||||
background-color: var(--color-fill-1);
|
||||
}
|
||||
:deep(.arco-btn-outline) {
|
||||
color: #6d4cfe !important;
|
||||
border-color: #6d4cfe !important;
|
||||
}
|
||||
:deep(.arco-modal-body) {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.pop-btn {
|
||||
background: #fff !important;
|
||||
border-color: #fff !important;
|
||||
color: #737478 !important;
|
||||
margin-left: -5px;
|
||||
}
|
||||
</style>
|
||||
165
src/views/components/dataEngine/keyBrandMovement.vue
Normal file
165
src/views/components/dataEngine/keyBrandMovement.vue
Normal file
@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef"></topHeader>
|
||||
<!-- 重点品牌列表 -->
|
||||
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
|
||||
<a-space align="center">
|
||||
<span>重点品牌列表 </span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-table :data="dataList" :pagination="false" style="font-size: 12px">
|
||||
<template #columns>
|
||||
<a-table-column title="排名" data-index="rank">
|
||||
<template #cell="{ record }">
|
||||
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
||||
<span v-else>{{ record.rank }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="品牌名称" data-index="name" />
|
||||
<a-table-column title="热度指数" data-index="keywords">
|
||||
<template #cell="{ record }">
|
||||
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="变化幅度" data-index="frequency">
|
||||
<template #cell="{ record }">
|
||||
<a-statistic
|
||||
style="font-size: 14px"
|
||||
v-if="record.trend > 0"
|
||||
:value="record.trend * 100"
|
||||
:value-style="{ color: '#F64B31' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-arrow-rise />
|
||||
</template>
|
||||
<template #suffix>%</template>
|
||||
</a-statistic>
|
||||
<a-statistic
|
||||
v-else
|
||||
style="font-size: 14px"
|
||||
:value="record.trend * 100"
|
||||
:value-style="{ color: '#25C883' }"
|
||||
></a-statistic>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="占总声量比例" data-index="content">
|
||||
<template #cell="{ record }"> <a-statistic :value="record.volume_rate * 100" />% </template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
<!-- 舆情 & 敏感动态-->
|
||||
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
|
||||
<a-space align="center">
|
||||
<span>舆情 & 敏感动态 </span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-table :data="otherList" :pagination="false" style="font-size: 12px">
|
||||
<template #columns>
|
||||
<a-table-column title="品牌" data-index="brand" />
|
||||
<a-table-column title="事件标题" data-index="title" />
|
||||
<a-table-column title="事件详情" data-index="content" />
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { fetchFocusBrandsList, fetchEventDynamicsList } from '@/api/all/index';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import star1 from '@/assets/img/hottranslation/star-fill1.png';
|
||||
import star2 from '@/assets/img/hottranslation/star-fill2.png';
|
||||
import star3 from '@/assets/img/hottranslation/star-fill3.png';
|
||||
import star4 from '@/assets/img/hottranslation/star-fill4.png';
|
||||
import star5 from '@/assets/img/hottranslation/star-fill5.png';
|
||||
import top1 from '@/assets/img/captcha/top1.svg';
|
||||
import top2 from '@/assets/img/captcha/top2.svg';
|
||||
import top3 from '@/assets/img/captcha/top3.svg';
|
||||
const topImages = [top1, top2, top3];
|
||||
const starImages = [star1, star2, star3, star4, star5];
|
||||
const topHeaderRef = ref();
|
||||
// 从topHeader获取统一的状态
|
||||
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
|
||||
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
|
||||
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
|
||||
const dataList = ref([]);
|
||||
const otherList = ref([]);
|
||||
|
||||
const getFocusBrandsList = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchFocusBrandsList(params);
|
||||
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
||||
dataList.value = res;
|
||||
};
|
||||
|
||||
const getEventDynamicsList = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchEventDynamicsList(params);
|
||||
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
||||
otherList.value = res;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getFocusBrandsList();
|
||||
getEventDynamicsList();
|
||||
});
|
||||
// 监听筛选条件变化
|
||||
watch([selectedIndustry, selectedTimePeriod], () => {
|
||||
getFocusBrandsList();
|
||||
getEventDynamicsList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
: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;
|
||||
}
|
||||
</style>
|
||||
359
src/views/components/dataEngine/keyWord.vue
Normal file
359
src/views/components/dataEngine/keyWord.vue
Normal file
@ -0,0 +1,359 @@
|
||||
<template>
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef"></topHeader>
|
||||
<!-- 关键词热度榜 -->
|
||||
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
|
||||
<a-space align="center">
|
||||
<span>关键词热度榜</span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-table :data="dataList" :pagination="false">
|
||||
<template #columns>
|
||||
<a-table-column title="排名" data-index="rank">
|
||||
<template #cell="{ record }">
|
||||
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
||||
|
||||
<span v-else>{{ record.rank }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="关键词名称" data-index="name" />
|
||||
<a-table-column title="热度指数" data-index="heatLevel">
|
||||
<template #cell="{ record }">
|
||||
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="变化幅度" data-index="heatLevel">
|
||||
<template #cell="{ record }">
|
||||
<a-statistic
|
||||
style="font-size: 14px"
|
||||
v-if="record.trend > 0"
|
||||
:value="record.trend * 100"
|
||||
:value-style="{ color: '#F64B31' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-arrow-rise />
|
||||
</template>
|
||||
<template #suffix>%</template>
|
||||
</a-statistic>
|
||||
<a-statistic
|
||||
v-else
|
||||
style="font-size: 14px"
|
||||
:value="record.trend * 100"
|
||||
:value-style="{ color: '#25C883' }"
|
||||
></a-statistic>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="情感倾向" data-index="sentiment">
|
||||
<template #cell="{ record }">
|
||||
<img
|
||||
v-if="record.felling == '2'"
|
||||
src="@/assets/img/hottranslation/good.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
<img
|
||||
v-else-if="record.felling == '1'"
|
||||
src="@/assets/img/hottranslation/normal.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
<img
|
||||
v-else-if="record.felling == '0'"
|
||||
src="@/assets/img/hottranslation/poor.png"
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
<!-- 行业情绪 -->
|
||||
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
|
||||
<a-space align="center">
|
||||
<span>行业情绪</span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-space align="center">
|
||||
<a-space direction="vertical">
|
||||
<template>
|
||||
<div ref="chartRef" style="width: 400px; height: 400px"></div>
|
||||
</template>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<a-space>
|
||||
<div id="container" style="height: 180px; width: 180px"></div>
|
||||
<a-space direction="vertical" style="font-size: 14px" v-if="fellingRate.length > 0">
|
||||
<a-space>
|
||||
<span style="width: 8px; height: 8px; background-color: #25c883; border-radius: 50%"></span>
|
||||
<span>正面情绪 </span>
|
||||
<span style="width: 40px">{{ fellingRate[0] * 100 }}%</span>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span style="width: 8px; height: 8px; background-color: #f64b31; border-radius: 50%"></span>
|
||||
<span>负面情绪 </span>
|
||||
<span style="width: 40px">{{ fellingRate[1] * 100 }}%</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<a-table :pagination="false" :span-method="dataSpanMethod" :data="rowData" style="margin-left: 40px">
|
||||
<template #columns>
|
||||
<a-table-column title="情绪分布">
|
||||
<template #cell="{ record }">
|
||||
<a-space v-if="record.felling == '2'">
|
||||
<img src="@/assets/img/hottranslation/good.png" style="width: 16px; height: 16px" />
|
||||
<a-space>正面情绪</a-space>
|
||||
</a-space>
|
||||
<a-space v-else-if="record.felling == '1'">
|
||||
<img src="@/assets/img/hottranslation/normal.png" style="width: 16px; height: 16px" />
|
||||
<a-space>中性情绪</a-space>
|
||||
</a-space>
|
||||
<a-space v-else-if="record.felling == '0'">
|
||||
<img src="@/assets/img/hottranslation/poor.png" style="width: 16px; height: 16px" />
|
||||
<a-space>负面情绪</a-space>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="主要观点" data-index="content" />
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<!-- 新兴关键词 -->
|
||||
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
|
||||
<a-space align="center">
|
||||
<span>新兴关键词 </span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-table :data="keywordList" :pagination="false">
|
||||
<template #columns>
|
||||
<a-table-column title="排名" data-index="rank" />
|
||||
<a-table-column title="新兴关键词名称" data-index="name" />
|
||||
<a-table-column title="首次大规模出现" data-index="first_appeared_at">
|
||||
<template #cell="{ record }">
|
||||
<div>{{ formatTimestamp(record.first_appeared_at) }}</div>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="当前热度指数" data-index="heatLevel">
|
||||
<template #cell="{ record }">
|
||||
<img v-for="i in record.hot" :key="i" :src="starImages[i - 1]" style="width: 16px; height: 16px" />
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="变化幅度" data-index="trend">
|
||||
<template #cell="{ record }">
|
||||
<a-statistic
|
||||
style="font-size: 14px"
|
||||
v-if="record.trend > 0"
|
||||
:value="record.trend * 100"
|
||||
:value-style="{ color: '#F64B31' }"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-arrow-rise />
|
||||
</template>
|
||||
<template #suffix>%</template>
|
||||
</a-statistic>
|
||||
<a-statistic
|
||||
v-else
|
||||
style="font-size: 14px"
|
||||
:value="record.trend * 100"
|
||||
:value-style="{ color: '#25C883' }"
|
||||
></a-statistic>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { fetchKeywordTrendsList, fetchIndustryEmotions, fetchNewKeywordList } from '@/api/all/index';
|
||||
import { ref, onMounted, onBeforeUnmount, watchEffect, computed } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import star1 from '@/assets/img/hottranslation/star-fill1.png';
|
||||
import star2 from '@/assets/img/hottranslation/star-fill2.png';
|
||||
import star3 from '@/assets/img/hottranslation/star-fill3.png';
|
||||
import star4 from '@/assets/img/hottranslation/star-fill4.png';
|
||||
import star5 from '@/assets/img/hottranslation/star-fill5.png';
|
||||
import top1 from '@/assets/img/captcha/top1.svg';
|
||||
import top2 from '@/assets/img/captcha/top2.svg';
|
||||
import top3 from '@/assets/img/captcha/top3.svg';
|
||||
|
||||
const starImages = [star1, star2, star3, star4, star5];
|
||||
const topImages = [top1, top2, top3];
|
||||
|
||||
const chartRef = (ref < HTMLElement) | (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 dataList = ref([]);
|
||||
const rowData = ref([]);
|
||||
const keywordList = ref([]);
|
||||
const fellingRate = ref([]);
|
||||
const getIndustryEmotions = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchIndustryEmotions(params);
|
||||
fellingRate.value.push(res['good_felling_rate']);
|
||||
fellingRate.value.push(res['bad_felling_rate']);
|
||||
|
||||
drawChart();
|
||||
rowData.value = res['industry_emotion_view_points'];
|
||||
let items = groupedData();
|
||||
console.log('行业情绪', items);
|
||||
};
|
||||
|
||||
const groupedData = () => {
|
||||
const groups = {
|
||||
negative: { name: '负面', items: [], color: '#F64B31' },
|
||||
neutral: { name: '中性', items: [], color: '#FFAA16' },
|
||||
positive: { name: '正面', items: [], color: '#25C883' },
|
||||
};
|
||||
rowData.value.forEach((item) => {
|
||||
if (item.felling === 0) groups.negative.items.push(item);
|
||||
else if (item.felling === 1) groups.neutral.items.push(item);
|
||||
else if (item.felling === 2) groups.positive.items.push(item);
|
||||
});
|
||||
|
||||
return groups;
|
||||
};
|
||||
|
||||
const getKeywordTrendsList = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchKeywordTrendsList(params);
|
||||
console.log('关键词热度榜', res);
|
||||
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
||||
dataList.value = res;
|
||||
};
|
||||
|
||||
const formatTimestamp = (timestamp) => {
|
||||
if (!timestamp) return '未记录';
|
||||
try {
|
||||
return dayjs.unix(timestamp).format('YYYY-MM-DD HH:mm');
|
||||
} catch (e) {
|
||||
console.error('时间格式转换错误', e);
|
||||
return '格式错误';
|
||||
}
|
||||
};
|
||||
|
||||
const getNewKeywordList = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchNewKeywordList(params);
|
||||
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
||||
keywordList.value = res;
|
||||
};
|
||||
|
||||
const drawChart = () => {
|
||||
var dom = document.getElementById('container');
|
||||
var myChart = echarts.init(dom, null, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false,
|
||||
});
|
||||
var option;
|
||||
option = {
|
||||
color: ['#25C883', '#F64B31'],
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
avoidLabelOverlap: false,
|
||||
data: fellingRate.value,
|
||||
labelLine: {
|
||||
show: false, // 不显示引导线
|
||||
},
|
||||
radius: ['40%', '55%'],
|
||||
},
|
||||
],
|
||||
};
|
||||
if (option && typeof option === 'object') {
|
||||
myChart.setOption(option);
|
||||
}
|
||||
};
|
||||
|
||||
// 监听筛选条件变化
|
||||
watch([selectedIndustry, selectedTimePeriod], () => {
|
||||
getKeywordTrendsList();
|
||||
getIndustryEmotions();
|
||||
getNewKeywordList();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getKeywordTrendsList();
|
||||
getIndustryEmotions();
|
||||
getNewKeywordList();
|
||||
});
|
||||
|
||||
// const chartData = computed(() => {
|
||||
// const result = [
|
||||
// { name: '正面', value: 0, color: '#25C883' },
|
||||
// { name: '中性', value: 0, color: '#FFAA16' },
|
||||
// { name: '负面', value: 0, color: '#F64B31' },
|
||||
// ];
|
||||
|
||||
// rawData.value.forEach((item) => {
|
||||
// if (item.felling === 2) result[0].value++;
|
||||
// else if (item.felling === 1) result[1].value++;
|
||||
// else result[2].value++;
|
||||
// });
|
||||
|
||||
// return result.filter((item) => item.value > 0); // 过滤空数据
|
||||
// });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
: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;
|
||||
}
|
||||
</style>
|
||||
217
src/views/components/dataEngine/topHeader.vue
Normal file
217
src/views/components/dataEngine/topHeader.vue
Normal file
@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 头部 -->
|
||||
<a-space
|
||||
direction="vertical"
|
||||
style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0; color: #737478; font-size: 14px"
|
||||
>
|
||||
<a-space align="center">
|
||||
<!-- 行业选择 -->
|
||||
<a-space align="center">
|
||||
<span>行业大类</span>
|
||||
<a-tag
|
||||
size="Medium"
|
||||
v-for="item in industriesTree"
|
||||
:key="item.id"
|
||||
:checkable="true"
|
||||
:checked="selectedIndustry == item.id"
|
||||
@check="handleIndustryCheck(item.id)"
|
||||
style="padding: 4px 16px; border-radius: 30px; height: 28px"
|
||||
:style="
|
||||
selectedIndustry == item.id
|
||||
? 'color: #6d4cfe; background-color: #f0edff'
|
||||
: 'color: #3C4043; background-color: #F7F8FA'
|
||||
"
|
||||
>{{ item.name }}</a-tag
|
||||
>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<a-space align="center" style="margin-left: 'auto'; margin-top: 20px">
|
||||
<!-- 二级类目 -->
|
||||
<a-space align="center">
|
||||
<span>二级类目</span>
|
||||
<a-tag
|
||||
size="Medium"
|
||||
v-for="item in subCategories"
|
||||
:key="item.value"
|
||||
:checkable="true"
|
||||
:checked="selectedSubCategory == item.value"
|
||||
@check="handleSubCategoryCheck(item.value)"
|
||||
style="padding: 4px 16px; border-radius: 30px; height: 28px"
|
||||
:style="
|
||||
selectedSubCategory == item.value
|
||||
? 'color: #6d4cfe; background-color: #f0edff'
|
||||
: 'color: #3C4043; background-color: #F7F8FA'
|
||||
"
|
||||
>{{ item.label }}</a-tag
|
||||
>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<a-space align="center" style="margin-left: 'auto'; margin-top: 20px">
|
||||
<!-- 时间筛选 -->
|
||||
<a-space align="center">
|
||||
<span>时间筛选</span>
|
||||
<a-tag
|
||||
size="Medium"
|
||||
v-for="item in timePeriods"
|
||||
:key="item.value"
|
||||
:checkable="true"
|
||||
:checked="selectedTimePeriod == item.value"
|
||||
@check="handleTimePeriodCheck(item.value)"
|
||||
style="padding: 4px 16px; border-radius: 30px; height: 28px"
|
||||
:style="
|
||||
selectedTimePeriod == item.value
|
||||
? 'color: #6d4cfe; background-color: #f0edff'
|
||||
: 'color: #3C4043; background-color: #F7F8FA'
|
||||
"
|
||||
>{{ item.label }}
|
||||
</a-tag>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<!-- 搜索区域 -->
|
||||
<a-space style="margin-left: 'auto'; margin-top: 20px">
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
<!-- Use the default slot to avoid extra spaces -->
|
||||
<template #default>搜索</template>
|
||||
</a-button>
|
||||
<a-button type="primary" style="background-color: #fff; color: #000">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
<!-- Use the default slot to avoid extra spaces -->
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { fetchIndustriesTree, fetchIndustryTopics, fetchIndustryTopicDetail } from '@/api/all/index';
|
||||
import star1 from '@/assets/img/hottranslation/star-fill1.png';
|
||||
import star2 from '@/assets/img/hottranslation/star-fill2.png';
|
||||
import star3 from '@/assets/img/hottranslation/star-fill3.png';
|
||||
import star4 from '@/assets/img/hottranslation/star-fill4.png';
|
||||
import star5 from '@/assets/img/hottranslation/star-fill5.png';
|
||||
const starImages = [star1, star2, star3, star4, star5];
|
||||
// 行业大类
|
||||
const industriesTree = ref([]);
|
||||
// 数据状态
|
||||
const selectedIndustry = ref();
|
||||
const selectedSubCategory = ref('all');
|
||||
const selectedTimePeriod = ref('7');
|
||||
|
||||
// 暴露这些状态给父组件
|
||||
defineExpose({
|
||||
selectedIndustry,
|
||||
selectedSubCategory,
|
||||
selectedTimePeriod,
|
||||
});
|
||||
// 行业热门话题洞察
|
||||
const dataList = ref([]);
|
||||
// 显示详情
|
||||
const visible = ref(false);
|
||||
const topicInfo = ref({});
|
||||
// 二级类目选项
|
||||
const subCategories = [
|
||||
{ value: 'all', label: '全部' },
|
||||
{ value: 'airline', label: '航司' },
|
||||
{ value: 'hotel', label: '酒店' },
|
||||
{ value: 'entertainment', label: '玩乐' },
|
||||
{ value: 'cruise', label: '游轮' },
|
||||
];
|
||||
|
||||
// 时间周期选项
|
||||
const timePeriods = [
|
||||
{
|
||||
value: '7',
|
||||
label: '近7天',
|
||||
},
|
||||
{
|
||||
value: '15',
|
||||
label: '近15天',
|
||||
},
|
||||
{
|
||||
value: '30',
|
||||
label: '近30天',
|
||||
},
|
||||
];
|
||||
|
||||
onMounted(() => {
|
||||
getIndustriesTree();
|
||||
});
|
||||
// 获取行业大类数据
|
||||
const getIndustriesTree = async () => {
|
||||
const res = await fetchIndustriesTree();
|
||||
industriesTree.value = res;
|
||||
selectedIndustry.value = res[0].id;
|
||||
getIndustryTopics();
|
||||
};
|
||||
|
||||
// 行业热门话题
|
||||
const getIndustryTopics = async () => {
|
||||
let parms = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchIndustryTopics(parms);
|
||||
dataList.value = res;
|
||||
};
|
||||
const handleIndustryCheck = (value) => {
|
||||
selectedIndustry.value = value;
|
||||
};
|
||||
|
||||
const handleSubCategoryCheck = (value) => {
|
||||
selectedSubCategory.value = value;
|
||||
};
|
||||
|
||||
const handleTimePeriodCheck = (value) => {
|
||||
selectedTimePeriod.value = value;
|
||||
};
|
||||
|
||||
// 详情
|
||||
const gotoDetail = async (record) => {
|
||||
console.log(record);
|
||||
const res = await fetchIndustryTopicDetail(record.id);
|
||||
console.log(res);
|
||||
visible.value = true;
|
||||
topicInfo.value = res;
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
getIndustryTopics();
|
||||
};
|
||||
|
||||
// 弹窗的取消
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
// 弹窗的确定
|
||||
const handleBeforeOk = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
:deep(.arco-table-th) {
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
|
||||
:deep(.arco-table-tr):hover {
|
||||
background-color: var(--color-fill-1);
|
||||
}
|
||||
:deep(.arco-btn-outline) {
|
||||
color: #6d4cfe !important;
|
||||
border-color: #6d4cfe !important;
|
||||
}
|
||||
:deep(.arco-modal-body) {
|
||||
padding: 0px;
|
||||
}
|
||||
</style>
|
||||
191
src/views/components/dataEngine/userPainPoints.vue
Normal file
191
src/views/components/dataEngine/userPainPoints.vue
Normal file
@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef"></topHeader>
|
||||
|
||||
<!-- 用户痛点观察 -->
|
||||
<a-space direction="vertical" style="background-color: #fff; width: 100%; padding: 24px; margin: 24px 0">
|
||||
<a-space align="center">
|
||||
<span>用户痛点观察 </span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-table :data="dataList" :pagination="false">
|
||||
<template #columns>
|
||||
<a-table-column title="排名" data-index="rank">
|
||||
<template #cell="{ record }">
|
||||
<img v-if="record.rank == 1" :src="topImages[0]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 2" :src="topImages[1]" style="width: 25px; height: 17px" />
|
||||
<img v-else-if="record.rank == 3" :src="topImages[2]" style="width: 25px; height: 17px" />
|
||||
<span v-else>{{ record.rank }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="痛点名称" data-index="name" />
|
||||
<a-table-column title="关键词" data-index="keywords">
|
||||
<template #cell="{ record }">
|
||||
<a-tag v-for="item in record.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="频次" data-index="frequency">
|
||||
<template #cell="{ record }">
|
||||
<a-tag v-if="record.frequency == 0" style="margin-right: 5px; background-color: #ebf7f2; color: #1bae71"
|
||||
>低频</a-tag
|
||||
>
|
||||
<a-tag
|
||||
v-else-if="record.frequency == 1"
|
||||
style="margin-right: 5px; background-color: #fff5de; color: #cc8b00"
|
||||
>中频</a-tag
|
||||
>
|
||||
<a-tag
|
||||
v-else-if="record.frequency == 2"
|
||||
style="margin-right: 5px; background-color: #ffe7e4; color: #c53c27"
|
||||
>高频</a-tag
|
||||
>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="代表性发言" data-index="content"> </a-table-column>
|
||||
<a-table-column title="操作" data-index="optional">
|
||||
<template #cell="{ record }">
|
||||
<a-button type="outline" @click="gotoDetail(record)">详情</a-button>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-space>
|
||||
|
||||
<a-modal :visible="visible" @ok="handleOk" @cancel="handleCancel" unmountOnClose>
|
||||
<template #title>
|
||||
<span style="text-align: left; width: 100%">用户痛点观察</span>
|
||||
</template>
|
||||
<div>
|
||||
<a-space direction="vertical" style="font-size: 12px">
|
||||
<a-space>
|
||||
<span style="margin-right: 16px; width: 60px">痛点</span>
|
||||
<span>{{ topicInfo.name }}</span>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span style="margin-right: 16px; width: 60px; font-size: 12px">关键词</span>
|
||||
<a-tag v-for="item in topicInfo.keywords" :key="item" style="margin-right: 5px">{{ item }}</a-tag>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span style="margin-right: 16px; width: 60px; font-size: 12px">频次</span>
|
||||
<a-tag v-if="topicInfo.frequency == 0" style="margin-right: 5px; background-color: #ebf7f2; color: #1bae71"
|
||||
>低频</a-tag
|
||||
>
|
||||
<a-tag
|
||||
v-else-if="topicInfo.frequency == 1"
|
||||
style="margin-right: 5px; background-color: #fff5de; color: #cc8b00"
|
||||
>中频</a-tag
|
||||
>
|
||||
<a-tag
|
||||
v-else-if="topicInfo.frequency == 2"
|
||||
style="margin-right: 5px; background-color: #ffe7e4; color: #c53c27"
|
||||
>高频</a-tag
|
||||
>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<span style="margin-right: 16px; width: 60px; font-size: 12px">代表性发言</span>
|
||||
<span>{{ topicInfo.content }}</span>
|
||||
</a-space>
|
||||
<a-space direction="top">
|
||||
<span style="margin-right: 16px; width: 60px; font-size: 12px">原始来源 </span>
|
||||
<a-space direction="vertical" style="margin-left: 15px">
|
||||
<a-space v-for="item in topicInfo.user_pain_point_sources" :key="item">
|
||||
<a-link style="background-color: initial" :href="item.link">{{ item.title }}</a-link>
|
||||
<img src="@/assets/img/hottranslation/xhs.png" style="width: 16px; height: 16px" />
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import topHeader from './topHeader.vue';
|
||||
import { fetchUserPainPointsDetail, fetchUserPainPointsList } from '@/api/all/index';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import top1 from '@/assets/img/captcha/top1.svg';
|
||||
import top2 from '@/assets/img/captcha/top2.svg';
|
||||
import top3 from '@/assets/img/captcha/top3.svg';
|
||||
const topImages = [top1, top2, top3];
|
||||
const visible = ref(false);
|
||||
const topicInfo = ref({});
|
||||
const topHeaderRef = ref();
|
||||
// 从topHeader获取统一的状态
|
||||
const selectedIndustry = computed(() => topHeaderRef.value?.selectedIndustry);
|
||||
const selectedSubCategory = computed(() => topHeaderRef.value?.selectedSubCategory);
|
||||
const selectedTimePeriod = computed(() => topHeaderRef.value?.selectedTimePeriod);
|
||||
const dataList = ref([]);
|
||||
|
||||
// 详情
|
||||
const gotoDetail = async (record) => {
|
||||
console.log(record);
|
||||
const res = await fetchUserPainPointsDetail(record.id);
|
||||
console.log(res);
|
||||
visible.value = true;
|
||||
topicInfo.value = res;
|
||||
};
|
||||
|
||||
const getUserPainPointsList = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchUserPainPointsList(params);
|
||||
console.log('关键词热度榜', res);
|
||||
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
|
||||
dataList.value = res;
|
||||
};
|
||||
// 弹窗的取消
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
// 弹窗的确定
|
||||
const handleOk = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
// 监听筛选条件变化
|
||||
watch([selectedIndustry, selectedTimePeriod], () => {
|
||||
getUserPainPointsList();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getUserPainPointsList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
: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;
|
||||
}
|
||||
</style>
|
||||
491
src/views/components/dataEngine/userPersona.vue
Normal file
491
src/views/components/dataEngine/userPersona.vue
Normal file
@ -0,0 +1,491 @@
|
||||
<template>
|
||||
<view>
|
||||
<topHeader ref="topHeaderRef"></topHeader>
|
||||
<a-space style="width: 100%; display: flex">
|
||||
<a-space direction="vertical" style="background-color: #fff; padding: 24px; flex: 1">
|
||||
<a-space align="center">
|
||||
<span>性别分布</span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<div id="container" style="height: 180px; width: 180px"></div>
|
||||
|
||||
<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>
|
||||
<span style="width: 40px" v-if="genderData.length > 0">{{ genderData[0].rate * 100 }}%</span>
|
||||
<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>
|
||||
<span style="width: 40px" v-if="genderData.length > 1">{{ genderData[1].rate * 100 }}%</span>
|
||||
<span>TGI</span>
|
||||
<span v-if="genderData.length > 1">{{ genderData[1].tgi }}</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<a-space direction="vertical" style="background-color: #fff; padding: 24px; flex: 1">
|
||||
<a-space style="display: flex; justify-content: space-between; width: 100%; font-size: 12px">
|
||||
<a-space align="center">
|
||||
<span>年龄分布</span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-space align="center">
|
||||
<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>
|
||||
|
||||
<a-space>
|
||||
<div id="age-container" style="height: 180px; width: 480px"></div>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<a-space direction="vertical" style="background-color: #fff; padding: 24px; flex: 1; margin-top: 24px">
|
||||
<a-space align="center">
|
||||
<span>地域分布</span>
|
||||
<a-popover position="tl">
|
||||
<a-button type="primary" class="pop-btn">
|
||||
<template #icon>
|
||||
<icon-question-circle />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>基于xxx获取数据xxx,一段文字描述该数据的获取方式和来源等xxx</p>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<a-space direction="vertical">
|
||||
<div id="chinaMap" style="height: 416px; width: 640px"></div>
|
||||
<a-space direction="vertical" style="font-size: 14px">
|
||||
<span>搜索指数</span>
|
||||
<a-space>
|
||||
<span>高</span>
|
||||
<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>
|
||||
<span>低</span>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<a-tabs default-active-key="1" @change="tabChange">
|
||||
<a-tab-pane key="1" title="省份">
|
||||
<a-table :data="geoList" :pagination="false">
|
||||
<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="城市">
|
||||
<a-table :data="geoList" :pagination="false">
|
||||
<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-tabs>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</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);
|
||||
const scope = ref(1); //地域范围,1-省,2-市
|
||||
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([]);
|
||||
// 监听筛选条件变化
|
||||
watch([selectedIndustry, selectedTimePeriod], () => {
|
||||
getAgeDistributionsList();
|
||||
getGeoDistributionsList();
|
||||
getGenderDistributionsList();
|
||||
drawChinaMap();
|
||||
});
|
||||
|
||||
// 获取年龄分布列表
|
||||
const getAgeDistributionsList = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchAgeDistributionsList(params);
|
||||
console.log('年龄分布:', res);
|
||||
ageValueData.value = res;
|
||||
drawAgeChart();
|
||||
};
|
||||
|
||||
// 获得地理分布列表
|
||||
const getGeoDistributionsList = async () => {
|
||||
const params = {
|
||||
scope: scope.value,
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchGeoDistributionsList(params);
|
||||
console.log('地理分布:', res);
|
||||
geoList.value = res;
|
||||
};
|
||||
// 获取性别分布列表
|
||||
const getGenderDistributionsList = async () => {
|
||||
const params = {
|
||||
industry_id: selectedIndustry.value,
|
||||
time_dimension: selectedTimePeriod.value,
|
||||
};
|
||||
const res = await fetchGenderDistributionsList(params);
|
||||
genderData.value = [];
|
||||
genderData.value = [...res];
|
||||
await nextTick();
|
||||
genderValueData.value = [];
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
genderValueData.value.push({
|
||||
value: res[i].rate * 100,
|
||||
});
|
||||
}
|
||||
|
||||
drawChart();
|
||||
};
|
||||
|
||||
const drawChart = () => {
|
||||
var dom = document.getElementById('container');
|
||||
var myChart = echarts.init(dom, null, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false,
|
||||
});
|
||||
var option;
|
||||
option = {
|
||||
color: ['#F64B31', '#2A59F3'],
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
avoidLabelOverlap: false,
|
||||
data: genderValueData.value,
|
||||
labelLine: {
|
||||
show: false, // 不显示引导线
|
||||
},
|
||||
radius: ['40%', '55%'],
|
||||
},
|
||||
],
|
||||
};
|
||||
if (option && typeof option === 'object') {
|
||||
myChart.setOption(option);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
const average = yAxis2.reduce((sum, val) => sum + val, 0) / yAxis2.length;
|
||||
|
||||
// 图表初始化(强制使用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',
|
||||
confine: true, // 确保tooltip不超出容器
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
label: {
|
||||
fontSize: 12, // 调小提示标签字号
|
||||
backgroundColor: 'rgba(0,0,0,0.7)',
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 10,
|
||||
right: 30,
|
||||
bottom: 25,
|
||||
left: 40,
|
||||
containLabel: false,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xAxis,
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 0,
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
hideOverlap: true, // 自动隐藏重叠标签
|
||||
},
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
length: 3, // 缩短刻度线
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
width: 0.5, // 减细轴线
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
// 左侧百分比轴
|
||||
type: 'value',
|
||||
name: '占比',
|
||||
nameLocation: 'top',
|
||||
nameGap: 8,
|
||||
nameTextStyle: {
|
||||
fontSize: 12,
|
||||
padding: [0, 0, 3, 0], // 微调名称位置
|
||||
},
|
||||
min: 0,
|
||||
max: Math.ceil(Math.max(...yAxis) / 20) * 20,
|
||||
interval: calcInterval(yAxis, 20),
|
||||
axisLabel: {
|
||||
formatter: '{value}%',
|
||||
fontSize: 8,
|
||||
margin: 4,
|
||||
showMinLabel: true,
|
||||
showMaxLabel: true,
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
color: 'rgba(255,255,255,0.3)',
|
||||
width: 0.8,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// 右侧TGI轴
|
||||
type: 'value',
|
||||
name: 'TGI指数',
|
||||
nameLocation: 'top',
|
||||
nameGap: 8,
|
||||
nameTextStyle: {
|
||||
fontSize: 10,
|
||||
color: '#F64B31',
|
||||
padding: [0, 0, 3, 0],
|
||||
},
|
||||
min: Math.floor(Math.min(...yAxis2) / 50) * 50,
|
||||
max: Math.ceil(Math.max(...yAxis2) / 50) * 50,
|
||||
interval: calcInterval(yAxis2, 50),
|
||||
axisLabel: {
|
||||
fontSize: 8,
|
||||
margin: 4,
|
||||
color: '#F64B31',
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#F64B31',
|
||||
width: 0.8,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
// 柱状图
|
||||
name: '占比',
|
||||
type: 'bar',
|
||||
barWidth: 10,
|
||||
itemStyle: {
|
||||
color: '#6d4cfe',
|
||||
borderRadius: [1, 1, 0, 0], // 微圆角
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter: '{c}%',
|
||||
fontSize: 8,
|
||||
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: {
|
||||
fontSize: 8,
|
||||
color: '#FFAE00',
|
||||
formatter: (params) => {
|
||||
// 改用回调函数
|
||||
const avg = params.data.coord[1]; // 获取平均值坐标
|
||||
return 'TGI:' + avg.toFixed(0); // 格式化显示
|
||||
},
|
||||
position: 'end',
|
||||
},
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
: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;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user