From 6dec692e6178870591560287d696b664f2fdf506 Mon Sep 17 00:00:00 2001 From: koreafood Date: Fri, 29 May 2026 20:20:34 +0900 Subject: [PATCH] feat(admin/sampleBoard): add route-based list/detail views Restructured the sample board admin page to support separate list, create, edit, and detail views synced with URL query parameters. Added computed state, route watchers, and navigation methods to handle view switching, updated form layout to full width for detail displays, replaced inline title links with accessible styled buttons, and improved save/delete workflows to maintain correct component and URL state. --- .../view/admin/pgBoard/SampleBoardPage.vue | 291 +++++++++++++----- 1 file changed, 210 insertions(+), 81 deletions(-) diff --git a/frontend/src/components/view/admin/pgBoard/SampleBoardPage.vue b/frontend/src/components/view/admin/pgBoard/SampleBoardPage.vue index 5fe372b..11b4fea 100644 --- a/frontend/src/components/view/admin/pgBoard/SampleBoardPage.vue +++ b/frontend/src/components/view/admin/pgBoard/SampleBoardPage.vue @@ -4,20 +4,85 @@
- -
- - Total {{ $filters.numberFormat(boardList.length) }} - -
- - + + + +
@@ -157,6 +167,29 @@ export default { }, }; }, + // 화면 파생 상태를 계산한다. + computed: { + // 현재 상세 화면 여부를 반환한다. + isDetailView() { + // 상세 조회 또는 등록 요청 여부를 반환한다. + return Boolean(this.getRouteDetailId()) || this.$route.query.mode === 'create'; + }, + }, + // 라우트 쿼리 변경을 감시한다. + watch: { + // 상세 화면 전환을 처리한다. + '$route.query': { + // 쿼리가 바뀔 때마다 화면 상태를 동기화한다. + handler() { + // 현재 라우트 상태에 맞춰 화면을 전환한다. + this.syncRouteState(); + }, + // 컴포넌트 초기 진입 시에도 즉시 실행한다. + immediate: false, + // 중첩된 쿼리 변경도 감시한다. + deep: true, + }, + }, // 화면 동작 메서드를 정의한다. methods: { // 빈 입력 폼 객체를 생성한다. @@ -179,6 +212,41 @@ export default { init() { // 목록을 먼저 조회한다. this.fetchBoardList(); + // 현재 라우트 상태를 화면에 반영한다. + this.syncRouteState(); + }, + // 라우트 쿼리에서 상세 식별자를 추출한다. + getRouteDetailId() { + // 상세 식별자 원본 값을 읽는다. + const detailId = Number(this.$route.query.detailId); + // 상세 식별자가 없거나 숫자가 아니면 null을 반환한다. + if (!this.$route.query.detailId || Number.isNaN(detailId)) { + // 유효한 상세 식별자가 없음을 반환한다. + return null; + } + // 숫자로 변환된 상세 식별자를 반환한다. + return detailId; + }, + // 현재 라우트 상태에 맞는 화면을 구성한다. + syncRouteState() { + // 라우트에서 상세 식별자를 읽는다. + const detailId = this.getRouteDetailId(); + // 상세 식별자가 있으면 상세 데이터를 조회한다. + if (detailId) { + // 상세 조회를 수행한다. + this.selectBoard(detailId); + // 상세 화면 처리 후 메서드를 종료한다. + return; + } + // 등록 화면 요청이면 빈 폼으로 초기화한다. + if (this.$route.query.mode === 'create') { + // 등록용 폼으로 초기화한다. + this.resetForm(); + // 등록 화면 처리 후 메서드를 종료한다. + return; + } + // 목록 화면이면 폼 상태를 기본값으로 되돌린다. + this.resetForm(); }, // 게시글 목록을 조회한다. async fetchBoardList() { @@ -199,6 +267,11 @@ export default { }, // 게시글 상세를 조회한다. async selectBoard(id) { + // 식별자가 없으면 함수 실행을 종료한다. + if (!id) { + // 함수 실행을 종료한다. + return; + } // 로딩 바를 표시한다. SDLUtil.showLoadingBar(true); try { @@ -227,6 +300,45 @@ export default { SDLUtil.showLoadingBar(false); } }, + // 상세 화면으로 이동한다. + openBoardDetail(id) { + // 이동할 식별자가 없으면 종료한다. + if (!id) { + // 함수 실행을 종료한다. + return; + } + // 이미 같은 상세 화면이면 중복 이동을 생략한다. + if (this.getRouteDetailId() === id) { + // 함수 실행을 종료한다. + return; + } + // 상세 화면 쿼리로 이동한다. + this.$router.push({ path: this.$route.path, query: { detailId: id } }); + }, + // 신규 등록 상세 화면으로 이동한다. + moveToCreate() { + // 이미 등록 화면이면 중복 이동을 생략한다. + if (this.$route.query.mode === 'create') { + // 폼만 다시 초기화한다. + this.resetForm(); + // 함수 실행을 종료한다. + return; + } + // 등록 화면 쿼리로 이동한다. + this.$router.push({ path: this.$route.path, query: { mode: 'create' } }); + }, + // 목록 화면으로 이동한다. + moveToList() { + // 이미 목록 화면이면 폼만 초기화한다. + if (Object.keys(this.$route.query).length === 0) { + // 폼 상태를 초기화한다. + this.resetForm(); + // 함수 실행을 종료한다. + return; + } + // 목록 화면 쿼리로 이동한다. + this.$router.push({ path: this.$route.path, query: {} }); + }, // 입력 폼을 신규 등록 상태로 초기화한다. resetForm() { // 모드를 등록으로 변경한다. @@ -295,10 +407,25 @@ export default { } // 저장 후 목록을 새로 조회한다. await this.fetchBoardList(); - // 저장된 게시글이 있으면 상세를 다시 로드한다. + // 저장된 게시글이 있으면 상세 화면 상태를 유지한다. if (savedData && savedData.id) { - // 저장된 게시글 상세를 선택한다. - await this.selectBoard(savedData.id); + // 저장된 결과를 현재 폼에 반영한다. + this.form = { + // 게시글 번호를 반영한다. + id: savedData.id, + // 제목을 반영한다. + title: savedData.title, + // 내용을 반영한다. + content: savedData.content, + // 작성자를 반영한다. + author: savedData.author, + // 등록일을 반영한다. + createdAt: savedData.createdAt, + }; + // 수정 모드로 전환한다. + this.mode = 'EDIT'; + // 저장된 게시글 상세 화면으로 주소를 동기화한다. + this.$router.replace({ path: this.$route.path, query: { detailId: savedData.id } }); } // 저장 완료 알림을 출력한다. SDLUtil.alert('저장되었습니다.'); @@ -337,8 +464,8 @@ export default { await axios.delete(`${SDLUtil.API_URL}/pg-board/samples/${id}`); // 삭제 후 목록을 새로 조회한다. await this.fetchBoardList(); - // 삭제 후 폼을 초기화한다. - this.resetForm(); + // 삭제 후 목록 화면으로 이동한다. + this.moveToList(); // 삭제 완료 알림을 출력한다. SDLUtil.alert('삭제되었습니다.'); } catch (err) { @@ -373,9 +500,11 @@ export default {