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
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>
|
|
<div class="d-flex justify-end mb-2">
|
|
<v-btn color="primary" @click="emit('close')">Back to List</v-btn>
|
|
</div>
|
|
</v-card>
|
|
</v-container>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.editor-container {
|
|
width: 100%;
|
|
height: 400px;
|
|
}
|
|
</style>
|