可信代码库-AI助手显示软件详情页面开发
This commit is contained in:
9750
package-lock.json
generated
9750
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -268,6 +268,49 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.jyh-card2 {
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: #F3F3F3;
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
color: #252B3A;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-key {
|
||||||
|
color: #595959;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.card-val {
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.card-url {
|
||||||
|
color: #5E7CE0;
|
||||||
|
font-family: HarmonyOS Sans;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.devui-status:before {
|
.devui-status:before {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
|
|||||||
269
src/views/Jyh/AI/Home/components/OssDetail.vue
Normal file
269
src/views/Jyh/AI/Home/components/OssDetail.vue
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
<template>
|
||||||
|
<div class="secret-container">
|
||||||
|
<div class="title">以下是Redis的软件详情</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<OssDetailCp ref="ossDetailRef" :versionId="versionId"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<d-button v-if="isLogin" class="output-btn" @click="openSubscription">订阅</d-button>
|
||||||
|
<div class="bubble-bottom-operations">
|
||||||
|
<img onclick="handleRefresh()" src="@/assets/imgs/jyh/ai/icon_generate.png" />
|
||||||
|
<i class="icon-file" title="复制" onclick="handleFile()"></i>
|
||||||
|
<i class="icon-like" title="点赞" onclick="handleLike()"></i>
|
||||||
|
<i class="icon-dislike" title="踩" onclick="handleDislike()"></i>
|
||||||
|
<i class="icon-clever-customer" title="智能客服" onclick="handleCustomerService()"></i>
|
||||||
|
<i class="icon-more-operate" title="更多操作" onclick="handleMore()"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, watch, nextTick } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import { getOrgInfo } from '@/views/Org/hooks/orgInfo';
|
||||||
|
import { Message } from 'vue-devui';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import NoData from '@/components/NoData/NoData.vue';
|
||||||
|
import { useAccountStore } from '@/stores/user';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
const { formatTime } = useTimeFormat();
|
||||||
|
import { getHomeNoticeList, getReleaseExport, releasePage, searchLanguageList } from '@/api/jyh';
|
||||||
|
import { useTimeFormat } from '@/utils/hooks/useTimeFormat';
|
||||||
|
import { useGlobalInfoStore } from '@/stores/Global';
|
||||||
|
import OssDetailCp from './OssDetail/index.vue'
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const filterTableRef = ref();
|
||||||
|
const ossDetailRef = ref(null);
|
||||||
|
const versionId = ref('3238');
|
||||||
|
const { namespace } = getOrgInfo();
|
||||||
|
const userInfo = useAccountStore();
|
||||||
|
const { isLogin } = storeToRefs(userInfo);
|
||||||
|
// 定义 tableKey,用于强制刷新表格
|
||||||
|
const tableKey = ref(0);
|
||||||
|
const tableData = ref([]);
|
||||||
|
|
||||||
|
// 定义 columns 数据
|
||||||
|
const columns = ref([
|
||||||
|
{ key: 'subject', label: '漏洞类型', visible: true, filterable: true, sortable: false },
|
||||||
|
{ key: 'overview', label: '漏洞内容', visible: true, filterable: true, sortable: false },
|
||||||
|
{ key: 'importance', label: '重要性', visible: true, filterable: true, sortable: false,width: 80 },
|
||||||
|
{ key: 'publishTime', label: '时间', visible: true, filterable: true, sortable: false },
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 调用子组件方法的示例函数
|
||||||
|
function openSubscription() {
|
||||||
|
if (ossDetailRef.value) {
|
||||||
|
// 假设子组件有一个名为 'someMethod' 的方法
|
||||||
|
ossDetailRef.value.openSubscription();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转
|
||||||
|
const gotoDetail = (row) => {
|
||||||
|
// 后续请换成params,暂时params传参没传过去
|
||||||
|
router.push({
|
||||||
|
path: 'repoDetail/' + row.id
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getReleaseList = async () => {
|
||||||
|
const { data, error } = await getHomeNoticeList();
|
||||||
|
if (!error && data) {
|
||||||
|
tableData.value = data.data || [];
|
||||||
|
console.log(tableData.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
nextTick(() => {
|
||||||
|
getReleaseList();
|
||||||
|
});
|
||||||
|
// getReleaseList();
|
||||||
|
});
|
||||||
|
|
||||||
|
const exportResult = async () => {
|
||||||
|
const selectDatas = tableData.value.map((d) => d.id);
|
||||||
|
if (!tableData.value.length || selectDatas.length <= 0) {
|
||||||
|
Message.info('未选择任何数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await getReleaseExport({
|
||||||
|
list: selectDatas,
|
||||||
|
});
|
||||||
|
if (data && !error) {
|
||||||
|
// 方法1:如果后端返回的是文件流(Blob)
|
||||||
|
const blob = new Blob([data.data]);
|
||||||
|
const downloadUrl = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// 创建一个临时的a标签触发下载
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = downloadUrl;
|
||||||
|
|
||||||
|
// 从Content-Disposition头中获取文件名,或者使用默认文件名
|
||||||
|
const contentDisposition = data.headers['content-disposition'];
|
||||||
|
let fileName = 'export.xlsx'; // 默认文件名
|
||||||
|
// if (contentDisposition) {
|
||||||
|
// const fileNameMatch = contentDisposition.match(/filename="?(.+)"?/);
|
||||||
|
// if (fileNameMatch && fileNameMatch[1]) {
|
||||||
|
// fileName = fileNameMatch[1].replace(/^[^0-9]*|[^0-9.xlsx]*$/g, "");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
link.setAttribute('download', fileName);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
document.body.removeChild(link);
|
||||||
|
window.URL.revokeObjectURL(downloadUrl);
|
||||||
|
} else {
|
||||||
|
console.error('导出失败:', error);
|
||||||
|
// 这里可以添加错误提示,比如使用Element Plus的ElMessage
|
||||||
|
ElMessage.error('文件导出失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 事件处理函数
|
||||||
|
function handleRefresh() {
|
||||||
|
console.log('刷新点击');
|
||||||
|
// 实际业务代码示例:
|
||||||
|
// location.reload(); // 重新加载页面
|
||||||
|
// 或调用API刷新数据
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFile() {
|
||||||
|
console.log('文件点击');
|
||||||
|
// 示例:触发文件上传
|
||||||
|
// document.getElementById('file-input').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLike() {
|
||||||
|
console.log('点赞点击');
|
||||||
|
// 示例:切换点赞状态
|
||||||
|
const likeBtn = document.querySelector('.icon-like');
|
||||||
|
likeBtn.classList.toggle('active');
|
||||||
|
// 这里可以添加AJAX请求提交点赞状态
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDislike() {
|
||||||
|
console.log('踩点击');
|
||||||
|
// 类似点赞的逻辑
|
||||||
|
const dislikeBtn = document.querySelector('.icon-dislike');
|
||||||
|
dislikeBtn.classList.toggle('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCustomerService() {
|
||||||
|
console.log('客服点击');
|
||||||
|
// 示例:打开客服对话框
|
||||||
|
// openChatWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMore() {
|
||||||
|
console.log('更多操作点击');
|
||||||
|
// 示例:显示下拉菜单
|
||||||
|
// showDropdownMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import 'devui-theme/styles-var/devui-var.scss';
|
||||||
|
|
||||||
|
.secret-container {
|
||||||
|
position: relative;
|
||||||
|
//min-height: 600px;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
margin-top: 16px;
|
||||||
|
border-radius: 15px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secret-op {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: $devui-contrast;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.devui-table__row td) {
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.devui-tag .devui-tag--default) {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border: 1px solid #e3e3ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.one-line {
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #9a9b9c;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cur-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #2d2d2e;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-btn {
|
||||||
|
border-radius: 16px;
|
||||||
|
background: #DFDFDF;
|
||||||
|
|
||||||
|
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secret-breadcrumb {
|
||||||
|
padding: 7px 20px 12px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble-bottom-operations {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px; /* 图标之间的间隔 */
|
||||||
|
align-items: center; /* 垂直居中图标 */
|
||||||
|
justify-content: end;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
296
src/views/Jyh/AI/Home/components/OssDetail/AIRepair/index.vue
Normal file
296
src/views/Jyh/AI/Home/components/OssDetail/AIRepair/index.vue
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
<template>
|
||||||
|
<div class="info-outside w-full">
|
||||||
|
<div class="info">
|
||||||
|
<div class="flex items-center justify-start w-full gap-1">
|
||||||
|
<img src="@/assets/imgs/jyh/aiIcon.png" />
|
||||||
|
<span class="text-base font-medium">AI修复建议:</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<br />通过AI技术精准诊断漏洞类型,智能匹配多维度定制化修复策略,实现从数据校验、代码加固到权限控制的全链路防御,助力漏洞修复效率提升,全方位保障开源软件使用安全。<br />
|
||||||
|
|
||||||
|
<!-- <ul>-->
|
||||||
|
<!-- <li v-for="info in infoList">{{ info }}</li>-->
|
||||||
|
<!-- </ul>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ai-container">
|
||||||
|
<template v-if="tableData.length > 0">
|
||||||
|
<div class="ai-container-card" style="width: 100%" v-for="(rowItem, index) in tableData" :key="index">
|
||||||
|
<Panel :expand="rowItem.expand">
|
||||||
|
<template #header>
|
||||||
|
<div class="ai-panel-header">
|
||||||
|
<div style="padding: 25px 8px 0px 20px">
|
||||||
|
<img style="width: 36px; height: 33px" src="@/assets/imgs/jyh/warning.png" />
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%; padding: 20px 20px 0px 0">
|
||||||
|
<div class="ai-panel-header-title">
|
||||||
|
<div>
|
||||||
|
<d-tag color="red-w98" class="mr-1" size="sm">严重</d-tag>
|
||||||
|
<span>{{ rowItem.libraryName || '--' }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="cursor: pointer; display: flex; gap: 8px; align-items: center"
|
||||||
|
@click="rowItem.expand = !rowItem.expand"
|
||||||
|
>
|
||||||
|
<d-icon :name="rowItem.expand ? 'icon-expand-new' : 'icon-drag-new'"></d-icon>
|
||||||
|
<span>{{ rowItem.expand ? '收起' : '展开' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ai-panel-header-sub-title">
|
||||||
|
<div>
|
||||||
|
<span class="weak-text">安全问题</span>
|
||||||
|
<span>{{ rowItem.vulnerabilities.length || '--' }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- <div>-->
|
||||||
|
<!-- <span class="weak-text">修复兼容性</span>-->
|
||||||
|
<!-- <span>{{ rowItem.vulnerabilities.length || '--' }}</span>-->
|
||||||
|
<!-- <d-tooltip-->
|
||||||
|
<!-- position="bottom"-->
|
||||||
|
<!-- content="这个评分是在比较当前版本和推荐版本之间的差异,并建议修复此组件时的复杂程度。"-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <img src="@/assets/imgs/jyh/help.png" class="icon-sm" />-->
|
||||||
|
<!-- </d-tooltip>-->
|
||||||
|
<!-- <d-tag class="ml-1" type="warning" size="sm">{{'--'}}</d-tag>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<div>
|
||||||
|
<span class="weak-text">升级版本</span>
|
||||||
|
<span>{{ rowItem.currentVersion || '--' }}</span>
|
||||||
|
<img src="@/assets/imgs/jyh/ic_digital_power_next_step_@2x.png" class="icon-sm" />
|
||||||
|
<span>{{ rowItem.recommendedVersion || '--' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body>
|
||||||
|
<div style="padding: 8px 20px 20px 20px; width: 100%">
|
||||||
|
<Table class="jyh-table" :tableConfig="tableConfig" :dataSource="rowItem.vulnerabilities" :libraryName="rowItem.libraryName" :currentVersion="rowItem.currentVersion"/>
|
||||||
|
<div class="w-full flex items-center justify-end">
|
||||||
|
<span>共{{ rowItem.vulnerabilities.length }}条</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Panel>
|
||||||
|
</div>
|
||||||
|
<div class="mt-20 mb-20 flex justify-end" v-if="tableData.length > 0">
|
||||||
|
<d-pagination
|
||||||
|
size="md"
|
||||||
|
:page-size-options="[10, 20, 50]"
|
||||||
|
:total="pager.total"
|
||||||
|
v-model:pageSize="pager.pageSize"
|
||||||
|
v-model:pageIndex="pager.pageIndex"
|
||||||
|
:max-items="5"
|
||||||
|
:can-change-page-size="true"
|
||||||
|
:can-view-total="true"
|
||||||
|
total-item-text="总计"
|
||||||
|
@page-index-change="fetchVulnList()"
|
||||||
|
@page-size-change="fetchVulnList()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<NoData v-else :small="false"></NoData>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps, onMounted, ref } from 'vue';
|
||||||
|
import Panel from '@/components/AIRepair/Panel.vue';
|
||||||
|
import Table from '@/components/AIRepair/Table.vue';
|
||||||
|
import NoData from '@/components/NoData/NoData.vue';
|
||||||
|
import { getVulnList, remediationSearchRemediations } from '@/api/jyh';
|
||||||
|
|
||||||
|
const infoList = ref([
|
||||||
|
'当前代码主要风险为易受常见的注入攻击,包括命令注入、SQL注入、表达式注入,针对每种攻击,应提供相应的防范措施,如数据校验、使用安全函数和限制程序权限。',
|
||||||
|
'相对上一次检查,【提升点】在于代码基础编码问题少了47.8%,问题总数降低29.3%;'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const tableConfig = ref([
|
||||||
|
{
|
||||||
|
field: 'vulnId',
|
||||||
|
header: '安全问题编号',
|
||||||
|
sortable: true,
|
||||||
|
sortMethod: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'vulnType',
|
||||||
|
header: '安全问题类型',
|
||||||
|
sortable: false,
|
||||||
|
sortMethod: null,
|
||||||
|
format: (val) => {
|
||||||
|
// val小写转大写
|
||||||
|
return (val || '').toUpperCase();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'vulnId',
|
||||||
|
header: 'CWE编号',
|
||||||
|
sortable: false,
|
||||||
|
sortMethod: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'cvssScore',
|
||||||
|
header: '分值',
|
||||||
|
sortable: true,
|
||||||
|
sortMethod: null
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
const pager = ref({
|
||||||
|
total: 10,
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
const propsData = defineProps(['versionId']);
|
||||||
|
|
||||||
|
const fetchVulnList = async () => {
|
||||||
|
const { data } = await remediationSearchRemediations({
|
||||||
|
// softwareId: 47,
|
||||||
|
softwareId: propsData.versionId,
|
||||||
|
current: pager.value.pageIndex,
|
||||||
|
size: pager.value.pageSize
|
||||||
|
// componentId: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.data.code === 200) {
|
||||||
|
tableData.value = data?.data?.data?.records || [];
|
||||||
|
pager.value.total = data?.data?.data?.total || 0;
|
||||||
|
// pager.value.total = data?.data?.data?.total || [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableData = ref([
|
||||||
|
// {
|
||||||
|
// "componentId": 1,
|
||||||
|
// "libraryName": "Spring Framework",
|
||||||
|
// "currentVersion": "5.3.26",
|
||||||
|
// "vulnerabilities": [
|
||||||
|
// {
|
||||||
|
// "vulnId": "CVE-2024-12345",
|
||||||
|
// "severity": "High",
|
||||||
|
// "vulnType": "Remote Code Execution",
|
||||||
|
// "cvssScore": 8.5,
|
||||||
|
// "remediation": {
|
||||||
|
// "recommendedFix": "Upgrade to version 5.3.27 or later.",
|
||||||
|
// "patchLink": "https://spring.io/security/advisory/CVE-2024-12345",
|
||||||
|
// "recommendedVersion": "5.3.27"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "componentId": 2,
|
||||||
|
// "libraryName": "Log4j",
|
||||||
|
// "currentVersion": "2.14.0",
|
||||||
|
// "vulnerabilities": [
|
||||||
|
// {
|
||||||
|
// "vulnId": "CVE-2023-67890",
|
||||||
|
// "severity": "Critical",
|
||||||
|
// "vulnType": "Information Disclosure",
|
||||||
|
// "cvssScore": 9.8,
|
||||||
|
// "remediation": {
|
||||||
|
// "recommendedFix": "Apply security patch 2.14.1.",
|
||||||
|
// "patchLink": "https://logging.apache.org/log4j/2.x/security.html",
|
||||||
|
// "recommendedVersion": "2.14.1"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
]);
|
||||||
|
onMounted(() => {
|
||||||
|
fetchVulnList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import 'devui-theme/styles-var/devui-var.scss';
|
||||||
|
|
||||||
|
.ai-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
justify-content: center;
|
||||||
|
&-card {
|
||||||
|
border-top: 2px solid $devui-danger;
|
||||||
|
background-color: $devui-base-bg;
|
||||||
|
border-radius: var(--devui-border-radius-card, 8px);
|
||||||
|
box-shadow: var(--devui-shadow-length-base, 0 2px 6px 0) var(--devui-light-shadow, rgba(37, 43, 58, 0.12));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-panel-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 80px;
|
||||||
|
&-title {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-sub-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 20px;
|
||||||
|
position: relative;
|
||||||
|
&:not(:last-child)::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: -10px;
|
||||||
|
height: 100%;
|
||||||
|
width: 1px;
|
||||||
|
background-color: $devui-line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.weak-text {
|
||||||
|
color: $devui-text-weak;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-sm {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-outside {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 1px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-image: linear-gradient(
|
||||||
|
134.79deg,
|
||||||
|
#a4f7df 0%,
|
||||||
|
#a0cefb 21%,
|
||||||
|
#5f9afe 43%,
|
||||||
|
#d9b9fc 62%,
|
||||||
|
#01efbb 73%,
|
||||||
|
#fbae46 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style: disc;
|
||||||
|
padding-left: 18px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
124
src/views/Jyh/AI/Home/components/OssDetail/CommunityInfo.vue
Normal file
124
src/views/Jyh/AI/Home/components/OssDetail/CommunityInfo.vue
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<div class="version-info">
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
基本信息
|
||||||
|
<!-- <span style="color: #3b82f6;font-size: 12px;font-weight: normal">(该数据由AI生成)</span>-->
|
||||||
|
</div>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="8">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-90">社区名称</span>
|
||||||
|
<span class="card-val">{{propsData.ReleaseInfo?.basicInfo?.communityName || propsData.ReleaseInfo?.criticalSoftwareAIGenerateInfo?.communityName || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="8">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-90">官网地址</span>
|
||||||
|
<a :href="propsData.ReleaseInfo?.basicInfo?.communityWebsite || propsData.ReleaseInfo?.criticalSoftwareAIGenerateInfo?.communityName" class="card-url">{{
|
||||||
|
propsData.ReleaseInfo?.basicInfo?.communityWebsite || propsData.ReleaseInfo?.criticalSoftwareAIGenerateInfo?.communityName ||'--' }}</a>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="8">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-90">联系方式</span>
|
||||||
|
<span class="card-val">{{
|
||||||
|
propsData.ReleaseInfo?.basicInfo?.communityContact || propsData.ReleaseInfo?.criticalSoftwareAIGenerateInfo?.communityContact ||'--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<div class="flex">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-90 flex-shrink-0">社区描述</span>
|
||||||
|
<span class="card-val flex-grow-1">{{
|
||||||
|
propsData.ReleaseInfo?.basicInfo?.communityDescription || propsData.ReleaseInfo?.criticalSoftwareAIGenerateInfo?.communityDescription ||'--' }}</span>
|
||||||
|
</div>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
</Card>
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
贡献者分布
|
||||||
|
</div>
|
||||||
|
<p class="card-desc">有关Pull Request创建者的公司信息和地区分布(使用公共GitHub信息进行分析)</p>
|
||||||
|
<div class="mt-3">
|
||||||
|
<chart-bar ref="chartBarRef" :companyGraph="companyGraph" :countryGraph="countryGraph" />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ChartBar from '@/components/ChartBar/index.vue';
|
||||||
|
import { computed, defineProps, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
const propsData = defineProps(['versionId','ReleaseInfo']);
|
||||||
|
|
||||||
|
const chartBarRef = ref();
|
||||||
|
|
||||||
|
const countryGraph = computed(() => {
|
||||||
|
if(propsData.ReleaseInfo.criticalSoftwareAIGenerateInfo&&propsData.ReleaseInfo.criticalSoftwareAIGenerateInfo.contributorCountries) {
|
||||||
|
let countriesObj = JSON.parse(propsData.ReleaseInfo.criticalSoftwareAIGenerateInfo.contributorCountries);
|
||||||
|
return Object.entries(countriesObj).map(([name, value]) => ({
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const companyGraph = computed(() => {
|
||||||
|
if(propsData.ReleaseInfo.criticalSoftwareAIGenerateInfo&&propsData.ReleaseInfo.criticalSoftwareAIGenerateInfo.organizationDistribution) {
|
||||||
|
let countriesObj = JSON.parse(propsData.ReleaseInfo.criticalSoftwareAIGenerateInfo.organizationDistribution);
|
||||||
|
return Object.entries(countriesObj).map(([name, value]) => ({
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
chartBarRef.value.onInited();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.card-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
<template>
|
||||||
|
<d-drawer v-model="detailDrawerCtrl" class="w-[800px] p-[20px]">
|
||||||
|
<div class="flex flex-col h-full">
|
||||||
|
<div class="flex justify-between items-center mb-[12px]">
|
||||||
|
<p class="text-[18px]/[26px] ">恶意代码详情</p>
|
||||||
|
<d-icon @click="detailDrawerCtrl = false" name="icon-close" size="18px" class="cursor-pointer text-[#71757F]" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col mb-[12px] flex-1 h-0">
|
||||||
|
<div class="malware-detail-card mb-[16px]">
|
||||||
|
<p class="title">文件基本信息</p>
|
||||||
|
<div class="content">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">文件名称</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.packageFullName || '--' }}</div>
|
||||||
|
<d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(props.detailRow?.packageFullName)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">文件路径</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.packagePath || '--' }}</div>
|
||||||
|
<d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(props.detailRow?.packagePath)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">文件版本号</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.packageVersion || '--' }}</div>
|
||||||
|
<d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(props.detailRow?.packageVersion)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">LLM名称</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.llmName || '--' }}</div>
|
||||||
|
<d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(props.detailRow?.llmName)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">敏感API总数</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.totalApisCalledCount }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">可疑文件总数</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.suspiciousFilesCount }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">是否恶意代码</div>
|
||||||
|
<div class="single-line-ellipsis">
|
||||||
|
<span class="card-val" v-if="props.detailRow?.isMalicious===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">大模型是否正常回复</div>
|
||||||
|
<div class="single-line-ellipsis">
|
||||||
|
<span class="card-val" v-if="props.detailRow?.isModelResponded===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="flex items-center">-->
|
||||||
|
<!-- <div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">文件sha256</div>-->
|
||||||
|
<!-- <div :title="malwareDetails?.code" class="single-line-ellipsis">{{ malwareDetails?.code }}</div>-->
|
||||||
|
<!-- <d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(malwareDetails?.code)" />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="malware-detail-card mb-[16px]">
|
||||||
|
<p class="title">调用的敏感API列表</p>
|
||||||
|
<div class="content">
|
||||||
|
<d-table
|
||||||
|
:data="malwareDetails?.apisCalled"
|
||||||
|
class="w-full bg-white malware-list-table"
|
||||||
|
:fix-header="malwareDetails?.apisCalled.length > 0"
|
||||||
|
|
||||||
|
max-height="300px"
|
||||||
|
>
|
||||||
|
<d-column field="isMalicious" header="是否恶意代码">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="card-val" v-if="scope.row.isMalicious===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column
|
||||||
|
field="maliciousCodeCategory"
|
||||||
|
header="恶意代码分类"
|
||||||
|
/>
|
||||||
|
<template #empty>
|
||||||
|
<NoData style="padding: 0; margin: -30px 0;" :width="80" />
|
||||||
|
</template>
|
||||||
|
</d-table>
|
||||||
|
|
||||||
|
<!-- <d-pagination-->
|
||||||
|
<!-- class="flex justify-end mt-[20px]"-->
|
||||||
|
<!-- :total="pager.total"-->
|
||||||
|
<!-- v-model:pageSize="pager.pageSize"-->
|
||||||
|
<!-- v-model:pageIndex="pager.pageIndex"-->
|
||||||
|
<!-- :can-view-total="true"-->
|
||||||
|
<!-- :can-change-page-size="true"-->
|
||||||
|
<!-- :max-items="5"-->
|
||||||
|
<!-- @page-index-change="pageIndexChange"-->
|
||||||
|
<!-- @page-size-change="pageSizeChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="malware-detail-card mb-[16px]">
|
||||||
|
<p class="title">包含恶意代码的文件列表</p>
|
||||||
|
<div class="content">
|
||||||
|
<d-table
|
||||||
|
:data="malwareDetails?.source"
|
||||||
|
class="w-full bg-white malware-list-table"
|
||||||
|
:fix-header="malwareDetails?.source.length > 0"
|
||||||
|
|
||||||
|
max-height="300px"
|
||||||
|
>
|
||||||
|
<d-column
|
||||||
|
field="sink"
|
||||||
|
header="恶意汇"
|
||||||
|
/>
|
||||||
|
<d-column
|
||||||
|
field="flow"
|
||||||
|
header="传播流向"
|
||||||
|
/>
|
||||||
|
<d-column
|
||||||
|
field="purpose"
|
||||||
|
header="代码目的"
|
||||||
|
/>
|
||||||
|
<d-column
|
||||||
|
field="analysis"
|
||||||
|
header="LLM对于这个代码片段的分析"
|
||||||
|
/>
|
||||||
|
<d-column
|
||||||
|
field="createTime"
|
||||||
|
header="创建时间"
|
||||||
|
/>
|
||||||
|
<template #empty>
|
||||||
|
<NoData style="padding: 0; margin: -30px 0;" :width="80" />
|
||||||
|
</template>
|
||||||
|
</d-table>
|
||||||
|
|
||||||
|
<!-- <d-pagination-->
|
||||||
|
<!-- class="flex justify-end mt-[20px]"-->
|
||||||
|
<!-- :total="pager.total"-->
|
||||||
|
<!-- v-model:pageSize="pager.pageSize"-->
|
||||||
|
<!-- v-model:pageIndex="pager.pageIndex"-->
|
||||||
|
<!-- :can-view-total="true"-->
|
||||||
|
<!-- :can-change-page-size="true"-->
|
||||||
|
<!-- :max-items="5"-->
|
||||||
|
<!-- @page-index-change="pageIndexChange"-->
|
||||||
|
<!-- @page-size-change="pageSizeChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="malware-detail-card flex-1 h-0">-->
|
||||||
|
<!-- <p class="title">关键日志</p>-->
|
||||||
|
<!-- <div class="content flex flex-col flex-1 h-0">-->
|
||||||
|
<!-- <div class="log-box flex-1 overflow-y-scroll">-->
|
||||||
|
<!-- {{ malwareDetails?.analysis }}-->
|
||||||
|
<!-- {{ malwareDetails?.code }}-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</d-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, shallowRef, defineProps, ref, defineExpose } from 'vue';
|
||||||
|
import NoData from "@/components/NoData/NoData.vue";
|
||||||
|
import { getMalwareDetailApi, getMalwareDetailApi2 } from '@/api/inboundTask/inboundTaskApi';
|
||||||
|
import {Message} from "vue-devui/message";
|
||||||
|
import { type MalwareDetailType } from "@/api/inboundTask/inboundTaskType";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
detailRow: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const detailDrawerCtrl = ref(false);
|
||||||
|
const malwareDetails = shallowRef<MalwareDetailType | null>(null);
|
||||||
|
const threatContentList = shallowRef([]);
|
||||||
|
const pager = reactive({
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getMalwareDetails(id) {
|
||||||
|
try {
|
||||||
|
const res = await getMalwareDetailApi2({
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
maliciousId: id
|
||||||
|
});
|
||||||
|
if(res.data.records&&res.data.records.length>0) {
|
||||||
|
malwareDetails.value = res.data.records[0];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Message.error('接口错误,查询详情失败');
|
||||||
|
}
|
||||||
|
getCurrentPageData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentPageData() {
|
||||||
|
// pager.total = malwareDetails.value.contentInfo.total;
|
||||||
|
// const endIndex = pager.pageIndex * pager.pageSize;
|
||||||
|
// threatContentList.value = malwareDetails.value.contentInfo.list.slice(endIndex - pager.pageSize, endIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageIndexChange() {
|
||||||
|
getCurrentPageData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageSizeChange() {
|
||||||
|
pager.pageIndex = 1;
|
||||||
|
getCurrentPageData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCopy(text: string) {
|
||||||
|
inputCopy(text);
|
||||||
|
}
|
||||||
|
const openDetail = (id) => {
|
||||||
|
detailDrawerCtrl.value = true;
|
||||||
|
getMalwareDetails(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputCopy(text: string) {
|
||||||
|
const aux = document.createElement('input');
|
||||||
|
aux.setAttribute('value', text);
|
||||||
|
document.body.appendChild(aux);
|
||||||
|
aux.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(aux);
|
||||||
|
Message({ type: 'success', message: '复制成功' });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 暴露方法,供父组件调用
|
||||||
|
defineExpose({
|
||||||
|
openDetail,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.malware-detail-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #EEF0F5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.title {
|
||||||
|
color: #191919;
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
background-color: #F3F3F3;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 20px 16px;
|
||||||
|
:deep(.devui-table__thead .header-container) {
|
||||||
|
background-color: #F3F3F3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.log-box {
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #F7F8FA;
|
||||||
|
border: 1px solid #EEF0F5;
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
.single-line-ellipsis {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<d-search
|
||||||
|
v-model="searchText"
|
||||||
|
icon-position="left"
|
||||||
|
class="w-[400px] mb-[12px]"
|
||||||
|
placeholder="请输入搜索内容"
|
||||||
|
@search="getMalwareList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<d-table
|
||||||
|
:data="tableData"
|
||||||
|
:show-loading="searchLoading"
|
||||||
|
class="jyh-table"
|
||||||
|
:fix-header="tableData.length > 0"
|
||||||
|
max-height="600px"
|
||||||
|
@sort-change="sortChange"
|
||||||
|
>
|
||||||
|
<d-column type="index" width="48px" />
|
||||||
|
<!-- <d-column field="packageFullName" width="220px">-->
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 文件名称-->
|
||||||
|
<!-- <TableFilter field="packageFullName" :condition="filterData.packageFullName" @filter-change="filterChange" />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <template #default="{ row }">-->
|
||||||
|
<!-- <a class="devui-link" @click="openMalwareDetail(row)">{{ row.packageFullName }}</a>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<!-- <d-column field="category">-->
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 恶意类别-->
|
||||||
|
<!-- <TableFilter-->
|
||||||
|
<!-- type="radio"-->
|
||||||
|
<!-- field="category"-->
|
||||||
|
<!-- :condition="filterData.category"-->
|
||||||
|
<!-- :filter-list="categoryFilterOptions"-->
|
||||||
|
<!-- @filter-change="filterChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<d-column field="packageName" header="文件名称" width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<a class="devui-link" @click="openMalwareDetail(row)">{{ row.packageFullName }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="packagePath" header="文件路径" width="180" show-overflow-tooltip/>
|
||||||
|
<d-column field="packageVersion" header="文件版本号" show-overflow-tooltip/>
|
||||||
|
<d-column field="llmName" header="LLM名称" show-overflow-tooltip/>
|
||||||
|
<d-column field="totalApisCalledCount" header="敏感API总数" show-overflow-tooltip/>
|
||||||
|
<d-column field="suspiciousFilesCount" header="可疑文件总数" show-overflow-tooltip/>
|
||||||
|
<d-column field="isMalicious" header="是否恶意代码">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="card-val" v-if="scope.row.isMalicious===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="isModelResponded" header="大模型是否正常回复" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="card-val" v-if="scope.row.isModelResponded===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="createTime" header="检测时间" width="180" show-overflow-tooltip/>
|
||||||
|
<!-- <d-column field="threatLevel">-->
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 威胁级别-->
|
||||||
|
<!-- <TableFilter-->
|
||||||
|
<!-- type="radio"-->
|
||||||
|
<!-- field="threatLevel"-->
|
||||||
|
<!-- :condition="filterData.threatLevel"-->
|
||||||
|
<!-- :filter-list="threatLevelFilterList"-->
|
||||||
|
<!-- @filter-change="filterChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <template #default="{ row }">-->
|
||||||
|
<!-- <d-status :type="ThreatLevelMap[row.threatLevel].statusType">-->
|
||||||
|
<!-- {{ ThreatLevelMap[row.threatLevel].label }}-->
|
||||||
|
<!-- </d-status>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<!-- <d-column field="confidenceCoefficient">-->
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 置信度-->
|
||||||
|
<!-- <TableFilter-->
|
||||||
|
<!-- type="radio"-->
|
||||||
|
<!-- field="confidenceCoefficient"-->
|
||||||
|
<!-- :condition="filterData.confidenceCoefficient"-->
|
||||||
|
<!-- :filter-list="confidenceCoefficientFilterList"-->
|
||||||
|
<!-- @filter-change="filterChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <template #default="{ row }">-->
|
||||||
|
<!-- <d-tag size="sm" :type="ConfidenceCoefficientMap[row.confidenceCoefficient].statusType">-->
|
||||||
|
<!-- {{ ConfidenceCoefficientMap[row.confidenceCoefficient].label }}-->
|
||||||
|
<!-- </d-tag>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<!-- <d-column field="testResult" header="检测结果" />-->
|
||||||
|
<template #empty>
|
||||||
|
<NoData v-if="!searchLoading" />
|
||||||
|
</template>
|
||||||
|
</d-table>
|
||||||
|
|
||||||
|
<d-pagination
|
||||||
|
class="flex justify-end mt-[20px]"
|
||||||
|
:total="pager.total"
|
||||||
|
v-model:pageSize="pager.pageSize"
|
||||||
|
v-model:pageIndex="pager.pageIndex"
|
||||||
|
:can-view-total="true"
|
||||||
|
:can-change-page-size="true"
|
||||||
|
:max-items="5"
|
||||||
|
@page-index-change="pageIndexChange"
|
||||||
|
@page-size-change="pageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Detail ref="MalwareDetailRef" :detailRow="detailRow" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, shallowRef } from 'vue';
|
||||||
|
import TableFilter from '@/components/TableFilter/TableFilter.vue';
|
||||||
|
import NoData from '@/components/NoData/NoData.vue';
|
||||||
|
import Detail from './Detail.vue';
|
||||||
|
import { getMalwareListApi } from '@/api/inboundTask/inboundTaskApi';
|
||||||
|
import { type MalwareListItemType } from '@/api/inboundTask/inboundTaskType';
|
||||||
|
import { Message } from 'vue-devui/message';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
enum ThreatLevelEnum {
|
||||||
|
HIGH = 'high',
|
||||||
|
MD = 'medium',
|
||||||
|
LOW = 'low'
|
||||||
|
}
|
||||||
|
enum ConfidenceCoefficientEnum {
|
||||||
|
HIGH = 'high',
|
||||||
|
MD = 'medium',
|
||||||
|
LOW = 'low'
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const versionId = route.params.versionId;
|
||||||
|
const searchText = ref('');
|
||||||
|
const searchLoading = ref(false);
|
||||||
|
const MalwareDetailRef = ref<InstanceType<typeof MalwareDetail> | null>(null)
|
||||||
|
const tableData = shallowRef<MalwareListItemType[]>([]);
|
||||||
|
const pager = reactive({
|
||||||
|
pageSize: 10,
|
||||||
|
pageIndex: 1,
|
||||||
|
total: 0
|
||||||
|
});
|
||||||
|
const ThreatLevelMap = {
|
||||||
|
[ThreatLevelEnum.HIGH]: { label: '高危', statusType: 'error' },
|
||||||
|
[ThreatLevelEnum.MD]: { label: '中危', statusType: 'warning' },
|
||||||
|
[ThreatLevelEnum.LOW]: { label: '低危', statusType: 'waiting' }
|
||||||
|
};
|
||||||
|
const ConfidenceCoefficientMap = {
|
||||||
|
[ConfidenceCoefficientEnum.HIGH]: { label: '高', statusType: 'success' },
|
||||||
|
[ConfidenceCoefficientEnum.MD]: { label: '中', statusType: 'warning' },
|
||||||
|
[ConfidenceCoefficientEnum.LOW]: { label: '低', statusType: 'danger' }
|
||||||
|
};
|
||||||
|
const confidenceCoefficientFilterList = [
|
||||||
|
{ label: '高', value: ConfidenceCoefficientEnum.HIGH },
|
||||||
|
{ label: '中', value: ConfidenceCoefficientEnum.MD },
|
||||||
|
{ label: '低', value: ConfidenceCoefficientEnum.LOW }
|
||||||
|
];
|
||||||
|
const threatLevelFilterList = [
|
||||||
|
{ label: '高危', value: ThreatLevelEnum.HIGH },
|
||||||
|
{ label: '中危', value: ThreatLevelEnum.MD },
|
||||||
|
{ label: '低危', value: ThreatLevelEnum.LOW }
|
||||||
|
];
|
||||||
|
const categoryFilterOptions = [{ label: 'Python', value: 'Python' }];
|
||||||
|
const filterData = reactive({
|
||||||
|
packageFullName: '',
|
||||||
|
threatLevel: '',
|
||||||
|
confidenceCoefficient: '',
|
||||||
|
category: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const detailRow = shallowRef<MalwareListItemType | null>(null);
|
||||||
|
|
||||||
|
init();
|
||||||
|
function init() {
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMalwareList() {
|
||||||
|
const params = {
|
||||||
|
current: pager.pageIndex,
|
||||||
|
size: pager.pageSize,
|
||||||
|
taskId: '',
|
||||||
|
softwareId: versionId
|
||||||
|
};
|
||||||
|
pager.total = 0;
|
||||||
|
tableData.value = [];
|
||||||
|
searchLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getMalwareListApi(params);
|
||||||
|
if (res.data?.code === 200) {
|
||||||
|
tableData.value = res.data.data.records.map((item) => {
|
||||||
|
return { ...item, threatLevel: ThreatLevelEnum.HIGH, confidenceCoefficient: ConfidenceCoefficientEnum.HIGH };
|
||||||
|
});
|
||||||
|
pager.total = res.data.data.total;
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg || '查询恶意代码列表失败');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Message.error('接口错误,查询恶意代码列表失败');
|
||||||
|
}
|
||||||
|
searchLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortChange(sortInfo: { field: string; direction: 'ASC' | 'DESC' | '' }) {
|
||||||
|
console.log(sortInfo);
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterChange(filterInfo: { field: string; value: any }) {
|
||||||
|
filterData[filterInfo.field] = filterInfo.value;
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageSizeChange() {
|
||||||
|
pager.pageIndex = 1;
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageIndexChange() {
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMalwareDetail(row: MalwareListItemType) {
|
||||||
|
detailRow.value = row;
|
||||||
|
// 添加检查
|
||||||
|
if (MalwareDetailRef.value && typeof MalwareDetailRef.value.openDetail === 'function') {
|
||||||
|
MalwareDetailRef.value.openDetail(row.id)
|
||||||
|
} else {
|
||||||
|
console.error('MalwareDetailRef 或 openDetail 方法不可用')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.malware-list-table {
|
||||||
|
:deep(.devui-table__row td) {
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.devui-status.devui-status__bg--warning:before) {
|
||||||
|
background-color: #fcc800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
271
src/views/Jyh/AI/Home/components/OssDetail/Dependency.vue
Normal file
271
src/views/Jyh/AI/Home/components/OssDetail/Dependency.vue
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
<template>
|
||||||
|
<div class="info w-full">
|
||||||
|
<div class="flex items-center justify-start w-full gap-1">
|
||||||
|
<img src="@/assets/imgs/jyh/infoIcon.png" />
|
||||||
|
<span class="text-base font-medium">治理标准:</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
1、在依赖冲突或者解决依赖软件漏洞的场景下,产品引入的依赖组件与社区的依赖关系不一致,从而通过产品自行录入,由使用方自行维护(可通过“产品快速补包流程维护”,<a
|
||||||
|
class="devui-link"
|
||||||
|
>操作指导</a
|
||||||
|
>)。该部分的依赖组件漏洞由产品自行解决。
|
||||||
|
</li>
|
||||||
|
<li>2、点击下面组件编码跳转到组件详情页面,可直接看到基于当前火车过滤的组件依赖路径。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-end gap-3 mt-3">
|
||||||
|
<d-button variant="text" icon="export">导出</d-button>
|
||||||
|
<div class="vertical-line"></div>
|
||||||
|
<d-button variant="text" @click="openAll(baseTreeTableData1)">
|
||||||
|
<div v-if="!isAllExpand" class="flex items-center justify-start gap-1">
|
||||||
|
<img style="width: 14px; height: 14px" src="@/assets/imgs/jyh/openAll.png" />
|
||||||
|
全部展开
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex items-center justify-start gap-1">
|
||||||
|
<img style="width: 14px; height: 14px" src="@/assets/imgs/jyh/closeAll.png" />
|
||||||
|
全部关闭
|
||||||
|
</div>
|
||||||
|
</d-button>
|
||||||
|
</div>
|
||||||
|
<Card simple class="jyh-card mt-3">
|
||||||
|
<div class="clear-padding">
|
||||||
|
<d-table ref="table" :data="baseTreeTableData1">
|
||||||
|
<d-column width="20px" type="index"> </d-column>
|
||||||
|
<d-column field="name" header="软件版本名称" show-overflow-tooltip></d-column>
|
||||||
|
<d-column field="groupid" header="groupid"></d-column>
|
||||||
|
<d-column field="code" header="软件编号">
|
||||||
|
<template #default="scope">
|
||||||
|
<a class="devui-link" @click="showDetail = true">{{ scope.row.code }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="packageName" header="packageName"></d-column>
|
||||||
|
<d-column field="version" header="version"></d-column>
|
||||||
|
<d-column field="language" header="编程语言"></d-column>
|
||||||
|
<d-column field="number" header="软件漏洞数量">
|
||||||
|
<template #default="scope">
|
||||||
|
<a class="devui-link" @click="showDetail = true">{{ scope.row.number }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
</d-table>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<div class="w-full flex justify-end my-4">
|
||||||
|
<d-pagination
|
||||||
|
size="sm"
|
||||||
|
:total="pager.total"
|
||||||
|
v-model:pageSize="pager.pageSize"
|
||||||
|
v-model:pageIndex="pager.pageIndex"
|
||||||
|
:can-view-total="true"
|
||||||
|
:can-change-page-size="true"
|
||||||
|
:can-jump-page="true"
|
||||||
|
:max-items="5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import { getDepencyList } from '@/api/jyh';
|
||||||
|
const checked = ref(false);
|
||||||
|
const showDetail = ref(false);
|
||||||
|
const table = ref(null);
|
||||||
|
const isAllExpand = ref(false);
|
||||||
|
const pager = ref({
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: 30,
|
||||||
|
total: 10
|
||||||
|
});
|
||||||
|
const filterList = ref([
|
||||||
|
{ name: '001', value: '001' },
|
||||||
|
{ name: '002', value: '002' },
|
||||||
|
{ name: '003', value: '003' },
|
||||||
|
{ name: '004', value: '004' }
|
||||||
|
]);
|
||||||
|
const handleFilterChange = (val) => {
|
||||||
|
if (!val.length) {
|
||||||
|
baseTreeTableData1.value = [...originSource.value];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filterList = val.map((item) => item.value);
|
||||||
|
baseTreeTableData1.value = originSource.value.filter((item) => filterList.includes(item.code));
|
||||||
|
};
|
||||||
|
const originSource = ref([
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '001',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '20',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '001',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '20',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '001',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '20'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '001',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '20'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '002',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '10',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '003',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '20',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '003',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '11'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '004',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '30'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
name: 'ssl',
|
||||||
|
groupid: '2000000',
|
||||||
|
code: '001',
|
||||||
|
packageName: '包名称',
|
||||||
|
version: '202306',
|
||||||
|
language: 'java',
|
||||||
|
number: '5'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
const baseTreeTableData1 = ref([...originSource.value]);
|
||||||
|
|
||||||
|
const packageFilterList = ref(
|
||||||
|
originSource.value.map((item) => {
|
||||||
|
return { name: item.packageName, value: item.packageName };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const versionFilterList = ref(
|
||||||
|
originSource.value.map((item) => {
|
||||||
|
return { name: item.version, value: item.version };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const languageFilterList = ref(
|
||||||
|
originSource.value.map((item) => {
|
||||||
|
return { name: item.language, value: item.language };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const openAll = (tree) => {
|
||||||
|
if (!tree || !tree.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.forEach((node) => {
|
||||||
|
table.value.store.toggleRowExpansion(node);
|
||||||
|
if (node.children) {
|
||||||
|
openAll(node.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
isAllExpand.value = !isAllExpand.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortNumberMethod = (a, b) => {
|
||||||
|
return a.number > b.number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getList = async () => {
|
||||||
|
const res = await getDepencyList('APACHE-001');
|
||||||
|
};
|
||||||
|
getList();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import 'devui-theme/styles-var/devui-var.scss';
|
||||||
|
.clear-padding {
|
||||||
|
margin: -16px -20px;
|
||||||
|
}
|
||||||
|
:deep(.devui-table__cell) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.devui-table__tree-operate {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #e1e8fd;
|
||||||
|
img {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
padding-left: 18px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-line {
|
||||||
|
width: 1px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #dcdcdc; /* 设置竖线颜色 */
|
||||||
|
margin: 0 8px; /* 设置竖线与按钮的间距 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
151
src/views/Jyh/AI/Home/components/OssDetail/LicenseInfo/index.vue
Normal file
151
src/views/Jyh/AI/Home/components/OssDetail/LicenseInfo/index.vue
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<div class="license-info-container">
|
||||||
|
<div class="license-info-content">
|
||||||
|
<d-layout>
|
||||||
|
<d-content class="dcontent">
|
||||||
|
<d-table class="jyh-table" v-if="list.length > 0" :data="list" style="width: 100%">
|
||||||
|
<template #empty>
|
||||||
|
<NoData></NoData>
|
||||||
|
</template>
|
||||||
|
<d-column type="index" width="40"></d-column>
|
||||||
|
<d-column v-for="(column, index) in tableConfig" :field="column.field" :header="column.header"></d-column>
|
||||||
|
<d-column header="详情" width="100px">
|
||||||
|
<template #default="scope">
|
||||||
|
<a class="devui-link" @click="openPanel(scope.row)">查看</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
</d-table>
|
||||||
|
<NoData v-else :small="false"></NoData>
|
||||||
|
</d-content>
|
||||||
|
</d-layout>
|
||||||
|
</div>
|
||||||
|
<div class="license-info-container-page" v-if="list.length > 0">
|
||||||
|
<d-pagination
|
||||||
|
size="sm"
|
||||||
|
:total="pager.total"
|
||||||
|
v-model:pageSize="pager.pageSize"
|
||||||
|
v-model:pageIndex="pager.pageIndex"
|
||||||
|
:can-view-total="true"
|
||||||
|
:can-change-page-size="true"
|
||||||
|
:can-jump-page="true"
|
||||||
|
:max-items="5"
|
||||||
|
@page-index-change="getList"
|
||||||
|
@page-size-change="getList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<d-drawer v-model="showDetail" style="width: 800px; padding: 20px">
|
||||||
|
<SidePanel :data="currentData" @close="close" />
|
||||||
|
</d-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, onMounted, ref } from 'vue';
|
||||||
|
import SidePanel from '@/components/LicenseInfo/SidePanel.vue';
|
||||||
|
import { getLicenseList } from '@/api/jyh';
|
||||||
|
import NoData from '@/components/NoData/NoData.vue';
|
||||||
|
const currentData = ref(null);
|
||||||
|
const propsData = defineProps(['versionId']);
|
||||||
|
const pager = ref({
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
});
|
||||||
|
const pageOptions = ref({});
|
||||||
|
const showDetail = ref(false);
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
showDetail.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openPanel = (row) => {
|
||||||
|
currentData.value = row;
|
||||||
|
showDetail.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableConfig = ref([
|
||||||
|
{
|
||||||
|
field: 'licenseName',
|
||||||
|
header: '许可证名称',
|
||||||
|
sortable: true,
|
||||||
|
sortMethod: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'category',
|
||||||
|
header: '许可证种类',
|
||||||
|
sortable: false,
|
||||||
|
sortMethod: null
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// field: 'softwareName',
|
||||||
|
// header: '影响软件',
|
||||||
|
// sortable: true,
|
||||||
|
// sortMethod: null
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// field: 'softwareVersion',
|
||||||
|
// header: '版本',
|
||||||
|
// sortable: true,
|
||||||
|
// sortMethod: null
|
||||||
|
// }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const list = ref([]);
|
||||||
|
|
||||||
|
const getList = async () => {
|
||||||
|
const res: any = await getLicenseList({
|
||||||
|
softwareId: propsData.versionId,
|
||||||
|
componentId: '',
|
||||||
|
current: pager.value.pageIndex,
|
||||||
|
size: pager.value.pageSize
|
||||||
|
});
|
||||||
|
if (!res.error) {
|
||||||
|
console.log(res);
|
||||||
|
list.value = res.data.data.records;
|
||||||
|
pager.value.total = res.data.data.total;
|
||||||
|
} else {
|
||||||
|
list.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.license-info-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
.license-info-content {
|
||||||
|
background-color: $devui-global-bg-normal;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
&-page {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dheader {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dfooter {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
264
src/views/Jyh/AI/Home/components/OssDetail/Malware/Malware.vue
Normal file
264
src/views/Jyh/AI/Home/components/OssDetail/Malware/Malware.vue
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<d-search
|
||||||
|
v-model="searchText"
|
||||||
|
icon-position="left"
|
||||||
|
class="w-[400px] mb-[12px]"
|
||||||
|
placeholder="请输入搜索内容"
|
||||||
|
@search="getMalwareList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<d-table
|
||||||
|
:data="tableData"
|
||||||
|
:show-loading="searchLoading"
|
||||||
|
class="jyh-table"
|
||||||
|
:fix-header="tableData.length > 0"
|
||||||
|
max-height="600px"
|
||||||
|
@sort-change="sortChange"
|
||||||
|
>
|
||||||
|
<d-column type="index" width="48px" />
|
||||||
|
<!-- <d-column field="packageFullName" width="220px">-->
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 文件名称-->
|
||||||
|
<!-- <TableFilter field="packageFullName" :condition="filterData.packageFullName" @filter-change="filterChange" />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <template #default="{ row }">-->
|
||||||
|
<!-- <a class="devui-link" @click="openMalwareDetail(row)">{{ row.packageFullName }}</a>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<!-- <d-column field="category">-->
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 恶意类别-->
|
||||||
|
<!-- <TableFilter-->
|
||||||
|
<!-- type="radio"-->
|
||||||
|
<!-- field="category"-->
|
||||||
|
<!-- :condition="filterData.category"-->
|
||||||
|
<!-- :filter-list="categoryFilterOptions"-->
|
||||||
|
<!-- @filter-change="filterChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<d-column field="packageName" header="文件名称" width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<a class="devui-link" @click="openMalwareDetail(row)">{{ row.packageFullName }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="packagePath" header="文件路径" width="180" show-overflow-tooltip/>
|
||||||
|
<d-column field="packageVersion" header="文件版本号" show-overflow-tooltip/>
|
||||||
|
<d-column field="llmName" header="LLM名称" show-overflow-tooltip/>
|
||||||
|
<d-column field="totalApisCalledCount" header="敏感API总数" show-overflow-tooltip/>
|
||||||
|
<d-column field="suspiciousFilesCount" header="可疑文件总数" show-overflow-tooltip/>
|
||||||
|
<d-column field="isMalicious" header="是否恶意代码">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="card-val" v-if="scope.row.isMalicious===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="isModelResponded" header="大模型是否正常回复" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="card-val" v-if="scope.row.isModelResponded===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="createTime" header="检测时间" width="180" show-overflow-tooltip/>
|
||||||
|
<!-- <d-column field="threatLevel">-->
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 威胁级别-->
|
||||||
|
<!-- <TableFilter-->
|
||||||
|
<!-- type="radio"-->
|
||||||
|
<!-- field="threatLevel"-->
|
||||||
|
<!-- :condition="filterData.threatLevel"-->
|
||||||
|
<!-- :filter-list="threatLevelFilterList"-->
|
||||||
|
<!-- @filter-change="filterChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <template #default="{ row }">-->
|
||||||
|
<!-- <d-status :type="ThreatLevelMap[row.threatLevel].statusType">-->
|
||||||
|
<!-- {{ ThreatLevelMap[row.threatLevel].label }}-->
|
||||||
|
<!-- </d-status>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<!-- <d-column field="confidenceCoefficient">-->
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 置信度-->
|
||||||
|
<!-- <TableFilter-->
|
||||||
|
<!-- type="radio"-->
|
||||||
|
<!-- field="confidenceCoefficient"-->
|
||||||
|
<!-- :condition="filterData.confidenceCoefficient"-->
|
||||||
|
<!-- :filter-list="confidenceCoefficientFilterList"-->
|
||||||
|
<!-- @filter-change="filterChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <template #default="{ row }">-->
|
||||||
|
<!-- <d-tag size="sm" :type="ConfidenceCoefficientMap[row.confidenceCoefficient].statusType">-->
|
||||||
|
<!-- {{ ConfidenceCoefficientMap[row.confidenceCoefficient].label }}-->
|
||||||
|
<!-- </d-tag>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<!-- <d-column field="testResult" header="检测结果" />-->
|
||||||
|
<template #empty>
|
||||||
|
<NoData v-if="!searchLoading" />
|
||||||
|
</template>
|
||||||
|
</d-table>
|
||||||
|
|
||||||
|
<d-pagination
|
||||||
|
class="flex justify-end mt-[20px]"
|
||||||
|
:total="pager.total"
|
||||||
|
v-model:pageSize="pager.pageSize"
|
||||||
|
v-model:pageIndex="pager.pageIndex"
|
||||||
|
:can-view-total="true"
|
||||||
|
:can-change-page-size="true"
|
||||||
|
:max-items="5"
|
||||||
|
@page-index-change="pageIndexChange"
|
||||||
|
@page-size-change="pageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MalwareDetail ref="MalwareDetailRef" :detailRow="detailRow" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, shallowRef } from 'vue';
|
||||||
|
import TableFilter from '@/components/TableFilter/TableFilter.vue';
|
||||||
|
import NoData from '@/components/NoData/NoData.vue';
|
||||||
|
import MalwareDetail from '@/views/Jyh/Version/Detail/Malware/MalwareDetail.vue';
|
||||||
|
import { getMalwareListApi } from '@/api/inboundTask/inboundTaskApi';
|
||||||
|
import { type MalwareListItemType } from '@/api/inboundTask/inboundTaskType';
|
||||||
|
import { Message } from 'vue-devui/message';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
enum ThreatLevelEnum {
|
||||||
|
HIGH = 'high',
|
||||||
|
MD = 'medium',
|
||||||
|
LOW = 'low'
|
||||||
|
}
|
||||||
|
enum ConfidenceCoefficientEnum {
|
||||||
|
HIGH = 'high',
|
||||||
|
MD = 'medium',
|
||||||
|
LOW = 'low'
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const versionId = route.params.versionId;
|
||||||
|
const searchText = ref('');
|
||||||
|
const searchLoading = ref(false);
|
||||||
|
const MalwareDetailRef = ref<InstanceType<typeof MalwareDetail> | null>(null)
|
||||||
|
const tableData = shallowRef<MalwareListItemType[]>([]);
|
||||||
|
const pager = reactive({
|
||||||
|
pageSize: 10,
|
||||||
|
pageIndex: 1,
|
||||||
|
total: 0
|
||||||
|
});
|
||||||
|
const ThreatLevelMap = {
|
||||||
|
[ThreatLevelEnum.HIGH]: { label: '高危', statusType: 'error' },
|
||||||
|
[ThreatLevelEnum.MD]: { label: '中危', statusType: 'warning' },
|
||||||
|
[ThreatLevelEnum.LOW]: { label: '低危', statusType: 'waiting' }
|
||||||
|
};
|
||||||
|
const ConfidenceCoefficientMap = {
|
||||||
|
[ConfidenceCoefficientEnum.HIGH]: { label: '高', statusType: 'success' },
|
||||||
|
[ConfidenceCoefficientEnum.MD]: { label: '中', statusType: 'warning' },
|
||||||
|
[ConfidenceCoefficientEnum.LOW]: { label: '低', statusType: 'danger' }
|
||||||
|
};
|
||||||
|
const confidenceCoefficientFilterList = [
|
||||||
|
{ label: '高', value: ConfidenceCoefficientEnum.HIGH },
|
||||||
|
{ label: '中', value: ConfidenceCoefficientEnum.MD },
|
||||||
|
{ label: '低', value: ConfidenceCoefficientEnum.LOW }
|
||||||
|
];
|
||||||
|
const threatLevelFilterList = [
|
||||||
|
{ label: '高危', value: ThreatLevelEnum.HIGH },
|
||||||
|
{ label: '中危', value: ThreatLevelEnum.MD },
|
||||||
|
{ label: '低危', value: ThreatLevelEnum.LOW }
|
||||||
|
];
|
||||||
|
const categoryFilterOptions = [{ label: 'Python', value: 'Python' }];
|
||||||
|
const filterData = reactive({
|
||||||
|
packageFullName: '',
|
||||||
|
threatLevel: '',
|
||||||
|
confidenceCoefficient: '',
|
||||||
|
category: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const detailRow = shallowRef<MalwareListItemType | null>(null);
|
||||||
|
|
||||||
|
init();
|
||||||
|
function init() {
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMalwareList() {
|
||||||
|
const params = {
|
||||||
|
current: pager.pageIndex,
|
||||||
|
size: pager.pageSize,
|
||||||
|
taskId: '',
|
||||||
|
softwareId: versionId
|
||||||
|
};
|
||||||
|
pager.total = 0;
|
||||||
|
tableData.value = [];
|
||||||
|
searchLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getMalwareListApi(params);
|
||||||
|
if (res.data?.code === 200) {
|
||||||
|
tableData.value = res.data.data.records.map((item) => {
|
||||||
|
return { ...item, threatLevel: ThreatLevelEnum.HIGH, confidenceCoefficient: ConfidenceCoefficientEnum.HIGH };
|
||||||
|
});
|
||||||
|
pager.total = res.data.data.total;
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg || '查询恶意代码列表失败');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Message.error('接口错误,查询恶意代码列表失败');
|
||||||
|
}
|
||||||
|
searchLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortChange(sortInfo: { field: string; direction: 'ASC' | 'DESC' | '' }) {
|
||||||
|
console.log(sortInfo);
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterChange(filterInfo: { field: string; value: any }) {
|
||||||
|
filterData[filterInfo.field] = filterInfo.value;
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageSizeChange() {
|
||||||
|
pager.pageIndex = 1;
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageIndexChange() {
|
||||||
|
getMalwareList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMalwareDetail(row: MalwareListItemType) {
|
||||||
|
detailRow.value = row;
|
||||||
|
// 添加检查
|
||||||
|
if (MalwareDetailRef.value && typeof MalwareDetailRef.value.openDetail === 'function') {
|
||||||
|
MalwareDetailRef.value.openDetail(row.id)
|
||||||
|
} else {
|
||||||
|
console.error('MalwareDetailRef 或 openDetail 方法不可用')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.malware-list-table {
|
||||||
|
:deep(.devui-table__row td) {
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.devui-status.devui-status__bg--warning:before) {
|
||||||
|
background-color: #fcc800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
<template>
|
||||||
|
<d-drawer v-model="detailDrawerCtrl" class="w-[800px] p-[20px]">
|
||||||
|
<div class="flex flex-col h-full">
|
||||||
|
<div class="flex justify-between items-center mb-[12px]">
|
||||||
|
<p class="text-[18px]/[26px] ">恶意代码详情</p>
|
||||||
|
<d-icon @click="detailDrawerCtrl = false" name="icon-close" size="18px" class="cursor-pointer text-[#71757F]" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col mb-[12px] flex-1 h-0">
|
||||||
|
<div class="malware-detail-card mb-[16px]">
|
||||||
|
<p class="title">文件基本信息</p>
|
||||||
|
<div class="content">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">文件名称</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.packageFullName || '--' }}</div>
|
||||||
|
<d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(props.detailRow?.packageFullName)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">文件路径</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.packagePath || '--' }}</div>
|
||||||
|
<d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(props.detailRow?.packagePath)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">文件版本号</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.packageVersion || '--' }}</div>
|
||||||
|
<d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(props.detailRow?.packageVersion)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">LLM名称</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.llmName || '--' }}</div>
|
||||||
|
<d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(props.detailRow?.llmName)" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">敏感API总数</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.totalApisCalledCount }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">可疑文件总数</div>
|
||||||
|
<div class="single-line-ellipsis">{{ props.detailRow?.suspiciousFilesCount }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">是否恶意代码</div>
|
||||||
|
<div class="single-line-ellipsis">
|
||||||
|
<span class="card-val" v-if="props.detailRow?.isMalicious===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">大模型是否正常回复</div>
|
||||||
|
<div class="single-line-ellipsis">
|
||||||
|
<span class="card-val" v-if="props.detailRow?.isModelResponded===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="flex items-center">-->
|
||||||
|
<!-- <div class="w-[160px] pr-[20px] flex-shrink-0 text-[#595959]">文件sha256</div>-->
|
||||||
|
<!-- <div :title="malwareDetails?.code" class="single-line-ellipsis">{{ malwareDetails?.code }}</div>-->
|
||||||
|
<!-- <d-icon name="icon-copy" class="ml-[8px] cursor-pointer" @click="handleCopy(malwareDetails?.code)" />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="malware-detail-card mb-[16px]">
|
||||||
|
<p class="title">调用的敏感API列表</p>
|
||||||
|
<div class="content">
|
||||||
|
<d-table
|
||||||
|
:data="malwareDetails?.apisCalled"
|
||||||
|
class="w-full bg-white malware-list-table"
|
||||||
|
:fix-header="malwareDetails?.apisCalled.length > 0"
|
||||||
|
|
||||||
|
max-height="300px"
|
||||||
|
>
|
||||||
|
<d-column field="isMalicious" header="是否恶意代码">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="card-val" v-if="scope.row.isMalicious===0">
|
||||||
|
<d-tag color="#5e7ce0" size="sm">否</d-tag>
|
||||||
|
</span>
|
||||||
|
<span class="card-val" v-else>
|
||||||
|
<d-tag color="#c7000b" size="sm">是</d-tag>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column
|
||||||
|
field="maliciousCodeCategory"
|
||||||
|
header="恶意代码分类"
|
||||||
|
/>
|
||||||
|
<template #empty>
|
||||||
|
<NoData style="padding: 0; margin: -30px 0;" :width="80" />
|
||||||
|
</template>
|
||||||
|
</d-table>
|
||||||
|
|
||||||
|
<!-- <d-pagination-->
|
||||||
|
<!-- class="flex justify-end mt-[20px]"-->
|
||||||
|
<!-- :total="pager.total"-->
|
||||||
|
<!-- v-model:pageSize="pager.pageSize"-->
|
||||||
|
<!-- v-model:pageIndex="pager.pageIndex"-->
|
||||||
|
<!-- :can-view-total="true"-->
|
||||||
|
<!-- :can-change-page-size="true"-->
|
||||||
|
<!-- :max-items="5"-->
|
||||||
|
<!-- @page-index-change="pageIndexChange"-->
|
||||||
|
<!-- @page-size-change="pageSizeChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="malware-detail-card mb-[16px]">
|
||||||
|
<p class="title">包含恶意代码的文件列表</p>
|
||||||
|
<div class="content">
|
||||||
|
<d-table
|
||||||
|
:data="malwareDetails?.source"
|
||||||
|
class="w-full bg-white malware-list-table"
|
||||||
|
:fix-header="malwareDetails?.source.length > 0"
|
||||||
|
|
||||||
|
max-height="300px"
|
||||||
|
>
|
||||||
|
<d-column
|
||||||
|
field="sink"
|
||||||
|
header="恶意汇"
|
||||||
|
/>
|
||||||
|
<d-column
|
||||||
|
field="flow"
|
||||||
|
header="传播流向"
|
||||||
|
/>
|
||||||
|
<d-column
|
||||||
|
field="purpose"
|
||||||
|
header="代码目的"
|
||||||
|
/>
|
||||||
|
<d-column
|
||||||
|
field="analysis"
|
||||||
|
header="LLM对于这个代码片段的分析"
|
||||||
|
/>
|
||||||
|
<d-column
|
||||||
|
field="createTime"
|
||||||
|
header="创建时间"
|
||||||
|
/>
|
||||||
|
<template #empty>
|
||||||
|
<NoData style="padding: 0; margin: -30px 0;" :width="80" />
|
||||||
|
</template>
|
||||||
|
</d-table>
|
||||||
|
|
||||||
|
<!-- <d-pagination-->
|
||||||
|
<!-- class="flex justify-end mt-[20px]"-->
|
||||||
|
<!-- :total="pager.total"-->
|
||||||
|
<!-- v-model:pageSize="pager.pageSize"-->
|
||||||
|
<!-- v-model:pageIndex="pager.pageIndex"-->
|
||||||
|
<!-- :can-view-total="true"-->
|
||||||
|
<!-- :can-change-page-size="true"-->
|
||||||
|
<!-- :max-items="5"-->
|
||||||
|
<!-- @page-index-change="pageIndexChange"-->
|
||||||
|
<!-- @page-size-change="pageSizeChange"-->
|
||||||
|
<!-- />-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="malware-detail-card flex-1 h-0">-->
|
||||||
|
<!-- <p class="title">关键日志</p>-->
|
||||||
|
<!-- <div class="content flex flex-col flex-1 h-0">-->
|
||||||
|
<!-- <div class="log-box flex-1 overflow-y-scroll">-->
|
||||||
|
<!-- {{ malwareDetails?.analysis }}-->
|
||||||
|
<!-- {{ malwareDetails?.code }}-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</d-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, shallowRef, defineProps, ref, defineExpose } from 'vue';
|
||||||
|
import NoData from "@/components/NoData/NoData.vue";
|
||||||
|
import { getMalwareDetailApi, getMalwareDetailApi2 } from '@/api/inboundTask/inboundTaskApi';
|
||||||
|
import {Message} from "vue-devui/message";
|
||||||
|
import { type MalwareDetailType } from "@/api/inboundTask/inboundTaskType";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
detailRow: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const detailDrawerCtrl = ref(false);
|
||||||
|
const malwareDetails = shallowRef<MalwareDetailType | null>(null);
|
||||||
|
const threatContentList = shallowRef([]);
|
||||||
|
const pager = reactive({
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getMalwareDetails(id) {
|
||||||
|
try {
|
||||||
|
const res = await getMalwareDetailApi2({
|
||||||
|
current: 1,
|
||||||
|
size: 10,
|
||||||
|
maliciousId: id
|
||||||
|
});
|
||||||
|
if(res.data.records&&res.data.records.length>0) {
|
||||||
|
malwareDetails.value = res.data.records[0];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Message.error('接口错误,查询详情失败');
|
||||||
|
}
|
||||||
|
getCurrentPageData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentPageData() {
|
||||||
|
// pager.total = malwareDetails.value.contentInfo.total;
|
||||||
|
// const endIndex = pager.pageIndex * pager.pageSize;
|
||||||
|
// threatContentList.value = malwareDetails.value.contentInfo.list.slice(endIndex - pager.pageSize, endIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageIndexChange() {
|
||||||
|
getCurrentPageData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageSizeChange() {
|
||||||
|
pager.pageIndex = 1;
|
||||||
|
getCurrentPageData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCopy(text: string) {
|
||||||
|
inputCopy(text);
|
||||||
|
}
|
||||||
|
const openDetail = (id) => {
|
||||||
|
detailDrawerCtrl.value = true;
|
||||||
|
getMalwareDetails(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputCopy(text: string) {
|
||||||
|
const aux = document.createElement('input');
|
||||||
|
aux.setAttribute('value', text);
|
||||||
|
document.body.appendChild(aux);
|
||||||
|
aux.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(aux);
|
||||||
|
Message({ type: 'success', message: '复制成功' });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 暴露方法,供父组件调用
|
||||||
|
defineExpose({
|
||||||
|
openDetail,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.malware-detail-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #EEF0F5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.title {
|
||||||
|
color: #191919;
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
background-color: #F3F3F3;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 20px 16px;
|
||||||
|
:deep(.devui-table__thead .header-container) {
|
||||||
|
background-color: #F3F3F3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.log-box {
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #F7F8FA;
|
||||||
|
border: 1px solid #EEF0F5;
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
.single-line-ellipsis {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
128
src/views/Jyh/AI/Home/components/OssDetail/SBOM.vue
Normal file
128
src/views/Jyh/AI/Home/components/OssDetail/SBOM.vue
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex align-center justify-start">
|
||||||
|
<d-search icon-position="left" style="width: 380px" placeholder="请输入搜索软件名称" v-model="searchText" @change="handleSearch"></d-search>
|
||||||
|
<d-checkbox class="ml-10" label="只显示有漏洞的软件" :isShowTitle="false" v-model="checked" />
|
||||||
|
</div>
|
||||||
|
<Card simple class="jyh-card mt-3">
|
||||||
|
<div class="clear-padding">
|
||||||
|
<d-table
|
||||||
|
class="jyh-table"
|
||||||
|
v-if="filteredTableData.length > 0"
|
||||||
|
:indent="32"
|
||||||
|
@check-change="treeCheckChange"
|
||||||
|
:data="filteredTableData"
|
||||||
|
row-key="id"
|
||||||
|
>
|
||||||
|
<d-column field="libraryName" header="组件名称" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<a class="devui-link" @click="setSelectRow(scope.row)">{{ scope.row.libraryName }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="latestVersion" header="版本"></d-column>
|
||||||
|
</d-table>
|
||||||
|
<NoData v-else :small="false"></NoData>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<d-drawer v-model="showDetail" style="width: 800px; padding: 20px">
|
||||||
|
<SBOMDetail :selectRow="selectRow" @close="showDetail = false" />
|
||||||
|
</d-drawer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, defineProps, computed } from 'vue';
|
||||||
|
import SBOMDetail from './SBOMDetail.vue';
|
||||||
|
import NoData from '@/components/NoData/NoData.vue';
|
||||||
|
import { dependencyTree, getVulnList } from '@/api/jyh';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
const propsData = defineProps(['versionId']);
|
||||||
|
const checked = ref(false);
|
||||||
|
const route = useRoute(); // 获取路由对象
|
||||||
|
const showDetail = ref(false);
|
||||||
|
const selectRow = ref({});
|
||||||
|
|
||||||
|
const tableData = ref([]);
|
||||||
|
const searchText = ref(''); // 搜索关键词
|
||||||
|
|
||||||
|
const setSelectRow = (row) => {
|
||||||
|
selectRow.value = row;
|
||||||
|
showDetail.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredTableData = computed(() => {
|
||||||
|
if (!searchText.value) return tableData.value; // 无关键词时返回全部数据
|
||||||
|
return tableData.value.filter(item =>
|
||||||
|
item.libraryName.toLowerCase().includes(searchText.value.toLowerCase())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function generateUUID() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
const r = Math.random() * 16 | 0;
|
||||||
|
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const renameComponentsToChildren = (obj) => {
|
||||||
|
// 如果传入的不是对象,直接返回
|
||||||
|
if (typeof obj !== 'object' || obj === null) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果对象有 `components` 属性,将其改为 `children`
|
||||||
|
if (obj.hasOwnProperty('component')) {
|
||||||
|
// 生成随机 UUID 并赋值给 id
|
||||||
|
obj.id = generateUUID(); // 使用浏览器/Node.js 原生 API
|
||||||
|
// obj.id = obj.component.id; // 将 components 改为 children
|
||||||
|
obj.libraryName = obj.component.libraryName; // 将 components 改为 children
|
||||||
|
obj.latestVersion = obj.component.latestVersion; // 将 components 改为 children
|
||||||
|
// delete obj.components; // 删除原来的 components 属性
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归处理对象的子元素
|
||||||
|
for (let key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) {
|
||||||
|
obj[key] = renameComponentsToChildren(obj[key]); // 递归处理子对象
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
const treeCheckChange = (val, row, selection) => {
|
||||||
|
console.log('treeCheckChange', selection);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchDependencyTree = async () => {
|
||||||
|
const { data } = await dependencyTree({
|
||||||
|
// softwareId: 47,
|
||||||
|
softwareId: propsData.versionId,
|
||||||
|
componentId: '',
|
||||||
|
current: 1,
|
||||||
|
size: 10
|
||||||
|
});
|
||||||
|
if (data.data.code === 200 && data?.data?.data?.components) {
|
||||||
|
tableData.value = renameComponentsToChildren(data?.data?.data?.components);
|
||||||
|
// pager.value.total = data?.data?.data?.total || [];
|
||||||
|
} else {
|
||||||
|
// tableData.value = baseTreeTableData1.value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchDependencyTree();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.clear-padding {
|
||||||
|
margin: -16px -20px;
|
||||||
|
}
|
||||||
|
:deep(.devui-table__cell) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.devui-table__tree-operate {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
186
src/views/Jyh/AI/Home/components/OssDetail/SBOMDetail.vue
Normal file
186
src/views/Jyh/AI/Home/components/OssDetail/SBOMDetail.vue
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<template>
|
||||||
|
<d-layout class="dlayout">
|
||||||
|
<d-header class="dheader">
|
||||||
|
<div class="title">
|
||||||
|
<h1>{{props.selectRow.component.libraryName || '--'}}</h1>
|
||||||
|
<d-icon style="cursor: pointer" name="icon-close" @click="emit('close')"></d-icon>
|
||||||
|
</div>
|
||||||
|
<div class="sub-title">
|
||||||
|
<!-- <span>安全漏洞:</span>
|
||||||
|
<span class="val">{{props.selectRow.component.confidence || 'low'}}</span> -->
|
||||||
|
<!-- <span class="split"></span> -->
|
||||||
|
<!-- <span>许可证</span>-->
|
||||||
|
<!-- <d-tag color="#3ac295" size="sm" class="ml-2">{{props.selectRow.component.licenseName || '--'}}</d-tag>-->
|
||||||
|
</div>
|
||||||
|
</d-header>
|
||||||
|
<!-- <div class="card-box">-->
|
||||||
|
<!-- <div class="card-body">-->
|
||||||
|
<!-- <d-row :gutter="10">-->
|
||||||
|
<!-- <d-col :span="24" class="flex">-->
|
||||||
|
<!-- <label style="flex: 0.2">简介</label>-->
|
||||||
|
<!-- <p>{{props.selectRow.component.libraryName || '--'}}</p>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
<!-- </d-row>-->
|
||||||
|
<!-- <!– <d-row :gutter="10">-->
|
||||||
|
<!-- <d-col :span="24" class="flex">-->
|
||||||
|
<!-- <label style="flex: 0.2">关联漏洞编号</label>-->
|
||||||
|
<!-- <p>{{props.selectRow.component.cveId || `cve-2025-${randomNum()}`}}</p>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
<!-- </d-row> –>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<div class="card-box">
|
||||||
|
<div class="card-header">组件详情</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<d-row :gutter="10">
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>当前版本</label>
|
||||||
|
<p>{{props.selectRow.component.currentVersion || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>最新版本</label>
|
||||||
|
<p>{{props.selectRow.component.latestVersion || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row :gutter="10">
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>深度</label>
|
||||||
|
<p>{{props.selectRow.component.depth || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>供应商</label>
|
||||||
|
<p>{{props.selectRow.component.vendor || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row :gutter="10">
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>包管理器平台</label>
|
||||||
|
<p>{{props.selectRow.component.platform || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>范围</label>
|
||||||
|
<p>{{props.selectRow.component.scope || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row :gutter="10">
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>程序语言</label>
|
||||||
|
<p>{{props.selectRow.component.language || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>活跃度</label>
|
||||||
|
<p>{{props.selectRow.component.activityScore || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row :gutter="10">
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>下载量</label>
|
||||||
|
<p>{{props.selectRow.component.downloadCount || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>识别方法</label>
|
||||||
|
<p>{{props.selectRow.component.recogMethod || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row :gutter="10">
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>匹配度</label>
|
||||||
|
<p>{{props.selectRow.component.matchingDegree || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12" class="flex">
|
||||||
|
<label>组件依赖关系</label>
|
||||||
|
<p>{{props.selectRow.component.treeLevel || '--'}}</p>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<!-- <d-row :gutter="10">-->
|
||||||
|
<!-- <d-col :span="12" class="flex">-->
|
||||||
|
<!-- <label>生命周期终点/服务终止</label>-->
|
||||||
|
<!-- <p><d-tag type="danger" size="sm">已停止维护</d-tag></p>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
<!-- </d-row>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</d-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, defineEmits, defineProps } from 'vue';
|
||||||
|
const props = defineProps(['selectRow']);
|
||||||
|
console.log(props.selectRow)
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
|
// 生成随机数的方法
|
||||||
|
const randomNum = () => {
|
||||||
|
return Math.floor(Math.random() * (99999 - 10000 + 1)) + 10000;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import 'devui-theme/styles-var/devui-var.scss';
|
||||||
|
|
||||||
|
.dlayout {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: start;
|
||||||
|
.dheader {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
d-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
color: #808080;
|
||||||
|
.val {
|
||||||
|
color: var(--devui-text);
|
||||||
|
}
|
||||||
|
.split {
|
||||||
|
width: 1px;
|
||||||
|
height: 14px;
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-box {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #eef0f5;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 15px;
|
||||||
|
.card-header {
|
||||||
|
padding: 10px 16px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-bottom: 1px solid #eef0f5;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.card-body {
|
||||||
|
margin: 10px 16px;
|
||||||
|
.flex {
|
||||||
|
margin: 10px 0;
|
||||||
|
label,
|
||||||
|
p {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
color: #595959;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
102
src/views/Jyh/AI/Home/components/OssDetail/SubscriptionModal.vue
Normal file
102
src/views/Jyh/AI/Home/components/OssDetail/SubscriptionModal.vue
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<div class="subscribe-container">
|
||||||
|
<div class="subscribe-tip mb-3">当您订阅的软件有漏洞预警通知时,将以应用号的方式通知。可在首页查看或取消订阅!</div>
|
||||||
|
<div class="subscribe-form">
|
||||||
|
<d-form ref="formRef" :data="formData" :rules="rules">
|
||||||
|
<d-form-item field="subscribeType" label="通知方式">
|
||||||
|
<d-checkbox-group name="subscribeType" v-model="formData.subscribeType" :options="subscribeTypeOptions" direction="row"></d-checkbox-group>
|
||||||
|
</d-form-item>
|
||||||
|
</d-form>
|
||||||
|
</div>
|
||||||
|
<div class="subscribe-operation mt-5">
|
||||||
|
<d-button @click="confirm" variant="solid" class="mr-2">确定</d-button>
|
||||||
|
<d-button @click="$emit('close')">取消</d-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { subscribeSoftware } from '@/api/jyh';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { Message } from 'vue-devui';
|
||||||
|
const subscribeTypeOptions = [
|
||||||
|
{ name: '短信', value: 'mobileSwitch', id: 1 },
|
||||||
|
{ name: '邮件', value: 'emailSwitch', id: 2 },
|
||||||
|
{ name: '站内通知', value: 'webSwitch', id: 3 },
|
||||||
|
];
|
||||||
|
const props = defineProps(['softwareData']);
|
||||||
|
const emit = defineEmits(['close','closeSub']);
|
||||||
|
const formRef = ref(null);
|
||||||
|
const formData = ref({
|
||||||
|
subscribeType: [],
|
||||||
|
});
|
||||||
|
const rules = {
|
||||||
|
subscribeType: [{ type: 'array', required: true, message: '请至少选择一种通知方式', trigger: 'change' }],
|
||||||
|
};
|
||||||
|
const sendSubscribe = async () => {
|
||||||
|
let alertType = {};
|
||||||
|
formData.value.subscribeType.forEach(item => {
|
||||||
|
alertType[item.value] = true;
|
||||||
|
})
|
||||||
|
const data = await subscribeSoftware({
|
||||||
|
softwareId: props.softwareData.id,
|
||||||
|
status: '1',
|
||||||
|
alertType,
|
||||||
|
}).catch(err => {
|
||||||
|
Message({
|
||||||
|
message: err.error,
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (!data?.data?.data?.data) {
|
||||||
|
Message({
|
||||||
|
message: data?.data?.data?.msg,
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Message({
|
||||||
|
message: '订阅成功!',
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
emit('close');
|
||||||
|
emit('closeSub');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const confirm = () => {
|
||||||
|
(formRef?.value as any)?.validate((isValid: boolean, invalidFields: any) => {
|
||||||
|
if (!isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendSubscribe();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.subscribe-container {
|
||||||
|
width: 600px;
|
||||||
|
|
||||||
|
.subscribe-tip {
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscribe-form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #F2F5FC;
|
||||||
|
padding: 20px 32px 0;
|
||||||
|
|
||||||
|
form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscribe-operation {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
518
src/views/Jyh/AI/Home/components/OssDetail/VersionInfo.vue
Normal file
518
src/views/Jyh/AI/Home/components/OssDetail/VersionInfo.vue
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
<template>
|
||||||
|
<div class="version-info">
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">基本信息
|
||||||
|
<i @click="toggleCard('basic')" :class="isExpanded.basic ? 'icon icon-chevron-up-2 float-right': 'icon icon-chevron-down-2 float-right'"></i>
|
||||||
|
</div>
|
||||||
|
<div v-if="isExpanded.basic">
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-90">软件名称</span>
|
||||||
|
<span class="card-val">{{ ReleaseInfo.basicInfo.softwareName || '--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-90">软件版本</span>
|
||||||
|
<span class="card-val">{{ ReleaseInfo.basicInfo.softwareVersion || '--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-90">主语言类型</span>
|
||||||
|
<span class="card-val">{{ ReleaseInfo.basicInfo.mainLanguageType || '--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row>
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-90">开发商</span>
|
||||||
|
<span class="card-val">{{ ReleaseInfo.basicInfo.developer || '--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-90">发布时间</span>
|
||||||
|
<span class="card-val">
|
||||||
|
{{ ReleaseInfo.basicInfo.releaseDate?dayjs(ReleaseInfo.basicInfo.releaseDate).format('YYYY-MM-DD') :'--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
<!-- <d-col :span="8">-->
|
||||||
|
<!-- <span class="card-key width-90">版本描述</span>-->
|
||||||
|
<!-- <span class="card-val">{{ ReleaseInfo.basicInfo.versionDescription || '--' }}</span>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
</d-row>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">参考信息
|
||||||
|
<i @click="toggleCard('reference')" :class="isExpanded.reference ? 'icon icon-chevron-up-2 float-right': 'icon icon-chevron-down-2 float-right'"></i>
|
||||||
|
</div>
|
||||||
|
<div v-if="isExpanded.reference">
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-110">集成风险</span>
|
||||||
|
<d-tag color="#F6933B">{{ ReleaseInfo.referenceInfo.integrationRisk || jcfxList[ReleaseInfo.basicInfo.integrationRiskLevel] || '--' }}</d-tag>
|
||||||
|
<!-- <span class="card-val">{{ ReleaseInfo.referenceInfo.integrationRisk || ReleaseInfo.basicInfo.integrationRisk || '--' }}</span>-->
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-110">代码量(L)</span>
|
||||||
|
<span class="card-val">{{ ReleaseInfo.referenceInfo.codeQuantity || '--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-110">漏洞数量</span>
|
||||||
|
<a style="cursor: pointer" @click="handleTabChange('ldxx')" class="card-url">
|
||||||
|
{{ ReleaseInfo.referenceInfo.vulnerabilityCount || '--' }}
|
||||||
|
</a>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="8">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-110">社区EOL日期</span>
|
||||||
|
<span class="card-val">
|
||||||
|
{{ ReleaseInfo.criticalSoftwareAIGenerateInfo.communityEolDate?dayjs(ReleaseInfo.criticalSoftwareAIGenerateInfo.communityEolDate).format('YYYY-MM-DD') :'--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="8">
|
||||||
|
<span class="card-key width-110">所属领域/产业</span>
|
||||||
|
<span class="card-val">{{ getIndustryName(ReleaseInfo.referenceInfo.industry) || '--' }}</span>
|
||||||
|
</d-col>
|
||||||
|
<!-- <d-col :span="8">-->
|
||||||
|
<!-- <span class="card-key width-110">版本火车名称</span>-->
|
||||||
|
<!-- <span class="card-val">CleanSource V8.0</span>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-140">源码下载包地址</span>
|
||||||
|
<a class="card-url" :href="ReleaseInfo.referenceInfo.sourceCodeDownloadUrl || '--'">{{ ReleaseInfo.referenceInfo.sourceCodeDownloadUrl || ReleaseInfo.basicInfo.sourceDownloadUrl || '--' }}</a>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-140">源码社区托管地址</span>
|
||||||
|
<a class="card-url" :href="ReleaseInfo.referenceInfo.sourceCodeCommunityUrl || '--'">{{ ReleaseInfo.referenceInfo.sourceCodeCommunityUrl || ReleaseInfo.basicInfo.communityWebsite || '--' }}</a>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<!-- <d-row>-->
|
||||||
|
<!-- <d-col :span="24">-->
|
||||||
|
<!-- <span class="card-key width-140">社区EOL披露地址</span>-->
|
||||||
|
<!-- <a class="card-url" :href="ReleaseInfo.referenceInfo.communityEolDisclosureUrl || '--'">{{ ReleaseInfo.referenceInfo.communityEolDisclosureUrl || ReleaseInfo.basicInfo.eolNoticeUrl || '--' }}</a>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
<!-- </d-row>-->
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
开源主License信息
|
||||||
|
<i @click="toggleCard('license')" :class="isExpanded.license ? 'icon icon-chevron-up-2 float-right': 'icon icon-chevron-down-2 float-right'"></i>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div v-if="isExpanded.license" class="mb-3">
|
||||||
|
<d-table class="jyh-table jyh-table-2" :header-bg="true"
|
||||||
|
:data="[
|
||||||
|
{ name: ReleaseInfo.basicInfo.mainLicenseName || ReleaseInfo.criticalSoftwareAIGenerateInfo.mainLicenseName,
|
||||||
|
description: ReleaseInfo.basicInfo.mainLicenseDescription || ReleaseInfo.criticalSoftwareAIGenerateInfo.mainLicenseDescription}
|
||||||
|
]">
|
||||||
|
<d-column type="index" header="序号" width="80"></d-column>
|
||||||
|
<!-- <d-column field="id" header="编号"></d-column>-->
|
||||||
|
<d-column field="name" header="名称"></d-column>
|
||||||
|
<d-column field="description" header="描述"></d-column>
|
||||||
|
<!-- <d-column field="applicationScenario" header="应用场景"></d-column>-->
|
||||||
|
</d-table>
|
||||||
|
</div>
|
||||||
|
<!-- <d-row>-->
|
||||||
|
<!-- <d-col :span="24">-->
|
||||||
|
<!-- <span class="card-key width-140">主License分析报告</span>-->
|
||||||
|
<!-- <a class="card-url" href="https://designcloud.uiplus.huawei.com/developer/main/index.html">https://designcloud.uiplus.huawei.com/developer/main/index.html</a>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
<!-- </d-row>-->
|
||||||
|
</Card>
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
Copyright报告
|
||||||
|
<i @click="toggleCard('copyright')" :class="isExpanded.copyright ? 'icon icon-chevron-up-2 float-right': 'icon icon-chevron-down-2 float-right'"></i>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div v-if="isExpanded.copyright" class="mb-3">
|
||||||
|
<d-table
|
||||||
|
class="jyh-table jyh-table-2"
|
||||||
|
:header-bg="true"
|
||||||
|
:data="[
|
||||||
|
{
|
||||||
|
reportName: ReleaseInfo.basicInfo.copyrightReportName || ReleaseInfo.criticalSoftwareAIGenerateInfo.copyrightReportName,
|
||||||
|
reportUrl: ReleaseInfo.basicInfo.copyrightReportUrl || ReleaseInfo.criticalSoftwareAIGenerateInfo.copyrightReportUrl,
|
||||||
|
entryTime: ReleaseInfo.basicInfo.copyrightEntryTime || ReleaseInfo.criticalSoftwareAIGenerateInfo.copyrightEntryTime
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<d-column field="reportName" header="Copyright报告">
|
||||||
|
<template #default="scope">
|
||||||
|
<a :href="scope.row.reportUrl">{{ scope.row.reportName }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="entryTime" header="录入时间"></d-column>
|
||||||
|
</d-table>
|
||||||
|
</div>
|
||||||
|
<!-- <d-row>-->
|
||||||
|
<!-- <d-col :span="24">-->
|
||||||
|
<!-- <span class="card-key width-140">主License分析报告</span>-->
|
||||||
|
<!-- <a class="card-url" href="https://designcloud.uiplus.huawei.com/developer/main/index.html">https://designcloud.uiplus.huawei.com/developer/main/index.html</a>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
<!-- </d-row>-->
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">实体
|
||||||
|
<i @click="toggleCard('entity')" :class="isExpanded.entity ? 'icon icon-chevron-up-2 float-right': 'icon icon-chevron-down-2 float-right'"></i>
|
||||||
|
</div>
|
||||||
|
<div v-if="isExpanded.entity">
|
||||||
|
<d-table
|
||||||
|
class="jyh-table jyh-table-2"
|
||||||
|
:header-bg="true"
|
||||||
|
:data="ReleaseInfo.entityInfoList"
|
||||||
|
>
|
||||||
|
<d-column field="entityType" header="类型"></d-column>
|
||||||
|
<d-column field="entityDownloadUrl" header="实体包下载地址">
|
||||||
|
<template #default="scope">
|
||||||
|
<a :href="scope.row.entityDownloadUrl" target="_blank">{{ scope.row.entityDownloadUrl }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="entityFilename" header="文件名称"></d-column>
|
||||||
|
<d-column field="entryTime" header="录入时间"></d-column>
|
||||||
|
<d-column field="integrityCheckResult" header="完整性结果">
|
||||||
|
<template #default="scope">
|
||||||
|
<a @click="visible = true">{{ scope.row.integrityCheckResult }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
</d-table>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
网络安全
|
||||||
|
<i @click="toggleCard('intSafe')" :class="isExpanded.intSafe ? 'icon icon-chevron-up-2 float-right': 'icon icon-chevron-down-2 float-right'"></i>
|
||||||
|
<!-- <span style="color: #3b82f6;font-size: 12px;font-weight: normal">(该数据由AI生成)</span>-->
|
||||||
|
</div>
|
||||||
|
<div v-if="isExpanded.intSafe">
|
||||||
|
<!-- <d-row class="mb-3">-->
|
||||||
|
<!-- <d-col :span="24">-->
|
||||||
|
<!-- <span class="card-key width-160">软件评分</span>-->
|
||||||
|
<!-- <span class="card-val">{{ ReleaseInfo.networkSecurity.softwareRating || '--' }}</span>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
<!-- </d-row>-->
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-160">社区安全问题求助</span>
|
||||||
|
<a class="card-url" :href="ReleaseInfo.basicInfo.securityHelpChannel || ReleaseInfo.criticalSoftwareAIGenerateInfo.securityHelpChannel || ''" target="_blank">{{
|
||||||
|
ReleaseInfo.basicInfo.securityHelpChannel || ReleaseInfo.criticalSoftwareAIGenerateInfo.securityHelpChannel || '--'
|
||||||
|
}}</a>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<div class="mb-3 flex">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-160">官网漏洞披露地址</span>
|
||||||
|
<d-table style="display: contents" class="jyh-table jyh-table-2" :header-bg="true" :data="vulnDisclosureUrl">
|
||||||
|
<!-- <d-column field="id" header="编号"></d-column> -->
|
||||||
|
<d-column field="厂商" header="厂商"></d-column>
|
||||||
|
<d-column field="软件名称" header="软件名称"></d-column>
|
||||||
|
<!-- <d-column field="source" header="来源"></d-column> -->
|
||||||
|
<d-column field="软件地址" header="来源url"> </d-column>
|
||||||
|
</d-table>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 flex">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-160">外部漏洞源的软件名称</span>
|
||||||
|
<d-table
|
||||||
|
style="display: contents"
|
||||||
|
class="jyh-table jyh-table-2"
|
||||||
|
:header-bg="true"
|
||||||
|
:data="externalVulnSourceInfo"
|
||||||
|
>
|
||||||
|
<!-- <d-column field="id" header="编号"></d-column> -->
|
||||||
|
<d-column field="厂商" header="厂商"></d-column>
|
||||||
|
<d-column field="软件名称" header="软件名称"></d-column>
|
||||||
|
<!-- <d-column field="source" header="来源"></d-column> -->
|
||||||
|
<d-column field="软件地址" header="来源url"> </d-column>
|
||||||
|
</d-table>
|
||||||
|
</div>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-160">是否涉及用户登录</span>
|
||||||
|
<span class="card-val">{{ securityInfoList.involvesUserLogin ? '是' : '否' }}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row>
|
||||||
|
<d-col :span="24">
|
||||||
|
<d-tooltip position="left" content="此内容由AI辅助生成">
|
||||||
|
<img
|
||||||
|
style="width: 14px; height: 14px;display: inline-block"
|
||||||
|
src="@/assets/imgs/jyh/aiLogo.png"
|
||||||
|
/>
|
||||||
|
</d-tooltip>
|
||||||
|
<span class="card-key width-160">是否安卓类APP组件</span>
|
||||||
|
<span class="card-val">{{ securityInfoList.isAndroidComponent ? '是' : '否' }}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<d-modal
|
||||||
|
title="完整性结果详情"
|
||||||
|
v-model="visible"
|
||||||
|
class="file-search-modal"
|
||||||
|
:style="{ width: '800px', height: '400px' }"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<Card simple class="jyh-card mb-4" style="box-shadow: none; padding: 0">
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-160">社区哈希类型</span>
|
||||||
|
<span class="card-val">sha512</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-160">社区哈希值</span>
|
||||||
|
<span class="card-val">sha5123b831bee9d575a2729065f830a65f830a65f83a65f852...</span>
|
||||||
|
<d-tooltip content="复制" :mouse-enter-delay="800">
|
||||||
|
<Icon name="icon-copy" class="ml-10 w-4 flex-shrink-0" color="#9FA7B3" @click="handleCopy" />
|
||||||
|
</d-tooltip>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-160">社区签名校验结果</span>
|
||||||
|
<span class="card-val card-success">通过</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-160">社区签名</span>
|
||||||
|
<span class="card-val">确认无社区签名</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-160">社区签名下载地址</span>
|
||||||
|
<span class="card-val">确认无社区签名</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-160">社区公钥</span>
|
||||||
|
<span class="card-val">确认无社区签名</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-160">社区公钥下载地址</span>
|
||||||
|
<span class="card-val">确认无社区签名</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
</Card>
|
||||||
|
<template #footer>
|
||||||
|
<d-modal-footer style="text-align: center">
|
||||||
|
<d-button variant="solid" color="primary" @click="handleClose">确认</d-button>
|
||||||
|
<d-button @click="handleClose">取消</d-button>
|
||||||
|
</d-modal-footer>
|
||||||
|
</template>
|
||||||
|
</d-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, defineEmits, onMounted, reactive,computed,defineProps,toRef } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { discussDetailType, userInfoType } from '@/api/discussion/types';
|
||||||
|
import { Message } from 'vue-devui/message';
|
||||||
|
import { useClipboard } from '@vueuse/core/index';
|
||||||
|
import { releaseInfo } from '@/api/jyh';
|
||||||
|
import { useGlobalInfoStore } from '@/stores/Global';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
const route = useRoute();
|
||||||
|
const emits = defineEmits(['changeTabs']);
|
||||||
|
const props = defineProps(['ReleaseInfo']);
|
||||||
|
// 触发事件的方法
|
||||||
|
const handleTabChange = (tabName) => {
|
||||||
|
emits('changeTabs', tabName); // 触发 changeTabs 事件,并传递参数
|
||||||
|
};
|
||||||
|
const jcfxList = ['','低','中','高'];
|
||||||
|
const visible = ref(false);
|
||||||
|
// 将 props.ReleaseInfo 转换为响应式引用
|
||||||
|
const ReleaseInfo = toRef(props, 'ReleaseInfo');
|
||||||
|
const globalInfo = useGlobalInfoStore();
|
||||||
|
const industryList = ref(globalInfo.industryList);
|
||||||
|
const getIndustryName = (value) => {
|
||||||
|
const industry = industryList.value.find((item) => item.value === value);
|
||||||
|
return industry ? industry.name : '未知行业'; // 如果找不到,返回默认值
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义每个 Card 的展开状态
|
||||||
|
const isExpanded = ref({
|
||||||
|
basic: false,
|
||||||
|
reference: false,
|
||||||
|
license: false,
|
||||||
|
copyright: false,
|
||||||
|
entity: false,
|
||||||
|
intSafe: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// 切换 Card 展开状态
|
||||||
|
const toggleCard = (cardType) => {
|
||||||
|
isExpanded.value[cardType] = !isExpanded.value[cardType];
|
||||||
|
};
|
||||||
|
|
||||||
|
const securityInfoList = computed(() => {
|
||||||
|
if(ReleaseInfo.value.securityInfoList&&ReleaseInfo.value.securityInfoList.length>0) {
|
||||||
|
return ReleaseInfo.value.securityInfoList[0]
|
||||||
|
} else if(ReleaseInfo.value.softwareSecurityAIGenerateInfo&&ReleaseInfo.value.softwareSecurityAIGenerateInfo.length>0) {
|
||||||
|
return ReleaseInfo.value.softwareSecurityAIGenerateInfo[0]
|
||||||
|
} else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const vulnDisclosureUrl = computed(() => {
|
||||||
|
if(ReleaseInfo.value.softwareSecurityAIGenerateInfo) {
|
||||||
|
const _vulnDisclosureUrl: any = JSON.parse(ReleaseInfo?.value.softwareSecurityAIGenerateInfo[0]?.vulnDisclosureUrl || '[]');
|
||||||
|
return _vulnDisclosureUrl || [];
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const externalVulnSourceInfo = computed(() => {
|
||||||
|
if(props.ReleaseInfo) {
|
||||||
|
const _vulnDisclosureUrl: any = JSON.parse(props.ReleaseInfo?.softwareSecurityAIGenerateInfo[0]?.externalVulnSourceInfo || '[]');
|
||||||
|
return _vulnDisclosureUrl || [];
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// 跳转
|
||||||
|
const gotoDetail = (row) => {
|
||||||
|
// 后续请换成params,暂时params传参没传过去
|
||||||
|
router.push({
|
||||||
|
path: 'repoDetail/' + row.version,
|
||||||
|
params: { tab: 'ldxx' } // 指定需要激活的 Tab
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转
|
||||||
|
const handleClose = (row) => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { copy } = useClipboard();
|
||||||
|
const handleCopy = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
copy('');
|
||||||
|
Message.success('复制成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getReleaseInfo = async (versionId) => {
|
||||||
|
const { data, error } = await releaseInfo({ softwareId: versionId });
|
||||||
|
if (!error && data) {
|
||||||
|
ReleaseInfo.value = data.data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// const versionId = route.params.versionId; // 获取路由参数 versionId
|
||||||
|
// if (versionId) {
|
||||||
|
// getReleaseInfo(versionId); // 调用接口获取数据
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import 'devui-theme/styles-var/devui-var.scss';
|
||||||
|
|
||||||
|
.jyh-card {
|
||||||
|
background: #F3F3F3 !important;
|
||||||
|
.card-title {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-key {
|
||||||
|
color: #777777;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
width: 112px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-val {
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-success {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #81c054;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: medium;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
height: 20px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
<template>
|
||||||
|
<div class="soft-detail-container">
|
||||||
|
<div class="mt-3">
|
||||||
|
<d-table class="jyh-table" v-if="tableData.length > 0" :striped="false" :data="tableData" table-layout="auto">
|
||||||
|
<d-column type="index" width="40"></d-column>
|
||||||
|
<d-column field="vulnId" header="编码">
|
||||||
|
<template #default="scope">
|
||||||
|
<a @click="openVulnDetail(scope.row)">{{ scope.row.vulnId }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<!-- <d-column field="cweId" header="CVE ID"></d-column> -->
|
||||||
|
<d-column field="cvssScore" header="CVSS 打分"></d-column>
|
||||||
|
<!-- <d-column field="fixStatus" header="修补状态">-->
|
||||||
|
<!-- <template #default="scope">-->
|
||||||
|
<!-- <a>{{ scope.row.version }}</a>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
<d-column field="severity" header="漏洞级别">
|
||||||
|
<template #default="scope">
|
||||||
|
<a>{{ scope.row.severity }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<d-column field="fixVersion" header="补丁版本">
|
||||||
|
<template #default="scope">
|
||||||
|
<a>{{ scope.row.fixVersion }}</a>
|
||||||
|
</template>
|
||||||
|
</d-column>
|
||||||
|
<!-- <d-column field="publishDate" header="补丁修复时间" fixed-right="0px">-->
|
||||||
|
<!-- <template #default="scope">-->
|
||||||
|
<!-- {{ dayjs(scope.row.publishDate).format('YYYY-MM-DD') }}-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </d-column>-->
|
||||||
|
</d-table>
|
||||||
|
|
||||||
|
<NoData v-else :small="false"></NoData>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-20 mb-20 flex justify-end" v-if="tableData.length > 0">
|
||||||
|
<d-pagination
|
||||||
|
size="md"
|
||||||
|
:page-size-options="[10, 20, 50]"
|
||||||
|
:total="pager.total"
|
||||||
|
v-model:pageSize="pager.pageSize"
|
||||||
|
v-model:pageIndex="pager.pageIndex"
|
||||||
|
:max-items="5"
|
||||||
|
:can-change-page-size="true"
|
||||||
|
:can-view-total="true"
|
||||||
|
total-item-text="总计"
|
||||||
|
@page-index-change="fetchVulnList"
|
||||||
|
@page-size-change="fetchVulnList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 删除组件 -->
|
||||||
|
<GModal
|
||||||
|
v-model="deletevModels"
|
||||||
|
ref="deleteModal"
|
||||||
|
title="删除?"
|
||||||
|
@confirm="confirmDelete"
|
||||||
|
showWarnIcon
|
||||||
|
confirmColor="danger"
|
||||||
|
>
|
||||||
|
<span class="inline-block text-G900 text-sm font-normal leading-[20px] break-all"
|
||||||
|
>你确认删除此项:{{ item.cla_name }} ?</span
|
||||||
|
>
|
||||||
|
</GModal>
|
||||||
|
|
||||||
|
<!-- 漏洞详情 -->
|
||||||
|
<d-drawer v-model="vulnDetailVisable" style="width: auto">
|
||||||
|
<VulnerabilityDetail
|
||||||
|
:vulnInfo="currentVuln"
|
||||||
|
:softwareId="currentVuln.softwareId"
|
||||||
|
:vulnId="currentVuln.id"
|
||||||
|
></VulnerabilityDetail>
|
||||||
|
</d-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as dayjs from 'dayjs';
|
||||||
|
import SettingTitle from '@/components/Setting/SettingTitle/index.vue';
|
||||||
|
import SettingText from '@/components/Setting/SettingText/index.vue';
|
||||||
|
import { ref, reactive, onMounted, defineProps } from 'vue';
|
||||||
|
import { getGroupClaList, setGroupClaStatus, deleteGroupCla } from '@/api/cla';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import { GModal } from '@/components/Setting/index';
|
||||||
|
import { reqCatch } from '@/utils/catch';
|
||||||
|
import { getOrgInfo } from '@/views/Org/hooks/orgInfo';
|
||||||
|
import { Message } from 'vue-devui';
|
||||||
|
import VulnerabilityDetail from '@/views/Jyh/Version/Detail/VulnerabilityDetail.vue';
|
||||||
|
import { getVulnList } from '@/api/jyh';
|
||||||
|
import NoData from '@/components/NoData/NoData.vue';
|
||||||
|
const { namespace } = getOrgInfo();
|
||||||
|
const propsData = defineProps(['versionId']);
|
||||||
|
const tableData = ref([]);
|
||||||
|
const formData = ref({
|
||||||
|
title: '',
|
||||||
|
type: '',
|
||||||
|
useType: '',
|
||||||
|
formData: '',
|
||||||
|
select: '',
|
||||||
|
time1: null,
|
||||||
|
name: '',
|
||||||
|
versionCode: '',
|
||||||
|
pulishTime: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const typeOptions = reactive(['主软件', '依赖软件']);
|
||||||
|
const riskOptions = reactive(['低', '中', '高']);
|
||||||
|
const useTypeOptions = reactive(['软件', '文档', '文档+软件']);
|
||||||
|
const isUseOptions = reactive(['是', '否']);
|
||||||
|
|
||||||
|
const pager = ref({
|
||||||
|
total: 10,
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除弹窗
|
||||||
|
const item = ref('');
|
||||||
|
const deleteModal = ref();
|
||||||
|
const deletevModels = ref(false);
|
||||||
|
const openDelete = (row: any) => {
|
||||||
|
item.value = row;
|
||||||
|
deleteModal.value.showFlag = true;
|
||||||
|
deletevModels.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const repoFun = (arr: any) => {
|
||||||
|
return arr ? (arr.length > 3 ? arr.slice(0, 3) : arr) : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const editRow = (row: any) => {
|
||||||
|
// 后续请换成params,暂时params传参没传过去
|
||||||
|
router.push({
|
||||||
|
path: 'cla/edit/' + row.object_id,
|
||||||
|
query: { cla: row.cla, pageType: 'edit' }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认删除
|
||||||
|
const confirmDelete = async (e: any) => {
|
||||||
|
// const res = await reqCatch(deleteGroupCla, { group_id: namespace.value, cla_id: item.value.object_id });
|
||||||
|
// if (!res.error) {
|
||||||
|
// getGroupClaListData();
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 去成员页
|
||||||
|
const goMember = (row: any) => {
|
||||||
|
// 后续请换成params,暂时params传参没传过去,注意路由有个:claID
|
||||||
|
router.push({
|
||||||
|
path: 'cla/claId/member',
|
||||||
|
query: { claId: row.object_id, claName: row.cla_name, group_path: namespace.value, pageType: 'edit' }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const getGroupClaListData = async () => {
|
||||||
|
// const params = {
|
||||||
|
// group_id: namespace.value,
|
||||||
|
// page: pager.value.pageIndex,
|
||||||
|
// per_page: pager.value.pageSize
|
||||||
|
// };
|
||||||
|
// const res = await reqCatch(getGroupClaList, params);
|
||||||
|
// if (!res.error) {
|
||||||
|
// tableData.value = res?.data?.data?.content;
|
||||||
|
// pager.value.total = res.data.data.total;
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
getGroupClaListData();
|
||||||
|
|
||||||
|
const setClaStatus = async (row: any) => {
|
||||||
|
// const params = {
|
||||||
|
// group_id: namespace.value,
|
||||||
|
// cla_id: row.object_id,
|
||||||
|
// status: row.status
|
||||||
|
// };
|
||||||
|
// const res = await reqCatch(setGroupClaStatus, params);
|
||||||
|
// if (!res.error) {
|
||||||
|
// Message.success('操作成功!');
|
||||||
|
// getGroupClaListData();
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
const signCla = (row: any) => {
|
||||||
|
router.push('/cla/' + row.object_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const vulnDetailVisable = ref(false);
|
||||||
|
const currentVuln = ref();
|
||||||
|
const openVulnDetail = (row) => {
|
||||||
|
vulnDetailVisable.value = true;
|
||||||
|
if (row.softwareId && row.id) {
|
||||||
|
currentVuln.value = row;
|
||||||
|
} else {
|
||||||
|
currentVuln.value = {
|
||||||
|
// softwareId: 47,
|
||||||
|
softwareId: propsData.versionId,
|
||||||
|
id: 72
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchVulnList = async () => {
|
||||||
|
const { data } = await getVulnList({
|
||||||
|
// softwareId: 47,
|
||||||
|
softwareId: propsData.versionId,
|
||||||
|
componentId: '',
|
||||||
|
current: pager.value.pageIndex,
|
||||||
|
size: pager.value.pageSize
|
||||||
|
});
|
||||||
|
if (data.data.code === 200) {
|
||||||
|
tableData.value = data?.data?.data?.records || [];
|
||||||
|
pager.value.total = data?.data?.data?.total || [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const valLists = ref([]);
|
||||||
|
onMounted(() => {
|
||||||
|
fetchVulnList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import 'devui-theme/styles-var/devui-var.scss';
|
||||||
|
|
||||||
|
.soft-detail-container {
|
||||||
|
//padding: 7px 20px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: $devui-contrast;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.devui-table__row td) {
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.devui-tag .devui-tag--default) {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border: 1px solid #e3e3ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.one-line {
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #9a9b9c;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cur-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #2d2d2e;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
position: relative;
|
||||||
|
padding: 12px 20px 12px 0;
|
||||||
|
border-radius: 8px 0px 8px 8px;
|
||||||
|
//background: #F2F5FC;
|
||||||
|
|
||||||
|
.output-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 28px;
|
||||||
|
bottom: 4px;
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.secret-breadcrumb {
|
||||||
|
padding: 7px 20px 12px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vuln-detail-container">
|
||||||
|
<div class="vuln-title mb-2">{{ vulnDetail.vulnId }}</div>
|
||||||
|
<div class="vuln-sub-title mb-3">
|
||||||
|
<span>{{ vulnDetail.cweId || '--' }}</span>
|
||||||
|
<span class="split-line"></span>
|
||||||
|
<span><span class="release-time">发布时间:</span>
|
||||||
|
{{ vulnDetail?.publishDate?dayjs(vulnDetail?.publishDate).format('YYYY-MM-DD') :'--' }}</span>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">漏洞名称</div>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<!-- <d-col :span="12">-->
|
||||||
|
<!-- <span class="card-key width-90">漏洞名称</span>-->
|
||||||
|
<!-- <span class="card-val">{{vulnDetail.vulnId || '--'}}</span>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">关联漏洞编号</span>
|
||||||
|
<span class="card-val">{{vulnDetail.cweId || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-90">简介</span>
|
||||||
|
<span class="card-val">{{vulnDetail.description || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-90">额外信息</span>
|
||||||
|
<span class="card-val">
|
||||||
|
<span class="card-val">{{vulnDetail.extraInfo || '--'}}</span>
|
||||||
|
</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row>
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-90">补丁链接</span>
|
||||||
|
<span class="card-val">
|
||||||
|
<span class="card-val">{{vulnDetail.patchLink || '--'}}</span>
|
||||||
|
</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
</Card>
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">安全漏洞</div>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">严重性</span>
|
||||||
|
<span class="card-val">{{vulnDetail.severity || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">分值</span>
|
||||||
|
<span class="card-val">{{vulnDetail.cvssScore || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
</Card>
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">安全问题</div>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">攻击向量</span>
|
||||||
|
<span class="card-val">{{vulnDetail.attackVector || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">攻击复杂度</span>
|
||||||
|
<span class="card-val">{{vulnDetail.attackComplexity || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">用户交互</span>
|
||||||
|
<span class="card-val">{{vulnDetail.userInteraction || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">需要特殊权限</span>
|
||||||
|
<span class="card-val">{{vulnDetail.privilegesRequired || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
</Card>
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">影响的组件</div>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">组件名</span>
|
||||||
|
<span class="card-val">{{componentInfo.moduleName || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">深度</span>
|
||||||
|
<span class="card-val">{{componentInfo.depth || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">当前版本</span>
|
||||||
|
<span class="card-val">{{componentInfo.currentVersion || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">最新版本</span>
|
||||||
|
<span class="card-val">{{componentInfo.latestVersion || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
<d-row>
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-90">组件依赖关系</span>
|
||||||
|
<span class="card-val">{{componentInfo.version || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
</Card>
|
||||||
|
<Card simple class="jyh-card mb-4">
|
||||||
|
<div class="card-title">修复建议</div>
|
||||||
|
<d-row class="mb-3">
|
||||||
|
<d-col :span="12">
|
||||||
|
<span class="card-key width-90">补丁版本</span>
|
||||||
|
<span class="card-val">{{remediationInfo?.recommendedVersion || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
<!-- <d-col :span="12">-->
|
||||||
|
<!-- <span class="card-key width-90">修复兼容性</span>-->
|
||||||
|
<!-- <span class="card-val">{{remediationInfo?.compatibility || '--'}}</span>-->
|
||||||
|
<!-- </d-col>-->
|
||||||
|
</d-row>
|
||||||
|
<d-row>
|
||||||
|
<d-col :span="24">
|
||||||
|
<span class="card-key width-90">修复建议</span>
|
||||||
|
<span class="card-val">{{remediationInfo?.recommendedFix || '--'}}</span>
|
||||||
|
</d-col>
|
||||||
|
</d-row>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getVulnDetail } from '@/api/jyh';
|
||||||
|
import { ref,defineProps } from 'vue';
|
||||||
|
import { useTimeFormat } from '@/utils/hooks/useTimeFormat';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
const { formatTime } = useTimeFormat();
|
||||||
|
const propsData = defineProps(['softwareId', 'vulnId', 'vulnInfo']);
|
||||||
|
const vulnDetail = ref({});
|
||||||
|
const componentInfo = ref({});
|
||||||
|
const remediationInfo = ref({})
|
||||||
|
|
||||||
|
const queryVulnDetail = async () => {
|
||||||
|
const data = await getVulnDetail({softwareId: propsData.softwareId, id: propsData.vulnId});
|
||||||
|
if (data?.data?.data?.data) {
|
||||||
|
vulnDetail.value = data.data.data.data.vulnerabilityInfo;
|
||||||
|
componentInfo.value = data.data.data.data.componentInfo;
|
||||||
|
remediationInfo.value = data.data.data.data.remediationInfo;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
queryVulnDetail();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.vuln-detail-container {
|
||||||
|
width: 800px;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.vuln-title {
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vuln-sub-title {
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.split-line {
|
||||||
|
width: 1px;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 16px;
|
||||||
|
height: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 0 8px;
|
||||||
|
background-color: #EEF0F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-time {
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-val {
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(100% - 90px);
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
404
src/views/Jyh/AI/Home/components/OssDetail/index.vue
Normal file
404
src/views/Jyh/AI/Home/components/OssDetail/index.vue
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
<template>
|
||||||
|
<div class="version-detail">
|
||||||
|
<div class="version-detail-header">
|
||||||
|
<div class="header-top">
|
||||||
|
<GAvatar style="margin-right: 12px" :src="avatarUrl" name="Vue" :width="40" :height="40"></GAvatar>
|
||||||
|
<div>
|
||||||
|
<div class="header-title1">{{ ReleaseInfo.basicInfo.softwareName || '--' }}</div>
|
||||||
|
<div class="header-div">
|
||||||
|
<span class="span1">版本:</span><span class="span2">{{ ReleaseInfo.basicInfo.softwareVersion || '--' }}</span>
|
||||||
|
<span class="split"></span>
|
||||||
|
<!-- <span class="span1">生命周期状态:</span><span class="span2">{{ softwareData.lifecycle }}</span>-->
|
||||||
|
<!-- <span class="split"></span>-->
|
||||||
|
<span class="span1">软件评分:</span><span class="span2"
|
||||||
|
style="
|
||||||
|
font-weight: bold;
|
||||||
|
color: #409EFF;
|
||||||
|
font-size: 16px;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
"
|
||||||
|
>{{ ReleaseInfo.networkSecurity.softwareRating || ReleaseInfo.basicInfo.securityScore || '--' }}</span>
|
||||||
|
<span class="split"></span>
|
||||||
|
<span class="span1">集成风险:</span>
|
||||||
|
<d-tag color="#F6933B">{{ ReleaseInfo.referenceInfo.integrationRisk || jcfxList[ReleaseInfo.basicInfo.integrationRiskLevel] || '--' }}</d-tag>
|
||||||
|
<!-- <d-tag color="#F6933B">{{ ReleaseInfo.referenceInfo.integrationRisk || '--' }}</d-tag>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fr">
|
||||||
|
<!-- <d-button v-if="!isSub" class="mr-2" variant="solid" @click="openSubscription">订阅</d-button>-->
|
||||||
|
<!-- <d-button v-else class="mr-2" variant="solid" disabled>已订阅</d-button>-->
|
||||||
|
<d-modal class="subscribe-modal" style="width: auto;" v-model="subscribeModalVisable" title="订阅漏洞预警通知">
|
||||||
|
<template #header>
|
||||||
|
<div class="subscribe-header">
|
||||||
|
<d-icon name="icon-info-o" color="var(--devui-info)" size="18px"></d-icon>
|
||||||
|
<span>订阅漏洞预警通知</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<SubscriptionModal @closeSub="isSub = true;" @close="colseSubscription" :softwareData="{id: softwareId}"></SubscriptionModal>
|
||||||
|
</d-modal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<d-tabs class="jyh-tabs jyh-tabs-2" type="wrapped" v-model="selectTab">
|
||||||
|
<d-tab id="bbxx" title="版本信息"></d-tab>
|
||||||
|
<d-tab id="SBOM" title="SBOM"></d-tab>
|
||||||
|
<d-tab id="ldxx" title="漏洞信息"></d-tab>
|
||||||
|
<d-tab id="Malware" title="恶意代码信息" />
|
||||||
|
<d-tab id="ai" title="AI修复建议"></d-tab>
|
||||||
|
<d-tab id="licenseInfo" title="许可证信息"></d-tab>
|
||||||
|
<d-tab id="communityInfo" title="开发者社区信息"></d-tab>
|
||||||
|
<!-- <d-tab id="DeepDetectionAnalysis" title="深度检测分析" />-->
|
||||||
|
</d-tabs>
|
||||||
|
</div>
|
||||||
|
<div class="version-detail-content">
|
||||||
|
<version-info :ReleaseInfo="ReleaseInfo" v-if="selectTab === 'bbxx'" @changeTabs="changeTabs"></version-info>
|
||||||
|
<vulnerabilities-list :versionId="versionId" v-else-if="selectTab === 'ldxx'"></vulnerabilities-list>
|
||||||
|
<community-info :versionId="versionId" :ReleaseInfo="ReleaseInfo" v-else-if="selectTab === 'communityInfo'"></community-info>
|
||||||
|
<Dependence :versionId="versionId" v-else-if="selectTab === 'ylgx'"></Dependence>
|
||||||
|
<SBOM :versionId="versionId" v-else-if="selectTab === 'SBOM'" />
|
||||||
|
<AIRepair :versionId="versionId" v-else-if="selectTab === 'ai'"></AIRepair>
|
||||||
|
<LicenseInfo :versionId="versionId" v-else-if="selectTab === 'licenseInfo'"></LicenseInfo>
|
||||||
|
<Malware :versionId="versionId" v-else-if="selectTab === 'Malware'" />
|
||||||
|
<!-- <DeepDetectionAnalysis :versionId="versionId" v-else-if="selectTab === 'DeepDetectionAnalysis'" />-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, watch, onMounted,defineProps,defineExpose } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
import { getOrgInfo } from '@/views/Org/hooks/orgInfo';
|
||||||
|
import VulnerabilitiesList from './VulnerabilitiesList.vue';
|
||||||
|
import CommunityInfo from './CommunityInfo.vue';
|
||||||
|
import VersionInfo from './VersionInfo.vue';
|
||||||
|
import SBOM from './SBOM.vue';
|
||||||
|
import AIRepair from './AIRepair/index.vue';
|
||||||
|
import LicenseInfo from './LicenseInfo/index.vue';
|
||||||
|
import Dependence from './Dependency.vue'
|
||||||
|
import Malware from "@/views/Jyh/Version/Detail/Malware/Malware.vue";
|
||||||
|
import DeepDetectionAnalysis from "@/views/Jyh/Version/Detail/DeepDetectionAnalysis/index.vue";
|
||||||
|
const { namespace } = getOrgInfo();
|
||||||
|
const route = useRoute(); // 获取路由对象
|
||||||
|
const avatarUrl = '/src/assets/imgs/jyh/工程首字母图标.png';
|
||||||
|
import SubscriptionModal from '@/views/Jyh/Version/Detail/SubscriptionModal.vue';
|
||||||
|
import { alertIsSubscribe, releaseInfo } from '@/api/jyh';
|
||||||
|
|
||||||
|
const selectTab = ref('bbxx');
|
||||||
|
const softwareId = ref();
|
||||||
|
// 删除弹窗
|
||||||
|
const item = ref('');
|
||||||
|
const deleteModal = ref();
|
||||||
|
const deletevModels = ref(false);
|
||||||
|
const isSub = ref(false);
|
||||||
|
const ReleaseInfo = ref({
|
||||||
|
basicInfo: {},
|
||||||
|
copyRightReportList: [],
|
||||||
|
entityList: [],
|
||||||
|
networkSecurity: {},
|
||||||
|
openSourceLicenseList: [],
|
||||||
|
referenceInfo: {},
|
||||||
|
});
|
||||||
|
const props = defineProps(['versionId']);
|
||||||
|
const versionId = props.versionId; // 获取路由参数 versionId
|
||||||
|
const jcfxList = ['','低','中','高'];
|
||||||
|
|
||||||
|
const getReleaseInfo = async(versionId) => {
|
||||||
|
const { data, error } = await releaseInfo({ softwareId: versionId });
|
||||||
|
if (!error && data) {
|
||||||
|
ReleaseInfo.value = data.data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAlertIsSubscribe = async(versionId) => {
|
||||||
|
const { data, error } = await alertIsSubscribe({ softwareId: versionId });
|
||||||
|
if (!error) {
|
||||||
|
if (data.data) {
|
||||||
|
isSub.value = data.data.data
|
||||||
|
} else {
|
||||||
|
isSub.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
softwareId.value = versionId;
|
||||||
|
if (versionId) {
|
||||||
|
getReleaseInfo(versionId); // 调用接口获取数据
|
||||||
|
getAlertIsSubscribe(versionId); // 调用接口获取数据
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听路由的 query.tab 变化
|
||||||
|
watch(
|
||||||
|
() => route.query.tab,
|
||||||
|
(newTab) => {
|
||||||
|
if (newTab) {
|
||||||
|
selectTab.value = newTab; // 根据 query 参数激活 Tab
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true } // 立即执行一次
|
||||||
|
);
|
||||||
|
|
||||||
|
const changeTabs = (tab:string) => {
|
||||||
|
selectTab.value = tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDelete = (row: any) => {
|
||||||
|
item.value = row;
|
||||||
|
deleteModal.value.showFlag = true;
|
||||||
|
deletevModels.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const changeIndex = () => {
|
||||||
|
// getGroupClaListData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeSize = () => {
|
||||||
|
pager.value.pageIndex = 1;
|
||||||
|
// getGroupClaListData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const repoFun = (arr: any) => {
|
||||||
|
return arr ? (arr.length > 3 ? arr.slice(0, 3) : arr) : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const editRow = (row: any) => {
|
||||||
|
// 后续请换成params,暂时params传参没传过去
|
||||||
|
router.push({
|
||||||
|
path: 'cla/edit/' + row.object_id,
|
||||||
|
query: { cla: row.cla, pageType: 'edit' }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认删除
|
||||||
|
const confirmDelete = async (e: any) => {
|
||||||
|
// const res = await reqCatch(deleteGroupCla, { group_id: namespace.value, cla_id: item.value.object_id });
|
||||||
|
// if (!res.error) {
|
||||||
|
// getGroupClaListData();
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 去成员页
|
||||||
|
const goMember = (row: any) => {
|
||||||
|
// 后续请换成params,暂时params传参没传过去,注意路由有个:claID
|
||||||
|
router.push({
|
||||||
|
path: 'cla/claId/member',
|
||||||
|
query: { claId: row.object_id, claName: row.cla_name, group_path: namespace.value, pageType: 'edit' }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const getGroupClaListData = async () => {
|
||||||
|
// const params = {
|
||||||
|
// group_id: namespace.value,
|
||||||
|
// page: pager.value.pageIndex,
|
||||||
|
// per_page: pager.value.pageSize
|
||||||
|
// };
|
||||||
|
// const res = await reqCatch(getGroupClaList, params);
|
||||||
|
// if (!res.error) {
|
||||||
|
// tableData.value = res?.data?.data?.content;
|
||||||
|
// pager.value.total = res.data.data.total;
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
getGroupClaListData();
|
||||||
|
|
||||||
|
const setClaStatus = async (row: any) => {
|
||||||
|
// const params = {
|
||||||
|
// group_id: namespace.value,
|
||||||
|
// cla_id: row.object_id,
|
||||||
|
// status: row.status
|
||||||
|
// };
|
||||||
|
// const res = await reqCatch(setGroupClaStatus, params);
|
||||||
|
// if (!res.error) {
|
||||||
|
// Message.success('操作成功!');
|
||||||
|
// getGroupClaListData();
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
const signCla = (row: any) => {
|
||||||
|
router.push('/cla/' + row.object_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscribeModalVisable = ref(false);
|
||||||
|
const openSubscription = () => {
|
||||||
|
subscribeModalVisable.value = true;
|
||||||
|
};
|
||||||
|
const colseSubscription = () => {
|
||||||
|
subscribeModalVisable.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 使用 defineExpose 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openSubscription
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import 'devui-theme/styles-var/devui-var.scss';
|
||||||
|
|
||||||
|
.version-detail {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.secret-op {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.split {
|
||||||
|
height: 60%;
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: $devui-contrast;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.devui-table__row td) {
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.devui-tag .devui-tag--default) {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border: 1px solid #e3e3ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.one-line {
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #9a9b9c;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cur-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #2d2d2e;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
position: relative;
|
||||||
|
padding: 12px 20px 12px 0;
|
||||||
|
border-radius: 8px 0px 8px 8px;
|
||||||
|
background: #f2f5fc;
|
||||||
|
|
||||||
|
.output-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 28px;
|
||||||
|
bottom: 4px;
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.secret-breadcrumb {
|
||||||
|
padding: 7px 20px 12px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-detail-header {
|
||||||
|
background: #ffffff;
|
||||||
|
//padding: 0 12%;
|
||||||
|
width: 100%;
|
||||||
|
//box-shadow: inset 0px -1px 0px 0px #EEF0F5;
|
||||||
|
.header-top {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.header-title1 {
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 26px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.header-div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 20px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
.span1 {
|
||||||
|
color: #808080;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.span2 {
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .devui-tag {
|
||||||
|
.devui-tag--md {
|
||||||
|
color: #f6933b;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: regular;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
text-align: left;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-detail-content {
|
||||||
|
//padding: 0 12%;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscribe-modal {
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
.subscribe-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
>span {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #191919;
|
||||||
|
font-family: HarmonyOS Sans SC;
|
||||||
|
font-weight: medium;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
</style>
|
||||||
@@ -18,7 +18,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<!--开源软件列表-->
|
<!--开源软件列表-->
|
||||||
<OssList v-if="msg.type===1"/>
|
<OssList v-if="msg.type===1"/>
|
||||||
|
<!--预警列表-->
|
||||||
<WarningList v-else-if="msg.type===2"/>
|
<WarningList v-else-if="msg.type===2"/>
|
||||||
|
<!--软件详情-->
|
||||||
|
<OssDetail v-else-if="msg.type===3"/>
|
||||||
|
|
||||||
<div class="conversation-cnxw">
|
<div class="conversation-cnxw">
|
||||||
<a class="flex conversation-cnxw-item">
|
<a class="flex conversation-cnxw-item">
|
||||||
@@ -136,6 +139,7 @@ import SwiperComponent from './SwiperComponent.vue';
|
|||||||
import GuessYouWantToAsk from './GuessYouWantToAsk.vue';
|
import GuessYouWantToAsk from './GuessYouWantToAsk.vue';
|
||||||
import OssList from './components/OssList.vue';
|
import OssList from './components/OssList.vue';
|
||||||
import WarningList from './components/WarningList.vue';
|
import WarningList from './components/WarningList.vue';
|
||||||
|
import OssDetail from './components/OssDetail.vue';
|
||||||
|
|
||||||
const inputValue = ref('');
|
const inputValue = ref('');
|
||||||
const startChat = ref(false);
|
const startChat = ref(false);
|
||||||
@@ -195,7 +199,7 @@ const onSubmit = (e, answer = undefined) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
// getAIAnswer(answer ?? e);
|
// getAIAnswer(answer ?? e);
|
||||||
getAIAnswer(mockAnswer,2);
|
getAIAnswer(mockAnswer,3);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAIAnswer = (content,type) => {
|
const getAIAnswer = (content,type) => {
|
||||||
|
|||||||
@@ -958,10 +958,10 @@
|
|||||||
resolved "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz"
|
resolved "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz"
|
||||||
integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==
|
integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==
|
||||||
|
|
||||||
"@devui-design/icons@^1.3.0":
|
"@devui-design/icons@^1.3.0", "@devui-design/icons@^1.4.0":
|
||||||
version "1.3.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npmjs.org/@devui-design/icons/-/icons-1.3.0.tgz"
|
resolved "https://mirrors.cloud.tencent.com/npm/@devui-design/icons/-/icons-1.4.0.tgz"
|
||||||
integrity sha512-eg9PcDXYn1BaUCo6qP7dszFJ4uRycxASLdKzpz27AW34HbHx4kFce36Fhfr2niRjGKYllcMgYe7hgP5KYVOZ8Q==
|
integrity sha512-taAX1RNW0QHUqQTRPqLTYTB2PZIqUplhWeF4hcmWkSTjpWlDNI40DssG/WRb3sISkfBk/4BMUxxC5XeTL3jo7A==
|
||||||
|
|
||||||
"@esbuild/win32-x64@0.18.20":
|
"@esbuild/win32-x64@0.18.20":
|
||||||
version "0.18.20"
|
version "0.18.20"
|
||||||
|
|||||||
Reference in New Issue
Block a user