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-06-16 14:42:26 +08:00
|
|
|
|
<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);
|
2025-06-17 11:18:39 +08:00
|
|
|
|
// 监听筛选条件变化
|
|
|
|
|
|
watch([selectedTimePeriod, selectedSubCategory], () => {
|
|
|
|
|
|
getIndustryTerms();
|
|
|
|
|
|
});
|
|
|
|
|
|
const search = () => {
|
|
|
|
|
|
getIndustryTerms();
|
|
|
|
|
|
};
|
|
|
|
|
|
watch(selectedIndustry, () => {
|
|
|
|
|
|
selectedSubCategory.value = 0;
|
|
|
|
|
|
getIndustryTerms();
|
|
|
|
|
|
});
|
2025-06-16 14:42:26 +08:00
|
|
|
|
|
2025-06-17 11:18:39 +08:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
getIndustryTerms();
|
|
|
|
|
|
});
|
2025-06-16 14:42:26 +08:00
|
|
|
|
const getIndustryTerms = async () => {
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
industry_id: selectedIndustry.value,
|
|
|
|
|
|
time_dimension: selectedTimePeriod.value,
|
|
|
|
|
|
};
|
2025-06-17 11:18:39 +08:00
|
|
|
|
if (selectedSubCategory.value != 0) {
|
|
|
|
|
|
params['industry_id'] = selectedSubCategory.value;
|
|
|
|
|
|
}
|
2025-06-16 14:42:26 +08:00
|
|
|
|
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) => {
|
2025-06-17 11:18:39 +08:00
|
|
|
|
const totalGroups = 4; // 总组数
|
2025-06-16 14:42:26 +08:00
|
|
|
|
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); // 移除可能的空项
|
|
|
|
|
|
};
|
|
|
|
|
|
</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>
|