当前位置: 首页 > news >正文

词云组件

效果图:

image

 

组件代码:

<template><div class="post"><div class="portal-title flex-h justify-between"><div class="flex-h"><img class="icon" :src="iconSrc" alt="" /><div class="name">{{ title }}</div></div></div><!-- 顶部词云 --><div class="data-container"><div v-for="(item, index) in dataItems" :key="index" class="data-item" :style="getItemStyle(index)"><img v-if="index < 3" :src="getCrownIcon(index)" alt="icon" class="crown-icon" /><span>{{ item.postName }}</span></div></div></div>
</template><script lang="ts" name="" setup>
import { ref, onMounted, watch } from "vue";
// 获取皇冠图标的路径
// 1
const goldIcon = "https:///word-icon1.png";
//2
const silverIcon = "https:///word-icon2.png";
// 3
const bronzeIcon = "https:///word-icon3.png";const getCrownIcon = (index: number): string => {switch (index) {case 0:return goldIcon;case 1:return silverIcon;case 2:return bronzeIcon;default:return "";}
};
// 定义父组件传递的 props
const props = defineProps({// 组件标题
  title: {type: String,default: "三维师像矩阵",},// 标题图标
  iconSrc: {type: String,default: "https:///mine/icon-threed.png",},dataSource: {type: Array,required: true,default: () => [],},
});const dataItems = ref<any>([]);const updateDataItems = (dataSource: any) => {// 遍历从后端返回的 data,提取 `postName` 和 `enrollmentCount`const newData = dataSource.map((item: any) => ({postName: item.postName,enrollmentCount: item.enrollmentCount,}));newData.sort((a: any, b: any) => Number(b.enrollmentCount) - Number(a.enrollmentCount));// 将新的数据赋值给 `dataItems`dataItems.value = newData;
};
// 监听 dataSource 变化
watch(() => props.dataSource,(newdataSource) => {if (newdataSource && Array.isArray(newdataSource)) {updateDataItems(newdataSource);}},{ immediate: true }, // 立即执行
);onMounted(() => {// 确保 props.dataSource 中的数据已经传递过来if (props.dataSource && Array.isArray(props.dataSource)) {updateDataItems(props.dataSource);}
});
/// 用于存储已生成的数据项位置,避免重叠
const positions: { top: number; left: number }[] = [{ top: 24, left: 38 },{ top: 46, left: 26 },{ top: 55, left: 59 },{ top: 3, left: 54 },{ top: 14, left: 75 },{ top: 40, left: 74 },{ top: 80, left: 69 },{ top: 75, left: 42 },{ top: 80, left: 7 },{ top: 64, left: 15 },{ top: 33, left: 2 },{ top: 12, left: 17 },
];
// 检查两个位置是否重叠(阈值调整为更大,确保不会重叠)
const isOverlap = (top: number, left: number) => {const threshold = 19; // 允许的最小距离阈值,单位:百分比return positions.some((pos) => Math.abs(pos.top - top) < threshold && Math.abs(pos.left - left) < threshold);
};// 递归生成不重叠的随机位置
const generateRandomPosition = (): { top: number; left: number } => {const topValue = Math.random() * 80;const leftValue = -1 + Math.random() * 85;// 检查是否重叠if (isOverlap(topValue, leftValue)) {return generateRandomPosition(); // 如果重叠,重新生成位置
  }// 不重叠则存储位置
  positions.push({ top: topValue, left: leftValue });return { top: topValue, left: leftValue };
};
//背景随机
// const bg = [
//   "https:///word-bg1.png",
//   "https:///word-bg2.png",
//   "https:///word-bg3.png",
//   "https:///word-bg4.png",
// ]
// const colorsList = ["#738FF7", "#F06B7F", "#45C1C0","#59BAF6"];
// 背景图和文字颜色的对应组合
const bgColorCombinations = [{bg: "https:///word-bg1.png",color: "#738FF7",},{bg: "https:///word-bg2.png",color: "#F06B7F",},{bg: "https:///word-bg3.png",color: "#45C1C0",},{bg: "https:///word-bg4.png",color: "#59BAF6",},
];
// 随机选择一个背景组合
const getRandomBgColor = () => {return bgColorCombinations[Math.floor(Math.random() * bgColorCombinations.length)];
};// 根据排名动态生成样式
const getItemStyle = (index: number): any => {// // 随机取一个背景图// const randomBg = bg[Math.floor(Math.random() * bg.length)];// // 随机取一个文字颜色// const randomColor = colorsList[Math.floor(Math.random() * colorsList.length)];// 随机选择一个背景组合const { bg, color } = getRandomBgColor();const baseStyle: any = {fontWeight: "normal",fontSize: "14px",padding: "6px 24px 4px",// backgroundImage: "url('@/assets/pic/backup.png')",
    backgroundImage: `url(${bg})`,backgroundSize: "cover",backgroundRepeat: "no-repeat",color: color, // 随机文字颜色display: "flex",alignItems: "center",justifyContent: "center",whiteSpace: "nowrap", // 防止文本换行position: "absolute",};// 动态生成位置
  let positionStyle: any;switch (index) {case 0:positionStyle = { top: "24%", left: "38%" };break;case 1:positionStyle = { top: "46%", left: "26%" };break;case 2:positionStyle = { top: "55%", left: "59%" };break;case 3:positionStyle = { top: "3%", left: "54%" };break;case 4:positionStyle = { top: "14%", left: "75%" };break;case 5:// positionStyle = { top: "40%", left: "74%" };positionStyle = { top: "12%", left: "17%" };break;case 6:positionStyle = { top: "80%", left: "69%" };break;case 7:positionStyle = { top: "75%", left: "42%" };break;case 8:positionStyle = { top: "80%", left: "7%" };break;case 9:// positionStyle = { top: "64%", left: "15%" };positionStyle = { top: "33%", left: "2%" };break;case 10:positionStyle = { top: "33%", left: "2%" };break;case 11:positionStyle = { top: "12%", left: "17%" };break;default:const { top, left } = generateRandomPosition(); // 生成不重叠的随机位置positionStyle = { top: `${top}%`, left: `${left}%` };break;}// 动态设置背景图,前三名使用不同的背景// const backgroundImage =//   index < 3//     ? `url(${new URL("@/assets/pic/backup2.png", import.meta.url)})`//     : `url(${new URL("@/assets/pic/backup.png", import.meta.url)})`;return {...baseStyle,// backgroundImage,//前三不同背景时启用// fontWeight: index < 3 ? "bold" : "normal",// backgroundSize: index < 3 ? "cover" : "contain",//前三背景图// fontSize: index === 0 ? "17px" : index === 1 ? "16px" : index === 2 ? "15px" : "14px", //不同大小fontSize: "14px", //统一大小padding: index === 0 ? "7px 15px 8px" : index === 1 ? "7px 12px 7px" : index === 2 ? "8px 12px 8px" : "11px 24px 11px",...positionStyle,};
};
</script><style lang="scss" scoped>
.post {position: relative;width: 100%;height: 260px;margin: 0 auto;padding: 12px 16px;margin-bottom: 55px;
}
.portal-title {.icon {width: 30px;height: 30px;margin-right: 8px;}.name {font-weight: 600;font-size: 16px;line-height: 24px;text-align: left;color: var(--theme-font-color);}
}.post-box-background {position: absolute;width: 100%;height: 100%;border-radius: 50%;top: 0;left: 0;transform: translate(-50%, -50%);z-index: 0;
}.data-container {position: absolute;width: 95%;height: 100%;z-index: 1;
}.data-item {text-align: center;padding: 6px 24px 4px;z-index: 1;white-space: nowrap;line-height: 1;letter-spacing: 2px;min-width: 100px;
}
span {letter-spacing: 1px;font-family: PingFang SC, PingFang SC;
}
.crown-icon {width: 20px;height: 20px;margin-right: 3px;
}
</style>

父组件使用:

  <BsyWordCloud:dataSource="dataSource"/> <script setup lang="ts">const dataSource=[{postName:'鞠躬尽瘁1'},{postName:'循循善诱2'},{postName:'良师益友3'},{postName:'最美教师4'},{postName:'最美教师5'},{postName:'人力资源6'},{postName:'人力资源7'},{postName:'人力资源8'},{postName:'人力资源9'}]
</script> 

 

http://www.hskmm.com/?act=detail&tid=22261

相关文章:

  • 2025 年超声波清洗机品牌最新权威推荐排行榜:龙门式 / 悬挂式 / 全自动等多类型设备厂家 TOP3 精选,助力企业精准选购
  • 树的统一迭代法
  • 2025 年冷却塔品牌最新推荐排行榜:玻璃钢冷却塔、闭式冷却塔、方型逆流式冷却塔优质厂家 TOP3 精选,赋能企业选购
  • DailyPaper-2025-9-30
  • Powershell 管理 后台/计划 作业(六)
  • 32. 最长有效括号
  • java17及以上版本如何抵御TemplatesImpl注入
  • 详细介绍:【C++实战(53)】C++11线程库:开启多线程编程新世界
  • 将图片某个区域批量填充白色(jsx代码)
  • 《初等数论(第四版,北京大学出版社,潘承洞,潘承彪著)》阅读笔记+心得
  • 完整教程:Word和WPS文字中的自动编号和文字间距过大怎么办?
  • markdown笔记文件批量打上时间戳
  • 251001
  • 微服务调整中心高可用设计:从踩坑到落地的实战指南(二)
  • NOIP2025模拟赛27
  • NOIP2025模拟赛28
  • 十月数据结构题没做
  • NOIP2025模拟赛30
  • 2025西安品牌新房,西安刚需新房,陕西优质新房住宅推荐,地建嘉信臻境,超2000㎡高端会所,满足多元化生活需求
  • 2025年未央区高端楼盘,西咸新区品质楼盘,西安高新品牌楼盘住宅口碑推荐,地建嘉信臻境周边配套丰富,教育医疗商业齐全
  • 2025西安高端新房,西安优质新房,西安品牌新房住宅推荐,地建嘉信臻境,沣东文商板块门户,享双地铁便利
  • 2025年西安洋房楼盘,陕西优质楼盘,西咸新区现房楼盘住宅口碑推荐,地建嘉信臻境超2000㎡高端会所,功能多样
  • Python 闭包的应用场景与实战案例
  • input() 函数
  • 如何确保CMS系统能够飞快响应用户请求?全面性能优化指南
  • 近期
  • Playwright MCP 的使用与调试技巧
  • 实用指南:零基础学AI大模型之LangChain-PromptTemplate
  • 文件上传攻击全面指南:从侦察到防御
  • 2025年陕西洋房楼盘,西安城西品质楼盘,沣东品牌楼盘住宅口碑推荐,地建嘉信臻境户型多元布局,满足全周期生活需求