From 482b35da8d5b1ee2e4669d362da64c80da941193 Mon Sep 17 00:00:00 2001 From: jschoi Date: Wed, 29 Oct 2025 12:27:13 +0900 Subject: [PATCH] =?UTF-8?q?feat:Deployment=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../atoms/organisms/DeploymentDialog.vue | 27 +- src/components/common/LayoutComponent.vue | 320 ++++++++++-------- .../run/executions/ViewComponent.vue | 42 ++- 3 files changed, 243 insertions(+), 146 deletions(-) diff --git a/src/components/atoms/organisms/DeploymentDialog.vue b/src/components/atoms/organisms/DeploymentDialog.vue index aba3aab..8dda54a 100644 --- a/src/components/atoms/organisms/DeploymentDialog.vue +++ b/src/components/atoms/organisms/DeploymentDialog.vue @@ -21,6 +21,8 @@ const props = defineProps<{ packagesError?: string; artifactPath?: string; token: string; + hideArtifactPath?: boolean; + hideUploadFile?: boolean; }>(); const emit = defineEmits<{ @@ -74,7 +76,9 @@ const remoteError = ref(""); const pkgOptions = computed(() => remotePackages.value.length ? remotePackages.value : (props.packages ?? []), ); - +/* ========================= 표시/숨김 스위치 ========================= */ +const showArtifactPath = computed(() => !!props.artifactPath); +const showUploadFile = computed(() => !showArtifactPath.value); function getCurrentUserId(): string { // 서버가 id를 요구한다고 하셔서, 저장돼 있을 법한 키 몇 개를 순차 확인 try { @@ -258,7 +262,16 @@ async function submit() { sw_type: sourceType.value === "edge" ? 1 : 0, creation_datetime: new Date().toISOString(), }; - + if (props.hideArtifactPath) { + // Upload 모드 → 파일 필수 + if (!file.value) return (errorMsg.value = "업로드 파일을 선택하세요."); + } + if (props.hideUploadFile) { + // Artifact 모드 → artifactPath 필수, 그리고 파일은 무시 + if (file.value) clearFile(); + if (!props.artifactPath) + return (errorMsg.value = "Artifact 경로가 없습니다."); + } try { saving.value = true; let res: any; @@ -501,7 +514,7 @@ onBeforeUnmount(() => window.removeEventListener("keydown", onEsc)); - + window.removeEventListener("keydown", onEsc)); - +
업로드 파일
파일 선택 - {{ file.name }} ({{ file.size.toLocaleString() }} bytes) + + {{ file.name }} ({{ file.size.toLocaleString() }} bytes) + (""); const projectName = ref(localStorage.getItem("projectName") || ""); const isAdmin = ref(false); -const adminMode = ref(false); +const adminMode = ref(false); // Settings 버튼으로 온/오프 const lastNonAdminPath = ref("/home"); +/* ================================ + * Auth / Role helpers + * ================================ */ function readAuth() { try { const raw = @@ -43,6 +46,7 @@ function readAuth() { return null; } } + function computeIsAdmin() { const auth = readAuth(); const roles = auth?.userInfo?.roles ?? auth?.roles ?? []; @@ -52,6 +56,7 @@ function computeIsAdmin() { : roles === "ROLE_ADMIN"; isAdmin.value = inRoles || authCd === "ADMIN"; } + function updateUsername() { const auth = readAuth(); username.value = auth?.userInfo?.username ?? auth?.username ?? ""; @@ -59,6 +64,8 @@ function updateUsername() { /* ================================ * Derived route state + * - /select 에서는 상단 메뉴 전체 숨김 + * - 관리자 탭 표시 조건: adminMode ON || 관리자 라우트 * ================================ */ const hideAllMenus = computed(() => route.path.startsWith("/select")); @@ -71,16 +78,18 @@ const isAdminRoute = computed(() => { const hitMeta = route.matched.some((r) => r.meta?.requiresAdmin); return hitPath || hitMeta; }); + const showAdminTabs = computed( () => adminMode.value || isAdminRoute.value, ); /* ================================ - * Menus + * Menus (기본/관리자) * ================================ */ const baseMenus = computed( () => (menuUtils?.menuItem ?? []) as MenuItem[], ); + const adminMenus = computed(() => { const fromUtil = (menuUtils?.adminMenuItem ?? []) as MenuItem[]; return fromUtil.length @@ -90,75 +99,50 @@ const adminMenus = computed(() => { { title: "Users", icon: "mdi-account-multiple", path: "/users" }, ]; }); + const isLinkActive = (path?: string) => !!path && route.path.startsWith(path); /* ================================ - * 사용자 메뉴 (우측) + * Header dropdown menu * ================================ */ const menu = ref([]); const menuItems: MenuItem[] = [ { title: "Select Project", click: () => goSelect() }, - { title: "Change Password", click: () => {} }, + { + title: "Change Password", + click: () => { + /* open modal */ + }, + }, { title: "Logout", icon: "mdi-logout", click: () => logOut() }, ]; /* ================================ - * 상단 Hover 하위 메뉴 스트립 - * ================================ */ -type DepthItem = { title: string; path: string }; - -const hoverBar = ref<{ - open: boolean; - items: DepthItem[]; -}>({ open: false, items: [] }); - -let hideTimer: number | null = null; - -function showHoverStrip(m: MenuItem) { - if (!m.depth?.length) return; - if (hideTimer) { - window.clearTimeout(hideTimer); - hideTimer = null; - } - hoverBar.value = { - open: true, - items: m.depth, - }; -} -function scheduleHideStrip() { - if (hideTimer) window.clearTimeout(hideTimer); - hideTimer = window.setTimeout(() => { - hoverBar.value.open = false; - }, 140); -} -function keepStrip() { - if (hideTimer) { - window.clearTimeout(hideTimer); - hideTimer = null; - } -} - -/* ================================ - * Navigation + * Navigation actions * ================================ */ function goMain() { adminMode.value = false; router.push("/home"); } + function goSelect() { adminMode.value = false; router.push("/select"); } + function toggleAdmin() { if (!isAdmin.value) return; if (adminMode.value) { + // 끌 때는 무조건 홈으로 adminMode.value = false; router.push("/home"); } else { adminMode.value = true; + // 켰는데 일반 라우트면 프로젝트로 이동 if (!isAdminRoute.value) router.push("/project"); } } + function logOut() { UserManagerService.signOut() .catch(console.error) @@ -181,16 +165,18 @@ function logOut() { function refreshProjectName() { projectName.value = localStorage.getItem("projectName") || ""; } + +// 라우트 변경 시 프로젝트명 동기화 및 마지막 일반 경로 저장 watch( () => route.fullPath, () => { refreshProjectName(); - // 라우트가 바뀌면 스트립 닫기 - hoverBar.value.open = false; if (!isAdminRoute.value) lastNonAdminPath.value = route.fullPath || "/home"; }, { immediate: true }, ); + +// storage 이벤트 동기화 function onStorage(e: StorageEvent) { if (!e.key || e.key === "projectName") { refreshProjectName(); @@ -212,9 +198,9 @@ onMounted(() => { menu.value = menuItems; window.addEventListener("storage", onStorage); }); + onBeforeUnmount(() => { window.removeEventListener("storage", onStorage); - if (hideTimer) window.clearTimeout(hideTimer); }); @@ -232,24 +218,54 @@ onBeforeUnmount(() => { AUTOFLOW WEB CONSOLE
- - - - -
- + +
+ +