feat: 行业词云图

This commit is contained in:
renxiaodong
2025-06-20 02:07:38 -04:00
parent 2700a06578
commit 1641320847

View File

@ -26,19 +26,18 @@
<a-tag
v-for="(tag, tagIndex) in row.tags"
:key="tagIndex"
class="cursor-pointer"
:style="{
fontSize: `${getFontSize(rowIndex, tagIndex)}px`,
lineHeight: `${getLineHeight(rowIndex, tagIndex) + 10}px`,
fontSize: `${getTagStyle(rowIndex, tagIndex, row.tags.length).fontSize}px`,
height: `${getTagStyle(rowIndex, tagIndex, row.tags.length).lineHeight}px`,
color: '#6d4cfe',
backgroundColor: '#F0EDFF',
margin: '12px',
margin: '0 12px 12px 0',
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',
borderRadius: '32px',
fontWeight: 400,
}"
@mouseenter="hoverTag = tag"
@mouseleave="hoverTag = null"
@ -85,30 +84,34 @@ const getIndustryTerms = async () => {
industry_id: selectedIndustry.value,
time_dimension: selectedTimePeriod.value,
};
if (selectedSubCategory.value != 0) {
if (selectedSubCategory.value !== 0) {
params['industry_id'] = selectedSubCategory.value;
}
const res = await fetchindustryTerms(params);
// 这里需要根据API返回的数据结构处理成tagRows需要的格式
tagRows.value = processTagData(res);
tagRows.value = processTagData(res.slice(0, 70));
};
// 标签数据(按行分组)
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 lineHeightStart = [28, 52, 58, 72, 58, 52, 28]; // 7行时
const fontSizeStart = [12, 26, 28, 38, 28, 26, 12]; // 7行时
const stepLineHeight = 10; // 行高每步递减
const stepFontSize = 6; // 字号每步递减
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
};
function getTagStyle(rowIndex, tagIndex, tagsLength) {
// 计算当前行的中间起点
const center = Math.floor(tagsLength / 2);
const distance = Math.abs(tagIndex - center);
// 递减,最小值保护
const lineHeight = Math.max(lineHeightStart[rowIndex] - stepLineHeight * distance, 28);
const fontSize = Math.max(fontSizeStart[rowIndex] - stepFontSize * distance, 12);
return { fontSize, lineHeight };
}
const getPaddingLeft = (rowIndex, tagIndex) => {
const centerRow = Math.floor(tagRows.value.length / 2);
@ -124,35 +127,34 @@ const getPadding = (rowIndex, tagIndex) => {
// 处理API返回数据为tagRows格式
const processTagData = (apiData) => {
const totalGroups = 4; // 总组数
const middleIndex = Math.floor(totalGroups / 2); // 中间位置索引3
const chunkSize = Math.ceil(apiData.length / totalGroups); // 每组大小
const arr = [];
// 计算应该分几行最多7行
const maxRows = 7;
const rowCount = Math.min(Math.ceil(apiData.length / 7), maxRows);
// 1. 先按顺序分组
const tempGroups = [];
for (let i = 0; i < totalGroups; i++) {
const start = i * chunkSize;
const end = start + chunkSize;
tempGroups.push(apiData.slice(start, end));
// 均分数据到rowCount行
const baseCount = Math.floor(apiData.length / rowCount);
let remain = apiData.length % rowCount;
const rows = [];
let start = 0;
for (let i = 0; i < rowCount; i++) {
let count = baseCount + (remain > 0 ? 1 : 0);
if (remain > 0) remain--;
rows.push(apiData.slice(start, start + count));
start += count;
}
// 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' };
// 居中分布,从中间往两侧
const result = [];
const center = Math.floor(rowCount / 2);
for (let i = 0; i < rowCount; i++) {
const pos = i % 2 === 0 ? center + Math.floor(i / 2) : center - Math.ceil(i / 2);
result[pos] = { tags: rows[i], align: 'center' };
}
}
return arr.filter(Boolean); // 移除可能的空项
return result;
};
</script>
<style scoped>
<style scoped lang="less">
/* 自定义样式 */
:deep(.arco-table-th) {
background-color: var(--color-fill-2);
@ -168,14 +170,29 @@ const processTagData = (apiData) => {
:deep(.arco-modal-body) {
padding: 0px;
}
.multi-row-tag-cloud {
width: 100%;
padding: 20px;
}
.tag-row {
.tag-row {
display: flex;
flex-wrap: wrap;
align-items: center;
// flex-wrap: wrap;
:deep(.arco-tag) {
transition: all 0.3s;
border-radius: 32px;
font-weight: 400;
// min-width: 100px;
max-width: 200px;
display: flex;
justify-content: center;
&:hover {
background-color: #ffffff !important;
color: #6d4cfe !important;
box-shadow: 0px 4px 12px 0px #0000001a;
}
}
}
}
/* 悬停放大效果 */