Files
butler_ddochi/board_notice/templates/board_notice/update_notice.html
2025-04-09 23:53:14 +09:00

194 lines
6.5 KiB
HTML

{% extends "components/base.html" %}
{% block title %}notice edit{% endblock %}
{% block main_area %}
<h1 class="pt-3">notice edit</h1>
<div class="container">
<div class="row mt-3">
<div class="col-md-6">
<form method="POST" id="post-form">
{% csrf_token %}
<div class="mb-3">
{{ form.title.label_tag }}
{{ form.title }}
</div>
<div class="mb-3">
{{ form.summary.label_tag }}
{{ form.summary }}
</div>
<div class="mb-3">
{{ form.tags.label_tag }}
{{ form.tags }}
</div>
<!-- 마크다운 에디터 -->
<h2>Contents</h2>
<div class="col-md-12">
{{ form.contents }}
</div>
<!-- 버튼 -->
<div class="d-flex justify-content-end mt-4">
<button type="submit" class="btn btn-primary me-2">Save</button>
<a href="{% url 'board_notice:notice_list' %}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
<div class="col-md-6">
<div id="preview" class="border p-3 bg-light h-100 overflow-auto"></div>
</div>
</div>
<!-- 마크다운 파서 및 스크립트 -->
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
<script>
const md = window.markdownit({
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs
.highlight(str, {language: lang})
.value;
} catch (__) {}
}
return '';
}
});
const textarea = document.getElementById("markdown-editor"); // ✅ 반드시 id="markdown-editor" 되어야 함
const preview = document.getElementById("preview");
let presignedUrlMap = new Map(); // object_name => presigned_url 매핑
// textarea 내용에서 presigned URL을 받아오기
async function generatePresignedUrls() {
const markdownContent = textarea.value;
const lines = markdownContent.split("\n");
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(/!\[Image\]\((.+)\)/);
if (match) {
const objectName = match[1];
if (!presignedUrlMap.has(objectName)) { // 이미 있으면 다시 안 불러옴
try {
const response = await fetch("/obs_minio/get_presigned_url/", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({object_name: objectName})
});
if (response.ok) {
const data = await response.json();
presignedUrlMap.set(objectName, data.presigned_url);
}
} catch (error) {
console.error("Error fetching presigned URL:", error);
}
}
}
}
}
// textarea의 내용을 presigned URL 적용해서 렌더링
function updatePreview() {
const markdownContent = textarea.value;
const lines = markdownContent.split("\n");
const previewLines = [...lines];
for (let i = 0; i < previewLines.length; i++) {
const match = previewLines[i].match(/!\[Image\]\((.+)\)/);
if (match) {
const objectName = match[1];
if (presignedUrlMap.has(objectName)) {
const presignedUrl = presignedUrlMap.get(objectName);
previewLines[i] = `![Image](${presignedUrl})`;
}
}
}
preview.innerHTML = md.render(previewLines.join("\n"));
document
.querySelectorAll("#preview pre code")
.forEach((block) => {
hljs.highlightElement(block);
});
}
// 페이지 처음 열릴 때
document.addEventListener("DOMContentLoaded", async function () {
await generatePresignedUrls(); // presigned URL 가져오기
updatePreview(); // 그리고 미리보기 렌더링
});
// 키 입력할 때
textarea.addEventListener("input", function () {
updatePreview(); // 항상 최신 입력을 presigned 매핑으로 렌더링
});
// 이미지 붙여넣기
textarea.addEventListener("paste", async function (event) {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (const item of items) {
if (item.type.startsWith("image/")) {
const file = item.getAsFile();
if (!file)
return;
const formData = new FormData();
formData.append("image", file);
try {
const response = await fetch("/obs_minio/upload/", {
method: "POST",
body: formData
});
if (response.ok) {
const data = await response.json();
const fullImageUrl = data.url;
const objectName = fullImageUrl
.split("/")
.slice(-2)
.join("/");
const markdownImage = `![Image](${objectName})\n`;
// 현재 커서 위치에 붙여넣기
const cursorPos = textarea.selectionStart;
const textBefore = textarea
.value
.substring(0, cursorPos);
const textAfter = textarea
.value
.substring(cursorPos);
textarea.value = textBefore + markdownImage + textAfter;
textarea.focus();
// 새 이미지니까 presigned map 갱신 필요
presignedUrlMap.delete(objectName);
await generatePresignedUrls(); // 새로 presigned URL 받아오기
updatePreview(); // 그리고 렌더링
} else {
alert("Image upload failed. Please try again.");
}
} catch (error) {
console.error("Error uploading image:", error);
alert("An error occurred during image upload.");
}
}
}
});
</script>
</div>
{% endblock %}