List.cshtml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. @model Admin.ViewModels.Forum.Posts.List.ListViewModel
  2. @using Library.Helpers
  3. @{
  4. ViewData["Title"] = "게시글 목록";
  5. var pageIndex = (Model.Parameter.Page <= 0 ? 1 : Model.Parameter.Page);
  6. var perPage = (Model.Parameter.PerPage <= 0 ? 20 : Model.Parameter.PerPage);
  7. }
  8. <div class="container-fluid">
  9. <h3 class="mb-3">@ViewData["Title"]</h3>
  10. <partial name="_StatusMessage" />
  11. <div class="card mb-3">
  12. <div class="card-body">
  13. <form class="row gx-2 gy-2 align-items-end filter-bar">
  14. <div class="col-12 col-sm-6 col-md-auto">
  15. <label class="form-label mb-1 small">게시판 그룹</label>
  16. <select id="boardId" class="form-select form-select-sm w-100">
  17. <option value="">- 전체 -</option>
  18. @foreach (var g in (Model?.BoardList ?? Enumerable.Empty<SelectListItem>()))
  19. {
  20. var selected = ((Model?.Parameter?.BoardID?.ToString() ?? "") == (g.Value ?? ""));
  21. <option value="@g.Value" selected="@(selected)">@g.Text</option>
  22. }
  23. </select>
  24. </div>
  25. <div class="col-12 col-sm-6 col-md-auto">
  26. <label class="form-label mb-1 small">검색</label>
  27. <select id="search" class="form-select form-select-sm w-100">
  28. <option value="">- 전체 -</option>
  29. <option value="0" selected="@(Model?.Parameter.Search == 0)">제목</option>
  30. <option value="1" selected="@(Model?.Parameter.Search == 1)">내용</option>
  31. <option value="2" selected="@(Model?.Parameter.Search == 2)">작성자</option>
  32. <option value="3" selected="@(Model?.Parameter.Search == 3)">댓글</option>
  33. </select>
  34. </div>
  35. <div class="col-12 col-sm-6 col-md-4 col-lg-3">
  36. <label class="form-label mb-1 small">키워드</label>
  37. <input type="text" id="keyword" class="form-control form-control-sm w-100" value="@(Model?.Parameter.Keyword ?? "")" placeholder="검색어" />
  38. </div>
  39. <div class="col-12 col-sm-6 col-md-auto">
  40. <label class="form-label mb-1 small">기간</label>
  41. <div class="input-group input-group-sm">
  42. <input type="date" id="startAt" class="form-control" value="@(Model?.Parameter.StartAt ?? "")" />
  43. <span class="input-group-text">~</span>
  44. <input type="date" id="endAt" class="form-control" value="@(Model?.Parameter.EndAt ?? "")" />
  45. </div>
  46. </div>
  47. <div class="col-12 col-sm-6 col-md-auto">
  48. <label class="form-label mb-1 small">정렬</label>
  49. <select id="sort" class="form-select form-select-sm w-100">
  50. <option value="0" selected="@(Model?.Parameter.Sort == 0)">최신순</option>
  51. <option value="1" selected="@(Model?.Parameter.Sort == 1)">조회순</option>
  52. <option value="2" selected="@(Model?.Parameter.Sort == 2)">댓글순</option>
  53. <option value="3" selected="@(Model?.Parameter.Sort == 3)">공감순</option>
  54. </select>
  55. </div>
  56. <div class="col-12 col-md-auto">
  57. <label class="form-label mb-1 small d-block">상태</label>
  58. <div class="d-flex flex-wrap gap-2">
  59. <div class="form-check form-check-inline m-0">
  60. <input class="form-check-input" type="checkbox" id="isNotice" checked="@(Model?.Parameter.IsNotice == true)" />
  61. <label class="form-check-label small" for="isNotice">공지</label>
  62. </div>
  63. <div class="form-check form-check-inline m-0">
  64. <input class="form-check-input" type="checkbox" id="isSecret" checked="@(Model?.Parameter.IsSecret == true)" />
  65. <label class="form-check-label small" for="isSecret">비밀</label>
  66. </div>
  67. <div class="form-check form-check-inline m-0">
  68. <input class="form-check-input" type="checkbox" id="isReply" checked="@(Model?.Parameter.IsReply == true)" />
  69. <label class="form-check-label small" for="isReply">답글</label>
  70. </div>
  71. <div class="form-check form-check-inline m-0">
  72. <input class="form-check-input" type="checkbox" id="isDeleted" checked="@(Model?.Parameter.IsDeleted == true)" />
  73. <label class="form-check-label small" for="isDeleted">삭제</label>
  74. </div>
  75. </div>
  76. </div>
  77. <div class="col-6 col-sm-4 col-md-auto">
  78. <label class="form-label mb-1 small d-block">&nbsp;</label>
  79. <button id="btnSearch" class="btn btn-sm btn-primary w-100">검색</button>
  80. </div>
  81. <div class="col-12 col-sm-auto ms-auto text-sm-end">
  82. <label class="form-label mb-1 small d-block">&nbsp;</label>
  83. <div class="d-flex align-items-center justify-content-end gap-2">
  84. <select id="perPage" class="form-select form-select-sm w-auto">
  85. <option value="10" selected="@(Model?.Parameter.PerPage == 10)">10</option>
  86. <option value="20" selected="@(Model?.Parameter.PerPage == 20)">20</option>
  87. <option value="50" selected="@(Model?.Parameter.PerPage == 50)">50</option>
  88. <option value="100" selected="@(Model?.Parameter.PerPage == 100)">100</option>
  89. </select>
  90. <a id="btnCreate" class="btn btn-sm btn-success" href="/Forum/Post/Create">추가</a>
  91. </div>
  92. </div>
  93. </form>
  94. </div>
  95. </div>
  96. <div class="card">
  97. <div class="table-responsive">
  98. <table class="table table-sm table-hover align-middle mb-0 table-fix">
  99. <thead class="table-light">
  100. <tr>
  101. <th class="text-center" style="width:70px">No</th>
  102. <th class="col-subject" style="min-width:320px">제목</th>
  103. <th class="col-author" style="width:120px">작성자</th>
  104. <th class="text-end">조회</th>
  105. <th class="text-end">공감</th>
  106. <th class="text-end">비공감</th>
  107. <th class="text-end">댓글</th>
  108. <th class="text-center">첨부</th>
  109. <th class="col-date" style="width:150px">작성일</th>
  110. </tr>
  111. </thead>
  112. <tbody>
  113. @if (Model?.Data == null || Model.Data.Count == 0)
  114. {
  115. <tr>
  116. <td colspan="10" class="text-center text-muted py-4">데이터가 없습니다.</td>
  117. </tr>
  118. }
  119. else
  120. {
  121. foreach (var item in Model.Data)
  122. {
  123. <tr class="@(item.IsActive ? "table-primary" : (item.IsNotice ? "table-warning" : ""))">
  124. <td class="text-center">@item.No</td>
  125. <td>
  126. <div class="d-flex align-items-center gap-2">
  127. @if (!string.IsNullOrEmpty(item.Thumbnail))
  128. {
  129. <img src="@item.Thumbnail" alt="" style="width:36px;height:36px;object-fit:cover;border-radius:.5rem;" />
  130. }
  131. <div>
  132. <div>
  133. @if (item.IsNotice)
  134. {
  135. <span class="badge text-bg-warning me-1">공지</span>
  136. }
  137. @if (item.IsSecret)
  138. {
  139. <span class="badge text-bg-secondary me-1">비밀</span>
  140. }
  141. @if (item.IsReply)
  142. {
  143. <span class="badge text-bg-info me-1">답글</span>
  144. }
  145. @if (item.IsSpeaker)
  146. {
  147. <span class="badge text-bg-primary me-1">작성자</span>
  148. }
  149. @if (item.IsAnonymous)
  150. {
  151. <span class="badge text-bg-dark me-1">익명</span>
  152. }
  153. <a class="text-decoration-none" href="@item.EditURL">@item.Subject</a>
  154. </div>
  155. </div>
  156. </div>
  157. </td>
  158. <td>@(item.Name ?? item.SID ?? "-")</td>
  159. <td class="text-end">@item.Views</td>
  160. <td class="text-end">@item.Likes</td>
  161. <td class="text-end">@item.Dislikes</td>
  162. <td class="text-end">@item.Comments</td>
  163. <td class="text-center">
  164. <span title="이미지">@item.Images</span> /
  165. <span title="미디어">@item.Medias</span> /
  166. <span title="파일">@item.Files</span> /
  167. <span title="태그">@item.Tags</span>
  168. </td>
  169. <td>@item.CreatedAt</td>
  170. </tr>
  171. }
  172. }
  173. </tbody>
  174. </table>
  175. </div>
  176. </<br />
  177. <partial name="_Pagination" model="Model!.Pagination" />
  178. </div>
  179. </div>
  180. <style>
  181. .table-responsive {
  182. overflow-x: auto;
  183. }
  184. .table-fix th, .table-fix td {
  185. white-space: nowrap;
  186. vertical-align: middle;
  187. }
  188. .table-fix .col-subject {
  189. white-space: normal;
  190. min-width: 280px;
  191. }
  192. .table-fix .col-author {
  193. min-width: 140px;
  194. }
  195. .table-fix .col-date {
  196. min-width: 160px;
  197. }
  198. .table-fix .col-actions {
  199. min-width: 120px;
  200. }
  201. .table-fix td .attach-compact > span {
  202. margin-right: 6px;
  203. }
  204. </style>
  205. @section Scripts {
  206. <script>
  207. function updateQueryString(resetPage = true) {
  208. const qp = new URLSearchParams();
  209. qp.set("BoardGroupID", $("#boardGroupId").val() || "");
  210. qp.set("BoardID", $("#boardId").val() || "");
  211. const searchVal = $("#search").val();
  212. if (searchVal !== null && searchVal !== "") qp.set("Search", searchVal);
  213. qp.set("Keyword", $("#keyword").val() || "");
  214. qp.set("StartAt", $("#startAt").val() || "");
  215. qp.set("EndAt", $("#endAt").val() || "");
  216. const sortVal = $("#sort").val();
  217. if (sortVal !== null && sortVal !== "") qp.set("Sort", sortVal);
  218. if ($("#isNotice").is(":checked")) qp.set("IsNotice", "true");
  219. if ($("#isSecret").is(":checked")) qp.set("IsSecret", "true");
  220. if ($("#isReply").is(":checked")) qp.set("IsReply", "true");
  221. if ($("#isDeleted").is(":checked")) qp.set("IsDeleted","true");
  222. qp.set("PerPage", $("#perPage").val() || "10");
  223. qp.set("Page", resetPage ? "1" : "@pageIndex");
  224. window.location.href = window.location.pathname + "?" + qp.toString();
  225. }
  226. $("#btnSearch").on("click", function (e)
  227. {
  228. e.preventDefault(); updateQueryString(true);
  229. });
  230. $("#keyword,#startAt,#endAt").on("keyup", function (e)
  231. {
  232. if (e.key === "Enter") updateQueryString(true);
  233. });
  234. </script>
  235. }