This commit is contained in:
2025-09-08 16:35:09 +09:00
parent 26a0f14e54
commit 51b97e2422
17 changed files with 1328 additions and 258 deletions
@@ -6,18 +6,18 @@
layout:decorate="~{layout/default_layout}"
>
<th:block layout:fragment="head">
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.js"></script>
<script>document.addEventListener('DOMContentLoaded', function() {initEditor(true)});</script>
</th:block>
<body>
<th:block layout:fragment="content" id="content">
<head>
<th:block id="head">
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
<link href="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.js"></script>
<script>document.addEventListener('DOMContentLoaded', function() {initEditor(true)});</script>
</th:block>
</head>
<section class="wrapper style2" >
<th:block sec:authorize="isAuthenticated()">
<div class="container" >
@@ -33,6 +33,14 @@
<p th:if="${srcPost.writeTime != null and srcPost.writeTime > 0}"
th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>
</header>
<div class="write_controllbox" style="margin-top: -1em; margin-bottom: 2em;">
<div class="write_option btn-example controlbox-category" to="#popLayer1">
</div>
<div class="write_option btn-example controlbox-hashtag" to="#popLayer2" id="hashtag_field">
</div>
<div class="write_option btn-example controlbox-location" id="location_field"></div>
</div>
</div>
</th:block>
</section>
@@ -46,44 +54,52 @@
<div class="container">
<div id="editor"></div>
<div class="write_controllbox">
<div class="write_option btn-example controlbox-category" to="#popLayer1">
</div>
<div style="width: 15px"></div>
<div class="write_option btn-example controlbox-hashtag" to="#popLayer2" id="hashtag_field">
</div>
<div style="width: 15px"></div>
<div class="write_option" id="location_field"></div>
</div>
<button id="save" class="button fit" style="margin-top: 1em;" onclick="save()">저장하기</button>
</div>
</th:block>
</section>
</th:block>
<th:block layout:fragment="popup_layer">
<div id="popLayer1" class="pop_layer category-popup">
<div class="pop_container">
<div class="pop_conts">
<h2>Categories</h2>
<div class="tag-list-label">Selected:</div>
<div id="selected-category-area" class="staging-area">
</div>
<div class="tag-list-label">Pre-loaded Categories:</div>
<div id="category-list" class="tag-list"></div>
<input type="text" id="category-input" placeholder="Add a new category" class="tag-input">
<button id="add-category-btn" class="button">Add</button>
<div class="btn_r">
<a href="#" id="apply-category-btn" class="button" style="margin-right: 0.5em;">Apply</a>
<a href="#" class="btn_layerClose">Close</a>
</div>
</div>
</div>
</div>
<div id="popLayer2" class="pop_layer hashtag-popup">
<div class="pop_container">
<div class="pop_conts">
<h2>Hashtags</h2>
<div class="tag-list-label">Selected:</div>
<div id="selected-hashtags-area" class="staging-area tag-list">
</div>
<div class="tag-list-label">Suggested Tags:</div>
<div id="hashtag-list" class="tag-list"></div>
<input type="text" id="hashtag-input" placeholder="Add a new hashtag" class="tag-input">
<button id="add-hashtag-btn" class="button">Add</button>
<div class="btn_r">
<a href="#" id="apply-hashtag-btn" class="button" style="margin-right: 0.5em;">Apply</a>
<a href="#" class="btn_layerClose">Close</a>
</div>
</div>
@@ -1,32 +0,0 @@
<!DOCTYPE html>
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head">
</th:block>
<th:block layout:fragment="content" id="content">
<div id="main_layer">
<th:block sec:authorize="isAnonymous()">
<h1>권한이 없는 뎁쇼?!</h1>
</th:block>
<th:block sec:authorize="isAuthenticated()">
<div class="post_layer" id="posts" th:each="posts, postsStat : ${chunkedPosts}">
<div class="post_layer" th:class="${#strings.append(rowKey, postsStat.index)}" th:each="post, postStat : ${posts}">
<div class="post_item"
th:if="${post.id != null and post.id != ''}"
onclick="goToEditor(this)"
th:attr="data-post-id=${post.id}">
<span id="postTitle" class="post_attr" th:text="${post.title}"></span>
<div id="content" class="post_attr content" th:attr="data=${post.content}"></div>
<span id="writeDate" class="post_attr" th:text="${#dates.format(post.writeTime, 'yyyy.MM.dd HH:mm:ss')}"></span>
<span id="postId" class="post_attr" th:text="${post.id}"></span>
</div>
</div>
</div>
</th:block>
</div>
</th:block>
</html>
@@ -9,22 +9,29 @@
<th:block layout:fragment="head">
</th:block>
</head>
<body>
<th:block layout:fragment="content" id="content">
<section class="wrapper style1">
<div class="container">
<div class="row justify-content-center">
<div class="col-8 col-12-narrower">
<div id="content">
<div id="content_inner">
<article>
<section th:each="post : ${Posts}">
<section th:each="post : ${postsPage.content}">
<div class="box post" th:id="${post.id}">
<a href="javascript:void(0);" th:onclick="'goToViewer(this.parentNode)'" class="image left">
<img th:src="${post.thumb != null and not #strings.isEmpty(post.thumb)} ? @{${post.thumb}} : @{/images/pic01.jpg}" alt="Post Thumbnail" />
</a>
<div class="inner">
<h3 th:text="${post.title != null and not #strings.isEmpty(post.title)} ? ${post.title} : 'untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm')} + ']'"></h3>
<h3 style="display: flex; justify-content: space-between; align-items: center;">
<span th:text="${post.title != null and not #strings.isEmpty(post.title)} ? ${post.title} : 'untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm')} + ']'"></span>
<span style="font-size: 0.75em; color: #888; font-weight: normal; white-space: nowrap; margin-left: 1em;">
(읽음: <span th:text="${post.readCount}">0</span>)
</span>
</h3>
<p style="font-size: 0.9em; color: #555; margin-bottom: 0.5em;" th:text="${'by ' + post.writer}"></p>
<p th:text="${#strings.abbreviate(post.html, 80)}" class="ellipsis"></p>
</div>
<footer sec:authorize="isAuthenticated()" style="text-align: right; margin-top: 1em;">
@@ -33,11 +40,34 @@
</div>
</section>
</article>
</div>
</div>
<nav aria-label="Page navigation" th:if="${postsPage.totalPages > 1}" style="text-align: center; margin-top: 2.5em; font-size: 0.9em;">
<ul class="pagination" style="display: inline-block; padding-left: 0; list-style: none; border-radius: 5px; border: 1px solid #e0e0e0; overflow: hidden;">
<li th:styleappend="${postsPage.isFirst()} ? 'opacity: 0.5; pointer-events: none;' : ''" style="display: inline; float: left;">
<a th:href="${postsPage.isFirst()} ? '#' : @{/blog/posts(page=${postsPage.number - 1})}"
class="button alt small" style="border-radius:0; margin:0; border-right: 1px solid #e0e0e0;">
&laquo; Prev
</a>
</li>
<li th:each="pageNum : ${#numbers.sequence(0, postsPage.totalPages - 1)}"
style="display: inline; float: left; border-right: 1px solid #e0e0e0;">
<a th:href="@{/blog/posts(page=${pageNum})}"
th:text="${pageNum + 1}"
th:class="${pageNum == postsPage.number} ? 'button small' : 'button alt small'"
style="border-radius:0; margin:0;">
</a>
</li>
<li th:styleappend="${postsPage.isLast()} ? 'opacity: 0.5; pointer-events: none;'" style="display: inline; float: left;">
<a th:href="${postsPage.isLast()} ? '#' : @{/blog/posts(page=${postsPage.number + 1})}"
class="button alt small" style="border-radius:0; margin:0;">
Next &raquo;
</a>
</li>
</ul>
</nav>
</div> </div>
</div>
</div>
</section>
</th:block>
</body>
</html>
@@ -1,53 +1,81 @@
<!DOCTYPE html>
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"
>
<head>
<th:block layout:fragment="head">
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.js"></script>
<script>document.addEventListener('DOMContentLoaded', function() {initEditor(false)});</script>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}">
</th:block>
</head>
<th:block layout:fragment="head">
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quill-table-better@1/dist/quill-table-better.js"></script>
<script>document.addEventListener('DOMContentLoaded', function() {initEditor(false)});</script>
</th:block>
<body>
<th:block layout:fragment="content" id="content">
<section class="wrapper style2">
<div class="container" sec:authorize="isAuthenticated()" onclick="loadEditor()" style="cursor: pointer;" title="클릭하여 수정하기">
<header class="major">
<h2 id="title_layer" th:text="${srcPost.title}">게시물 제목이 여기에 표시됩니다</h2>
<p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>
<h2 id="title_layer">
<span th:text="${srcPost.title}">게시물 제목이 여기에 표시됩니다</span>
<span style="font-size: 0.8em; color: #888; font-weight: normal; margin-left: 0.5em;">
(읽음: <span th:text="${srcPost.readCount}">0</span>)
</span>
</h2>
<p>
<span th:if="${srcPost.writer != null}" th:text="${'by ' + srcPost.writer + ' | '}" style="font-weight: 600;"></span>
<span th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></span>
</p>
</header>
<div class="write_controllbox" style="margin-top: -1em; margin-bottom: 2em;">
<div class="write_option controlbox-category">
</div>
<div class="write_option controlbox-hashtag" id="hashtag_field">
</div>
<div class="write_option controlbox-location" id="location_field">
</div>
</div>
</div>
<div class="container open-login-popup" sec:authorize="isAnonymous()" to="#loginPopup" style="cursor: pointer;">
<header class="major">
<h2 id="title_layer" th:text="${srcPost.title}">게시물 제목이 여기에 표시됩니다</h2>
<p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>
<h2 id="title_layer_anon">
<span th:text="${srcPost.title}">게시물 제목이 여기에 표시됩니다</span>
<span style="font-size: 0.8em; color: #888; font-weight: normal; margin-left: 0.5em;">
(읽음: <span th:text="${srcPost.readCount}">0</span>)
</span>
</h2>
<p>
<span th:if="${srcPost.writer != null}" th:text="${'by ' + srcPost.writer + ' | '}" style="font-weight: 600;"></span>
<span th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></span>
</p>
</header>
<div class="write_controllbox" style="margin-top: -1em; margin-bottom: 2em;">
<div class="write_option controlbox-category">
</div>
<div class="write_option controlbox-hashtag" id="hashtag_field_anon">
</div>
<div class="write_option controlbox-location" id="location_field_anon">
</div>
</div>
</div>
</section>
<section class="wrapper style1">
<div class="container">
<div id="content">
<div id="content_inner">
<article>
<div id="editor"></div>
<div class="write_controllbox">
<div class="write_option controlbox-category">
</div>
<div style="width: 15px"></div>
<div class="write_option controlbox-hashtag" id="hashtag_field">
</div>
<div style="width: 15px"></div>
<div class="write_option" id="location_field">
</div>
<div class="vote-controls" style="margin-top: 2em; text-align: center; border-top: 1px solid #e0e0e0; padding-top: 2em;" th:data-post-id="${srcPost.id}">
<button class="button" onclick="handleVote(this, 'like')">
👍 Like (<span class="like-count" th:text="${srcPost.voteCount}">0</span>)
</button>
<button class="button" onclick="handleVote(this, 'unlike')" style="margin-left: 1em;">
👎 Unlike (<span class="unlike-count" th:text="${srcPost.unlikeCount}">0</span>)
</button>
</div>
<h3 id="write" th:text="${srcPost.firstAddress}"></h3>
@@ -67,5 +95,4 @@
</div>
</section>
</th:block>
</body>
</html>
@@ -10,17 +10,57 @@
<th:block layout:fragment="content" id="content">
<div id="main_layer">
<div class="layer">
<th:block id="where" th:each="location : ${locations}">
<div class="where_item" style="font-family: monospace; white-space: nowrap;">
<span th:text="${location.timeString}"></span> |
<span th:text="${location.mAddressLines}"></span> |
<span th:text="${location.bettween}"></span> |
<span th:text="${#numbers.formatDecimal(location.mLatitude, 1, 3)}"></span> |
<span th:text="${#numbers.formatDecimal(location.mLongitude, 1, 3)}"></span> |
<span th:text="${location.mCountryName}"></span>
<th:block th:each="location : ${locationPage.content}">
<div class="location-item"> <div class="location-header">
<span class="location-time" th:text="${location.displayTime}"></span>
<span class="location-between" th:text="${location.bettween}"></span>
</div>
<div class="location-body">
<p class="location-address" th:text="${location.mAddressLines}"></p>
<p class="location-coords">
<span>위도: <b th:text="${#numbers.formatDecimal(location.mLatitude, 1, 3)}"></b></span>
<span>경도: <b th:text="${#numbers.formatDecimal(location.mLongitude, 1, 3)}"></b></span>
<span class="location-country" th:text="${'(' + location.mCountryName + ')'}"></span>
</p>
</div>
</div>
</th:block>
</div>
<div class="pagination">
<th:block th:with="currentPage=${locationPage.number},
totalPages=${locationPage.totalPages}">
<a class="nav-link" th:if="${currentPage > 0}" th:href="@{/bums/where.bs(page=0)}">&lt;&lt; 처음</a>
<span class="nav-link disabled" th:unless="${currentPage > 0}">&lt;&lt; 처음</span>
<a class="nav-link" th:if="${locationPage.hasPrevious()}" th:href="@{/bums/where.bs(page=${currentPage - 1})}">&lt; 이전</a>
<span class="nav-link disabled" th:unless="${locationPage.hasPrevious()}">&lt; 이전</span>
<th:block th:with="startPage=${T(java.lang.Math).max(0, currentPage - 3)},
endPage=${T(java.lang.Math).min(totalPages - 1, currentPage + 3)}">
<th:block th:each="pageNum : ${#numbers.sequence(startPage, endPage)}">
<a class="page-num"
th:if="${pageNum != currentPage}"
th:href="@{/bums/where.bs(page=${pageNum})}"
th:text="${pageNum + 1}">
</a>
<span class="page-num current-page"
th:if="${pageNum == currentPage}"
th:text="${pageNum + 1}">
</span>
</th:block>
</th:block>
<a class="nav-link" th:if="${locationPage.hasNext()}" th:href="@{/bums/where.bs(page=${currentPage + 1})}">다음 &gt;</a>
<span class="nav-link disabled" th:unless="${locationPage.hasNext()}">다음 &gt;</span>
<a class="nav-link" th:if="${currentPage < totalPages - 1}" th:href="@{/bums/where.bs(page=${totalPages - 1})}">&gt;&gt;</a>
<span class="nav-link disabled" th:unless="${currentPage < totalPages - 1}">&gt;&gt;</span>
</th:block>
</div>
</div>
</th:block>
</html>