fix(project): 드래그 후 모달 재오픈 방지 + 타임라인 한글 날짜·휠 좌우이동

- 드래그 이동 후 location.reload() 시 URL 의 ?task= 가 남아 업무 모달이
  다시 뜨던 문제 수정: ?task 처리 직후 history.replaceState 로 파라미터 제거
- 타임라인 날짜 라벨 한글화(format minor/major 함수: 'M월 D일 (요일)', 'YYYY년 M월')
- 타임라인 마우스 휠 = 좌우 이동(horizontalScroll), 확대/축소는 Ctrl+휠

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 13:27:41 +09:00
parent 03916a61b1
commit a6adb5fc41
4 changed files with 45 additions and 9 deletions
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260625k" /> <link rel="stylesheet" href="/static/project.css?v=20260625l" />
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" /> <link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
{% endblock %} {% endblock %}
@@ -45,5 +45,5 @@
{% endif %} {% endif %}
</div> </div>
<script src="/static/project.js?v=20260625k" defer></script> <script src="/static/project.js?v=20260625l" defer></script>
{% endblock %} {% endblock %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260625k" /> <link rel="stylesheet" href="/static/project.css?v=20260625l" />
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" /> <link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
{% endblock %} {% endblock %}
@@ -100,5 +100,5 @@
<script> <script>
window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }}; window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }};
</script> </script>
<script src="/static/project.js?v=20260625k" defer></script> <script src="/static/project.js?v=20260625l" defer></script>
{% endblock %} {% endblock %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260625k" /> <link rel="stylesheet" href="/static/project.css?v=20260625l" />
<!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host --> <!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host -->
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" /> <link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
<!-- 타임라인 vis-timeline — self-host --> <!-- 타임라인 vis-timeline — self-host -->
@@ -290,5 +290,5 @@
</script> </script>
<script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script> <script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script>
<script src="/static/project.js?v=20260625k" defer></script> <script src="/static/project.js?v=20260625l" defer></script>
{% endblock %} {% endblock %}
+39 -3
View File
@@ -242,9 +242,39 @@
}); });
if (timeline) { timeline.destroy(); timeline = null; } if (timeline) { timeline.destroy(); timeline = null; }
if (!items.length) { node.innerHTML = '<div class="pj-empty">기간이 설정된 업무가 없습니다.</div>'; return; } if (!items.length) { node.innerHTML = '<div class="pj-empty">기간이 설정된 업무가 없습니다.</div>'; return; }
const koWd = function (d) { return ["일", "월", "화", "수", "목", "금", "토"][d.getDay()]; };
const pad2 = function (n) { return ("0" + n).slice(-2); };
timeline = new vis.Timeline(node, new vis.DataSet(items), { timeline = new vis.Timeline(node, new vis.DataSet(items), {
locale: "ko", stack: true, height: "520px", zoomKey: "ctrlKey", stack: true, height: "520px",
horizontalScroll: true, // 마우스 휠 = 좌우 이동(패닝)
verticalScroll: false,
zoomKey: "ctrlKey", // 확대/축소는 Ctrl+휠
margin: { item: 8 }, margin: { item: 8 },
format: {
minorLabels: function (date, scale) {
const d = new Date(date.valueOf());
switch (scale) {
case "millisecond": case "second": case "minute": case "hour":
return pad2(d.getHours()) + ":" + pad2(d.getMinutes());
case "weekday": case "day": return d.getDate() + "일 (" + koWd(d) + ")";
case "week": return d.getDate() + "일";
case "month": return (d.getMonth() + 1) + "월";
case "year": return d.getFullYear() + "년";
default: return d.getDate() + "일";
}
},
majorLabels: function (date, scale) {
const d = new Date(date.valueOf());
switch (scale) {
case "millisecond": case "second": case "minute": case "hour":
return (d.getMonth() + 1) + "월 " + d.getDate() + "일 (" + koWd(d) + ")";
case "weekday": case "day": case "week":
return d.getFullYear() + "년 " + (d.getMonth() + 1) + "월";
case "month": return d.getFullYear() + "년";
default: return "";
}
},
},
}); });
timeline.on("select", function (props) { timeline.on("select", function (props) {
if (!canManage || !props.items.length) return; if (!canManage || !props.items.length) return;
@@ -709,9 +739,15 @@
// 최초 렌더 — 달력은 서버 렌더, 클릭 핸들러만 연결 // 최초 렌더 — 달력은 서버 렌더, 클릭 핸들러만 연결
wireCalendar(); wireCalendar();
// 사이드 트리에서 업무 클릭(?task=ID)으로 들어오면 해당 업무 모달 자동 열기 // 사이드 트리에서 업무 클릭(?task=ID)으로 들어오면 해당 업무 모달 자동 열기.
const taskParam = new URLSearchParams(location.search).get("task"); // 단, 처리 후 URL 에서 task 파라미터를 제거한다 — 드래그 이동 후 location.reload()
// 시 이 파라미터가 남아 모달이 다시 뜨는 것을 방지.
const params = new URLSearchParams(location.search);
const taskParam = params.get("task");
if (taskParam) { if (taskParam) {
params.delete("task");
const qs = params.toString();
history.replaceState({}, "", location.pathname + (qs ? "?" + qs : ""));
const t = tasks.find(function (x) { return String(x.id) === String(taskParam); }); const t = tasks.find(function (x) { return String(x.id) === String(taskParam); });
if (t) openTaskModal(t); if (t) openTaskModal(t);
} }