first
This commit is contained in:
332
safekiso_admin/base/pages/board/[...boardId]/edit/[_cid].vue
Normal file
332
safekiso_admin/base/pages/board/[...boardId]/edit/[_cid].vue
Normal file
@@ -0,0 +1,332 @@
|
||||
<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="btn btn-sm btn-primary"
|
||||
@click="doHeadingAction(headingAction)"
|
||||
>
|
||||
{{ headingAction }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-7xl mx-auto sm:px-4 lg:px-4">
|
||||
<!-- Content goes here -->
|
||||
<input
|
||||
id="title"
|
||||
v-model="title"
|
||||
type="text"
|
||||
name="title"
|
||||
placeholder="제목을 입력하세요."
|
||||
class="mb-3 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
/>
|
||||
<quill-editor
|
||||
v-model:content="content"
|
||||
class="min-h-[30rem]"
|
||||
:modules="modules"
|
||||
:toolbar="toolbarOptions"
|
||||
placeholder="본문을 입력하세요."
|
||||
@ready="onEditorReady($event)"
|
||||
/>
|
||||
|
||||
<base-attachment-ctl1
|
||||
v-if="attachmentEnabled"
|
||||
:attachments="attachments"
|
||||
:read-only-flag="false"
|
||||
:update-attachments="updateAttachments"
|
||||
/>
|
||||
|
||||
<div class="mt-5 flex justify-between items-center flex-wrap">
|
||||
<div class="ml-4 mt-4"></div>
|
||||
<div class="ml-4 mt-4 flex-shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
class="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('취소')"
|
||||
>
|
||||
{{ '취소' }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="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 BlotFormatter from 'quill-blot-formatter/dist/BlotFormatter';
|
||||
|
||||
definePageMeta({
|
||||
// middleware: 'check-auth-admin',
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const bid = ref('');
|
||||
let cid: string | string[] = '';
|
||||
|
||||
bid.value = route.params.boardId[0];
|
||||
cid = route.params['_cid'];
|
||||
|
||||
const modules = {
|
||||
name: 'blotFormatter',
|
||||
module: BlotFormatter,
|
||||
options: {},
|
||||
};
|
||||
|
||||
const pageTitle = cid == undefined ? '광장 - 새글 작성' : '광장 - 글 수정';
|
||||
const pageDescription =
|
||||
cid == undefined ? '새로운 글을 작성 합니다.' : '내 글을 수정합니다.';
|
||||
|
||||
// 해당 페이지 우측 상단에 표시될 액션 버튼들
|
||||
const headingActions = [];
|
||||
|
||||
const title = ref('');
|
||||
const content = ref({ ops: [] });
|
||||
const attachments = ref([]);
|
||||
const status = ref(0);
|
||||
|
||||
function updateAttachments(newAttachments) {
|
||||
console.log('newAttachments=', newAttachments);
|
||||
attachments.value = newAttachments;
|
||||
}
|
||||
|
||||
const toolbarOptions = [
|
||||
['bold', 'italic', 'underline', 'strike'], // toggled buttons
|
||||
['blockquote', 'code-block'],
|
||||
|
||||
// [{ header: 1 }, { header: 2 }], // custom button values
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ script: 'sub' }, { script: 'super' }], // superscript/subscript
|
||||
[{ indent: '-1' }, { indent: '+1' }], // outdent/indent
|
||||
// [{ direction: 'rtl' }], // text direction
|
||||
|
||||
// [{ size: ['small', false, 'large', 'huge'] }], // custom dropdown
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
||||
|
||||
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
|
||||
[{ font: [] }],
|
||||
[{ align: [] }],
|
||||
['link', 'video', 'image'],
|
||||
['clean'], // remove formatting button
|
||||
];
|
||||
|
||||
let quill = null;
|
||||
|
||||
function selectLocalImage() {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
input.click();
|
||||
|
||||
// Listen upload local image and save to server
|
||||
input.onchange = () => {
|
||||
const file = input.files[0];
|
||||
|
||||
// file type is only image.
|
||||
if (/^image\//.test(file.type)) {
|
||||
console.warn('upload images...');
|
||||
saveToServer(file);
|
||||
} else {
|
||||
console.warn('You could only upload images.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function selectLocalFiles() {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
input.click();
|
||||
|
||||
// Listen upload local image and save to server
|
||||
input.onchange = () => {
|
||||
console.log('we got file(s) : ', input.files);
|
||||
};
|
||||
}
|
||||
|
||||
async function saveToServer(file: File) {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('upload-file', file, file.name);
|
||||
// save it
|
||||
|
||||
formData.append('target', 'just');
|
||||
|
||||
console.log('formData=', formData);
|
||||
|
||||
const responseJson = await _crossCtl.doUpload('just', formData);
|
||||
|
||||
console.log('responseJson=', responseJson);
|
||||
|
||||
if (responseJson['responseCode'] == 200) {
|
||||
insertToEditor(
|
||||
_crossCtl.config['API_BASE_URL'].replace('/api/', '') +
|
||||
responseJson['files'][0]['localUrl']
|
||||
);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
function insertToEditor(url: string) {
|
||||
// push image url to rich editor.
|
||||
|
||||
const range = quill.getSelection();
|
||||
quill.insertEmbed(range.index, 'image', url);
|
||||
}
|
||||
|
||||
function onEditorReady(e) {
|
||||
console.log('onEditorReady() e = ', e);
|
||||
|
||||
quill = e;
|
||||
|
||||
// quill editor add image handler
|
||||
|
||||
e.getModule('toolbar').addHandler('image', () => {
|
||||
selectLocalImage();
|
||||
});
|
||||
|
||||
e.getModule('toolbar').addHandler('attachment', () => {
|
||||
selectLocalFiles();
|
||||
});
|
||||
|
||||
if (cid != undefined) {
|
||||
loadContent();
|
||||
}
|
||||
}
|
||||
|
||||
const attachmentEnabled = ref(false);
|
||||
|
||||
async function loadContent() {
|
||||
const responseJson = await _crossCtl.doComm('select', 'board', {
|
||||
boardId: bid.value,
|
||||
hero: cid,
|
||||
});
|
||||
|
||||
if (responseJson['responseCode'] != 200) {
|
||||
alert(responseJson['responseMessage']);
|
||||
} else {
|
||||
console.log('huk responseJson=', responseJson);
|
||||
const tmpDatas = responseJson['data'];
|
||||
console.log('huk tmpDatas=', tmpDatas);
|
||||
if (tmpDatas.length == 1) {
|
||||
attachmentEnabled.value =
|
||||
responseJson['metaData']['attachmentEnabled'];
|
||||
|
||||
title.value = tmpDatas[0]['title'];
|
||||
|
||||
// content.value = tmpDatas[0]['content'];
|
||||
quill.root.innerHTML = tmpDatas[0]['content'];
|
||||
|
||||
attachments.value = JSON.parse(tmpDatas[0]['attachments']);
|
||||
status.value = tmpDatas[0]['status'];
|
||||
|
||||
// $dayjs(val).format('YY/MM/DD A h:mm:ss')
|
||||
} else {
|
||||
alert('bad count. count = ' + tmpDatas.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
const attachments = [
|
||||
{ name: 'resume_front_end_developer.pdf', href: '#' },
|
||||
{ name: 'coverletter_front_end_developer.pdf', href: '#' },
|
||||
];
|
||||
*/
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function doHeadingAction(tag) {
|
||||
console.log('on doHeadingAction(), tag=', tag);
|
||||
}
|
||||
|
||||
function doFooterAction(tag) {
|
||||
console.log('on doFooterAction(), tag=', tag);
|
||||
|
||||
switch (tag) {
|
||||
case '저장':
|
||||
// console.log('quill.root.innerHTML=', quill.root.innerHTML);
|
||||
// console.log('content=', content.value);
|
||||
updateContent();
|
||||
break;
|
||||
|
||||
case '취소':
|
||||
// router.back();
|
||||
navigateTo('/board/' + bid.value + '/list');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateContent() {
|
||||
if (title.value.trim() == '') {
|
||||
alert('제목을 입력해 주세요.');
|
||||
return;
|
||||
}
|
||||
let emptyContent = true;
|
||||
|
||||
console.log('content.value = ', content.value);
|
||||
console.log('quill.root.innerHTML = ', quill.root.innerHTML);
|
||||
|
||||
for (let i = 0; i < content.value['ops'].length; i++) {
|
||||
console.log("content.value['ops'][i] = ", content.value['ops'][i]);
|
||||
|
||||
if (typeof content.value['ops'][i]['insert'] == 'string') {
|
||||
if (content.value['ops'][i]['insert'].trim() != '') {
|
||||
emptyContent = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
emptyContent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (emptyContent == true) {
|
||||
alert('본문을 입력해 주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const responseJson = await _crossCtl.doComm(
|
||||
cid == undefined ? 'insert' : 'update',
|
||||
'board',
|
||||
{
|
||||
boardId: bid.value,
|
||||
hero: cid,
|
||||
title: title.value,
|
||||
content: quill.root.innerHTML,
|
||||
attachments: attachments.value,
|
||||
status: status.value,
|
||||
}
|
||||
);
|
||||
|
||||
if (responseJson['responseCode'] != 200) {
|
||||
alert(responseJson['responseMessage']);
|
||||
} else {
|
||||
// router.back();
|
||||
navigateTo('/board/' + bid.value + '/list');
|
||||
}
|
||||
}
|
||||
|
||||
// refresh();
|
||||
</script>
|
||||
216
safekiso_admin/base/pages/board/[...boardId]/list.vue
Normal file
216
safekiso_admin/base/pages/board/[...boardId]/list.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Page head goes here -->
|
||||
<div class="px-3 py-5">
|
||||
<div
|
||||
class="-ml-4 -mt-4 flex justify-between items-center flex-wrap sm:flex-nowrap"
|
||||
>
|
||||
<div class="ml-4 mt-4">
|
||||
<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="ml-4 mt-4 flex-shrink-0">
|
||||
<button
|
||||
v-for="(headingAction, index) in headingActions"
|
||||
:key="headingAction"
|
||||
type="button"
|
||||
:class="index > 0 ? 'ml-3' : ''"
|
||||
class="inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto"
|
||||
@click="doHeadingAction(headingAction)"
|
||||
>
|
||||
{{ headingAction }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w mx-auto px-3">
|
||||
<!-- Content goes here -->
|
||||
|
||||
<BaseBoardList1
|
||||
:headings="listHeadings"
|
||||
:actions="listActions"
|
||||
:data="listData"
|
||||
:action-key="actionKey"
|
||||
:column-filter="columnFilter"
|
||||
:do-action="doAction"
|
||||
/>
|
||||
|
||||
<BasePagination1
|
||||
:total-page-count="totalPageCount"
|
||||
:current-page-number="currentPageNumber"
|
||||
:page-size="pageSize"
|
||||
:records-total="recordsTotal"
|
||||
:page-move="pageMove"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
// middleware: 'check-auth-user',
|
||||
});
|
||||
|
||||
const pageTitle = ref('');
|
||||
const pageDescription = ref('');
|
||||
|
||||
// 해당 페이지 우측 상단에 표시될 액션 버튼들
|
||||
const headingActions = ['글쓰기'];
|
||||
|
||||
// 리스트 쓰는 경에만 해당. 안되는 경우 모두 지울것.
|
||||
const listSource = 'list';
|
||||
const listTarget = 'board';
|
||||
|
||||
const listActions = [];
|
||||
const actionKey = 'cid';
|
||||
|
||||
const listHeadings = [
|
||||
{
|
||||
title: '제목',
|
||||
widthRatio: '100',
|
||||
key: 'title',
|
||||
},
|
||||
|
||||
{
|
||||
title: '글쓴이',
|
||||
widthRatio: '0',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '날짜',
|
||||
widthRatio: '0',
|
||||
key: 'created',
|
||||
},
|
||||
|
||||
{
|
||||
title: '조회',
|
||||
widthRatio: '0',
|
||||
key: 'hit_count',
|
||||
},
|
||||
|
||||
{
|
||||
title: '댓글',
|
||||
widthRatio: '0',
|
||||
key: 'comment_count',
|
||||
},
|
||||
];
|
||||
|
||||
const listData = ref([]);
|
||||
|
||||
const boardMeta = ref({});
|
||||
|
||||
const totalPageCount = ref(1);
|
||||
const route = useRoute();
|
||||
const currentPageNumber = ref(route.query.page ? Number(route.query.page) : 1);
|
||||
const pageSize = ref(3);
|
||||
const recordsTotal = ref(0);
|
||||
// const order = [{ column: 'serial', dir: 'desc' }];
|
||||
// const columns = { serial: { data: 'serial' } };
|
||||
const { $dayjs } = useNuxtApp();
|
||||
function columnFilter(key, val) {
|
||||
// console.log("columnFilter(), key = ", key, ", val = ", val);
|
||||
|
||||
if (key == 'updated' || key == 'created') {
|
||||
if (boardMeta.value['ago_enabled'] == 1) {
|
||||
return $dayjs(val).fromNow();
|
||||
} else {
|
||||
return $dayjs(val).format('YYYY/MM/DD A h:mm:ss');
|
||||
}
|
||||
|
||||
// return $dayjs(val).format('YY/MM/DD');
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function doHeadingAction(tag) {
|
||||
// console.log('on doHeadingAction(), tag=', tag);
|
||||
|
||||
switch (tag) {
|
||||
case '글쓰기':
|
||||
navigateTo('/board/' + currnetBoardId.value + '/new');
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// alert('headingAction : ' + tag);
|
||||
}
|
||||
|
||||
function doAction(tag, target) {
|
||||
console.log('on doAction(), tag=', tag, ', target=', target);
|
||||
|
||||
// alert('doAction : ' + tag + ', target = ' + target);
|
||||
|
||||
switch (tag) {
|
||||
case '보기':
|
||||
navigateTo('/board/' + currnetBoardId.value + '/view/' + target);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function pageMove(targetPageIdex) {
|
||||
// console.log('on pageMove(), targetPageIdex=', targetPageIdex);
|
||||
currentPageNumber.value = targetPageIdex;
|
||||
navigateTo(
|
||||
'/board/' + currnetBoardId.value + '/list?page=' + targetPageIdex
|
||||
);
|
||||
refresh();
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
const responseJson = await _crossCtl.doComm(listSource, listTarget, {
|
||||
hero: currnetBoardId.value,
|
||||
start: (currentPageNumber.value - 1) * pageSize.value,
|
||||
length: pageSize.value,
|
||||
});
|
||||
|
||||
console.log('responseJson=', responseJson);
|
||||
if (responseJson['responseCode'] != 200) {
|
||||
if (responseJson['responseMessage'] == 'Unauthorized') {
|
||||
alert(
|
||||
'이 게시판을 볼 수 있는 권한이 없습니다. 확인을 누르면 서비스 메인 화면으로 이동합니다. '
|
||||
);
|
||||
navigateTo('/', { replace: true });
|
||||
} else {
|
||||
alert(responseJson['responseMessage']);
|
||||
}
|
||||
} else {
|
||||
boardMeta.value = responseJson['metaData'];
|
||||
pageTitle.value = responseJson['metaData']['title'];
|
||||
pageDescription.value = responseJson['metaData']['description'];
|
||||
|
||||
currentPageNumber.value = responseJson['currentPageNumber'];
|
||||
totalPageCount.value = responseJson['totalPageCount'];
|
||||
pageSize.value = parseInt(responseJson['pageSize']);
|
||||
recordsTotal.value = responseJson['recordsTotal'];
|
||||
|
||||
listData.value = responseJson['data'];
|
||||
}
|
||||
}
|
||||
|
||||
// refresh();
|
||||
|
||||
console.log('huk params = ', route.params);
|
||||
|
||||
const currnetBoardId = ref('');
|
||||
|
||||
if (route.params.boardId instanceof Array) {
|
||||
if (route.params.boardId.length != 1) {
|
||||
throwError('$404');
|
||||
} else {
|
||||
console.log('huk 3');
|
||||
currnetBoardId.value = route.params.boardId[0];
|
||||
}
|
||||
} else {
|
||||
throwError('$404');
|
||||
}
|
||||
|
||||
refresh();
|
||||
</script>
|
||||
310
safekiso_admin/base/pages/board/[...boardId]/new.vue
Normal file
310
safekiso_admin/base/pages/board/[...boardId]/new.vue
Normal file
@@ -0,0 +1,310 @@
|
||||
<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="btn btn-sm btn-primary"
|
||||
@click="doHeadingAction(headingAction)"
|
||||
>
|
||||
{{ headingAction }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-7xl mx-auto sm:px-4 lg:px-4">
|
||||
<!-- Content goes here -->
|
||||
<input
|
||||
id="title"
|
||||
v-model="title"
|
||||
type="text"
|
||||
name="title"
|
||||
placeholder="제목을 입력하세요."
|
||||
class="mb-3 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
/>
|
||||
<quill-editor
|
||||
v-model:content="content"
|
||||
class="min-h-[30rem]"
|
||||
:modules="modules"
|
||||
:toolbar="toolbarOptions"
|
||||
placeholder="본문을 입력하세요."
|
||||
@ready="onEditorReady($event)"
|
||||
/>
|
||||
|
||||
<base-attachment-ctl1
|
||||
v-if="attachmentEnabled"
|
||||
:attachments="attachments"
|
||||
:read-only-flag="false"
|
||||
:update-attachments="updateAttachments"
|
||||
:board-id="bid"
|
||||
:secure-enabled="false"
|
||||
/>
|
||||
|
||||
<div class="mt-5 flex justify-between items-center flex-wrap">
|
||||
<div class="ml-4 mt-4"></div>
|
||||
<div class="ml-4 mt-4 flex-shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
class="mr-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('취소')"
|
||||
>
|
||||
{{ '취소' }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
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="doFooterAction('저장')"
|
||||
>
|
||||
{{ '저장' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import BlotFormatter from 'quill-blot-formatter/dist/BlotFormatter';
|
||||
|
||||
definePageMeta({
|
||||
// middleware: 'check-auth-user',
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
console.log('huk params = ', route.params);
|
||||
|
||||
const bid = ref('');
|
||||
|
||||
if (route.params.boardId instanceof Array) {
|
||||
if (route.params.boardId.length != 1) {
|
||||
throwError('$404');
|
||||
} else {
|
||||
console.log('huk 3');
|
||||
bid.value = route.params.boardId[0];
|
||||
}
|
||||
} else {
|
||||
throwError('$404');
|
||||
}
|
||||
|
||||
console.log('huk bid = ', bid.value);
|
||||
|
||||
const modules = {
|
||||
name: 'blotFormatter',
|
||||
module: BlotFormatter,
|
||||
options: {},
|
||||
};
|
||||
|
||||
const responseJson = await _crossCtl.doComm('select', 'board:info', {
|
||||
hero: bid.value,
|
||||
});
|
||||
|
||||
console.log('responseJson = ', responseJson);
|
||||
|
||||
const boardInfo = ref({});
|
||||
|
||||
const attachmentEnabled = ref(false);
|
||||
|
||||
if (responseJson['responseCode'] != 200) {
|
||||
alert(responseJson['responseMessage']);
|
||||
} else {
|
||||
if (responseJson['data'].length > 0) {
|
||||
boardInfo.value = responseJson['data'][0];
|
||||
|
||||
attachmentEnabled.value =
|
||||
responseJson['data'][0]['attachment_enabled'] == 1;
|
||||
|
||||
console.log('attachmentEnabled.value=', attachmentEnabled.value);
|
||||
}
|
||||
}
|
||||
|
||||
const pageTitle = boardInfo.value['title'] + ' - ' + '새글 작성';
|
||||
const pageDescription = '새로운 글을 작성 합니다.';
|
||||
|
||||
// 해당 페이지 우측 상단에 표시될 액션 버튼들
|
||||
const headingActions = [];
|
||||
|
||||
const title = ref('');
|
||||
const content = ref({ ops: [] });
|
||||
const attachments = ref([]);
|
||||
const status = ref(0);
|
||||
|
||||
function updateAttachments(newAttachments) {
|
||||
console.log('newAttachments=', newAttachments);
|
||||
attachments.value = newAttachments;
|
||||
}
|
||||
|
||||
const toolbarOptions = [
|
||||
['bold', 'italic', 'underline', 'strike'], // toggled buttons
|
||||
['blockquote', 'code-block'],
|
||||
|
||||
// [{ header: 1 }, { header: 2 }], // custom button values
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ script: 'sub' }, { script: 'super' }], // superscript/subscript
|
||||
[{ indent: '-1' }, { indent: '+1' }], // outdent/indent
|
||||
// [{ direction: 'rtl' }], // text direction
|
||||
|
||||
// [{ size: ['small', false, 'large', 'huge'] }], // custom dropdown
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
||||
|
||||
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
|
||||
[{ font: [] }],
|
||||
[{ align: [] }],
|
||||
['link', 'video', 'image'],
|
||||
['clean'], // remove formatting button
|
||||
];
|
||||
|
||||
let quill = null;
|
||||
|
||||
function selectLocalImage() {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
input.click();
|
||||
|
||||
// Listen upload local image and save to server
|
||||
input.onchange = () => {
|
||||
const file = input.files[0];
|
||||
|
||||
// file type is only image.
|
||||
if (/^image\//.test(file.type)) {
|
||||
console.warn('upload images...');
|
||||
saveToServer(file);
|
||||
} else {
|
||||
console.warn('You could only upload images.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function selectLocalFiles() {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
input.click();
|
||||
|
||||
// Listen upload local image and save to server
|
||||
input.onchange = () => {
|
||||
console.log('we got file(s) : ', input.files);
|
||||
};
|
||||
}
|
||||
|
||||
async function saveToServer(file: File) {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('upload-file', file, file.name);
|
||||
// save it
|
||||
|
||||
formData.append('target', 'just');
|
||||
formData.append('attachedTo', bid.value);
|
||||
|
||||
console.log('formData=', formData);
|
||||
|
||||
const responseJson = await _crossCtl.doUpload('just', formData);
|
||||
|
||||
console.log('responseJson=', responseJson);
|
||||
|
||||
if (responseJson['responseCode'] == 200) {
|
||||
insertToEditor(
|
||||
// _crossCtl.config['API_BASE_URL'].replace('/api/', '') +
|
||||
responseJson['files'][0]['localUrl']
|
||||
);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
function insertToEditor(url: string) {
|
||||
// push image url to rich editor.
|
||||
|
||||
const range = quill.getSelection();
|
||||
quill.insertEmbed(range.index, 'image', url);
|
||||
}
|
||||
|
||||
function onEditorReady(e) {
|
||||
console.log('onEditorReady() e = ', e);
|
||||
|
||||
quill = e;
|
||||
|
||||
// quill editor add image handler
|
||||
|
||||
e.getModule('toolbar').addHandler('image', () => {
|
||||
selectLocalImage();
|
||||
});
|
||||
}
|
||||
|
||||
function doFooterAction(tag) {
|
||||
console.log('on doFooterAction(), tag=', tag);
|
||||
|
||||
switch (tag) {
|
||||
case '저장':
|
||||
// console.log('quill.root.innerHTML=', quill.root.innerHTML);
|
||||
// console.log('content=', content.value);
|
||||
updateContent();
|
||||
break;
|
||||
|
||||
case '취소':
|
||||
// router.back();
|
||||
navigateTo('/board/' + bid.value + '/list', { replace: true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateContent() {
|
||||
if (title.value.trim() == '') {
|
||||
alert('제목을 입력해 주세요.');
|
||||
return;
|
||||
}
|
||||
let emptyContent = true;
|
||||
|
||||
console.log('content.value = ', content.value);
|
||||
console.log('quill.root.innerHTML = ', quill.root.innerHTML);
|
||||
|
||||
for (let i = 0; i < content.value['ops'].length; i++) {
|
||||
console.log("content.value['ops'][i] = ", content.value['ops'][i]);
|
||||
|
||||
if (typeof content.value['ops'][i]['insert'] == 'string') {
|
||||
if (content.value['ops'][i]['insert'].trim() != '') {
|
||||
emptyContent = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
emptyContent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (emptyContent == true) {
|
||||
alert('본문을 입력해 주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const responseJson = await _crossCtl.doComm('insert', 'board', {
|
||||
boardId: bid.value,
|
||||
title: title.value,
|
||||
content: quill.root.innerHTML,
|
||||
attachments: attachments.value,
|
||||
status: status.value,
|
||||
});
|
||||
|
||||
console.log('responseJson=', responseJson);
|
||||
|
||||
if (responseJson['responseCode'] != 200) {
|
||||
alert(responseJson['responseMessage']);
|
||||
} else {
|
||||
// router.back();
|
||||
navigateTo('/board/' + bid.value + '/list', { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
// refresh();
|
||||
</script>
|
||||
208
safekiso_admin/base/pages/board/[...boardId]/view/[_cid].vue
Normal file
208
safekiso_admin/base/pages/board/[...boardId]/view/[_cid].vue
Normal file
@@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Page head goes here -->
|
||||
<div class="max-w mx-auto px-3">
|
||||
<!-- Content goes here -->
|
||||
<BaseBoardView1
|
||||
:name="name"
|
||||
:profile-url="profileUrl"
|
||||
:title="title"
|
||||
:content="content"
|
||||
:flags="flags"
|
||||
:attachments="attachments"
|
||||
:hit-count="hitCount"
|
||||
:like-count="likeCount"
|
||||
:dislike-count="dislikeCount"
|
||||
:comment-count="commentCount"
|
||||
:report-count="reportCount"
|
||||
:status="status"
|
||||
:updated="updated"
|
||||
:created="created"
|
||||
/>
|
||||
<BaseAttachmentCtl1
|
||||
v-if="attachmentEnabled"
|
||||
:attachments="attachments"
|
||||
:read-only-flag="true"
|
||||
/>
|
||||
|
||||
<div class="mt-5 flex justify-between items-center flex-wrap">
|
||||
<div class="ml-4 mt-4"></div>
|
||||
<div class="ml-4 mt-4 flex-shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
class="mr-3 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
|
||||
v-if="myFlag"
|
||||
type="button"
|
||||
class="mr-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('삭제')"
|
||||
>
|
||||
{{ '삭제' }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="myFlag"
|
||||
type="button"
|
||||
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="doFooterAction('수정')"
|
||||
>
|
||||
{{ '수정' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BaseCommentCtl1
|
||||
v-if="commentEnabled"
|
||||
class="mt-5"
|
||||
:tid="commentTargetId"
|
||||
:read-only-flag="!_crossCtl.isAuthenticated"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
// middleware: 'check-auth-user',
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
console.log('huk params = ', route.params);
|
||||
|
||||
const bid = ref('');
|
||||
let cid: string | string[] = '';
|
||||
|
||||
bid.value = route.params.boardId[0];
|
||||
cid = route.params['_cid'];
|
||||
|
||||
console.log('huk 7');
|
||||
|
||||
const commentTargetId = ref(cid.toString());
|
||||
|
||||
const name = ref('');
|
||||
const profileUrl = ref('');
|
||||
const title = ref('');
|
||||
const content = ref('');
|
||||
const flags = ref([]);
|
||||
const attachments = ref([]);
|
||||
|
||||
const hitCount = ref(0);
|
||||
const likeCount = ref(0);
|
||||
const dislikeCount = ref(0);
|
||||
const commentCount = ref(0);
|
||||
const reportCount = ref(0);
|
||||
const status = ref(0);
|
||||
const updated = ref('');
|
||||
const created = ref('');
|
||||
|
||||
const myFlag = ref(false);
|
||||
|
||||
const commentEnabled = ref(false);
|
||||
const attachmentEnabled = ref(false);
|
||||
|
||||
const { $dayjs } = useNuxtApp();
|
||||
|
||||
function doFooterAction(tag) {
|
||||
console.log('on doFooterAction(), tag=', tag);
|
||||
|
||||
switch (tag) {
|
||||
case '수정':
|
||||
_crossCtl.openModal(
|
||||
'confirm',
|
||||
'수정 확인',
|
||||
'정말로 수정하시겠습니까?',
|
||||
['수정', '취소'],
|
||||
(serial, btnIdx) => {
|
||||
console.log('btnIdx=', btnIdx);
|
||||
if (btnIdx == 0) {
|
||||
navigateTo('/board/' + bid.value + '/edit/' + cid);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case '삭제':
|
||||
doDelete();
|
||||
break;
|
||||
|
||||
case '닫기':
|
||||
// router.back();
|
||||
navigateTo('/board/' + bid.value + '/list', { replace: true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function doDelete() {
|
||||
_crossCtl.openModal(
|
||||
'confirm',
|
||||
'삭제 확인',
|
||||
'정말로 삭제하시겠습니까?',
|
||||
['삭제', '취소'],
|
||||
async (serial, btnIdx) => {
|
||||
console.log('btnIdx=', btnIdx);
|
||||
if (btnIdx == 0) {
|
||||
const responseJson = await _crossCtl.doComm('delete', 'board', {
|
||||
boardId: bid.value,
|
||||
hero: cid,
|
||||
});
|
||||
|
||||
if (responseJson['responseCode'] != 200) {
|
||||
alert(responseJson['responseMessage']);
|
||||
} else {
|
||||
// router.back();
|
||||
navigateTo('/board/' + bid.value + '/list', {
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
const responseJson = await _crossCtl.doComm('select', 'board', {
|
||||
boardId: bid.value,
|
||||
hero: cid,
|
||||
});
|
||||
|
||||
console.log('huk responseJson=', responseJson);
|
||||
|
||||
if (responseJson['responseCode'] != 200) {
|
||||
alert(responseJson['responseMessage']);
|
||||
} else {
|
||||
const tmpDatas = responseJson['data'];
|
||||
console.log('huk tmpDatas=', tmpDatas);
|
||||
if (tmpDatas.length == 1) {
|
||||
commentEnabled.value = responseJson['metaData']['commentEnabled'];
|
||||
attachmentEnabled.value = responseJson['metaData']['attachmentEnabled'];
|
||||
name.value = tmpDatas[0]['name'];
|
||||
profileUrl.value = tmpDatas[0]['profile_url'];
|
||||
title.value = tmpDatas[0]['title'];
|
||||
content.value = tmpDatas[0]['content'];
|
||||
flags.value = JSON.parse(tmpDatas[0]['flags']);
|
||||
attachments.value = JSON.parse(tmpDatas[0]['attachments']);
|
||||
hitCount.value = tmpDatas[0]['hit_count'];
|
||||
likeCount.value = tmpDatas[0]['like_count'];
|
||||
dislikeCount.value = tmpDatas[0]['dislike_count'];
|
||||
commentCount.value = tmpDatas[0]['comment_count'];
|
||||
reportCount.value = tmpDatas[0]['report_count'];
|
||||
status.value = tmpDatas[0]['status'];
|
||||
updated.value = $dayjs(tmpDatas[0]['updated']).format(
|
||||
'YY/MM/DD A h:mm:ss'
|
||||
);
|
||||
created.value = $dayjs(tmpDatas[0]['created']).format(
|
||||
'YY/MM/DD A h:mm:ss'
|
||||
);
|
||||
|
||||
myFlag.value = tmpDatas[0]['myFlag'];
|
||||
|
||||
// $dayjs(val).format('YY/MM/DD A h:mm:ss')
|
||||
} else {
|
||||
console.log('bad count. count = ' + tmpDatas.length);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user