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.

183 lines
5.6 KiB

<script setup lang="ts">
import {
defineProps,
defineEmits,
ref,
computed,
onMounted,
watch,
onBeforeUnmount,
} from "vue";
import * as monaco from "monaco-editor";
import "monaco-editor/min/vs/editor/editor.main.css";
import { AttachmentsService } from "@/components/service/management/AttachmentsService";
const props = defineProps<{ id: number | string }>();
const emit = defineEmits<{ (e: "close"): void }>();
const loading = ref(false);
const detailRaw = ref<any | null>(null);
const editorRef = ref<HTMLDivElement | null>(null);
let editorInstance: monaco.editor.IStandaloneCodeEditor | null = null;
const formatIso = (s?: string) =>
s ? String(s).replace("T", " ").slice(0, 19) : "-";
const mapToViewModel = (raw: any) => ({
title: raw?.title ?? "-",
fileName: raw?.originalName ?? "-",
filePath: raw?.storagePath ?? "-",
createdDate: formatIso(raw?.regDt),
modifiedDate: "-",
createdId: raw?.regUserId ?? "-",
description: raw?.description ?? "-",
});
const info = computed(() => mapToViewModel(detailRaw.value || {}));
function ensureEditor() {
if (editorInstance || !editorRef.value) return;
editorInstance = monaco.editor.create(editorRef.value, {
value: "",
language: "plaintext",
theme: "vs-dark",
readOnly: true,
automaticLayout: true,
minimap: { enabled: false },
lineNumbers: "on",
});
}
async function loadPreviewFromStoragePath(objectName?: string) {
const key = (objectName || "").trim();
if (!key) return;
// ✅ 전용 메서드로 호출 (쿼리 방식)
const res = await AttachmentsService.readTextByPath(key);
const text =
typeof res?.data === "string" ? res.data : String(res?.data ?? "");
ensureEditor();
editorInstance?.setValue(text || "# (empty)");
}
/** 상세 조회 후 storagePath로 프리뷰 호출 */
async function fetchDetail(id: number | string) {
const idNum = typeof id === "string" ? Number(id) : id;
if (!Number.isFinite(idNum as number)) return;
loading.value = true;
try {
const res = await AttachmentsService.view(idNum as number);
detailRaw.value = res?.data ?? res;
ensureEditor();
editorInstance?.setValue(
"# Preview (loading...)\n" +
`# title: ${info.value.title}\n` +
`# file : ${info.value.fileName}\n`,
);
await loadPreviewFromStoragePath(
detailRaw.value?.storagePath || detailRaw.value?.storedName,
);
} catch (e) {
console.error("[TrainingScript View] fetch detail error:", e);
} finally {
loading.value = false;
}
}
onMounted(() => {
ensureEditor();
fetchDetail(props.id);
});
watch(
() => props.id,
(now) => {
if (now !== undefined && now !== null && now !== "") fetchDetail(now);
},
);
onBeforeUnmount(() => {
editorInstance?.dispose();
editorInstance = null;
});
</script>
<template>
<v-container fluid class="h-100 pa-5 d-flex flex-column align-center">
<v-card flat class="bg-shades-transparent w-100 mb-6">
<v-card-item class="text-h5 font-weight-bold pt-0 pa-5 pl-0">
<div class="d-flex flex-row justify-start align-center">
<div>View Details</div>
</div>
</v-card-item>
</v-card>
<v-card flat class="bordered-box mb-6 w-100 rounded-lg pa-8">
<v-card-title class="grey lighten-4 py-2 px-4">
<span class="font-weight-bold">Training Script Information</span>
</v-card-title>
<v-card-text class="px-6 pb-6 pt-4">
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold"
>Training Script Title</v-col
>
<v-col cols="9" class="pa-2">{{ info.title }}</v-col>
</v-row>
<v-divider class="my-2" />
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold">File Name</v-col>
<v-col cols="9" class="pa-2">{{ info.fileName }}</v-col>
</v-row>
<v-divider class="my-2" />
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold">File Path</v-col>
<v-col cols="9" class="pa-2" style="word-break: break-all">{{
info.filePath
}}</v-col>
</v-row>
<v-divider class="my-2" />
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold">Created Date</v-col>
<v-col cols="3" class="pa-2">{{ info.createdDate }}</v-col>
<v-col cols="3" class="text-h6 font-weight-bold">Modified Date</v-col>
<v-col cols="3" class="pa-2">{{ info.modifiedDate }}</v-col>
</v-row>
<v-divider class="my-2" />
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold">Created ID</v-col>
<v-col cols="9" class="pa-2">{{ info.createdId }}</v-col>
</v-row>
<v-divider class="my-2" />
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold">Description</v-col>
<v-col cols="9" class="pa-2">{{ info.description }}</v-col>
</v-row>
</v-card-text>
</v-card>
<!-- 미리보기 -->
<v-card flat class="bordered-box mb-6 w-100 rounded-lg pa-8">
<v-card-title class="grey lighten-4 py-2 px-4">
<span class="font-weight-bold">Training Script Preview</span>
</v-card-title>
<v-card-text class="px-6 pb-6 pt-4">
<div ref="editorRef" class="editor-container"></div>
</v-card-text>
<v-sheet class="d-flex justify-end mb-2">
<v-btn color="primary" @click="emit('close')">Back to List</v-btn>
</v-sheet>
</v-card>
</v-container>
</template>
<style scoped>
.editor-container {
width: 100%;
height: 400px;
}
</style>