Files
SAFEKISO/safekiso_admin/base/pages/admin/board/[...mode]/[_bid].vue
2026-04-07 14:50:23 +09:00

505 lines
21 KiB
Vue

<template>
<div>
<!-- Page head goes here -->
<div class="px-4 py-5 sm:px-6">
<div
class="flex justify-between items-center flex-wrap sm:flex-nowrap"
>
<div class="">
<h3 class="text-lg leading-6 font-medium text-gray-900">
{{ pageTitle }}
</h3>
<p class="mt-1 text-sm text-gray-500">
{{ pageDescription }}
</p>
</div>
<div class="flex-shrink-0">
<button
v-for="(headingAction, index) in headingActions"
:key="headingAction"
type="button"
:class="index > 0 ? 'ml-3' : ''"
class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-3 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
@click="doHeadingAction(headingAction)"
>
{{ headingAction }}
</button>
</div>
</div>
</div>
<div class="px-4 sm:px-6">
<!-- Content goes here -->
<div class="">
<div
class="mt-6 grid grid-cols-1 gap-y-6 gap-x-4 sm:grid-cols-6"
>
<div class="sm:col-span-1">
<label
for="boardId"
class="block text-sm font-medium text-gray-700"
>
게시판 아이디
</label>
<div class="mt-1">
<input
id="boardId"
v-model="boardId"
type="text"
name="boardId"
class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
/>
</div>
</div>
<div class="sm:col-span-5">
<label
for="title"
class="block text-sm font-medium text-gray-700"
>
게시판 제목
</label>
<div class="mt-1">
<input
id="title"
v-model="title"
type="text"
name="title"
class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
/>
</div>
</div>
<div class="col-span-6">
<label
for="description"
class="block text-sm font-medium text-gray-700"
>
게시판 설명
</label>
<div class="mt-1">
<textarea
id="description"
v-model="description"
name="description"
rows="3"
style="resize: none"
class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md"
placeholder="게시판에 대한 간단한 설명을 입력하세요."
/>
</div>
<p class="flex mt-2 text-sm text-gray-500">
<QuestionMarkCircleIcon
class="mr-1 flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
사용자들에게 표시되는 내용이니 신중하게 입력하세요.
빈칸으로 그냥 수도 있습니다.
</p>
</div>
<div class="sm:col-span-3">
<label
for="readLevelMin"
class="block text-sm font-medium text-gray-700"
>
읽기제한
</label>
<div class="mt-1">
<select
id="readLevelMin"
v-model="readLevelMin"
name="readLevelMin"
class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
>
<option value="5">관리자 이상</option>
<option value="4">자격 사용자 이상</option>
<option value="0">로그인 사용자 이상</option>
<option value="-1">익명 사용자 이상</option>
</select>
</div>
</div>
<div class="sm:col-span-3">
<label
for="writeLevelMin"
class="block text-sm font-medium text-gray-700"
>
쓰기제한
</label>
<div class="mt-1">
<select
id="writeLevelMin"
v-model="writeLevelMin"
name="writeLevelMin"
class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
>
<option value="5">관리자 이상</option>
<option value="4">자격 사용자 이상</option>
<option value="0">로그인 사용자 이상</option>
</select>
</div>
</div>
<div class="sm:col-span-6">
<fieldset>
<legend class="sr-only">기타 옵션</legend>
<div
class="block text-sm font-medium text-gray-700"
aria-hidden="true"
>
기타 옵션
</div>
<div class="mt-4 space-y-4">
<div class="relative flex items-start">
<div class="flex h-5 items-center">
<input
id="commentEnabled"
v-model="commentEnabled"
name="commentEnabled"
type="checkbox"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
</div>
<div class="ml-3 text-sm">
<label
for="commentEnabled"
class="font-medium text-gray-700"
>댓글 기능</label
>
<p class="text-gray-500">
쓰기 권한이 있는 사용자들은 댓글을
보고 있고, 읽기 권한이 있는
사용자들은 있습니다.
</p>
</div>
</div>
<div class="relative flex items-start">
<div class="flex h-5 items-center">
<input
id="attachmentEnabled"
v-model="attachmentEnabled"
name="attachmentEnabled"
type="checkbox"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
</div>
<div class="ml-3 text-sm">
<label
for="attachmentEnabled"
class="font-medium text-gray-700"
>파일 첨부</label
>
<p class="text-gray-500">
쓰기 권한이 있는 사용자들은 파일을
첨부할 있습니다.
</p>
</div>
</div>
<div class="relative flex items-start">
<div class="flex h-5 items-center">
<input
id="agoEnabled"
v-model="agoEnabled"
name="agoEnabled"
type="checkbox"
class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
/>
</div>
<div class="ml-3 text-sm">
<label
for="agoEnabled"
class="font-medium text-gray-700"
>작성일 축약 표시</label
>
<p class="text-gray-500">
작성일을 날짜와 시간 모두 표시하지
않고 짧게 표시합니다.
</p>
</div>
</div>
</div>
</fieldset>
</div>
<div class="col-span-6">
<label
for="memo"
class="block text-sm font-medium text-gray-700"
>
관리자 메모
</label>
<div class="mt-1">
<textarea
id="memo"
v-model="memo"
name="memo"
rows="3"
style="resize: none"
class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md"
placeholder=""
/>
</div>
<p class="flex mt-2 text-sm text-gray-500">
<QuestionMarkCircleIcon
class="mr-1 flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
게시판에 대해서 관리 목적상 필요한 간단한 메모를
저장할 있습니다. 관리자에게만 표시됩니다.
</p>
</div>
</div>
</div>
</div>
<div class="max-w-7xl mx-auto sm:px-4 lg:px-4">
<!-- Footer Buttons goes here -->
<div class="mt-5 flex justify-between items-center flex-wrap">
<div class="ml-4 mt-4">
<button
v-if="currnetMode != 'new'"
class="ml-3 inline-flex items-center rounded-md border border-transparent bg-red-600 px-3 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
@click="doFooterAction(status == 0 ? '삭제' : '복구')"
>
{{ status == 0 ? '삭제' : '복구' }}
</button>
</div>
<div class="ml-4 mt-4 flex-shrink-0">
<button
class="inline-flex items-center rounded-md border border-transparent bg-indigo-100 px-3 py-2 text-sm font-medium leading-4 text-indigo-700 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
@click="doFooterAction('닫기')"
>
{{ '닫기' }}
</button>
<button
class="ml-3 inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-3 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
@click="doFooterAction('저장')"
>
{{ '저장' }}
</button>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { QuestionMarkCircleIcon } from '@heroicons/vue/24/solid';
definePageMeta({
middleware: 'check-auth-admin',
});
const router = useRouter();
const pageTitle = '새로운 게시판 생성';
const pageDescription = '새로운 게시판 생성 정보를 입력하세요.';
// 해당 페이지 우측 상단에 표시될 액션 버튼들
const headingActions = [];
const bid = ref('');
const boardId = ref('');
const title = ref('');
const description = ref('');
const readLevelMin = ref(5);
const writeLevelMin = ref(5);
const commentEnabled = ref(false);
const attachmentEnabled = ref(false);
const agoEnabled = ref(false);
const memo = ref('');
const status = ref(0);
function doHeadingAction(tag) {
console.log('on doHeadingAction(), tag=', tag);
switch (tag) {
default:
alert('unhandled heading action. tag = ' + tag);
}
}
function doFooterAction(tag) {
console.log('111 on doFooterAction(), tag=', tag);
switch (tag) {
case '저장':
updateContent();
break;
case '닫기':
router.back();
break;
case '삭제':
deleteContent();
break;
case '복구':
reviveContent();
break;
default:
alert('unhandled footer action. tag = ' + tag);
}
}
async function deleteContent() {
const responseJson = await _crossCtl.doComm('delete', 'board:info', {
hero: bid.value,
});
if (responseJson['responseCode'] == 200) {
status.value = 4;
alert(responseJson['responseMessage']);
} else {
// router.back();
alert(responseJson['responseMessage']);
}
}
function reviveContent() {
status.value = 0;
updateContent();
}
async function updateContent() {
if (boardId.value.trim() == '') {
alert('게시판 아이디를 입력해 주세요.');
return;
}
if (title.value.trim() == '') {
alert('게시판 제목을 입력해 주세요.');
return;
}
const responseJson = await _crossCtl.doComm(
bid.value == '' ? 'insert' : 'update',
'board:info',
bid.value == ''
? {
boardId: boardId.value,
title: title.value,
description: description.value,
readLevelMin: readLevelMin.value,
writeLevelMin: writeLevelMin.value,
commentEnabled: commentEnabled.value,
attachmentEnabled: attachmentEnabled.value,
agoEnabled: agoEnabled.value,
memo: memo.value,
}
: {
hero: bid.value,
boardId: boardId.value,
title: title.value,
description: description.value,
readLevelMin: readLevelMin.value,
writeLevelMin: writeLevelMin.value,
commentEnabled: commentEnabled.value,
attachmentEnabled: attachmentEnabled.value,
agoEnabled: agoEnabled.value,
memo: memo.value,
status: status.value,
}
);
console.log('huk responseJson = ', responseJson);
if (responseJson['responseCode'] != 200) {
if (responseJson['responseMessage'].startsWith('ER_DUP_ENTRY: ')) {
alert('게시판 아이디가 이미 존재하고 있습니다.');
} else {
alert(responseJson['responseMessage']);
}
} else {
// router.back();
alert(responseJson['responseMessage']);
}
}
const route = useRoute();
const currnetMode = ref('');
if (route.params.mode instanceof Array) {
console.log('huk 1');
if (route.params.mode.length != 1) {
console.log('huk 2');
throwError('$404');
} else {
console.log('huk 3');
currnetMode.value = route.params.mode[0];
switch (currnetMode.value) {
case 'new':
case 'edit':
console.log('huk 4');
break;
default:
throwError('$404');
console.log('huk 5');
}
}
} else {
if (route.params.mode == '' && route.params._bid == 'new') {
currnetMode.value = 'new';
} else {
throwError('$404');
}
}
console.log('huk 7');
if (currnetMode.value == '') {
console.log('missing params...');
} else {
console.log('mode = ', currnetMode.value);
if (currnetMode.value == 'edit') {
const responseJson = await _crossCtl.doComm(
'select',
'board:info:all',
{
hero: route.params._bid,
}
);
console.log('responseJson=', responseJson);
if (responseJson['responseCode'] == 200) {
if (responseJson['data'].length != 1) {
alert(
'게시판 아이디가 잘못되었습니다. 이전 화면으로 돌아 갑니다.'
);
navigateTo('/admin/board/list');
} else {
const targetData = responseJson['data'][0];
bid.value = targetData['bid'];
boardId.value = targetData['id'];
title.value = targetData['title'];
description.value = targetData['description'];
readLevelMin.value = targetData['read_level_min'];
writeLevelMin.value = targetData['write_level_min'];
commentEnabled.value = targetData['comment_enabled'] == 1;
attachmentEnabled.value = targetData['attachment_enabled'] == 1;
agoEnabled.value = targetData['ago_enabled'] == 1;
memo.value = targetData['memo'];
status.value = targetData['status'];
}
} else {
alert(responseJson['responseMessage']);
}
}
}
// const targetBoardInfo = await _crossCtl.getBoardInfo(route);
// console.log('huk targetBoardInfo = ', targetBoardInfo);
console.log('huk params = ', route.params);
</script>