You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
5.5 KiB
208 lines
5.5 KiB
<script setup lang="ts">
|
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
import { storage } from "@/utils/storage";
|
|
import { storeToRefs } from "pinia";
|
|
import { useAutoflowStore } from "@/stores/autoflowStore";
|
|
import type { AxiosError } from "axios";
|
|
import { WorkflowStepService } from "@/components/service/management/workflowStepService";
|
|
|
|
const props = defineProps<{
|
|
editData: any;
|
|
mode: "create" | "edit";
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: "close-modal"): void;
|
|
(e: "saved", value: any): void;
|
|
}>();
|
|
|
|
const isEdit = computed(() => props.mode === "edit");
|
|
const { projectId } = storeToRefs(useAutoflowStore());
|
|
|
|
const saving = ref(false);
|
|
const errorMsg = ref("");
|
|
|
|
// 폼 상태(필수 UI만: stepName, status)
|
|
type StepStatus = "Running" | "Success" | "Fail";
|
|
const form = ref({
|
|
stepName: "",
|
|
status: "Running" as StepStatus,
|
|
});
|
|
|
|
function hydrateFormFromEdit(data: any) {
|
|
if (!data) return;
|
|
form.value.stepName = data?.stepName ?? data?.name ?? "";
|
|
form.value.status = (data?.status as StepStatus) ?? "Running";
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (isEdit.value) hydrateFormFromEdit(props.editData);
|
|
});
|
|
watch(
|
|
() => props.editData,
|
|
(v) => {
|
|
if (isEdit.value) hydrateFormFromEdit(v);
|
|
},
|
|
);
|
|
|
|
const nowLocalIso = (): string => {
|
|
const t = new Date(Date.now() - new Date().getTimezoneOffset() * 60000);
|
|
return t.toISOString().slice(0, 23); // 'YYYY-MM-DDTHH:mm:ss.SSS'
|
|
};
|
|
|
|
const regUserId = (() => {
|
|
try {
|
|
const authObj =
|
|
(typeof storage?.getAuth === "function" ? storage.getAuth() : null) ??
|
|
JSON.parse(localStorage.getItem("autoflow-auth") || "{}");
|
|
|
|
return (
|
|
authObj?.userInfo?.username ??
|
|
authObj?.userinfo?.username ??
|
|
authObj?.username ??
|
|
authObj?.userId ??
|
|
""
|
|
);
|
|
} catch {
|
|
return "";
|
|
}
|
|
})();
|
|
|
|
async function submit() {
|
|
errorMsg.value = "";
|
|
|
|
// 기본 검증
|
|
const stepName = form.value.stepName.trim();
|
|
if (!stepName) {
|
|
errorMsg.value = "Step Name은 필수입니다.";
|
|
return;
|
|
}
|
|
if (!regUserId) {
|
|
errorMsg.value = "로그인 사용자 정보를 찾을 수 없습니다.";
|
|
return;
|
|
}
|
|
if (!projectId.value) {
|
|
errorMsg.value = "프로젝트가 선택되지 않았습니다.";
|
|
return;
|
|
}
|
|
|
|
// 백엔드 엔티티에 맞춘 payload
|
|
const now = nowLocalIso();
|
|
const payload = {
|
|
stepName,
|
|
status: form.value.status, // not null
|
|
regUserId, // not null
|
|
regDt: now, // not null (스웨거 요구)
|
|
version: 1, // not null (초기값)
|
|
projectId: projectId.value, // not null
|
|
|
|
// 선택 항목은 이번 버전에서 제외(pipelineId 없음 = 빈값/누락)
|
|
// pipelineId: undefined,
|
|
// startTime: undefined,
|
|
// endTime: undefined,
|
|
// logPath: undefined,
|
|
};
|
|
|
|
try {
|
|
saving.value = true;
|
|
|
|
if (isEdit.value) {
|
|
// 수정: id 추출(행에서 넘어온 deviceKey 또는 id 허용)
|
|
const rawId = props.editData?.id ?? props.editData?.deviceKey;
|
|
const id = Number(rawId);
|
|
|
|
if (!id) {
|
|
errorMsg.value = "수정할 ID가 없습니다.";
|
|
return;
|
|
}
|
|
|
|
const { data } = await WorkflowStepService.update(id, payload as any);
|
|
emit("saved", data);
|
|
emit("close-modal");
|
|
} else {
|
|
const { data } = await WorkflowStepService.add(payload as any);
|
|
emit("saved", data);
|
|
emit("close-modal");
|
|
}
|
|
} catch (e) {
|
|
const err = e as AxiosError;
|
|
console.error("워크플로우 스텝 저장 실패:", err);
|
|
errorMsg.value = "저장에 실패했습니다. 잠시 후 다시 시도하세요.";
|
|
} finally {
|
|
saving.value = false;
|
|
}
|
|
}
|
|
|
|
function onEsc(e: KeyboardEvent) {
|
|
if (e.key === "Escape") emit("close-modal");
|
|
}
|
|
onMounted(() => window.addEventListener("keydown", onEsc));
|
|
onBeforeUnmount(() => window.removeEventListener("keydown", onEsc));
|
|
</script>
|
|
|
|
<template>
|
|
<v-card>
|
|
<!-- 타이틀: 동일 스타일 -->
|
|
<v-card-title
|
|
class="text-white font-weight-bold text-h6"
|
|
style="background-color: #1976d2"
|
|
>
|
|
{{ isEdit ? "Edit Workflow Step" : "Create Workflow Step" }}
|
|
</v-card-title>
|
|
|
|
<!-- 폼 영역 -->
|
|
<v-card-text class="pa-6">
|
|
<div class="text-subtitle-1 font-weight-medium mb-4">
|
|
Workflow Step Information
|
|
</div>
|
|
|
|
<v-form @submit.prevent="submit">
|
|
<div class="mb-5">
|
|
<label class="text-subtitle-2 font-weight-medium mb-1 d-block">
|
|
Step Name
|
|
</label>
|
|
<v-text-field
|
|
v-model="form.stepName"
|
|
variant="outlined"
|
|
:disabled="saving"
|
|
dense
|
|
hide-details
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="text-subtitle-2 font-weight-medium mb-1 d-block">
|
|
Status
|
|
</label>
|
|
<v-select
|
|
v-model="form.status"
|
|
:items="['Running', 'Success', 'Fail']"
|
|
variant="outlined"
|
|
:disabled="saving"
|
|
dense
|
|
hide-details
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="errorMsg" class="mt-3 text-error">{{ errorMsg }}</div>
|
|
</v-form>
|
|
</v-card-text>
|
|
|
|
<!-- 액션 영역: 동일 버튼 배치/텍스트 -->
|
|
<v-card-actions class="justify-end" style="padding: 16px 24px">
|
|
<v-btn color="success" :loading="saving" @click="submit">
|
|
{{ isEdit ? "Update" : "Save" }}
|
|
</v-btn>
|
|
<v-btn
|
|
text
|
|
class="white--text"
|
|
:disabled="saving"
|
|
@click="$emit('close-modal')"
|
|
>
|
|
Close
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</template>
|