diff --git a/package.json b/package.json index b217511..3047a1d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@sentry/vue": "^7.101.1", "@types/crypto-js": "^4.1.1", "@vueuse/core": "^10.2.1", + "ant-design-vue": "4.x", "axios": "^1.6.8", "canvas-confetti": "^1.9.2", "crypto-js": "^4.2.0", diff --git a/src/api/inboundTask/inboundTaskApi.ts b/src/api/inboundTask/inboundTaskApi.ts index d9ce45f..006138f 100644 --- a/src/api/inboundTask/inboundTaskApi.ts +++ b/src/api/inboundTask/inboundTaskApi.ts @@ -63,3 +63,10 @@ export function queryTaskSoftwareDetail(params: {taskId: number}) { }); } +export function uvdV1Chat(params) { + return request({ + url: `/uvd/v1/chat`, + method: 'post', + data: params, + }); +} diff --git a/src/api/jyh/home.ts b/src/api/jyh/home.ts new file mode 100644 index 0000000..cd4ec6d --- /dev/null +++ b/src/api/jyh/home.ts @@ -0,0 +1,310 @@ +import request from '@/utils/request'; + +export function getStatistics (): Promise { + return request({ + url: `/trust/data/homeStatistics`, + method: 'post', + data: { + current: 1, + size: 10 + } + }); +} +export function alertsPage(): Promise { + return request({ + url: `/api/v1/alerts/page`, + method: 'get' + }); +} + +// 1. 定义排行类型(与父组件对应) +export type RankType = 'usageCount' | 'falseNegativeCount' | 'falsePositiveCount'; +// 2. 定义机构类型(原有) +export type InstitutionKey = '全部' | '华中科技大学' | '武汉大学' | '信息支援部队工程大学'; +/** + * 获取工具排行数据 + * @param institutionKey - 机构标识(全部/华中科技大学/武汉大学/信息支援部队工程大学) + * @returns 对应机构的工具数据 + */ +/** + * 获取工具排行数据 + * @param institutionKey - 机构标识 + * @param rankType - 排行类型(使用次数/漏报次数/误报次数) + * @returns 对应机构+对应类型的工具数据 + */ +export function getToolRankData( + institutionKey: InstitutionKey = '全部', + rankType: RankType = 'usageCount' // 新增:排行类型参数 +): Promise { + // 1. 定义完整数据:为每个工具补充“使用次数/漏报次数/误报次数”模拟值 + const allInstitutionData = [ + { + institution: '华中科技大学', + score: 92, + tools: [ + { + name: '代码扫描工具V2.1', + score: 95, + falsePositiveRate: 2.3, + falseNegativeRate: 3.1, + usageRate: 89, + usageCount: 1280, // 模拟:使用次数 + falseNegativeCount: 15, // 模拟:漏报次数(按漏报率*使用次数估算) + falsePositiveCount: 28 // 模拟:误报次数(按误报率*使用次数估算) + }, + { + name: '漏洞检测系统V3.0', + score: 89, + falsePositiveRate: 3.5, + falseNegativeRate: 4.2, + usageRate: 76, + usageCount: 950, + falseNegativeCount: 40, + falsePositiveCount: 33 + }, + { + name: '合规性校验工具', + score: 86, + falsePositiveRate: 1.8, + falseNegativeRate: 2.5, + usageRate: 68, + usageCount: 720, + falseNegativeCount: 18, + falsePositiveCount: 13 + } + ] + }, + { + institution: '武汉大学', + score: 88, + tools: [ + { + name: '开源风险评估工具', + score: 91, + falsePositiveRate: 2.8, + falseNegativeRate: 3.3, + usageRate: 82, + usageCount: 1120, + falseNegativeCount: 37, + falsePositiveCount: 31 + }, + { + name: '依赖检查器V2.5', + score: 85, + falsePositiveRate: 3.9, + falseNegativeRate: 2.9, + usageRate: 73, + usageCount: 880, + falseNegativeCount: 25, + falsePositiveCount: 34 + }, + { + name: '协议解析工具', + score: 87, + falsePositiveRate: 1.5, + falseNegativeRate: 2.1, + usageRate: 91, + usageCount: 1350, + falseNegativeCount: 28, + falsePositiveCount: 20 + } + ] + }, + { + institution: '信息支援部队工程大学', + score: 94, + tools: [ + { + name: '军密级检测工具', + score: 96, + falsePositiveRate: 1.2, + falseNegativeRate: 1.8, + usageRate: 95, + usageCount: 1560, + falseNegativeCount: 28, + falsePositiveCount: 19 + }, + { + name: '供应链审计系统', + score: 93, + falsePositiveRate: 2.1, + falseNegativeRate: 2.4, + usageRate: 88, + usageCount: 1080, + falseNegativeCount: 26, + falsePositiveCount: 23 + }, + { + name: '涉密代码识别工具', + score: 90, + falsePositiveRate: 1.7, + falseNegativeRate: 1.5, + usageRate: 79, + usageCount: 920, + falseNegativeCount: 14, + falsePositiveCount: 16 + } + ] + } + ]; + + // 2. 根据“机构key”筛选数据(逻辑不变) + let filteredData: any[]; + switch (institutionKey) { + case '全部': + // 全部机构:聚合所有工具,保留“所属机构”字段 + filteredData = allInstitutionData.reduce((acc, curr) => { + const toolWithInstitution = curr.tools.map(tool => ({ + ...tool, + belongInstitution: curr.institution + })); + return [...acc, ...toolWithInstitution]; + }, [] as any[]); + break; + case '华中科技大学': + case '武汉大学': + case '信息支援部队工程大学': + // 单个机构:返回该机构完整数据 + filteredData = allInstitutionData.filter(item => item.institution === institutionKey); + break; + default: + filteredData = []; + } + + // 3. 模拟接口返回(直接返回筛选后的数据,父组件按需取对应类型的次数) + return new Promise(resolve => { + setTimeout(() => { + resolve({ + code: 200, + message: 'success', + data: filteredData // 数据中包含所有次数字段,父组件按rankType取对应值 + }); + }, 300); + }); + + // 实际接口调用:携带“机构key”和“排行类型”参数 + // return request({ + // url: `/trust/data/toolRank`, + // method: 'get', + // params: { + // institutionKey, + // rankType // 传排行类型给后端 + // } + // }); +} + +// 工具精度数据接口 +export function getToolAccuracyData(institutionKey: string): Promise { + // 直接返回模拟数据 + return new Promise(resolve => { + setTimeout(() => { + // 不同机构的精度数据 + const accuracyMap = { + '华中科技大学': { + falsePositiveRate: 2.5, + falseNegativeRate: 3.2, + bestTool: '代码扫描工具V2.1', + coverageRate: 92 + }, + '武汉大学': { + falsePositiveRate: 3.1, + falseNegativeRate: 2.8, + bestTool: '协议解析工具', + coverageRate: 88 + }, + '信息支援部队工程大学': { + falsePositiveRate: 1.5, + falseNegativeRate: 1.7, + bestTool: '军密级检测工具', + coverageRate: 96 + } + }; + + resolve({ + code: 200, + message: 'success', + data: accuracyMap[institutionKey] || { + falsePositiveRate: 0, + falseNegativeRate: 0, + bestTool: '暂无数据', + coverageRate: 0 + } + }); + }, 300); + }); + + // 实际接口调用(注释掉,使用模拟数据) + // return request({ + // url: `/trust/data/toolAccuracy`, + // method: 'get', + // params: { institutionKey } + // }); +} + +/** + * 获取功能卡片数值数据 + * @param institutionKey - 机构标识(全部/华中科技大学/武汉大学/信息支援部队工程大学) + * @returns 对应机构的功能卡片统计数据(申请数/入库数/任务数等) + */ +export function getFunctionCardData(institutionKey: InstitutionKey = '全部'): Promise { + // 1. 定义各机构的功能卡片原始数据(模拟不同机构的数值差异) + const allInstitutionCardData = { + '全部': { + statistics: { + softwareGovernanceApply: '1286', // 软件治理申请数(全部机构总和) + softwareSmartStorage: '856', // 软件智能入库数 + governanceTaskList: '498', // 治理任务列表数 + trustedSelectionSet: '18', // 可信选型设置数 + trustedSoftwareList: '1624' // 可信软件列表数 + } + }, + '华中科技大学': { + statistics: { + softwareGovernanceApply: '428', // 华科申请数(占比约33%) + softwareSmartStorage: '296', // 华科入库数 + governanceTaskList: '168', // 华科任务数 + trustedSelectionSet: '124', // 华科选型数 + trustedSoftwareList: '542' // 华科可信软件数 + } + }, + '武汉大学': { + statistics: { + softwareGovernanceApply: '386', // 武大申请数 + storageCount: '258', // 武大入库数 + governanceTaskList: '152', // 武大任务数 + trustedSelectionSet: '112', // 武大选型数 + trustedSoftwareList: '486' // 武大可信软件数 + } + }, + '信息支援部队工程大学': { + statistics: { + softwareGovernanceApply: '472', // 国防科大申请数(军工相关需求多,数值偏高) + softwareSmartStorage: '302', // 国防科大入库数 + governanceTaskList: '178', // 国防科大任务数 + trustedSelectionSet: '140', // 国防科大选型数 + trustedSoftwareList: '596' // 国防科大可信软件数 + } + } + }; + + // 2. 根据传入的机构key,匹配对应数据(直接从原始数据中取值) + const responseData = allInstitutionCardData[institutionKey] || allInstitutionCardData['全部']; + + // 3. 模拟接口返回(统一格式:与工具排行接口保持一致的 code/message 结构) + return new Promise(resolve => { + setTimeout(() => { + resolve({ + code: 200, + message: 'success', + data: responseData // 返回对应机构的统计数据(包含 statistics 字段) + }); + }, 200); // 延迟200ms,模拟网络请求 + }); + + // 实际接口调用(后续替换为真实请求,携带机构参数) + // return request({ + // url: `/trust/data/functionCard`, + // method: 'get', + // params: { institutionKey } // 传机构参数给后端 + // }); +} diff --git a/src/api/jyh/index.ts b/src/api/jyh/index.ts index 74548d9..34ff651 100644 --- a/src/api/jyh/index.ts +++ b/src/api/jyh/index.ts @@ -498,3 +498,12 @@ export function fetchCheckAuthenticate(data) { data: data, }); } + + +export const getNoticeAdd = (data): Promise => { + return request({ + url: `/notice/add`, + method: 'POST', + data + }); +}; diff --git a/src/api/jyh/scanCenter.ts b/src/api/jyh/scanCenter.ts new file mode 100644 index 0000000..affd7b1 --- /dev/null +++ b/src/api/jyh/scanCenter.ts @@ -0,0 +1,213 @@ +import request from '@/utils/request'; + +// @/api/scanCenter.ts +/** + * 获取历史检测记录 + * @param params 分页和筛选参数 { page, size, status } + * @returns 历史记录列表 + */ +export function getScanHistory(params: { page: number; size: number; status?: string }): Promise { + return new Promise(resolve => { + setTimeout(() => { + // 模拟历史记录数据 + const mockRecords = [ + { + taskId: 'scan_1689234567890', + target: 'https://example.com', + type: 'url', + status: 'completed', + riskLevel: '高危', + startTime: '2024-07-13T09:23:45', + completeTime: '2024-07-13T09:25:12', + duration: 87000, + riskStats: { critical: 1, high: 3, medium: 2, low: 5 } + }, + { + taskId: 'scan_1689231234567', + target: 'app-release.apk', + type: 'file', + status: 'completed', + riskLevel: '中危', + startTime: '2024-07-12T15:42:18', + completeTime: '2024-07-12T15:47:33', + duration: 315000, + riskStats: { critical: 0, high: 0, medium: 2, low: 1 } + }, + { + taskId: 'scan_1689228901234', + target: 'SBOM内容', + type: 'sbom', + status: 'failed', + riskLevel: '-', + startTime: '2024-07-12T14:15:30', + completeTime: '2024-07-12T14:16:05', + duration: 35000, + errorMsg: '格式解析错误:缺少必填字段"version"' + }, + { + taskId: 'scan_1689225678901', + target: 'https://test-api.com/v1', + type: 'url', + status: 'completed', + riskLevel: '低危', + startTime: '2024-07-12T10:08:22', + completeTime: '2024-07-12T10:09:45', + duration: 83000, + riskStats: { critical: 0, high: 0, medium: 0, low: 2 } + } + ]; + + // 筛选逻辑 + let filtered = mockRecords; + if (params.status && params.status !== 'all') { + filtered = filtered.filter(item => item.status === params.status); + } + + // 分页逻辑 + const total = filtered.length; + const start = (params.page - 1) * params.size; + const records = filtered.slice(start, start + params.size); + + resolve({ + code: 200, + message: 'success', + data: { + total, + records, + page: params.page, + size: params.size, + pages: Math.ceil(total / params.size) + } + }); + }, 300); + }); + + // 实际接口调用(注释掉,使用模拟数据) + // return request({ + // url: `/scan/history`, + // method: 'get', + // params + // }); +} + +/** + * 提交检测任务 + * @param data 任务数据 { type, target, file?, content? } + * @returns 任务信息 + */ +export function submitScanTask(data: { + type: 'file' | 'url' | 'sbom'; + target: string; + file?: FormData; + content?: string; +}): Promise { + return new Promise(resolve => { + setTimeout(() => { + // 生成唯一任务ID + const taskId = `scan_${Date.now()}`; + const now = new Date().toISOString(); + + resolve({ + code: 200, + message: '任务提交成功', + data: { + taskId, + type: data.type, + target: data.target, + status: 'pending', // 初始状态:等待中 + progress: 0, + startTime: now, + estimatedCompleteTime: new Date(Date.now() + 300000).toISOString(), // 预计5分钟后完成 + currentStep: '等待调度' + } + }); + }, 500); + }); + + // 实际接口调用(注释掉,使用模拟数据) + // return request({ + // url: `/scan/submit`, + // method: 'post', + // data + // }); +} + +/** + * 获取检测任务状态 + * @param taskId 任务ID + * @returns 任务当前状态信息 + */ +export function getScanStatus(taskId: string): Promise { + return new Promise(resolve => { + setTimeout(() => { + // 解析任务ID中的时间戳(模拟任务创建时间) + const createTime = parseInt(taskId.split('_')[1] || Date.now().toString()); + const now = Date.now(); + const elapsed = now - createTime; + + // 模拟状态流转 + let status: string, progress: number, currentStep: string, completeTime: string | null = null; + + if (elapsed < 3000) { // 前3秒:等待中 + status = 'pending'; + progress = 0; + currentStep = '等待调度'; + } else if (elapsed < 15000) { // 3-15秒:检测中(进度递增) + status = 'scanning'; + progress = Math.min(Math.floor((elapsed - 3000) / 12000 * 100), 90); + currentStep = progress < 30 ? '初始化检测环境' : + progress < 60 ? '执行漏洞扫描' : '生成初步报告'; + } else { // 15秒后:完成(80%概率)或失败(20%概率) + const isSuccess = Math.random() > 0.2; + status = isSuccess ? 'completed' : 'failed'; + progress = 100; + completeTime = new Date().toISOString(); + + if (isSuccess) { + currentStep = '检测完成'; + } else { + currentStep = '检测失败'; + } + } + + // 模拟风险统计(仅当任务完成时) + const riskStats = status === 'completed' + ? { + critical: Math.floor(Math.random() * 2), + high: Math.floor(Math.random() * 3), + medium: Math.floor(Math.random() * 5), + low: Math.floor(Math.random() * 8) + } + : null; + + // 模拟错误信息(仅当任务失败时) + const errorMsg = status === 'failed' + ? ['网络连接超时', '目标资源不可访问', '检测引擎异常'][Math.floor(Math.random() * 3)] + : null; + + resolve({ + code: 200, + message: 'success', + data: { + taskId, + status, + progress, + currentStep, + startTime: new Date(createTime).toISOString(), + estimatedCompleteTime: new Date(createTime + 300000).toISOString(), + completeTime, + riskStats, + errorMsg, + totalChecks: status === 'completed' ? 20 + Math.floor(Math.random() * 30) : null, + completedChecks: status === 'scanning' ? Math.floor(progress / 100 * 40) : null + } + }); + }, 300); + }); + + // 实际接口调用(注释掉,使用模拟数据) + // return request({ + // url: `/scan/status/${taskId}`, + // method: 'get' + // }); +} diff --git a/src/api/repo/index.ts b/src/api/repo/index.ts index 435680d..10f43e4 100644 --- a/src/api/repo/index.ts +++ b/src/api/repo/index.ts @@ -1543,3 +1543,176 @@ export const getOriginalComment = (params: types.DiscussionCommentProps): Promis ); } +// 获取漏洞数据 +export const getVulnDetail = (alertId: types.VulnDetailProps): Promise => { + return reqCatch(() => + request({ + url: `/api/v1/alerts/${alertId}`, + method: 'get' + }) + ); +}; + +// 获取漏洞数据 +export const getVulnAffected = (params: types.VulnDetailProps): Promise => { + return reqCatch(() => + request({ + url: `/api/v1/alerts/component`, + method: 'get', + params + }) + ); +}; + +// 获取漏洞数据 +export const getVulnPatch = (params: types.VulnDetailProps): Promise => { + return reqCatch(() => + request({ + url: `/api/v1/alerts/patch`, + method: 'get', + params + }) + ); +}; + +// 获取漏洞数据 +export const getVulnerabilitiespage = (data: any): Promise => { + return reqCatch(() => + request({ + url: `/org/api/v1/vulnerabilities/page`, + method: 'post', + data + }) + ); +}; +export const getVulnerabilitiesDetails = (id: any): Promise => { + return reqCatch(() => + request({ + url: `/org/api/v1/vulnerabilities/${id}`, + method: 'get' + }) + ); +}; +export const getVulnerabilitiesComponents = (data: any): Promise => { + return reqCatch(() => + request({ + url: `/org/api/v1/vulnerabilities/components/page`, + method: 'post', + data + }) + ); +}; + +export const getVulnerabilitiesAlertsPatch = (params: any): Promise => { + return reqCatch(() => + request({ + url: `/api/v1/alerts/patch`, + method: 'get', + params + }) + ); +}; + +export const getVulnerabilitiesAlertsUsers = (id: any): Promise => { + return reqCatch(() => + request({ + url: `/org/api/v1/vulnerabilities/${id}/alerts`, + method: 'get' + }) + ); +}; + +export const storageStartTask = (data: any): Promise => { + return reqCatch(() => + request({ + url: `/task/storage/startTask`, + method: 'post', + data + }) + ); +}; + +// 出库申请 +export const storageStartOutTask = (data: any): Promise => { + return reqCatch(() => + request({ + url: `/task/storage/startOutTask`, + method: 'post', + data + }) + ); +}; + +// 发送预警 +export const sendWarning = (data: any): Promise => { + return reqCatch(() => + request({ + url: `/org/api/v1/send/alert/alertPublish`, + method: 'post', + data + }) + ); +}; + +// 获取预警详情 +export const getEarlyWarningInfo = (data: any): Promise => { + return reqCatchV2(() => + request({ + url: '/org/api/v1/send/alert/alertInfo', + method: 'POST', + data + }) + ); +}; + +// 获取预警列表 +export const getGetAlertListPage = (data: any): Promise => { + return reqCatchV2(() => + request({ + url: '/org/api/v1/send/alert/getAlertListPage', + method: 'post', + data + }) + ); +}; +// 获取预警处理人员列表 +export const getGetAlertUserListPage = (data: any): Promise => { + return reqCatchV2(() => + request({ + url: '/org/api/v1/send/alert/getNoticeDealList', + method: 'post', + data + }) + ); +}; + +// 获取预警列表详情 +export const getGetAlertDetails = (id: any): Promise => { + return reqCatchV2(() => + request({ + url: `/org/api/v1/send/alert/getVulnerabilityDetail?vulnId=${id}`, + method: 'get' + }) + ); +}; + +// 获取预警列表详情 +export const getNoticeDealCount = (id: any): Promise => { + return reqCatchV2(() => + request({ + url: `/org/api/v1/send/alert/getNoticeDealCount?vulnId=${id}`, + method: 'get' + }) + ); +}; + +// 分页查询订阅预警信息接口 +export const getAlertSubscribesPage = (data: any): Promise => { + return reqCatchV2(() => + request({ + url: `/org/api/v1/send/alert/getAlertSubscribesPage`, + method: 'post', + data + }) + ); +}; diff --git a/src/assets/img/footer-bg.fbfa26c4.png b/src/assets/img/footer-bg.fbfa26c4.png new file mode 100644 index 0000000..5c545a5 Binary files /dev/null and b/src/assets/img/footer-bg.fbfa26c4.png differ diff --git a/src/assets/img/footer-logo.10d1ef46.png b/src/assets/img/footer-logo.10d1ef46.png new file mode 100644 index 0000000..f3c3bbb Binary files /dev/null and b/src/assets/img/footer-logo.10d1ef46.png differ diff --git a/src/assets/img/header-logo.01f15c97.png b/src/assets/img/header-logo.01f15c97.png new file mode 100644 index 0000000..143ea9e Binary files /dev/null and b/src/assets/img/header-logo.01f15c97.png differ diff --git a/src/assets/img/qr_code.4a6eae3c.jpg b/src/assets/img/qr_code.4a6eae3c.jpg new file mode 100644 index 0000000..9929b2c Binary files /dev/null and b/src/assets/img/qr_code.4a6eae3c.jpg differ diff --git a/src/assets/imgs/jyh/aiLogo2.png b/src/assets/imgs/jyh/aiLogo2.png new file mode 100644 index 0000000..fdd9e3b Binary files /dev/null and b/src/assets/imgs/jyh/aiLogo2.png differ diff --git a/src/components/Header/Search.vue b/src/components/Header/Search.vue index 9268564..0a8cd9f 100644 --- a/src/components/Header/Search.vue +++ b/src/components/Header/Search.vue @@ -2,7 +2,7 @@ + v-model.trim="softwareName" placeholder="请输入名称搜索" @clear="handleClear" clearable >