<template>
<div class="packageBug-container sbom-container">
<div class="query-list">
<div class="query-item">
<span class="label">漏洞编号:</span>
<el-input
v-model="vulId"
style="width: 250px"
placeholder="输入漏洞编号,Enter键回车搜索"
:prefix-icon="Search"
@keyup.enter="search"
/>
</div>
<div class="query-item">
<span class="label">漏洞级别:</span>
<el-select v-model="vulSeverity" clearable placeholder="请选择" @change="search">
<el-option
v-for="(item, itemIndex) in vulSeverityList"
:key="itemIndex"
:label="item.label"
:value="item.prop"
/>
</el-select>
</div>
</div>
<div class="sbom-table">
<el-table
:data="tableData"
stripe
border
highlight-current-row
scrollbar-always-on
>
<template #empty>
<div class="no-data">
<img src="@/assets/images/no-data.png" alt="" />
<p>暂无相关内容</p>
</div>
</template>
<template v-for="(col,colIndex) in columns" :key="colIndex">
<el-table-column
:label="col.label"
:width="col.width"
:minWidth="col.minWidth"
:show-overflow-tooltip="col.showTooltip"
>
<template #default="scope">
<div v-if="col.type === 'link'" @click="openEchart(scope.row)" class="link">{{ scope.row[col.prop] }}</div>
<div v-else-if="col.hasBgcolor" :class="['bgColor', formatCellColor(scope.row[col.prop])]">{{ scope.row[col.prop] }}</div>
<div v-else-if="col.type === 'button'" class="link" @click="goPath(scope.row)">查看</div>
<span v-else>{{ scope.row[col.prop] }}</span>
</template>
</el-table-column>
</template>
</el-table>
<div class="sbom-pagination">
<el-pagination
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[10, 15, 20, 25, 30]"
:total="pagination.total"
:page-size="pagination.pageSize"
v-model:page-size="pagination.pageSize"
v-model:currentPage="pagination.pageNum"
@current-change="handlePageChange"
@size-change="handleSizeChange"
/>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import SbomDataService from "@/services/SbomDataService";
import ResponseData from "@/types/ResponseData";
import { vulSeverityList } from "@/utils"
import { Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus';
import { mapGetters} from 'vuex';
function defaultPagination() {
return {
pageNum: 1,
pageSize: 10,
total: 0
}
}
const columns1 = [
{ label: '漏洞编号', prop: 'vulId', type: 'link', minWidth: 250 },
{ label: '受影响分支', prop: 'affectedBranches', width: 200 },
{ label: '未分析分支', prop: 'notAnalyzedBranches', width: 200 },
{ label: '已修复分支', prop: 'affectedBranchesRepair', width: 200 },
{ label: '修复中分支', prop: 'affectedBranchesOpen', width: 200 },
{ label: '未修复分支', prop: 'affectedBranchesUnrepair', width: 200 },
{ label: '操作', prop: 'operate', type: 'button', width: 150 },
]
const columns2 = [
{ label: '漏洞编号', prop: 'vulId', type: 'link', minWidth: 250 },
{ label: '漏洞评分系统', prop: 'scoringSystem', width: 250 },
{ label: '漏洞评分', prop: 'score', width: 250, hasBgcolor: true },
{ label: '漏洞评分向量', prop: 'vector', minWidth: 400, showTooltip: true },
{ label: '漏洞关联组件purl', prop: 'purl', width: 400, showTooltip: true },
{ label: '操作', prop: 'operate', type: 'button', width: 150 },
]
export default defineComponent({
name: "SbomVulnerabilityTable",
components: {},
props: {
packageId: {
type: String,
default: ''
},
severity: {
type: String,
default: ''
},
productName: {
type: String,
default: ''
},
isByProductName: {
type: Boolean,
default: false
},
},
data() {
return {
Search: Search,
pagination: defaultPagination(),
tableData: [] as Map<string, any>[],
graphColors: [
{ label: '漏洞', prop: 'vulnerability', color: '#FF0000' },
{ label: '漏洞影响的组件', prop: 'dependency', color: '#0dcfcf' },
{ label: '漏洞影响的软件', prop: 'package', color: '#FF9126' },
{ label: '漏洞影响的传递性依赖', prop: 'transitiveDep', color: '#5A94F8' },
],
colors: {
vulnerability: '#FF0000',
dependency: '#0dcfcf',
package: '#FF9126',
transitiveDep: '#5A94F8',
},
vulSeverityList,
vulId: '',
vulSeverity: '',
};
},
computed:{
...mapGetters([
'getProductName'
]),
columns() {
if(this.isByProductName) {
return columns1
} else {
return columns2
}
}
},
watch: {
severity(newVal) {
this.vulSeverity = newVal
this.search()
},
packageId() {
this.search()
},
productName() {
if(this.isByProductName) {
this.queryPackageVulnerability()
}
}
},
methods: {
search() {
this.pagination.pageNum = 1
this.queryPackageVulnerability()
},
handlePageChange(val: number) {
this.pagination.pageNum = val
this.queryPackageVulnerability()
},
handleSizeChange( val: number) {
this.pagination.pageNum = 1
this.pagination.pageSize = val
this.queryPackageVulnerability()
},
queryPackageVulnerability() {
if(this.isByProductName) {
let str = `page=${this.pagination.pageNum - 1}&size=${this.pagination.pageSize}`
if(this.packageId) {
str = `${str}&packageId=${this.packageId}`
}
if(this.vulSeverity) {
str = `${str}&severity=${this.vulSeverity}`
}
if(this.vulId) {
str = `${str}&vulId=${this.vulId}`
}
SbomDataService.queryVulnerability(this.productName, str)
.then((response: ResponseData) => {
this.tableData = response.data.content;
this.pagination.total = response.data.totalElements;
})
.catch((e: any) => {
if (e.response && e.response.status == 500) {
ElMessage({
message: e.response.data,
type: 'error'
})
}
this.tableData = []
this.pagination.total = 0
});
} else {
let str = `page=${this.pagination.pageNum - 1}&size=${this.pagination.pageSize}`
if(this.vulSeverity) {
str = `${str}&severity=${this.vulSeverity}`
}
if(this.vulId) {
str = `${str}&vulId=${this.vulId}`
}
SbomDataService.queryPackageVulnerability(
this.packageId,
str
)
.then((response: ResponseData) => {
this.tableData = response.data.content;
this.pagination.total = response.data.totalElements;
})
.catch((e: any) => {
if (e.response && e.response.status == 500) {
ElMessage({
message: e.response.data,
type: 'error'
})
}
this.tableData = []
this.pagination.total = 0
});
}
},
goPath(item: any) {
if(item.references && item.references.length) {
const url = item.references[0].second
window.open(url, '_blank')
}
},
formatCellColor(score: number) {
if(score <= 0) {
return 'none'
} else if (score > 0 && score < 4) {
return 'low'
} else if (score >= 4 && score < 7) {
return 'medium'
} else if (score >= 7 && score < 9) {
return 'high'
} else if (score >= 9) {
return 'critical'
} else {
return ''
}
},
openEchart(rowInfo) {
const { href } = this.$router.resolve({
path: "/sbomVulImpactChart",
query: {
productName: this.productName,
vulId: rowInfo.vulId,
},
});
window.open(href, '_blank');
},
},
mounted() {
this.vulSeverity = this.severity
this.queryPackageVulnerability();
},
});
</script>
<style lang="less" scoped>
.packageBug-container{
padding-top: 20px;
.query-list{
margin-bottom: 20px;
padding-right: 20px;
}
}
</style>
<style lang="less">
.el-dialog__body .switch{
position: absolute;
top: 20px;
right: 50px;
}
</style>