|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { onMounted, ref } from "vue";
|
|
|
|
|
import Plotly from "plotly.js-dist-min";
|
|
|
|
|
|
|
|
|
|
const pieChartRef = ref<HTMLElement | null>(null);
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
if (pieChartRef.value) {
|
|
|
|
|
Plotly.newPlot(
|
|
|
|
|
pieChartRef.value,
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
values: [40, 25, 20, 15],
|
|
|
|
|
labels: ["Success", "Pending", "Failed", "Cancelled"],
|
|
|
|
|
type: "pie",
|
|
|
|
|
marker: {
|
|
|
|
|
colors: ["#4caf50", "#ff9800", "#f44336", "#9e9e9e"],
|
|
|
|
|
},
|
|
|
|
|
textinfo: "label+percent",
|
|
|
|
|
textfont: { color: "#fff", size: 14 },
|
|
|
|
|
hole: 0.4,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
paper_bgcolor: "#1e1e1e",
|
|
|
|
|
plot_bgcolor: "#1e1e1e",
|
|
|
|
|
showlegend: true,
|
|
|
|
|
legend: {
|
|
|
|
|
font: { color: "#ffffff", size: 12 },
|
|
|
|
|
orientation: "h",
|
|
|
|
|
x: 0.5,
|
|
|
|
|
xanchor: "center",
|
|
|
|
|
y: -0.2,
|
|
|
|
|
},
|
|
|
|
|
margin: { t: 20, b: 40, l: 0, r: 0 },
|
|
|
|
|
},
|
|
|
|
|
{ displayModeBar: false },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const recentRuns = [
|
|
|
|
|
{ name: "Model A - v1", status: "success", time: "2025-05-12 09:12" },
|
|
|
|
|
{ name: "Model B - tuning", status: "success", time: "2025-05-14 08:59" },
|
|
|
|
|
{ name: "Model C - test run", status: "failed", time: "2025-05-13 18:13" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const datasetUpdates = [
|
|
|
|
|
{ name: "DrivingLog2025", count: 7 },
|
|
|
|
|
{ name: "CameraFrames", count: 3 },
|
|
|
|
|
{ name: "LidarScans", count: 2 },
|
|
|
|
|
{ name: "Traffic_log", count: 2 },
|
|
|
|
|
{ name: "Traffic_log2", count: 2 },
|
|
|
|
|
];
|
|
|
|
|
const dummyWorkflow = [
|
|
|
|
|
{ title: "Volcano Test Pipeline", date: "2025-05-13 08:12" },
|
|
|
|
|
{ title: "Volcano Test Pipeline", date: "2025-05-13 08:12" },
|
|
|
|
|
{ title: "XGBoost Training", date: "2025-05-13 08:12" },
|
|
|
|
|
{ title: "Data Preprocess Flow", date: "2025-05-13 08:12" },
|
|
|
|
|
];
|
|
|
|
|
const tableHeader = [
|
|
|
|
|
{ label: "Model Name", width: "10%", style: "word-break: keep-all;" },
|
|
|
|
|
{ label: "Version", width: "10%", style: "word-break: keep-all;" },
|
|
|
|
|
{ label: "Deployed At", width: "10%", style: "word-break: keep-all;" },
|
|
|
|
|
{ label: "Status", width: "10%", style: "word-break: keep-all;" },
|
|
|
|
|
{ label: "Download", width: "10%", style: "word-break: keep-all;" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const data = ref({
|
|
|
|
|
results: [
|
|
|
|
|
{
|
|
|
|
|
deviceKey: "1",
|
|
|
|
|
name: "LaneDetectionModel",
|
|
|
|
|
version: "v1.2.0",
|
|
|
|
|
time: "2025-05-13 14:32",
|
|
|
|
|
status: "Active",
|
|
|
|
|
download: "Finished",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
deviceKey: "2",
|
|
|
|
|
name: "TrafficSignClassifier",
|
|
|
|
|
version: "v0.9.3",
|
|
|
|
|
time: "2025-05-13 09:00",
|
|
|
|
|
status: "Pending",
|
|
|
|
|
download: "-",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
deviceKey: "3",
|
|
|
|
|
name: "PathPlannerModel",
|
|
|
|
|
version: "v2.0.1",
|
|
|
|
|
time: "2025-05-12 17:44",
|
|
|
|
|
status: "Failed",
|
|
|
|
|
download: "Failed",
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
allSelected: false,
|
|
|
|
|
selected: [],
|
|
|
|
|
});
|
|
|
|
|
const handleRefresh = () => {
|
|
|
|
|
alert("Refresh 작업 진행중...");
|
|
|
|
|
};
|
|
|
|
|
const getSelectedAllData = () => {
|
|
|
|
|
data.value.selected = data.value.allSelected
|
|
|
|
|
? data.value.results.map(({ deviceKey }) => ({ deviceKey }))
|
|
|
|
|
: [];
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<v-container fluid>
|
|
|
|
|
<div class="d-flex justify-space-between align-center mb-6">
|
|
|
|
|
<h2 class="text-h6 font-weight-bold">배터리 상태 예측 모델 프로젝트</h2>
|
|
|
|
|
<v-btn color="primary" prepend-icon="mdi-refresh" @click="handleRefresh"
|
|
|
|
|
>Refresh</v-btn
|
|
|
|
|
>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<v-row>
|
|
|
|
|
<v-col cols="12" md="6">
|
|
|
|
|
<v-card class="pa-0" style="min-height: 360px; max-height: 360px">
|
|
|
|
|
<div style="padding: 16px; border-bottom: 1px solid #ccc">
|
|
|
|
|
<h3 class="text-subtitle-1 font-weight-bold mb-0">
|
|
|
|
|
Workflow Success Rate
|
|
|
|
|
</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="overflow-y: auto; padding: 8px 16px">
|
|
|
|
|
<div ref="pieChartRef" style="height: 280px"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-col>
|
|
|
|
|
<v-col cols="12" md="6">
|
|
|
|
|
<v-card class="pa-0" style="min-height: 360px; max-height: 360px">
|
|
|
|
|
<div style="padding: 16px; border-bottom: 1px solid #ccc">
|
|
|
|
|
<h3 class="text-subtitle-1 font-weight-bold mb-0">
|
|
|
|
|
Recently Registered Workflow
|
|
|
|
|
</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="overflow-y: auto; max-height: 300px; padding: 8px 16px">
|
|
|
|
|
<v-list density="comfortable" nav>
|
|
|
|
|
<v-list-item v-for="(item, idx) in dummyWorkflow" :key="idx">
|
|
|
|
|
<template #title>
|
|
|
|
|
<div class="d-flex justify-space-between align-center w-100">
|
|
|
|
|
<span class="text-body-2 font-weight-medium">{{
|
|
|
|
|
item.title
|
|
|
|
|
}}</span>
|
|
|
|
|
<span class="text-caption text-grey-lighten-1">{{
|
|
|
|
|
item.date
|
|
|
|
|
}}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</v-list-item>
|
|
|
|
|
</v-list>
|
|
|
|
|
</div>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-col>
|
|
|
|
|
</v-row>
|
|
|
|
|
|
|
|
|
|
<v-row class="mt-4">
|
|
|
|
|
<v-col cols="12" md="6">
|
|
|
|
|
<v-card class="pa-0" style="min-height: 360px; max-height: 360px">
|
|
|
|
|
<div style="padding: 16px; border-bottom: 1px solid #ccc">
|
|
|
|
|
<h3 class="text-subtitle-1 font-weight-bold mb-0">Recent Run</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="overflow-y: auto; max-height: 300px; padding: 8px 16px">
|
|
|
|
|
<v-list density="comfortable">
|
|
|
|
|
<v-list-item
|
|
|
|
|
v-for="(run, idx) in recentRuns"
|
|
|
|
|
:key="idx"
|
|
|
|
|
class="py-2"
|
|
|
|
|
>
|
|
|
|
|
<div class="d-flex align-center">
|
|
|
|
|
<v-avatar
|
|
|
|
|
size="28"
|
|
|
|
|
:color="
|
|
|
|
|
run.status === 'success'
|
|
|
|
|
? 'green lighten-1'
|
|
|
|
|
: 'red lighten-1'
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<v-icon size="20" color="white">
|
|
|
|
|
{{ run.status === "success" ? "mdi-check" : "mdi-close" }}
|
|
|
|
|
</v-icon>
|
|
|
|
|
</v-avatar>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
class="d-flex flex-column text-right ml-4"
|
|
|
|
|
style="flex: 1"
|
|
|
|
|
>
|
|
|
|
|
<span class="font-weight-medium text-body-2">
|
|
|
|
|
{{ run.name }}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="text-caption text-grey-darken-1">
|
|
|
|
|
{{ run.time }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</v-list-item>
|
|
|
|
|
</v-list>
|
|
|
|
|
</div>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-col>
|
|
|
|
|
<v-col cols="12" md="6">
|
|
|
|
|
<v-card class="pa-0" style="min-height: 360px; max-height: 360px">
|
|
|
|
|
<div style="padding: 16px; border-bottom: 1px solid #ccc">
|
|
|
|
|
<h3 class="text-subtitle-1 font-weight-bold mb-0">
|
|
|
|
|
Dataset Update Activity
|
|
|
|
|
</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="overflow-y: auto; max-height: 300px; padding: 8px 16px">
|
|
|
|
|
<v-list dense>
|
|
|
|
|
<v-list-item v-for="(data, idx) in datasetUpdates" :key="idx">
|
|
|
|
|
<v-list-item-title>{{ data.name }}</v-list-item-title>
|
|
|
|
|
<v-progress-linear
|
|
|
|
|
:model-value="data.count * 10"
|
|
|
|
|
height="8"
|
|
|
|
|
color="primary"
|
|
|
|
|
class="mt-1"
|
|
|
|
|
/>
|
|
|
|
|
<v-list-item-subtitle
|
|
|
|
|
>{{ data.count }} updates</v-list-item-subtitle
|
|
|
|
|
>
|
|
|
|
|
</v-list-item>
|
|
|
|
|
</v-list>
|
|
|
|
|
</div>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-col>
|
|
|
|
|
</v-row>
|
|
|
|
|
|
|
|
|
|
<v-card class="rounded-lg pa-4 mt-4">
|
|
|
|
|
<div class="d-flex justify-space-between align-center mt-8 mb-2 px-2">
|
|
|
|
|
<div class="d-flex align-center">
|
|
|
|
|
<span class="text-subtitle-1 font-weight-bold">Model Deployment</span>
|
|
|
|
|
</div>
|
|
|
|
|
<v-btn
|
|
|
|
|
variant="text"
|
|
|
|
|
class="text-caption font-weight-bold"
|
|
|
|
|
append-icon="mdi-arrow-right"
|
|
|
|
|
style="text-transform: none"
|
|
|
|
|
>
|
|
|
|
|
Go to Model Deploy
|
|
|
|
|
</v-btn>
|
|
|
|
|
</div>
|
|
|
|
|
<v-col cols="12">
|
|
|
|
|
<v-sheet>
|
|
|
|
|
<v-table density="comfortable" fixed-header height="625">
|
|
|
|
|
<colgroup>
|
|
|
|
|
<col style="width: 5%" />
|
|
|
|
|
<col
|
|
|
|
|
v-for="(item, i) in tableHeader"
|
|
|
|
|
:key="i"
|
|
|
|
|
:style="`width:${item.width}`"
|
|
|
|
|
/>
|
|
|
|
|
</colgroup>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>
|
|
|
|
|
<v-checkbox
|
|
|
|
|
v-model="data.allSelected"
|
|
|
|
|
style="min-width: 36px"
|
|
|
|
|
:indeterminate="data.allSelected === true"
|
|
|
|
|
hide-details
|
|
|
|
|
@change="getSelectedAllData"
|
|
|
|
|
></v-checkbox>
|
|
|
|
|
</th>
|
|
|
|
|
<th
|
|
|
|
|
v-for="(item, i) in tableHeader"
|
|
|
|
|
:key="i"
|
|
|
|
|
class="text-center font-weight-bold"
|
|
|
|
|
:style="item.style"
|
|
|
|
|
>
|
|
|
|
|
{{ item.label }}
|
|
|
|
|
</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody class="text-body-2">
|
|
|
|
|
<tr
|
|
|
|
|
v-for="(item, i) in data.results"
|
|
|
|
|
:key="i"
|
|
|
|
|
class="text-center"
|
|
|
|
|
>
|
|
|
|
|
<td>
|
|
|
|
|
<v-checkbox
|
|
|
|
|
v-model="data.selected"
|
|
|
|
|
hide-details
|
|
|
|
|
:value="{ deviceKey: item.deviceKey }"
|
|
|
|
|
/>
|
|
|
|
|
</td>
|
|
|
|
|
<td>{{ item.name }}</td>
|
|
|
|
|
<td>{{ item.version }}</td>
|
|
|
|
|
<td>{{ item.time }}</td>
|
|
|
|
|
<td>{{ item.status }}</td>
|
|
|
|
|
<td>{{ item.download }}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</v-table>
|
|
|
|
|
</v-sheet>
|
|
|
|
|
</v-col>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-container>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
ul {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding-left: 0;
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
}
|
|
|
|
|
li {
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|