feat: 增加项目详情页面,修改部分bug

This commit is contained in:
NiceAsiv 2024-08-08 23:35:39 +08:00
parent 697c1463ca
commit f76bca6b66
5 changed files with 375 additions and 74 deletions

View File

@ -15,7 +15,7 @@ export default {
name: "DashbroadIndex", name: "DashbroadIndex",
component: () => import("@/views/dashboard/index.vue"), component: () => import("@/views/dashboard/index.vue"),
meta: { meta: {
title: "仪表盘首页", title: "仪表盘",
showLink: true showLink: true
} }
} }

View File

@ -1,22 +1,32 @@
export default { export default {
path: "/project", path: "/project",
name: "Project", name: "Project",
redirect: "/project/index", redirect: "/project/manage",
meta: { meta: {
title: "项目管理", title: "项目管理",
icon: "ri:projector-line", icon: "ri:projector-line",
showLink: true,
rank: 1 rank: 1
}, },
component: () => import("@/layout/index.vue"), component: () => import("@/layout/index.vue"),
children: [ children: [
{ {
path: "/project/index", path: "/project/manage",
name: "ProjectIndex", name: "ProjectManagement",
component: () => import("@/views/project/ProjectManagement.vue"), component: () => import("@/views/project/ProjectManagement.vue"),
meta: { meta: {
title: "项目管理", title: "项目列表",
showLink: true showLink: true
} }
},
{
path: "/project/detail",
name: "ProjectDetail",
component: () => import("@/views/project/ProjectDetail.vue"),
meta: {
title: "项目详情",
showLink: false
}
} }
] ]
}; };

View File

@ -1,70 +1,359 @@
<!-- src/components/ProjectDetails.vue --> <!-- src/components/ProjectDetails.vue -->
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
const route = useRoute();
const projectName = ref("");
onMounted(() => {
projectName.value = route.params.name as string;
});
const projectDetails = ref({
creator: "Asiv",
createTime: "2024-7-11 15:30:46",
team: "Asiv's Team",
scanDetails: {
softwareComposition: {
scanTimes: "0x111",
avgScanTime: "20s",
sourceBranch: "main"
},
componentCount: 23,
vulnerabilities: 0,
licenses: 0
}
});
</script>
<template> <template>
<div class="project-details"> <div class="project-details-container">
<h1>项目详情: {{ projectName }}</h1> <el-card>
<p>创建人: {{ projectDetails.creator }}</p> <el-row :gutter="20">
<p>创建时间: {{ projectDetails.createTime }}</p> <el-col :span="12">
<p>团队: {{ projectDetails.team }}</p> <h2>{{ projectName }}</h2>
<p>创建人: {{ creator }}</p>
<p>创建时间: {{ createTime }}</p>
</el-col>
<el-col :span="12" class="text-right">
<el-button type="primary">扫描</el-button>
<el-button>设置</el-button>
</el-col>
</el-row>
</el-card>
<h2>扫描详情</h2> <!-- <el-row :gutter="20" class="stats-row">-->
<div class="scan-info"> <!-- <el-col :span="8">-->
<p> <!-- <div class="stats-card">-->
软件成分分析扫描次数: <!-- <h3>组件版本数</h3>-->
{{ projectDetails.scanDetails.softwareComposition.scanTimes }} <!-- <div ref="componentVersionsChart" class="chart-container" />-->
</p> <!-- </div>-->
<p> <!-- </el-col>-->
平均扫描时间: <!-- <el-col :span="8">-->
{{ projectDetails.scanDetails.softwareComposition.avgScanTime }} <!-- <div class="stats-card">-->
</p> <!-- <h3>安全漏洞</h3>-->
<p> <!-- <div ref="securityIssuesChart" class="chart-container" />-->
源代码分支: <!-- </div>-->
{{ projectDetails.scanDetails.softwareComposition.sourceBranch }} <!-- </el-col>-->
</p> <!-- <el-col :span="8">-->
</div> <!-- <div class="stats-card">-->
<!-- <h3>有许可证的组件版本数</h3>-->
<!-- <div ref="licensedVersionsChart" class="chart-container" />-->
<!-- </div>-->
<!-- </el-col>-->
<!-- </el-row>-->
<h2>组件信息</h2> <el-tabs v-model="activeTab">
<p>组件数量: {{ projectDetails.scanDetails.componentCount }}</p> <el-tab-pane label="软件依赖成分分析" name="1">
<p>漏洞数: {{ projectDetails.scanDetails.vulnerabilities }}</p> <el-row :gutter="20">
<p>许可证数量: {{ projectDetails.scanDetails.licenses }}</p> <el-col :span="8">
<div ref="componentsChart" class="chart-container" />
</el-col>
<el-col :span="8">
<div ref="securityChart" class="chart-container" />
</el-col>
<el-col :span="8">
<div ref="licensedChart" class="chart-container" />
</el-col>
</el-row>
<el-table :data="components" stripe>
<el-table-column prop="name" label="组件" />
<el-table-column prop="platform" label="管理平台" />
<el-table-column prop="currentVersion" label="当前版本" />
<el-table-column prop="latestVersion" label="最新版本" />
<el-table-column prop="highRisk" label="高危" />
<el-table-column prop="mediumRisk" label="中危" />
<el-table-column prop="lowRisk" label="低危" />
</el-table>
</el-tab-pane>
<el-tab-pane label="源代码静态检测" name="2">
<!-- 这里可以添加源代码静态检测的内容 -->
</el-tab-pane>
</el-tabs>
</div> </div>
</template> </template>
<style scoped lang="scss"> <script setup lang="ts">
.project-details { import { ref, onMounted } from "vue";
padding: 20px; import * as echarts from "echarts";
interface ComponentInfo {
name: string;
platform: string;
currentVersion: string;
latestVersion: string;
highRisk: number;
mediumRisk: number;
lowRisk: number;
} }
.scan-info { const projectName = ref("NiceAsiv/TestCaseDroid");
padding: 10px; const creator = ref("NiceAsiv");
background: #f5f5f5; const createTime = ref("2024-06-30, 6:13:43 PM");
border-radius: 4px; const activeTab = ref("1");
const components = ref<ComponentInfo[]>([
{
name: "log4j-core",
platform: "Maven",
currentVersion: "2.14.1",
latestVersion: "2.23.1",
highRisk: 2,
mediumRisk: 0,
lowRisk: 1
},
{
name: "htmlunit",
platform: "Maven",
currentVersion: "2.1",
latestVersion: "2.70.0",
highRisk: 1,
mediumRisk: 0,
lowRisk: 2
},
{
name: "TestCaseDroid",
platform: "Maven",
currentVersion: "1.2",
latestVersion: "1.0",
highRisk: 0,
mediumRisk: 0,
lowRisk: 0
},
{
name: "maven-jar-plugin",
platform: "Maven",
currentVersion: "3.2.0",
latestVersion: "4.0.0-beta-1",
highRisk: 0,
mediumRisk: 0,
lowRisk: 0
},
{
name: "maven-assembly-plugin",
platform: "Maven",
currentVersion: "3.3.0",
latestVersion: "3.7.1",
highRisk: 0,
mediumRisk: 0,
lowRisk: 0
}
]);
const componentsChart = ref(null);
const securityChart = ref(null);
const licensedChart = ref(null);
const componentVersionsChart = ref(null);
const securityIssuesChart = ref(null);
const licensedVersionsChart = ref(null);
onMounted(() => {
const componentsChartInstance = echarts.init(componentsChart.value);
const securityChartInstance = echarts.init(securityChart.value);
const licensedChartInstance = echarts.init(licensedChart.value);
const componentVersionsChartInstance = echarts.init(
componentVersionsChart.value
);
const securityIssuesChartInstance = echarts.init(securityIssuesChart.value);
const licensedVersionsChartInstance = echarts.init(
licensedVersionsChart.value
);
const componentsOption = {
title: {
text: "组件版本分布"
},
tooltip: {
trigger: "item"
},
legend: {
top: "5%",
left: "center"
},
series: [
{
name: "组件版本数",
type: "pie",
radius: "50%",
data: [
{ value: 2, name: "不合规" },
{ value: 21, name: "合规" }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
};
const securityOption = {
title: {
text: "安全漏洞"
},
tooltip: {
trigger: "item"
},
legend: {
top: "5%",
left: "center"
},
series: [
{
name: "安全漏洞数",
type: "pie",
radius: "50%",
data: [
{ value: 3, name: "高" },
{ value: 1, name: "中" },
{ value: 2, name: "低" },
{ value: 1, name: "未知" }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
};
const licensedOption = {
title: {
text: "许可证信息"
},
tooltip: {
trigger: "item"
},
legend: {
top: "5%",
left: "center"
},
series: [
{
name: "许可证数",
type: "pie",
radius: "50%",
data: [
{ value: 2, name: "不合规" },
{ value: 15, name: "合规" }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
};
const componentVersionsOption = {
xAxis: {
type: "category",
data: ["不合规", "合规"]
},
yAxis: {
type: "value"
},
series: [
{
data: [2, 21],
type: "bar",
barWidth: "60%",
itemStyle: {
color: function (params) {
var colorList = ["#f56c6c", "#67c23a"];
return colorList[params.dataIndex];
}
}
}
]
};
const securityIssuesOption = {
xAxis: {
type: "category",
data: ["高", "中", "低", "未知"]
},
yAxis: {
type: "value"
},
series: [
{
data: [3, 1, 2, 1],
type: "bar",
barWidth: "60%",
itemStyle: {
color: function (params) {
var colorList = ["#f56c6c", "#e6a23c", "#f39c12", "#909399"];
return colorList[params.dataIndex];
}
}
}
]
};
const licensedVersionsOption = {
xAxis: {
type: "category",
data: ["不合规", "合规"]
},
yAxis: {
type: "value"
},
series: [
{
data: [2, 15],
type: "bar",
barWidth: "60%",
itemStyle: {
color: function (params) {
var colorList = ["#f56c6c", "#67c23a"];
return colorList[params.dataIndex];
}
}
}
]
};
componentsChartInstance.setOption(componentsOption);
securityChartInstance.setOption(securityOption);
licensedChartInstance.setOption(licensedOption);
componentVersionsChartInstance.setOption(componentVersionsOption);
securityIssuesChartInstance.setOption(securityIssuesOption);
licensedVersionsChartInstance.setOption(licensedVersionsOption);
});
</script>
<style scoped lang="scss">
.project-details-container {
padding: 20px; /* 添加内边距 */
margin-left: 20px; /* 添加左侧间隙 */
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
}
.text-right {
text-align: right;
}
.chart-container {
height: 200px;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
}
.stats-row {
margin-top: 20px;
}
.stats-card {
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
} }
</style> </style>

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { useRouter } from "vue-router";
const getStatLabel = (key: string): string => { const getStatLabel = (key: string): string => {
const labels: { [key: string]: string } = { const labels: { [key: string]: string } = {
@ -33,8 +34,13 @@ const projects = ref([
projectSource: "Github" projectSource: "Github"
} }
]); ]);
</script>
const router = useRouter();
const navigateToDetails = () => {
router.push({ path: "/project/detail" });
};
</script>
<template> <template>
<div class="project-management"> <div class="project-management">
<div class="stats-container"> <div class="stats-container">
@ -47,8 +53,8 @@ const projects = ref([
<button class="btn">+ 新建项目</button> <button class="btn">+ 新建项目</button>
</div> </div>
<div class="filter-container"> <div class="filter-container">
<el-input type="text" placeholder="选择搜索类型(ID或name或者tags)" /> <input type="text" placeholder="选择搜索类型(ID或name或者tags)" />
<el-input type="text" placeholder="根据搜索类型搜索" /> <input type="text" placeholder="根据搜索类型搜索" />
</div> </div>
<table class="project-table"> <table class="project-table">
<thead> <thead>
@ -68,11 +74,7 @@ const projects = ref([
<tbody> <tbody>
<tr v-for="project in projects" :key="project.name"> <tr v-for="project in projects" :key="project.name">
<td> <td>
<a <a href="#" @click.prevent="navigateToDetails">
:href="'https://' + project.projectLink"
target="_blank"
rel="noopener noreferrer"
>
{{ project.name }} {{ project.name }}
</a> </a>
</td> </td>
@ -98,7 +100,6 @@ const projects = ref([
</table> </table>
</div> </div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.project-management { .project-management {
padding: 20px; padding: 20px;
@ -169,6 +170,7 @@ const projects = ref([
.project-table td a { .project-table td a {
color: #3498db; color: #3498db;
text-decoration: none; text-decoration: none;
cursor: pointer;
} }
.scan-btn { .scan-btn {

View File

@ -50,4 +50,4 @@
"**/*.js", "**/*.js",
"node_modules" "node_modules"
] ]
} }