feat(project): 업무 설명란을 간단한 워드프로세서 에디터로 — 서식 도구 추가

굵게/기울임/밑줄/글머리목록/번호목록/인용구/링크/서식지우기 버튼을
설명란 위에 추가. execCommand 기반(같은 파일의 이미지 삽입에서 이미
insertHTML 로 쓰던 것과 같은 패턴)이고, 생성되는 태그(b/i/u/ul/ol/li/
blockquote/a)는 전부 기존 sanitize_description_html 허용 목록 안이라
서버 쪽 변경 없음. 버튼 mousedown 에서 preventDefault 해 클릭 시 에디터
선택범위가 풀리는 것 방지, 커서 위치에 따라 버튼 활성 상태 표시.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 00:23:12 +09:00
parent a7a294ce93
commit 79be87e7e9
5 changed files with 79 additions and 8 deletions
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260917a" />
<link rel="stylesheet" href="/static/project.css?v=20260917b" />
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
{% endblock %}
@@ -45,5 +45,5 @@
{% endif %}
</div>
<script src="/static/project.js?v=20260916o" defer></script>
<script src="/static/project.js?v=20260917a" defer></script>
{% endblock %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260917a" />
<link rel="stylesheet" href="/static/project.css?v=20260917b" />
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
{% endblock %}
@@ -290,12 +290,24 @@
<label>마감 시간<input type="time" id="pj-t-dtime" disabled /></label>
</div>
<!-- 설명 -->
<!-- 설명 — 간단한 워드프로세서식 서식 도구 + 이미지 삽입 -->
<div class="pj-desc-toolbar">
<span class="pj-extra-label">설명</span>
<label class="pj-upload-btn">이미지 추가<input type="file" id="pj-desc-img-input" accept="image/*" hidden /></label>
<span class="pj-muted-sm" id="pj-desc-hint"></span>
</div>
<div class="pj-desc-fmtbar" id="pj-desc-fmtbar">
<button type="button" class="pj-desc-fmtbtn" data-cmd="bold" title="굵게 (Ctrl+B)"><b>B</b></button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="italic" title="기울임 (Ctrl+I)"><i>I</i></button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="underline" title="밑줄 (Ctrl+U)"><u>U</u></button>
<span class="pj-desc-fmtsep"></span>
<button type="button" class="pj-desc-fmtbtn" data-cmd="insertUnorderedList" title="글머리 기호 목록">☰•</button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="insertOrderedList" title="번호 매기기 목록">☰1</button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="formatBlock" data-arg="blockquote" title="인용구"></button>
<span class="pj-desc-fmtsep"></span>
<button type="button" class="pj-desc-fmtbtn" data-cmd="createLink" title="링크 삽입">🔗</button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="removeFormat" title="서식 지우기">✕서식</button>
</div>
<div class="pj-desc-wrap">
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
</div>
@@ -380,5 +392,5 @@
<script>
window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }};
</script>
<script src="/static/project.js?v=20260916o" defer></script>
<script src="/static/project.js?v=20260917a" defer></script>
{% endblock %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260917a" />
<link rel="stylesheet" href="/static/project.css?v=20260917b" />
<!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host -->
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
<!-- 타임라인 vis-timeline — self-host -->
@@ -277,12 +277,24 @@
<label>마감 시간<input type="time" id="pj-t-dtime" disabled /></label>
</div>
<!-- 설명 -->
<!-- 설명 — 간단한 워드프로세서식 서식 도구 + 이미지 삽입 -->
<div class="pj-desc-toolbar">
<span class="pj-extra-label">설명</span>
<label class="pj-upload-btn">이미지 추가<input type="file" id="pj-desc-img-input" accept="image/*" hidden /></label>
<span class="pj-muted-sm" id="pj-desc-hint"></span>
</div>
<div class="pj-desc-fmtbar" id="pj-desc-fmtbar">
<button type="button" class="pj-desc-fmtbtn" data-cmd="bold" title="굵게 (Ctrl+B)"><b>B</b></button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="italic" title="기울임 (Ctrl+I)"><i>I</i></button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="underline" title="밑줄 (Ctrl+U)"><u>U</u></button>
<span class="pj-desc-fmtsep"></span>
<button type="button" class="pj-desc-fmtbtn" data-cmd="insertUnorderedList" title="글머리 기호 목록">☰•</button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="insertOrderedList" title="번호 매기기 목록">☰1</button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="formatBlock" data-arg="blockquote" title="인용구"></button>
<span class="pj-desc-fmtsep"></span>
<button type="button" class="pj-desc-fmtbtn" data-cmd="createLink" title="링크 삽입">🔗</button>
<button type="button" class="pj-desc-fmtbtn" data-cmd="removeFormat" title="서식 지우기">✕서식</button>
</div>
<div class="pj-desc-wrap">
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
</div>
@@ -393,5 +405,5 @@
</script>
<script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script>
<script src="/static/project.js?v=20260916o" defer></script>
<script src="/static/project.js?v=20260917a" defer></script>
{% endblock %}