态势感知平台-全球开源风险态势感知监控中心抽离情报流组件
This commit is contained in:
272
src/views/Jyh/security/components/IntelligenceFeed.vue
Normal file
272
src/views/Jyh/security/components/IntelligenceFeed.vue
Normal file
@@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<div class="index-module__NV_5cW__chartCard list-card">
|
||||
<h3 class="chartCard__title">情报流</h3>
|
||||
<!-- 新增:ref + 鼠标事件 -->
|
||||
<div class="chartCard__container list-container"
|
||||
ref="scrollContainer1"
|
||||
@mouseenter="pauseScroll(1)"
|
||||
@mouseleave="resumeScroll(1)">
|
||||
<div class="project-list">
|
||||
<div class="project-item" v-for="(item, index) in feedList" :key="index">
|
||||
<span class="project-rank" :class="item.type">{{ item.type }}</span>
|
||||
<div class="project-info">
|
||||
<span class="project-name">{{ item.content }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||
|
||||
// 接收外部数据作为属性
|
||||
const props = defineProps<{
|
||||
initialData?: Array<any>;
|
||||
}>();
|
||||
|
||||
// 情报流数据
|
||||
const feedList = ref([
|
||||
{
|
||||
type: 'CISA',
|
||||
content: 'CISA KEV新增: WordPress 的 OneClick Chat to Order 插件,存在不安全的直接对象引用漏洞 (CVE-2025-13526) '
|
||||
},
|
||||
{
|
||||
type: '投毒',
|
||||
content: '投毒监控: npm 基建级包大规模投毒事件,影响chalk, debug, ansi-styles 等 18 个包'
|
||||
},
|
||||
{
|
||||
type: 'CNVD',
|
||||
content: 'CNVD预警: Google Chrome信息泄露漏洞(CNVD-2025-3038304)'
|
||||
},
|
||||
{
|
||||
type: 'GitHub',
|
||||
content: 'GitHub安全通告: MovableType 软件的 ContentType 页面 "编辑分类集" 功能存在存储型跨站脚本漏洞'
|
||||
},
|
||||
{
|
||||
type: '修复',
|
||||
content: '漏洞修复: Node.js 发布 v20.11.0 版本,修复 3 个高危漏洞'
|
||||
},
|
||||
{
|
||||
type: '供应链',
|
||||
content: '供应链预警: Maven 中央仓库发现 12 个伪造开源组件,包含后门程序'
|
||||
}
|
||||
]);
|
||||
|
||||
// 如果提供了初始数据,则使用提供的数据
|
||||
watch(() => props.initialData, (newData) => {
|
||||
if (newData && newData.length > 0) {
|
||||
feedList.value = newData;
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 1. 定义滚动容器的ref
|
||||
const scrollContainer1 = ref<HTMLDivElement | null>(null);
|
||||
|
||||
// 2. 存储定时器(键:1/2,值:定时器ID)
|
||||
const scrollTimers: { [key: number]: NodeJS.Timeout | null } = {
|
||||
1: null,
|
||||
2: null
|
||||
};
|
||||
|
||||
// 3. 滚动配置
|
||||
const scrollStep = 1; // 每次滚动的步长(像素,越小越流畅)
|
||||
const scrollInterval = 20; // 滚动间隔(毫秒,越小越流畅)
|
||||
|
||||
// 4. 通用自动滚动函数
|
||||
const autoScroll = (containerKey: number) => {
|
||||
// 获取对应容器
|
||||
const container = containerKey === 1 ? scrollContainer1.value : null;
|
||||
if (!container) return;
|
||||
|
||||
// 获取列表内容元素(project-list)
|
||||
const projectList = container.querySelector('.project-list') as HTMLDivElement;
|
||||
if (!projectList) return;
|
||||
|
||||
// 若内容高度 ≤ 容器高度,不执行滚动
|
||||
if (projectList.scrollHeight <= container.clientHeight) {
|
||||
// 清除现有定时器
|
||||
if (scrollTimers[containerKey]) {
|
||||
clearInterval(scrollTimers[containerKey]!);
|
||||
scrollTimers[containerKey] = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除现有定时器(避免重复)
|
||||
if (scrollTimers[containerKey]) {
|
||||
clearInterval(scrollTimers[containerKey]!);
|
||||
}
|
||||
|
||||
// 启动定时器滚动
|
||||
scrollTimers[containerKey] = setInterval(() => {
|
||||
// 向下滚动步长
|
||||
container.scrollTop += scrollStep;
|
||||
|
||||
// 滚动到底部时,重置为0(循环滚动)
|
||||
// 加1是为了兼容像素精度问题
|
||||
if (projectList.scrollHeight - container.scrollTop <= container.clientHeight + 1) {
|
||||
container.scrollTop = 0; // 直接重置(也可以用平滑滚动:container.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
}, scrollInterval);
|
||||
};
|
||||
|
||||
// 5. 暂停滚动
|
||||
const pauseScroll = (containerKey: number) => {
|
||||
if (scrollTimers[containerKey]) {
|
||||
clearInterval(scrollTimers[containerKey]!);
|
||||
scrollTimers[containerKey] = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 6. 恢复滚动
|
||||
const resumeScroll = (containerKey: number) => {
|
||||
autoScroll(containerKey);
|
||||
};
|
||||
|
||||
// 7. 组件挂载时启动滚动
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
autoScroll(1);
|
||||
});
|
||||
});
|
||||
|
||||
// 8. 组件卸载时清除定时器
|
||||
onUnmounted(() => {
|
||||
Object.keys(scrollTimers).forEach(key => {
|
||||
const k = Number(key);
|
||||
if (scrollTimers[k]) {
|
||||
clearInterval(scrollTimers[k]!);
|
||||
scrollTimers[k] = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 使用主页面相同的CSS类定义以确保样式一致性 */
|
||||
.index-module__NV_5cW__chartCard {
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
background: #ffffffe6;
|
||||
border: 1px solid #ffffff4d;
|
||||
border-radius: 1rem;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 4px 16px #00000014;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.index-module__NV_5cW__chartCard.list-card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.chartCard__title {
|
||||
color: #333;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 1rem 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.chartCard__container {
|
||||
width: 100%;
|
||||
height: 300px; /* 图表高度 */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 列表容器样式:隐藏原生滚动条(可选,视觉更美观) */
|
||||
.chartCard__container.list-container {
|
||||
height: auto;
|
||||
max-height: 300px; /* 限制列表高度,超出滚动 */
|
||||
overflow-y: auto;
|
||||
padding: 0 0.5rem;
|
||||
/* 隐藏滚动条 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE/Edge */
|
||||
}
|
||||
|
||||
/* 隐藏webkit内核浏览器的滚动条 */
|
||||
.chartCard__container.list-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 项目列表样式 */
|
||||
.project-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.project-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.8rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 0.5rem;
|
||||
border-left: 3px solid #2951E0;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
/* 鼠标悬停项目项时的样式 */
|
||||
.project-item:hover {
|
||||
background: #f0f4ff;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.project-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
.project-rank {
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
background: #2951E0;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
/* 特定类型的颜色定义 - 使用中文类名 */
|
||||
/* .project-rank.CISA {
|
||||
background: #ff0000;
|
||||
}
|
||||
|
||||
.project-rank.投毒 {
|
||||
background: #ffa500;
|
||||
}
|
||||
|
||||
.project-rank.CNVD {
|
||||
background: #ffff00;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.project-rank.GitHub {
|
||||
background: #0000ff;
|
||||
}
|
||||
|
||||
.project-rank.修复 {
|
||||
background: #008000;
|
||||
}
|
||||
|
||||
.project-rank.供应链 {
|
||||
background: #800080;
|
||||
} */
|
||||
|
||||
.project-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user