From 8114ca58c5a7c31c51bcbb44471923583bbfca49 Mon Sep 17 00:00:00 2001 From: jschoi Date: Tue, 30 Sep 2025 17:53:05 +0900 Subject: [PATCH] =?UTF-8?q?=20feat:=20DataGroup=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80,=20Executions=20Status?= =?UTF-8?q?=20=EC=88=98=EC=A0=95,=20=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.dev | 2 +- components.d.ts | 4 +- .../atoms/organisms/DatagroupBaseDoalog.vue | 210 +++ .../atoms/organisms/DatasetBaseDoalog.vue | 5 +- .../organisms/TrainingScriptBaseDoalog.vue | 4 +- .../atoms/organisms/WorkflowsBaseDialog.vue | 27 +- src/components/common/LayoutComponent.vue | 25 +- src/components/models/management/Kubeflow.ts | 4 +- .../service/management/DataGroupService.ts | 5 +- .../service/management/kubeflowService.ts | 2 +- .../templates/Datasets/ListComponent.vue | 45 +- .../templates/datagroup/ListComponent.vue | 460 ++++--- .../templates/home/ListComponent.vue | 1218 ++++++++++------- .../run/executions/ListComponent.vue | 247 +--- .../run/executions/ViewComponent.vue | 5 - .../run/experiment/ViewComponent.vue | 105 +- .../templates/stepconfig/ViewComponent.vue | 3 - .../trainingscript/ListComponent.vue | 44 +- .../trainingscriptgroup/ListComponent.vue | 570 ++++++++ .../templates/workflow/ListComponent.vue | 34 +- .../{Datagroup.vue => DatagroupView.vue} | 0 src/pages/SignupView.vue | 1 - src/pages/TrainingscriptgroupView.vue | 9 + src/router/index.js | 8 +- src/utils/menuUtils.js | 35 +- typed-router.d.ts | 3 +- 26 files changed, 2058 insertions(+), 1017 deletions(-) create mode 100644 src/components/atoms/organisms/DatagroupBaseDoalog.vue create mode 100644 src/components/templates/trainingscriptgroup/ListComponent.vue rename src/pages/{Datagroup.vue => DatagroupView.vue} (100%) create mode 100644 src/pages/TrainingscriptgroupView.vue diff --git a/.env.dev b/.env.dev index 7373427..42b5c38 100644 --- a/.env.dev +++ b/.env.dev @@ -1,3 +1,3 @@ NODE_ENV = "development" -VITE_APP_API_SERVER_URL = "http://localhost:80" +VITE_APP_API_SERVER_URL = "http://localhost:8080" VITE_ROOT_PATH = "" \ No newline at end of file diff --git a/components.d.ts b/components.d.ts index f638129..e95cea5 100644 --- a/components.d.ts +++ b/components.d.ts @@ -10,6 +10,7 @@ declare module 'vue' { export interface GlobalComponents { AppFooter: typeof import('./src/components/AppFooter.vue')['default'] CompareComponent: typeof import('./src/components/templates/run/executions/CompareComponent.vue')['default'] + DatagroupBaseDoalog: typeof import('./src/components/atoms/organisms/DatagroupBaseDoalog.vue')['default'] DatasetBaseDoalog: typeof import('./src/components/atoms/organisms/DatasetBaseDoalog.vue')['default'] DeploymentDialog: typeof import('./src/components/atoms/organisms/DeploymentDialog.vue')['default'] DrawerComponent: typeof import('./src/components/common/DrawerComponent.vue')['default'] @@ -26,7 +27,8 @@ declare module 'vue' { IconRunBtn: typeof import('./src/components/atoms/button/IconRunBtn.vue')['default'] IconSettingBtn: typeof import('./src/components/atoms/button/IconSettingBtn.vue')['default'] LayoutComponent: typeof import('./src/components/common/LayoutComponent.vue')['default'] - ListComponent: typeof import('./src/components/templates/Datasets/ListComponent.vue')['default'] + ListComponent: typeof import('./src/components/templates/datagroup/ListComponent.vue')['default'] + ListComponentback: typeof import('./src/components/templates/run/executions/ListComponentback.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SidebarHeader: typeof import('./src/components/common/SidebarHeader.vue')['default'] diff --git a/src/components/atoms/organisms/DatagroupBaseDoalog.vue b/src/components/atoms/organisms/DatagroupBaseDoalog.vue new file mode 100644 index 0000000..7ac1510 --- /dev/null +++ b/src/components/atoms/organisms/DatagroupBaseDoalog.vue @@ -0,0 +1,210 @@ + + + diff --git a/src/components/atoms/organisms/DatasetBaseDoalog.vue b/src/components/atoms/organisms/DatasetBaseDoalog.vue index cd335c6..f78f1ce 100644 --- a/src/components/atoms/organisms/DatasetBaseDoalog.vue +++ b/src/components/atoms/organisms/DatasetBaseDoalog.vue @@ -28,6 +28,7 @@ function hydrateFormFromEdit(d: any) { form.value.description = (d?.description ?? "") + ""; } +const getRefId = () => String(props.editData?.refId ?? "0"); onMounted(() => { if (isEdit.value) hydrateFormFromEdit(props.editData); }); @@ -39,7 +40,7 @@ watch( ); const dialogTitle = computed(() => - isEdit.value ? "Edit Training Script" : "Create Training Script", + isEdit.value ? "Edit DataSet Script" : "Create DataSet Script", ); // 로그인 사용자 @@ -94,7 +95,7 @@ async function submit() { return (errorMsg.value = "프로젝트가 선택되지 않았습니다."); const fd = new FormData(); - fd.append("refId", "0"); + fd.append("refId", getRefId()); fd.append("refType", "DATASET"); fd.append("title", title); fd.append("description", desc); diff --git a/src/components/atoms/organisms/TrainingScriptBaseDoalog.vue b/src/components/atoms/organisms/TrainingScriptBaseDoalog.vue index a616696..02754e3 100644 --- a/src/components/atoms/organisms/TrainingScriptBaseDoalog.vue +++ b/src/components/atoms/organisms/TrainingScriptBaseDoalog.vue @@ -28,6 +28,7 @@ function hydrateFormFromEdit(d: any) { form.value.description = (d?.description ?? "") + ""; } +const getRefId = () => String(props.editData?.refId ?? "0"); onMounted(() => { if (isEdit.value) hydrateFormFromEdit(props.editData); }); @@ -59,7 +60,6 @@ const regUserId = (() => { } })(); -// ✅ 여기만 변경 async function submit() { errorMsg.value = ""; @@ -95,7 +95,7 @@ async function submit() { return (errorMsg.value = "프로젝트가 선택되지 않았습니다."); const fd = new FormData(); - fd.append("refId", "0"); + fd.append("refId", getRefId()); fd.append("refType", "TRAINING_SCRIPT"); fd.append("title", title); fd.append("description", desc); diff --git a/src/components/atoms/organisms/WorkflowsBaseDialog.vue b/src/components/atoms/organisms/WorkflowsBaseDialog.vue index 068830b..038aa27 100644 --- a/src/components/atoms/organisms/WorkflowsBaseDialog.vue +++ b/src/components/atoms/organisms/WorkflowsBaseDialog.vue @@ -169,14 +169,10 @@ async function submit() { const authObj = (typeof storage?.getAuth === "function" ? storage.getAuth() : null) ?? JSON.parse(localStorage.getItem("autoflow-auth") || "{}"); - const regUserId = - authObj?.userInfo?.username ?? - authObj?.userinfo?.username ?? - authObj?.username ?? - authObj?.userId ?? - ""; - if (!regUserId) { + const ui = authObj?.userInfo ?? authObj?.userinfo ?? authObj ?? {}; + const userId = Number(ui.id); // 숫자 ID + if (!userId) { errorMsg.value = "로그인 사용자 정보를 찾을 수 없습니다."; return; } @@ -198,28 +194,21 @@ async function submit() { errorMsg.value = "수정할 ID가 없습니다."; return; } - - // ① 기존 값 조회 const viewRes = await WorkflowService.view(id); const current = (viewRes?.data ?? viewRes) || {}; - // ② name/description만 변경, 그 외는 기존 값 유지해서 null 덮어쓰기 방지 const updatePayload = cleanUndefined({ id, - name, // 변경 - description: form.value.description?.trim() || "", // 변경 - - // ===== 기존 유지 필드 ===== + name, + description: form.value.description?.trim() || "", displayName: current.displayName, namespace: current.namespace, pipelineId: current.pipelineId, kubeflowStatus: current.kubeflowStatus, version: current.version, - - regUserId: current.regUserId ?? regUserId, - projectId: current.projectId ?? projectId.value, + regUserId: current.regUserId, regDt: current.regDt, - modDt: now, + projectId: current.projectId ?? projectId.value, }); const { data } = await WorkflowService.update(id, updatePayload); @@ -236,7 +225,7 @@ async function submit() { display_name: name, description: form.value.description?.trim() || "", namespace: "default", - regUserId, + regUserId: userId, projectId: projectId.value!, uploadfile: form.value.file, }; diff --git a/src/components/common/LayoutComponent.vue b/src/components/common/LayoutComponent.vue index b4cc1a2..a9a6049 100644 --- a/src/components/common/LayoutComponent.vue +++ b/src/components/common/LayoutComponent.vue @@ -12,12 +12,9 @@ const router = useRouter(); const username = ref(""); const projectName = ref(localStorage.getItem("projectName") || ""); -// ---------------------- -// Admin 판별 + Admin 모드 -// ---------------------- -const isAdmin = ref(false); // 관리자 계정 여부 -const adminMode = ref(false); // 설정 버튼으로 토글되는 관리자 메뉴 모드 -const lastNonAdminPath = ref("/home"); // 마지막 일반 경로 저장 +const isAdmin = ref(false); +const adminMode = ref(false); +const lastNonAdminPath = ref("/home"); function computeIsAdmin() { try { @@ -79,11 +76,19 @@ function updateUsername() { const auth = storage.getAuth?.() ?? null; username.value = auth?.userInfo?.username ?? auth?.username ?? ""; } +function syncAdminModeWithRoute() { + const isAdminRoute = route.matched.some((r) => r.meta?.requiresAdmin); + if (!isAdminRoute && adminMode.value) { + adminMode.value = false; + } +} + function refreshProjectName() { const v = localStorage.getItem("projectName"); projectName.value = v ? v : ""; } function goSelect() { + adminMode.value = false; router.push("/select"); } function logOut() { @@ -103,13 +108,17 @@ function logOut() { // storage 변경 반영 function onStorage(e) { - if (!e.key || e.key === "projectName") refreshProjectName(); + if (!e.key || e.key === "projectName") { + refreshProjectName(); + adminMode.value = false; + } if (!e.key || e.key === "autoflow-auth" || e.key === "auth") { updateUsername(); computeIsAdmin(); } } const goMain = () => { + adminMode.value = false; router.push("/home"); }; @@ -120,6 +129,7 @@ watch( refreshProjectName(); const isAdminRoute = route.matched.some((r) => r.meta?.requiresAdmin); if (!isAdminRoute) lastNonAdminPath.value = route.fullPath || "/home"; + syncAdminModeWithRoute(); }, { immediate: true }, ); @@ -130,6 +140,7 @@ onMounted(() => { refreshProjectName(); menu.value = menuItems; window.addEventListener("storage", onStorage); + syncAdminModeWithRoute(); }); onBeforeUnmount(() => { window.removeEventListener("storage", onStorage); diff --git a/src/components/models/management/Kubeflow.ts b/src/components/models/management/Kubeflow.ts index 9d3be98..f2edec4 100644 --- a/src/components/models/management/Kubeflow.ts +++ b/src/components/models/management/Kubeflow.ts @@ -3,13 +3,11 @@ export type KubeflowUploadDto = { display_name?: string; description?: string; namespace?: string; - regUserId: string; + regUserId: string | number; // number도 허용 projectId: number | string; uploadfile: File | Blob; }; -export type kubeflow = FormData; - export function toKubeflowForm(dto: KubeflowUploadDto): FormData { const fd = new FormData(); fd.append("name", dto.name); diff --git a/src/components/service/management/DataGroupService.ts b/src/components/service/management/DataGroupService.ts index 53ed3ea..c89360d 100644 --- a/src/components/service/management/DataGroupService.ts +++ b/src/components/service/management/DataGroupService.ts @@ -2,6 +2,7 @@ import { DataGroup, DataGroupSearch, } from "@/components/models/management/DataGroup"; + import { request } from "@/components/service/index"; export const DataGroupService = { add: (payload: DataGroup) => { @@ -16,8 +17,8 @@ export const DataGroupService = { view: (id: Number) => { return request.get(`/api/datagroup/${id}`, {}); }, - update: (id: number, payload: DataGroup) => { - return request.put(`/api/datagroup/${id}`, payload); + update: (id: number, updatePayload: DataGroup) => { + return request.put(`/api/datagroup/${id}`, updatePayload); }, search: (payload: DataGroupSearch) => { return request.get("/api/datagroup/search", payload); diff --git a/src/components/service/management/kubeflowService.ts b/src/components/service/management/kubeflowService.ts index dabdf08..d27d7a7 100644 --- a/src/components/service/management/kubeflowService.ts +++ b/src/components/service/management/kubeflowService.ts @@ -1,4 +1,4 @@ -import { kubeflow } from "@/components/models/management/Kubeflow"; +import { Kubeflow } from "@/components/models/management/Kubeflow"; import { request } from "@/components/service/index"; export const KubeflowService = { upload: (payload: kubeflow) => { diff --git a/src/components/templates/Datasets/ListComponent.vue b/src/components/templates/Datasets/ListComponent.vue index 7b8db49..98c6c30 100644 --- a/src/components/templates/Datasets/ListComponent.vue +++ b/src/components/templates/Datasets/ListComponent.vue @@ -2,13 +2,18 @@ import IconDeleteBtn from "@/components/atoms/button/IconDeleteBtn.vue"; import IconModifyBtn from "@/components/atoms/button/IconModifyBtn.vue"; import IconInfoBtn from "@/components/atoms/button/IconInfoBtn.vue"; -import { onMounted, ref } from "vue"; +import { computed, onMounted, ref, watch } from "vue"; import { storage } from "@/utils/storage"; import ViewComponent from "@/components/templates/Datasets/ViewComponent.vue"; import DatasetBaseDoalog from "@/components/atoms/organisms/DatasetBaseDoalog.vue"; import { AttachmentsService } from "@/components/service/management/AttachmentsService"; import { commonStore } from "@/stores/commonStore"; +import { useRoute, useRouter } from "vue-router"; +const route = useRoute(); +const router = useRouter(); +const activeRefId = ref(null); +const activeRefName = computed(() => String(route.query.refName)); const store = commonStore(); const openView = ref(false); const openModify = ref(false); @@ -69,6 +74,12 @@ const data = ref({ userOption: [] as any[], }); +function getRefIdFromRoute(q: any): number | null { + const raw = q?.refId; + const n = Number(raw); + return Number.isFinite(n) && n > 0 ? n : null; +} + // 유저명 function readUsernameFromStorage(): string { try { @@ -140,6 +151,7 @@ const fetchList = async () => { sortField: "id", sortDirection: "DESC", refType: "DATASET", + refId: activeRefId.value, }; try { @@ -286,6 +298,7 @@ const openCreateModal = () => { data.value.selectedData = { username: username.value, projectId: getProjectId(), + refId: activeRefId.value, }; data.value.isCreateVisible = true; }; @@ -318,8 +331,18 @@ const getSelectedAllData = () => { }; onMounted(() => { username.value = readUsernameFromStorage(); + activeRefId.value = getRefIdFromRoute(route.query); fetchList(); }); + +watch( + () => route.query.refId, + () => { + activeRefId.value = getRefIdFromRoute(route.query); + data.value.params.pageNum = 1; + fetchList(); + }, +);